ARM关于地址重映射_第1页
ARM关于地址重映射_第2页
ARM关于地址重映射_第3页
ARM关于地址重映射_第4页
ARM关于地址重映射_第5页
已阅读5页,还剩73页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、关于地址重映射1、通俗解释ARM芯片的地址重映射 映射就是一一对应的意思。重映射就是重新分配这种一一对应的关 系。 我们可以把存储器看成一个具有输出和输入口的黑盒子。如下图所示,输入量是地址,输出的是对应地址上存储的数据。当然这个黑盒子是由很复杂的半导体 电路具现的,具体的实现的方式我们现在不管。存储单位一般是字节。这样,每个字节的存储单元对应一个地址,当一个合法地址从存储器的地址总线输入后,该地 址对应的存储单元上存储的数据就会出现在数据总线上面。 图1 普通的单片机把可执行代码和数据存放到存储器中。单片机中的CPU从储器中取指令代码和数据。其中存储器

2、中每个物理存储单元与其地址是一一对应而且是不可变的。如下图,CPU读取0x00000000地址上存储单元的过程。                    图 2 ARM比较复杂。ARM芯片与普通单片机在存储器地址方面的不同在于:ARM芯片中有些物理 存储单元的地址可以根据设置变换。就是说一个物理存储单元现在对应一个地址,经过设置以后,这个存储单元就对应了另外一个地址了。图3是随意举了个例子

3、 (不要与ARM芯片对应),旨在说明地址重映射的过程。图3表示把 0x00000000地址上的存储单元映射到新的地址0x00000007上。CPU存取0x00000007就是存取0x00000000上的物理存储 单元。                   图 3 下面图4,图5是对ARM芯片的两种地址重映射方式的图示。 假设我们的应用程序存放在外扩 FLASH当中,那么应用程序的异常向量

4、表就存放在0x80000000起始的64个(其中有32个存放异常向量)物理存储单元中。但是ARM核发生异常 (中断)后是从 0x000000000x0000003F地址范围取异常向量的。所以要把0x800000000x8000003F范围内的存储单元重新映射到 0x000000000x0000003F地址范围上。以后CPU存取0x000000000x0000003F地址就是存取 0x800000000x8000003F范围内的存储单元。图4只显示出第一个异常向量的地址重映射,整个异常向量表的地址重映射等同这个过程。  图4 图5 图 5描述示了ARM芯片

5、的另外一种映射方式。这个映射可以由用户决定采用还是不采用(相关代码在工程文件的startup.s中,这个文件是第三方提供,用 户可以修改)。这个映射主要是为了提高应用程序异常相应得速度。当我们把应用程序存放在片内FLASH的时候,异常向量表存放在 0x000000000x0000003F存储单元内。每次发生异常,CPU从0x000000000x0000003F地址上取异常向量。但是对 RAM的存取速度远高于对FLASH的存取速度,所以为了提高异常相应速度我们采取以下做法: Step 1:先把 0x000000000x0000003F(FLASH)存储单元内的异常向量表复制&

6、#160;到0x400000000x4000003F(片内RAM的最低端64个字节的存储单元)范围内存储单元中。  Step 2:把0x400000000x4000003F范围内存储单元地址重新映射 到0x000000000x0000003F地址范围。 这样做了以后,当异常发生的时候,CPU取异常向量就是从RAM区中的异常向量表中区,速度快 了。比如复位中断发生,CPU从地址0x00000000取指令,但此时由于已经过地址重新映射,这个0x00000000被地址转换器转换成 0x40000000,CPU实际上是取的RAM区中0x400000000这个存储

7、单元内的指令(异常向量)。 当然用户可以不进行这种映射。片内 FLASH中0x000000000x0000003F存储单元具有一模一样的异常向量表。只不过不进行这种处理,异常相应速度慢一点。但是这种速度上 的差别很多情况下是不必要在意的。     图中的地址转换器受控制寄存器MENMAP的控制,用户可以设置MENMAP实现对地址重映射的控制。这个地址转换器显然是通过内部硬件电路实现的。  2、存储器的重映射为存储器分配地址的过程称为存储器映射,那么什么叫存储器重映射呢?为了增加系统的灵活性,系统中有部分地址可以同时出现在不同

