重映射
摘要:重映射是ARM嵌人式软件开发中一个非常重要的概念,它是 系统初始化过程中一个重要的环节。本文详细论述了什么是重映射, 为什么要进行重映射,以及怎样实现重映射,并以LPC2210处理器 为例给出了从片外Flash启动和重映射的实现方法。同时对在ARM 嵌入式软件开发过程中经常遇到一些基本概念,比如存储器映射、分 散加载文件等进行了详细的解释。实验证明在采用存储器重映射后可 以显著提高系统的运行效率,同时此方法还具有不增加代码量的优点。 关t词:启动代码;重映射;分散加载文件;ARM? 0 引言 引言引言 引言 多数嵌入式系统中,在进入主程序main之前,需要执行初始化序列 对应用程序的运行环境进行配置。启动代码一般用汇编语言来编写, 它主要对关键设备进行初始化和配置,为应用程序的运行创造条件。 在整个启动过程中,重映射是其中重要一环,正确地理解和实现重映 射有助于深入理解系统的工作原理。 本文使用的处理器为PhilipsLPC2210,由于它的片内只集成了一块 位于0x40000000~0x40004000,大小为16K的SRAM,因此扩展 了一块位于0x80000000~0x801FFFFF,大小为2M字节的Flash。 在实验中,程序将首先从外部Flash启动,然后将异常向量表从Flash 存储器复制到片内SRAM中,并进行存储器重映射将异常向量表映 射到片内SRAM。虽然本文的内容都是以LPC2210处理器为例来进 行说明的,但对其他ARM系列的处理器来说也是适用的。? l 复位和初始化 复位和初始化复位和初始化 复位和初始化 ARM处理器在复位后处于SVC模式,中断是禁止的,并且处于ARM 状态。必须设置好各个异常模式堆栈的位置和大小,应用程序运行时 所在的处理器模式和状态,并为它分配合适的堆栈和空间,以及使能 中断和启用缓存(如果有的话)的时机。一般来说,总是需要按照一个 合理的顺序来初始化系统,图1从总体上描述了一个适用于ARM嵌 入式系统的可能的初始化序列。 ? ? 可以看出,整个初始化序列可以分为2大的部分:用户代码和C库。 C库初始化代码是由集成开发环境(这里使用的是ADS1.2)自动生成 的,在完成任务之后,它最终将控制权转交给用户代码。 __scatterload的主要作用是复制代码和数据,它会根据应用程序的 设置(比如,可以通过分散加载文件精确地指定各部分代码或者数据 在加载时和运行时的位置)自动生成代码将程序装载到用户希望的地 址。 rt_entry部分的作用是执行运行时库的初始化工作。 需要编码的是用户代码部分: 首先设置各个模式的堆栈指针(由于LPC2210中没有MMU/MPU、 缓存和TCM,因此这里只需要设置各个模式的堆栈指针即可,程序 如list1所示); //list 1:设置各种模式的堆栈指针值 Reset_Handler ?IMPORT stack_base? //stack_base的值在分散加载文件中设置 ?LDR r0,=stackbase ?MSR CPSR_c,#Mode_FIQ|I_Bit|F_Bit ?SUB sp,r0,? #offset_FIQ_Stack ?MSR CPSR_c,#Mode_IRQ|I_Bit|F_Bit ?SUB sp,? #offset_IRQ_Stack //?设置其它模式的堆栈指针 其次,如果使用了分散加载描述文件(由于实际系统中存储器类型的 多样性),那么必须实现函数user_initial_stackheap();最后是启用中 断。 设置完各个模式的堆栈指针之后,要对关键的I/O设备进行初始化, 对处理器时钟、存储器加速模块,以及外部存储器模块进行设置。然 后跳转到C库的main,首先由C库代码按照分散加载文件的描述将 代码和数据从加载地址拷贝到运行地址并对未初始化数据进行清零, 然后跳转到rt_entry对C运行时库进行初始化,最后将控制权转交给 用户代码(即主程序main)。 ? 2 存储器重映射 存储器重映射存储器重映射 存储器重映射 2.1存储器映射与分散加载文件 存储器映射与分散加载文件存储器映射与分散加载文件 存储器映射与分散加载文件 嵌入式系统中经常同时存在多种不同类型的存储介质,从访问速度比 较快的SRAM、DRAM到比较慢的Flash ROM、EEPROM等。 嵌入式系统通常对实时性的要求很高,因此希望合理地分配存储空间。 在ADS1.2中,是通过编写分散加载文件进行描述的。分散加载文件 描述了源文件编译后生成的目标文件在存储器中的位置,它同时指定 了目标文件的加载地址和执行地址。每个目标文件都有一个唯一的加 载地址和一个唯一的运行地址。如果一个目标文件的加载地址和执行 地址不同,那么C库的scatterload部分负责将它从加载地址处复制 到执行地址处。 2.2为什么引入重映射机制 为什么引入重映射机制为什么引入重映射机制 为什么引入重映射机制 重映射与处理器的异常处理机制密切相关。 当异常产生时,CPU会从预先设定好的内存地址中取出异常处理程 序的入口地址,并跳转到异常处理程序。不同型号的处理器的异常处 理机制一般也会不同,ARM系列处理器将所有异常处理程序的地址 都保存在一个连续的存储器空间中,位于这个存储器空间上的异常入 口的集合就称为“异常向量表”。由于普通的跳转指令B label的跳转 范围只有32M,为了实现4G范围内的任意跳转,可以采用指令LDR PC,target_addr,它将target_addr的值看作是一个地址值装载到 PC中,于是跳转到那个地址处继续执行。 这样异常向量表通常包括32字节的相对PC的跳转指令和32字节的 异常处理程序地址。 为了在系统掉电后能够重新建立异常向量表,必须将其存储在非易失 性存储器中,但是非易失性存储器的访问速度非常慢,使得处理器只 能插入多个空闲周期来等待它,特别是在一些需要频繁调用异常处理 程序的场合,这种负面影响很容易成为系统的瓶颈,于是引入了重映 射机制来提高系统对异常的实时响应能力。 重映射一般是在系统初始化过程中完成的。由于处理器对片内RAM 的访问速度最快,因此片内RAM成为存放异常向量表的首选位置。 整个重映射的过程非常简单,首先将异常向量从非易失性存储器中拷 贝到片内RAM中,然后执行重映射命令,将位于片内RAM中的异常向量块映射到异常向量地址空间中。此后,系统若产生异常,处理 器将从已映射到异常向量地址空间的片内RAM中读取异常向量。 2.3 LPC2210处理器的重映射机制 处理器的重映射机制处理器的重映射机制 处理器的重映射机制 在LPC2210中,重映射是通过寄存器MEMMAP来控制的,其用法 如表1所示。 ? 在LPC2000中,存储器重映射的部分包括异常向量区(32Byte)和额 外的32Byte,一共是64Byte,重新映射的代码位置与地址 0x0~0x0000003F重叠。也就是说,当处于用户RAM模式时,如果 访问0x0~0x3F的数据,实际上是在对0x40000000~0x4000003F进 行访问。同样如果切换到外部存储器模式,并且同样对0x0~0x3F进 行访问,就变成访问0x80000000~0x8000003F中的数据/指令了。 ?2.4 存储器重映射的实现 存储器重映射的实现存储器重映射的实现 存储器重映射的实现 为了实现对系统启动过程的精确控制,最好把与异常向量和启动代码相关的程序分别放在vectors.s和init.s这2个文件中,这样可以使用 分散加载机制精确的控制代码在内存中的布局。为了在系统复位或掉 电重启后,程序能够正常运行,必须将异常向量表加载到非易失性存 储器Flash中,而且必须位于从0开始的地址处。由于LPC2210处 理器没有片内Flash存储器,必须正确的配置引脚13和16使系统 从外部存储器启动。启动代码如list2所示,系统启动后首先从标号 Instruct_2开始执行,然后修改MEMMAP寄存器的内容(其中 CM_ctl_reg等于MEMMAP寄存器的地址),于是异常向量表从片内 RAM重新映射,最后将异常向量表从片外Flash拷贝到片内Flash。 此后在发生异常后,处理器就自动从片内RAM取出异常处理程序的 地址,从而能够显著提高程序的性能。 //list2:部分启动代码 CM_ctl_reg EQU 0xE01FCO4O RAM??????? EQU 0x2 Reset_Handler ?LDR pc,=Instruct_2 Instruct_2 ?LDR rl,=CM_ctl_reg ?LDR r0,[r1] ?AND r0,r0,#RemapMask ?0RR r0,r0,#RAM? STR rO,[r1] ?MOV r9,#0x80000000 ?MOV r10,#0x40000000 ?LDMIA r9!,{r0-r7} ?STMIA r10!,{r0-r7} ?LDMIA r9!,{r0-r7} 在上面的复制完成之后,异常向量表就会同时出现在地址0x0、 0x40000000以及0x80000000。注意执行区IRAM的基址不是 0x40000000,而是0x40000040,因为起始的64Byte是为异常向量表 预留的。整个过程如果图2所示。 ? 总结: (略) 备注:这篇文章其实讲的是异常向量表的重映射。。。 ================================================== ================================================== == 地址映射 把芯片里和芯片外的FLASH、RAM、BootBlock和外设进行统一编 址。一般来说,芯片厂商都把这些地址分好了,搞清楚在哪里,使用 就行了。外扩FLASH和RAM的时候,需要进行地址映射。使用ADS 编译器的时候,有一个scf文件,用来描述地址映射的。而比较新的 Keil MDK 采用的可视化的方法。 ARM7TDMI的地址映射位置:ARM7TDMI采用32位寻址,4G的地 址空间。一般而言。在0x0000000放FLASH、0x40000000放RAM、 0x80000000以上是外部存储器、0xE0000000向上是APB(低速外 设,如GPIO、UART等)、0xFFFFFFFF向下是APB(高速设备, 如USB,向量中断控制器)。一个比较怪异的地址是BootBlock的, 单独来说。 BootBlock: ARM芯片厂商提供的芯片引导程序,判断用户代码是否有效、芯片 是否加密,芯片是否工作在ISP状态、芯片是否工作在IAP状态。 这段代码在芯片启动的时候用,生产芯片的时候,厂商已经固化进去了,用户不用管。比较难搞的事情是这段芯片的地址,这段代码一般 是放在FLASH中的,放在地址0x00000000似乎最合理,但是这个 地址,中断向量表已经用了,于是干脆放到FLASH的尾部了。另一 个问题出来了,不同的芯片,内部的FLASH不一样大,这段代码的 地址该怎么定哪?办法就是重映射。 BootBlock的重映射: BootBlock的实际位置在片内FLASH的尾部,但是编地址的时候, 重新映射到地址0x80000000向下的地方。这样以来,编程的时候采 用的地址是固定在接近0x80000000地方的,而实际的位置是在 FLASH的尾部。地址重映射:通过芯片的存储器管理部件,把不同 的程序可见地址映射到同一个物理位置。还有一种更加实用的地址重 映射叫异常向量表重映射。 异常向量表重映射: 异常向量表在0x00000000的位置,BootBlock启动完之后,就到这 里开始程序的运行,分别对应ARM的7种异常状态进行处理。? 存储器映射: 为存储器分配地址的过程称为存储器映射,那么什么叫存储器重映射 呢?为了增加系统的灵活性,系统中有部分地址可以同时出现在不同 的地址上,这就叫做存储器重映射。重映射主要包括引导块 “Boot? Block”重映射和异常向量表的重映射。 1.引导块 引导块引导块 引导块“Boot? Block”及其重映射 及其重映射及其重映射 及其重映射??????? Boot? Block是芯片设计厂商在LPC2000系列ARM内部固化的 一段代码,用户无法对其进行修改或者删除。这段代码在复位时被首 先运行,主要用来判断运行哪个存储器上面的程序,检查用户代码是 否有效,判断芯片是否被加密,系统的在应用编程(IAP)以及在系 统编程功能(ISP)等。 ?????? Boot Block存在于内部Flash,LPC2200系列大小为8kb,它 占用了用户的Flash空间,但也有其他的LPC系列不占用FLash空 间的,而部分没有内部Flash空间的ARM处理器仍然存在 Boot? Block。 ?????? 重映射的原因: ?????? Boot? Block中有些程序可被用户调用,如擦写片内Flash的IAP 代码。为了增加用户代码的可移植性,所以最好把Boot? Block的代 码固定的某个地址上。但由于各芯片的片内Flash大小不尽相同,如 果把Boot? Block的地址安排在内部Flash结束的位置上,那就无法 固定Boot? Block的地址。 ?????? 为了解决上面的问题,于是芯片厂家将Boot? Block的地址重映 射到片内存储器空间的最高端,即接近2Gb的地方,这样无论片内 存储器的大小如何,都不会影响Boot? Block的地址。因此当 Boot? Block中包含可被用户调用的IAP操作的代码时,不用修改IAP 的操作地址就可以在不同的LPC系列的ARM上运行了。 2.异常向量表及其重映射 ????? ARM内核在发生异常后,会使程序跳转到位于0x0000~0x001C 的异常向量表处,再经过向量跳转到异常服务程序。但ARM单条指 令的寻址范围有限,无法用一条指令实现4G范围的跳转,所以应在 其后面的0x0020~0x003F地址上放置跳转目标,这样就可以实现4G 范围内的任意跳转,因此一个异常向量表实际上占用了16个字的存 储单元。以下为一张中断向量表: ??????????????????????????????????? LDR???? PC,ResetAddr ??????????????????????????????????? LDR???? PC,UndefinedAddr?? ??????????????????????????????????? LDR???? PC,SWI_Addr ??????????????????????????????????? LDR???? PC,PrefetchAddr ??????????????????????????????????? LDR???? PC,DataAbortAddr ??????????????????????????????????? DCD???? 0xb9205f80 ??????????????????????????????????? LDR???? PC,[PC,#-0xff0] ??????????????????????????????????? LDR???? PC,FIQ_Addr ??????????????????? ResetAddr??????????? DCD???? ResetInit ??????????????????? UndefinedAddr????? DCD???? Undefined???????????????????? SWI_Addr??????????? DCD???? SoftwareInterrupt ??????????????????? PrefetchAddr??????? DCD???? PrefetchAbort ??????????????????? DataAbortAddr???? DCD???? DataAbort ??????????????????? Nouse????????????????? DCD???? 0 ??????????????????? IRQ_Addr???????????? DCD???? 0 ??????????????????? FIQ_Addr???????????? DCD???? FIQ_Handler ????? 重映射的原因: ????? 由于ARM处理器的存储器结构比较复杂,可能同时存在片内存 储器和片外存储器等,他们在存储器映射上的起始地址都不一样,因 此ARM内核要访问的中断向量表可能不在0x0000~0x003F地址上, 因此采用了存储器重映射来实现将存在与不同地方的中断向量表都 映射到0x0000~0x003F地址上。 ????? 注意:Boot? Block 也存在中断向量表,而且复位后这段代码首 先映射到 0x0000~0x003F地址上,也就是说复位后首先运行的是 Boot Block程序。各个存储区域的中断向量表也不尽相同。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |