张毒俊 牟龙华 摘要:重映射是ARM嵌人式软件开发中一个非常重要的概念,它是系统初始化过程中一个重要的环节。本文详细论述了什么是重映射,为什么要进行重映射,以及怎样实现重映射,并以LPC2210处理器为例给出了从片外Flash启动和重映射的实现方法。同时对在ARM嵌入式软件开发过程中经常遇到一些基本概念,比如存储器映射、分散加载文件等进行了详细的解释。实验证明在采用存储器重映射后可以显著提高系统的运行效率,同时此方法还具有不增加代码量的优点。 关t词:启动代码;重映射;分散加载文件;ARM
0 引言 本文使用的处理器为PhilipsLPC2210,由于它的片内只集成了一块位于0x40000000~0x40004000,大小为16K的SRAM,因此扩展了一块位于0x80000000~0x801FFFFF,大小为2M字节的Flash。在实验中,程序将首先从外部Flash启动,然后将异常向量表从Flash存储器复制到片内SRAM中,并进行存储器重映射将异常向量表映射到片内SRAM。虽然本文的内容都是以LPC2210处理器为例来进行说明的,但对其他ARM系列的处理器来说也是适用的。
__scatterload的主要作用是复制代码和数据,它会根据应用程序的设置(比如,可以通过分散加载文件精确地指定各部分代码或者数据在加载时和运行时的位置)自动生成代码将程序装载到用户希望的地址。 rt_entry部分的作用是执行运行时库的初始化工作。 需要编码的是用户代码部分: //list 1:设置各种模式的堆栈指针值 LDR r0,=stackbase //?设置其它模式的堆栈指针 其次,如果使用了分散加载描述文件(由于实际系统中存储器类型的多样性),那么必须实现函数user_initial_stackheap();最后是启用中断。 设置完各个模式的堆栈指针之后,要对关键的I/O设备进行初始化,对处理器时钟、存储器加速模块,以及外部存储器模块进行设置。然后跳转到C库的main,首先由C库代码按照分散加载文件的描述将代码和数据从加载地址拷贝到运行地址并对未初始化数据进行清零,然后跳转到rt_entry对C运行时库进行初始化,最后将控制权转交给用户代码(即主程序main)。
2 存储器重映射 2.2为什么引入重映射机制 为了在系统掉电后能够重新建立异常向量表,必须将其存储在非易失性存储器中,但是非易失性存储器的访问速度非常慢,使得处理器只能插入多个空闲周期来等待它,特别是在一些需要频繁调用异常处理程序的场合,这种负面影响很容易成为系统的瓶颈,于是引入了重映射机制来提高系统对异常的实时响应能力。 重映射一般是在系统初始化过程中完成的。由于处理器对片内RAM的访问速度最快,因此片内RAM成为存放异常向量表的首选位置。整个重映射的过程非常简单,首先将异常向量从非易失性存储器中拷贝到片内RAM中,然后执行重映射命令,将位于片内RAM中的异常向量块映射到异常向量地址空间中。此后,系统若产生异常,处理器将从已映射到异常向量地址空间的片内RAM中读取异常向量。 2.3 LPC2210处理器的重映射机制 在LPC2000中,存储器重映射的部分包括异常向量区(32Byte)和额外的32Byte,一共是64Byte,重新映射的代码位置与地址0x0~0x0000003F重叠。也就是说,当处于用户RAM模式时,如果访问0x0~0x3F的数据,实际上是在对0x40000000~0x4000003F进行访问。同样如果切换到外部存储器模式,并且同样对0x0~0x3F进行访问,就变成访问0x80000000~0x8000003F中的数据/指令了。 2.4 存储器重映射的实现 //list2:部分启动代码 CM_ctl_reg EQU 0xE01FCO4O Reset_Handler Instruct_2 在上面的复制完成之后,异常向量表就会同时出现在地址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程序。各个存储区域的中断向量表也不尽相同。 |
联系客服