8、的地址上,这就叫做存储器重映射。重映射主要包括引导块“Boot  Block”重映射和异常向量表的重映射。 2.1  引导块“Boot  Block”及其重映射       Boot  Block是芯片设计厂商在LPC2000系列ARM内部固化的一段代码,用户无法对其进行修改或者删除。这段代码在复位时被首先运行,主要用来判断运行 哪个存储器上面的程序,检查用户代码是否有效,判断芯片是否被加密,系统的在应用编程(IAP)以及在系统编程功能(ISP)等。   

9、    Boot Block存在于内部Flash,LPC2200系列大小为8kb,它占用了用户的Flash空间,但也有其他的LPC系列不占用FLash空间的,而部分没有内部Flash空间的ARM处理器仍然存在Boot  Block。       重映射的原因:Boot  Block中有些程序可被用户调用,如擦写片内Flash的IAP代码。为了增加用户代码的可移植性,所以最好把Boot  Block的代码固定的某个地址上。但由于各芯片的片内Flash大小不尽相同,如果把Boot

10、  Block的地址安排在内部Flash结束的位置上,那就无法固定Boot  Block的地址。  为了解决上面的问题,于是芯片厂家将Boot  Block的地址重映射到片内存储器空间的最高端,即接近2Gb的地方,这样无论片内存储器的大小如何,都不会影响Boot  Block的地址。因此当Boot  Block中包含可被用户调用的IAP操作的代码时,不用修改IAP的操作地址就可以在不同的LPC系列的ARM上运行了。2.2  异常向量表及其重映射      ARM内核在发生异常

11、后,会使程序跳转到位于0x00000x001C的异常向量表处,再经过向量跳转到异常服务程序。但ARM单条指令的寻址范围有 限,无法用一条指令实现4G范围的跳转,所以应在其后面的0x00200x003F地址上放置跳转目标,这样就可以实现4G范围内的任意跳转,因此一个 异常向量表实际上占用了16个字的存储单元。以下为一张中断向量表:      LDR     PC, ResetAddr      LDR   &

12、#160; PC, UndefinedAddr        LDR     PC, SWI_Addr      LDR     PC, PrefetchAddr      LDR     PC, DataAbortAddr     

13、60;DCD     0xb9205f80      LDR     PC, PC, #-0xff0      LDR     PC, FIQ_Addr                 &#

14、160;  ResetAddr            DCD     ResetInit                    UndefinedAddr      DCD 

15、60;   Undefined                    SWI_Addr            DCD     SoftwareInterrupt      

16、0;             PrefetchAddr        DCD     PrefetchAbort                    DataAbort

17、Addr     DCD     DataAbort                    Nouse                  D

18、CD     0                    IRQ_Addr             DCD     0       

19、             FIQ_Addr             DCD     FIQ_Handler      重映射的原因: 由于ARM处理器的存储器结构比较复杂,可能同时存在片内存储 器和片外存储器等,他们在存储器映射上的起始地址都不一样,

20、因此ARM内核要访问的中断向量表可能不在0x00000x003F地址上,因此采用了存储 器重映射来实现将存在与不同地方的中断向量表都映射到0x00000x003F地址上。      注意:Boot  Block 也存在中断向量表,而且复位后这段代码首先映射到 0x00000x003F地址上,也就是说复位后首先运行的是Boot Block程序。各个存储区域的中断向量表也不尽相同。      从上面可以看出中断向量表可以来自4个不同的存储区域。通过下图的激活方

21、式,可以在0x00000x003F地址处访问到从其他存储区域重映射过来的中断向量表。      注意:除了“用户片内FLASH模式”外,其他模式下都无法访问片内FLASH的0x00000x003F区域。2.3  系统启动代码      从系统上电到正式运行用户的main函数之前,要运行一段代码,这段代码称为“启动代码”。      启动代码大部分是由汇编指令构成,它可以实现向量表定义,堆栈的初始化,系统变量初始化,中断系统初始

22、化,I/O初始化,外围初始化,地址重映射等操作。      启动代码与ARM的Boot Block不同,Boot Block是芯片厂家固化在芯片中的,不能对其进行修改,而启动代码是用户添加的。系统上电后首先运行的是Boot Block,然后才运行启动代码。Remap地址重映射<转>0.什么是Remap我的理解是:在ROM从0x0用几句指令引导系统之后,把RAM 映射到0x0 就是Remap。1.Remap 的作用当ARM 处理器上电或者Reset 之后,处理器从0x0 取指。因此,必须保证系统上电时,0x0 处有指令可以执行。所以,

23、上电的时候,0x0地址处必定是ROM 或者Flash(NOR)。但是,为了加快启动的速度,也方便可以更改异常向量表,加快中断响应速度,往往把异常向量表映射到更快、更宽(32bit/16bit)的RAM 中。但是异常向量表的开始地址是由ARM 架构决定的,必须位于0x0处,因此,必须把RAM映射到0x0。2.Remap 的配置Remap的实现和ARM 处理器的实现相关。1)如果处理器有专门的寄存器可以完成Remap。那么Remap 是通过Remap 寄存器的相应bit置1完成的。如Atmel AT91xx2)如果处理器没有专门的寄存器,但是memory的bank控制寄存器可以用来配置bank 的

24、起始地址,那么只要把RAM的起始地址编程为0x0,也可以完成remap。如samsung s3c45103)如果上面两种机制都没有,那么Remap 就不要做了。因为处理器实现决定了SDRAM 对应的bank地址是不能改变的。如Samsung S3c2410.3.Remap 配置前后要做的工作Remap 前后,不同之处就是RAM 的位置变了。为了达到Remap 的目的,就是加快启动的速度和异常处理速度,一定要初始化异常堆栈和建立异常向量表的。4.如果象2410那样不能Remap 的话怎么办?2410 不是不能Remap 吗?为了加快启动速度,可以这样做1)使用它的NAND boot 模式。为什么

25、NAND boot 会比较快,那是因为2410 里面有块小石头“SteppingStone”,一块4KB SRAM,它是映射在0x0 的。启动程序会自动被copy 到这个石头里面。自然异常向量的入口放到这个地方,一样可以达到比NOR boot快的启动、异常响应速度。2)如果你对NOR Boot情有独衷,那么你只好把你的异常向量的入口copy 到SDRAM 里面,实现所谓的High Vector存储器地址重映射是当前很多先进控制器所具有的功能。在上一节中已经提到了0 地址处存储器重映射的例子,简而言之,地址重映射就是可以通过软件配置来改变一块存储器物理地址的一种机制或方法。当一段程序对运行自己的

26、存储器进行重映射的时候,需要特别注意保证程序执行流程在重映射前后的承接关系。下面是一种典型的存储器地址重映射情况:500)this.width=500;"> 系统上电后的缺省状态是0 地址上放有ROM,这块ROM 有两个地址:从0起始和从0x10000 起始,里面存储了初始化代码。当进行地址remap 以后,从0起始的地址被定向到了RAM 上,ROM 则只保留有唯一的从0x10000 起始的地址了。如果存储在ROM 里的Reset_Handler 一直在0 0x4000 的地址上运行,则当执行完remap 以后,下面的指令将从RAM 里预取,必然会导致程序执行流程的中

27、断。根据系统特点,可以用下面的办法来解决这个问题:(1) 上电后系统从0 地址开始自动执行,设计跳转指令在remap 发生前使PC指针指向0x10000 开始的ROM 地址中去,因为不同地址指向的是同一块ROM,所以程序能够顺利执行。(2) 这时候0 - 0x4000 的地址空间空闲,不被程序引用,执行remap 后把RAM引进。因为程序一直在0x10000 起始的ROM 空间里运行,remap 对运行流程没有任何影响。(3) 通过在ROM 里运行的程序,对RAM 进行相应的代码和数据拷贝,完成应用程序运行的初始化。下面是一段实现上述步骤的例程:ENTRY;启动时,从0 开始,设法跳转到“真”

28、的ROM 地址(0x10000 开始的空间里)LDR pc, =start;insert vector table hereStart ;Begin of Reset_Handler; 进行remap 设置LDR r1, =Ctrl_reg ;假定控制remap 的寄存器LDR r0, r1ORR r0, r0, #Remap_bit ;假定对控制寄存器进行remap 设置STR r0, r1;接下去可以进行从ROM 到RAM 的代码和数据拷贝。除此之外,还有另外一种常见的remap 方式,如下图500)this.width=500;"> 原来RAM 和ROM 各有自

29、己的地址,进行重映射以后RAM 和ROM 的地址都发生了变化,这种情况下,可以采用以下的方案:(1) 上电后,从0 地址的ROM 开始往下执行。(2) 根据映射前的地址,对RAM 进行必要的代码和数据拷贝。(3) 拷贝完成后,进行remap 操作。(4) 因为RAM 在remap 前准备好了内容,使得PC 指针能继续在RAM 里取到正确的指令。不同的系统可能会有多种灵活的remap 方案,根据上面提到的两个例子,可以总结出最根本的考虑是:要使程序指针在remap 以后能继续往下得到正确的指令。实例分析:基于S3C4510B系统的启动流程及REMAP1 S3C4510B 简介S3C4510B,基

30、于以太网,16/32 位RISC 微处理器。芯片部集成了8KB 的Cache/SRAM 和Ethernet控制器,片外可扩展ROM、Flash、SDRAM 等存储芯片。S3C4510B 芯片内部没有程序存储器,所有程序都被存储在片外扩展的ROM 和Flash 中。开始启动时,存有启动代码的ROM 或Flash 将被映射为0x00 地址,系统从此开始运行。但在实际应用中,为提高系统的实时性,加快代码的执行速度,系统启动后程序往往要被搬移到RAM 中,因为RAM 的存取速度要比ROM 快得多,这样大大提升系统的性能。由于S3C4510B 芯片中的异常中断入口地址被固定在0x00开始的8 个字中,系

31、统只能将地址空间重新分配,把RAM 映射到0x00 地址处,这正是Remap 的原因所在。S3C4510B 内部有几个特殊寄存器,用于实现地址空间和芯片内外存储介质的映射。这几个寄存器的简介如下:SYSCFG:设置特殊寄存器的起始地址和片内SRAM 的起始地址。EXTDBWTH:设置各Bank寄存器所映射芯片的数据线宽度。ROMCON0ROMCON5:设置系统内片扩展ROM和Flash的起始和终止地址。DRAMCON0DRAMCON3:设置系统内片外扩展RAM 的起始和终止地址。S3C4510B 芯片内特殊寄存器段的物理地址为0x3ff0000,各特殊寄存器的偏移地址详见S3C4510B的技术

32、手册。2S3C4510B 系统中Remap 的实现地址空间的重新分配,与处理器的硬件结构紧密相关。总体来说,32位系统中的地址重映射机制可以分为两种情况:一类是处理器内部专门的寄存器可以完成Remap,这样只需将Remap 寄存器的相应位置1,由硬件逻辑来完成地址的重新映射,如AtmelAT91xx 系列;另一类没有专门的Remap 控制寄存器,需要重新改写处理器内部用于控制Memory 起止地址的Bank 寄存器,来实现Remap 过程。S3C4510B属于第二种情况。2.1 硬件系统结构及地址分配假设系统是以Samsung 公司给出的测试板为参考建立的,其中ROM 的容量为512KB,8

33、位数据总线,Remap 前的地址范围为0x00000000x0100000,Remap 后的地址范围为0x10000000x1100000;RAM 的容量为16MB,32 位数据总线,Remap 前的地址范围为0x01000000x100000,Remap 后的地址范围为0x00000000x1000000;Flash的容量为2MB,16 位数据总线,Remap 前后地址不变,均为0x11000000x1300000。Remap 前后的地址映射关系如图2所示。2.2 系统启动过程及Remap实现系统的地址重映射应该在系统的启动中完成,以下是S3C4510B 的Remap 启动过程。系统特殊寄存

34、器的设置。主要是配置如上所述的用于实现地址空间和芯片内外存储介质映射的寄存器,在本系统中配置如下:SYSCFG=0x87ffff90EXTDBWTH=0x3001ROMCON0=0x01000060ROMCON1=0x13044060DRAMCON0=0x11004060初始化系统堆栈。在ARM7 的体系结构中共有七种工作模式,不同的模式有不同堆栈指针,互不干扰。各模式对应于不同异常中断,至于哪些模式的堆栈需要初始化取决于用户使用了哪些中断,以及系统需要处理些异常类型。一般来说,管理者(SVC)堆栈必须设置,如果使用了IRQ 中断,则IRQ 堆栈也必须设置。有一点需要注意的是,为保证Remap

35、 后程序运行正常,所有堆栈应设置在RAM 的高端地址中。初始化I/O 口、UART、定时器、中断控制器以及系统中所用到的其它资源。在初始化异常向量表或修改异常向量表中的入口地址前,要关掉所有中断。异常向量表的初始化。将民常中煌怛处理程序的入口地址写入RAM中相应的异常向量。必须保证的是,异常向量表绝对不会被从ROM 搬移到RAM 中的代码和数据所覆盖,为此,异常向量表一般被定义在RAM中的高端地址中。程序代码及数据的搬移。Remap 后,RAM 被映射到0x0000的地址空间,ROM 则被移到高端地址上。为保证Remap 后程序能够瞠运行,ROM 中的代码和数据必须地址不变地被移到RAM 中。

36、这是Remap成功的关键。两种途径可以实现搬移。一种是不管实际的代码空间有多大,直接将ROM 地址空间整个搬移到RAM 中。当然,这种方法并不适合在真正的启动代码中使用,但在做初步的Remap 测试时,可以用来检验堆栈及异常中断的设置是否合理。另一种方法较复杂,它使用了SDT链接器ARMLink产生的定位信息,仅把RO风吹草动的有效代码和数据段到RAM 中。ARMLink 将编译后的程序链接成ELF 文件。映像文件内部共有三种输出段:RO 段、RW段和ZI 段。这三种输出段分别包含了只读代码及包含在代码段中的少量数据、可读写的数据、初始化为0的数据,ARMLink 同时还产生了这三种输出段的起

37、始和终止定位信息:Image$RO$Base、Image$RO$Limit、Image$RW$Base、Image$Limit、Image$Linit和Image$ZI$Limit。可以在程序中使用这些定位信息。将ROM 中的代码和数据搬移到RAM中,其实现代码如下:数据定义:BaseOfROMDCD|Image$RO$Base|TopOfROMDCD|Image$RO$Limit|BaseOfBSSDCD|Image$RW$Base|BaseOfZeroDCD|Image$ZI$Base|EndOfBSSDCD|Image$ZI$Limit|源程序:;将ROM中的程序搬移到RAM 中,重映射

38、后的地址不变adrr0,ResetEntry;ROM 中程序起始地址movr3,#(RamBaseAddr<<16);RamBaseAddr=0x100Idrr1,BaseOfROMIdrr2,TopOfROMAddr1,r1,r3Addr2,r2,r30Idmiar0!,r4-r11Stmiar1!,r4-r11Cmpr1,r2Bcc%B0;将RW 段中预初始化的变量搬移到RAM中subr1,r1,r2subr0,r0,r1;将r0 指向RO 段的结束,即RW 段的开始ldrr1,BaseOfBSSIdrr2,BaseOfZeroAddr1,r1,3Addr2,r2,r31;基于

39、局部标号的相对跳转,PC+偏移地址,产生与位置无关的代码cmpr1,r2ldrccr4,r0,#4strccr4,r1,#4bcc%B1;接着把ZI 段搬移到RAM 中,并其将初始化为0movr0,#0Idrr2,EndOfBSSAddr2,r2,r32cmpr1,2strccr0,r1,#4bcc%B2地址的重新映射。S3C4510B 中的Remap 过程其实很简单,只需重新设置ROMCON0ROMCON5和DRAMCON0DRAMCON3。在本系统中只需重新设置ROMCON0和DRAMCON0。源代码:;/*内存控制寄存器重新设置-存储空间重新映射地址空间*/EXPORTRemapMemo

40、ryRemapMemorymovr12,r14adrr0,RemapMemldmiar0,r1-r11ldrr0,=ROMCON0;ROMCON0 为Bank寄存器的起始地址stmiar0,r1-r11blExceptionTalbeInit;中断向量表重新初始化movpc,r12RemapMemDCD&11040060;/*ROMCON00x10000000x1100000*/DCD&10000398;/*DRACON00x00x1000000*/进入C 代码空间,开始主程序的运行。此时代码应该运行于RAM中。上面的步骤可以根据实际需要进行适当的添加或删节。值得注意的是:汇编

41、生成的代码应该是与位置无关的代码,即代码在运行期间可以被映射到不同的地址空间,其中的跳转指令都是基于PC 寄存器的相对跳转指令。基于PC 的标号是位于目标指令前或者程序中数据定义伪操作前的标号,这种符号在汇编时将被处理成PC 值加上或减去一个数字常量。3 异常中断的处理在Remap 的启动代码中,需要特别注意的是异常中断的处理。在S3C4510B中,异常中断的入口地址是固定的,按表1次序排列。表1异常类型 工作模式 正常地址 复位 管理 0x00000000 未定义指令 未定义 0x00000004 软件中断(SWI) 管理 0x00000008 预取中止 中止 0x0000000 数据中止

42、中止 0x00000010 预留 -0x00000014 IRQ(中断) IRQ 0x00000018 FIQ(快速中断) FIQ 0x0000001 地址重新映射之后,入口地址被映射到RAM 中,中断处理代码也被搬移到RAM 地址空是。此时,中断响应和中断处理的速度都将大大加快,这将有利于提高整个系统的实时性。异常中断向量表的设计结构如图3 所示。下面是各部分的源代码(以IRQ异常中断为例)。异常向量表的定义:(系统初始化时,将异常处理代码入口地址写入异常中的向量表)_RAM_END_ADDREQU0x01000000;重映射后RAM的终止地址MAP(_RAM_END_ADDR-0x100)

43、SYS_RST_VECTOR#4UDF_INS_VECTOR#4SWI_SVC_VECTOR#4INS_ABT_VECTOR#4DAT_ABT_VECTOR#4RESERVED_VECTOR#4IRQ_SVC_VECTOR#4FIQ_SVC_VECTOR#4异常初始化代码:bIRQ_SVC_HANDLER;0x18IRQ_SVC_HANDLERSUBsp,sp,#4;满递减堆栈STMFDsp!,r0LDRr0,=IRQ_SVC_VECTOR;读取中断向量,;IRQ_SVC_VECTOR=SystemrqHandleLDRr0,r0STRr0,sp,#4LDMFDsp!,r0,pc;跳转到异常中

44、断处理代码入口异常处理入口代码:SystemIrqHandlerIMPORTISR_IrqHandlerSTMFDsp!,r0-r12,lrBLISR_IrqHandler;跳转到C 代码中异常中断处理程序ISR_IrqHandlerLDMFDsp!,r0-r12,lrSUBSpc,lr,#4在如上的结构中,不管系统是否进行了地址的重映射,异常中断向量都可以在运行时动态改变,大大提高了中断处理中的灵活性。中断向量可以在运行时指向不同的异常处理代码入口。ARM映像文件及其地址映射<转>1、什么是arm的映像文件,arm映像文件其实就是可执行文件,包括bin或hex两种格式,可以直接烧

45、到ROM里执行。在axd调试过程中,我们调试的是axf文件,其实这也是一种映像文件,它只是在bin文件中加了一个文件头和一些调试信息。映像文件一般由域组成,域最多由三个输出段组成(RO,RW,ZI),输出段又由输入段组成。所谓域,指的就是整个 bin映像文件所处在的区域,它又分为加载域和运行域。对于嵌入式系统而言,程序映象都是存储在Flash存储器等一些非易失性器件中的,而在运行时,程 序中的RW段必须重新装载到可读写的RAM中。简单来说,程序的加载时域就是指程序烧入Flash中的状态,运行时域是指程序执行时的状态。一般来说 flash里的 整个bin文件所在的地址空间就是加载域,当然在程序一

46、般都不会放在 flash里执行,一般都会搬到sdram里运行工作,它们在被搬到sdram里工作所处的地址空间就是运行域。我们输入的代码,一般有代码部分和数据部 分,这就是所谓的输入段,经过编译后就变成了bin文件中ro段和rw段,还有所谓的zi段,这就是输出段。在ARM的集成开发环境中,只读的代码段和常 量被称作RO段(ReadOnly);可读写的全局变量和静态变量被称作RW段(ReadWrite);RW段中要被初始化为零的变量被称为ZI段 (ZeroInit)。对于加载域中的输出段,一般来说RO段后面紧跟着RW段,RW段后面紧跟着ZI段。在运行域中这些输出段并不连续,但RW和ZI一 定是连着

47、的。ZI段和RW段中的数据其实可以是RW属性。 2、 简单地址映射对于比较简单的情况,可以在ADS集成开发环境的ARM LINKER选项output中指定RO Base和RW Base,即在simple模式下,告知连接器RO和RW的连接基地址。这种模式下,ARM Linker会输出以下符号,它们指示了在运行域中各个输出段所处的地址空间,在使用的时候可以用IMPORT引入:| Image$RO$Base|: 表示RO段在运行域中的起始地址|Image$RO$Limit|:表示RO区末地址后面的地址,即RW数据源的起始地址 |Image$RW$Base|:RW区在RAM里的执行区起始地址,也就是编

48、译器选项RW_Base指定的地址 |Image$ZI$Base|:ZI区在RAM里面的起始地址 |Image$ZI$Limit|:ZI区在RAM里面的结束地址后面的一个地址 RO Base对应的就是| Image$RO$Base|,RW Base 对应的是|Image$RW$Base|,由于ZI段是包含在RW段里的,所以|Image$RW$Limit| 就等于|Image$ZI$limit| 。下面给出一个例子,假设RO Base设为0x00000000,后面的RW Base 地址是0x30000000,然后在Options选项中有Image entry point ,是一个初始程序的入口地址

49、,设为0x00000000,。现在要做的就是将RW section移到以0x30000000开始的地方,并且创造一个ZI section。    首先比较Image$RO$Limit和Image$RW$Base,如果相等,说明execution view下RW section的地址和load view 下RW section的地址相同,这样,不需要移动RW section;如果不等, 说明需要移动RW section 到它在execution view中的地方,把ROM里|Image$RO$Limt|开始的RW初始数据拷贝到RAM里面|Image$RW$Base|

50、开始的地址,当 RAM这边的目标地址到达|Image$ZI$Base|后就表示RW区的结束和ZI区的开始,接下去就对这片ZI区进行清零操作,直到遇到结束地 址|Image$ZI$Limit|。ARM映像文件及其地址映射(二) 示例代码如下:       IMPORT |Image$RO$Limit|        IMPORT |Image$RW$Base|          IMPORT |Im

51、age$ZI$Base|          IMPORT |Image$ZI$Limit| IMPORT main ; 声明C程序中的Main()函数AREA Start,CODE,READONLY ; 声明代码段StartENTRY ; 标识程序入口CODE32 ; 声明32位ARM指令 Reset LDR SP,=0x40003F00        ; 初始化C程序的运行环境     LDR 

52、    R0,=|Image$RO$Limit| ;得到RW数据源的起始地址     LDR     R1,=|Image$RW$Base| ;RW区在RAM里的执行区起始地址     LDR     R3,=|Image$ZI$Base| ;ZI区在RAM里面的起始地址    CMP   R0,R1 ;检查RW section的地址在load view和e

53、xecution view下是否相等     BEQ     LOOP1 ;如果相等就不移动RW section,直接建立ZI scetion     LOOP0                    ;否则就copy RW section到execution view下指定的地址

54、0;       CMP       R1,R3 LDRCC R2,R0,#4    ;它把从R0中的地址开始的section copy到R1中的地址开始的section    STRCC   R2,R1,#4      BCC     LOOP0     LOOP1  

55、0;      LDR     R1,=|Image$ZI$Limit|   ;ZI section末地址       MOV     R2,#0     ;将ZI section 需要的初始化量装入R2     LOOP2        

56、 CMP     R3,R1    ;建立并初始化ZI section     STRCC   R2,R3,#4     BCC     LOOP2          B main ; 跳转到C程序代码Main()函数        

57、60; END注:LDRCC R2,R0,#4 ;将地址为R0的内存单元数据读取到R2中,然后R0=R0+4CC(小于)为条件码。当我们把程序编写好以后,就要进行编译和链接了,在ADS1.2中选择MAKE按钮,会出现一个Errors and Warnings 的对话框,在该栏中显示编译和链接的结果,如果没有错误,在文件的最后应该能看到Image component sizes,后面紧跟的依次是Code,RO Data ,RW Data ,ZI Data ,Debug 各个项目的字节数,最后会有他们的一个统计数据,后面的字节数是根据用户不同的程序而来的。Image component sizes

58、     Code   RO Data    RW Data   ZI Data   Debug17256   158096       8         184     112580 Object Totals1064    

59、   299       0           0        796 Library Totals= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =Code    RO Data

60、    RW Data   ZI Data   Debug18320   158395       8         184     113376 Grand Totals= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

61、= = = = = = = =Total RO Size(Code+RO Data)               176715(172.57KB)Total RW Size(RW Data+ZI Data)              192 ( 0.19KB)     Total ROM Size(

62、Code+RO Data+RW Data)     176723(172.58KB)= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =Code :显示代码占用了多少字节。RO Data 显示只读数据占用了多少字节。 RW Data 显示读写数据占用了多少字节。ZI Data 显示零初始化的数据占用了多少字节。Debug 显示调试数据占用了多少字节。Object Totals 显示链接到一起以后生成映像的对象占用了多少字节

63、。Library Totals 显示已提取并作为单个对象添加到映像中的库成员占用了多少字节。Grand Totals 显示映像的真实大小。 Grand Totals=Library Totals+Object Totals下面就以上面的数据为例来介绍几个变量的计算:|Image$RO$Base|=Image entry point=0x00000000;表示程序代码存放的起始地址|Image$RO$Limit|=|Image$RO$Base|+Total RO Size(Code+Ro Data)=0x0+176715+1=0x0002B24C(因为要满足4的倍数,所以+1)|Image$RW

64、$Base|=0x30000000;由RW Base指定|Image$RW$Limit|=|Image$RW$Base|+Total RW Size(RW Data+ZI Data)=0x30000000+192=0x300000C0|Image$ZI$Base|=|Image$RW$Base|+RW Data=0x30000000+8=0x30000008|Image$ZI$Limit|=|Image$RW$Limit|3、复杂地址映射对于复杂情况,如RO段被分成几部分并映射到存储空间的多个地方时,需要创建一个称为“分布装载描述文件”的文本文 件,通知连接器把程序的某一部分连接在存储器的某个

65、地址空间。需要指出的是,分布装载描述文件中的定义要按照系统重定向后的存储器分布情况进行。在引导程 序完成初始化的任务后,应该把主程序转移到RAM中去运行,以加快系统的运行速度。如下图,为了解决复杂memory map的问题需要用到scatter load 机制。_main() 和 main()之不同:当所有的系统初始化工作完成之后,就需要把程序流程转入主应用程序,即呼叫主应用程序。最简单的一种情况是:IMPORT mainB main直接从启动代码跳转到应用程序的主函数入口,当然主函数名字可以由用户随便定义。在ARM ADS环境中,还另外提供了一套系统级的呼叫机制。IMPORT _mainB

66、_main_main()是编译系统提供的一个函数,负责完成库函数的初始化和初始化应用程序执行环境,最后自动跳转到main()。所以说,前者是库函数,后者就是我们自己编写的main()主函数;因此我们用的B _main其实是执行库函数,然后该库函数再调用我们的main() 函数,因此在单步调试时会看到先要跑一段程序(其实是库函数),然后再单步到我们自己的main函数(这个同时也说明如果有B _main 则就对应必须有main函数,否则编译出错),如果我们用 B main来进入我们的主函数的话,那在单步调试时就看到直接进入到我们自己的main函数了,中间不会看到其他程序;那么用B _main和用B

67、 main 这两这进入我们的main函数方式有什么不同呢?如果采用前者则会由编译器加入一段"段拷贝"程序,即我们说的从加载域到执行域转化程序;而采用后者就没有这个了, 因此如果要进行 "段拷贝"只能自己动手编写程序来实现了,完成段拷贝后就可以进入我们的主函数了,当然这个主函数不一定是叫做main(),可以起个其他好听的名字,这 个有别于使用B _main方式;不管采用哪种方式进入我们的程序,都要有一段"段拷贝"程序,跑完了段拷贝后才能可以进入我们主程序了!(顺便提一 下:startup.s这个文件并没有所谓的"段拷贝&quo

68、t;功能,再看也无益!)对含有启动程序来说,"执行地址与加载地址相同"不容易实现:如果执行地址与加载地址相同哪当然不需要做"段拷 贝",但是个人理解编译器还会加入"段拷贝"程序(如果用B _main的话),只是因为条件不满足而不执行而已;但是对含有启动程序来说,"执行地址与加载地址相同"就不容易了.因为启动程序是要烧到非易失存储 器里,用来在上电执行的,而这个程序必定会有RW段,如果RW放在非易失存储器,如FLASH,那就不好实现RW功能了,因此要给RW移动到能够实现RW 功能的存储器,如SRAM等.因此,对含有启

69、动程序来说,"执行地址与加载地址相同"就不容易实现;程序的入口点在C 库中的_main 处,在该点,库代码执行以下操作:1. 将非零(只读和读写)运行区域从其载入地址复制到运行地址。2. 清零ZI 区域。3. 跳转到_rt_entry。嵌入式编程中关于const,static,extern,volatile的用法一.const的用法:为什么使用const?采用符号常量写出的代码更容易维护;指针常常是边读边移动,而不是边写边移动;许多函数参数是只读不写的。const最常见用途是作为数组的界和switch分情况标号(也可以用枚举符代替)用法1:常量   取代

70、了C中的宏定义,声明时必须进行初始化。const限制了常量的使用方式,并没有描述常量应该如何分配。如果编译器知道了某const的所有使用,它 甚至可以不为该const分配空间。最简单的常见情况就是常量的值在编译时已知,而且不需要分配存储。C+ Program Language   用const声明的变量虽然增加了分配空间,但是可以保证类型安全。C标准中,const定义的常量是全局的,C+中视声明位置而定。用法2:指针和常量   使用指针时涉及到两个对象:该指针本身和被它所指的对象。将一个指针的声明用const“预先固定”将使那个对象而不是使这个指针成为常量。要将指针本身 而不是被指对象声明为常量,必须使用声明运算符*const。所以出现在 * 之前的const是作为基础类型的一部分:char *const cp; /到char的const指针 (后两个声明是等同的)char co

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论