1、ARM处理器系统初始化过程 1 禁止MMU,关闭中断,禁止cache;2 根据硬件设计配制好处理器时钟、DRAM时钟、定时器时钟;3 根据系统中所用的flash和DRAM芯片容量和电气参数设置它们的起始地址、容量、刷新频率等;4 将固化在flash芯片中的程序搬移到DRAM内存中;5 使能cache,使能MMU,跳转到DRAM内存中运行继续初始化,包括根据具体应用以及系统中的硬件配置初始化各个功能模块、安装好异 常中断处理程序、使能中断等;6 进行操作系统相关初始化;禁止MMU,关闭中断,禁止cache通过写系统控制协处理器的寄存器1 的第0 位可以允许和禁止MMU。在复位后这位是0,MMU
2、被禁止。关闭中断与打开中断中断是一种高效的对话机制,但有时并不想程序运行的过程中中断运行,比如正在打印东西,但程序突然中断了,又让另外一个程序输出打印内容,这样在打印机上就会乱得不得了,同时有两份以上的文件交错地打印在一张纸上。像不可剥夺的资源,就一定要关闭中断,让它占有这个资源。在ARM里,没有像x86那样有清除中断指令CLI。那么在ARM里是怎么样实现关中断和开中断的呢?下面就来看看ARM的关中断和开中断实现。void Lock(void)stmdbsp!, r0mrsr0, cpsrorr r0,r0,#0xC0msr cpsr_cxsf,r0ldmia sp!,r0上面这段程序是通过设
3、置CPSR的第6,7位来实现的,因为第6,7位是设置为1时,就不再响应中断。void UnLock(void)stmdbsp!, r0mrsr0, cpsrbic r0,r0,#0xC0msr cpsr_cxsf,r0ldmia sp!,r0上面是重新开中断的命令,同样是设置CPSR的第6,7位,但它的值是0,就可接收中断了。如果在多个任务之间进行共享数据,一般是需要使用关中断和开中断实现数据同步的,其实中这种关中断和开中断,就是进入临界区和退出临界区。如果是像PC机那样有多个CPU的话,关中断并不能防止这种情况。系统的在应用编程(IAP)以及在系统编程功能(ISP)等。中断向量表ARM要求中
4、断向量表必须放置在从0地址开始,连续8X4字节的空间内。每当一个中断发生以后,ARM处理器便强制把PC指针置为向量表中对应中断类型的地址值。因为每个中断只占据向量表中1个字的存储空间,只能放置一条ARM指令,使程序跳转到存储器的其他地方,再执行中断处理。中断向量表的程序实现通常如下表示:AREA Boot ,CODE, READONLYENTRYB ResetHandlerB UndefHandlerB SWIHandlerB PreAbortHandlerB DataAbortHandlerBB IRQHandlerB FIQHandler其中关键字ENTRY是指定编译器保留这段代码,因为编
5、译器可能会认为这是一段亢余代码而加以优化。链接的时候要确保这段代码被链接在0地址处,并且作为整个程序的入口。Q:为什么在中断向量表中不直接LDR PC,异常地址.而是使用一个标号,然有再在后面使用DCD定义这个标号A:因为LDR指令只能跳到当前PC 4kB范围内,而B指令能跳转到32MB范围,而现在这样在LDR PC, xxxx这条指令不远处用xxxxDCD定义一个字,而这个字里面存放最终异常服务程序的地址,这样可以实现4GB全范围跳转.Q: LDR 不是可以全空间跳转的吗 ARM微控制器基础与实战程序清单5.3.A: LDR伪指令通过设置指令缓冲池才能实现全范围跳转,而LDR指令则只能实现4
6、KB范围跳转.MEMMAP=0:开机默认值,Boot装载模式-向量表(0x00000000-0x0000003c)映射的是BootBlock中的0x7FFFE000-0x7FFFF03c中的值;芯片复位时,启动boot装载程序,boot装载程序检查P0.14口的状态和用户的异常向量,判断是进入ISP状态还是启动用户程序,若启动用户程序,则自动设置MEMMAP=1(片内flash启动)或3(片外程序存储器启动)。很奇怪的,我在实验中,当使用无片内flash的LPC2210时即使设置P0.14为高低都没关系,芯片会跳过继而执行片外flash中的代码. MEMMAP=1:中断向量表就在片内flash
7、中,地址就是0x00000000-0x0000003c,相当于没有映射;MEMMAP=2:最为主要的设置,即是重映射的关键之所在,当设置MEMMAP=2 时,中断向量表(0x00000000-0x0000003c)映射的是片内SRAM中的0x40000000-0x4000003c中的值,而因为是SRAM,所以在程序运行的过程中是可以改变的,这样就可以达到重映射的目的啦(中断向量表可以随时修改)。 MEMMAP=3:中断向量表就在片外flash中,中断向量表(0x00000000-0x0000003c)映射到是片外flash中的0x80000000-0x8000003c中的值;功能上与MEMMA
8、P=1时的差不多,因为一旦程序固化到flash中,即为只读,只是数值映射而已!设置MEMMAP; Memory Mapping (when Interrupt Vectors are in RAM)MEMMAP EQU 0xE01FC040 ; Memory Mapping Control IF :DEF:REMAP LDR R0, =MEMMAP IF :DEF:EXTMEM_MODE MOV R1, #3 ELIF :DEF:RAM_MODE MOV R1, #2 ELSE MOV R1, #1 ENDIF STR R1, R0 ENDIFMEMMAP有两个控制位 MEMMAP1:000
9、BOOT装载程序模式01 User FLASH模式10 用户RAM模式11 用户外部存储器模式10模式也就是RAM模式 我们访问地址0X00是跟访问RAM地址0X40000000中的数据是完全一样的 向RAM中写进数据 然后通过数据窗口观察0X0地址的变化 应该是同步变化的存储器类型和时序配置 主要是对系统存储器控制器(MMU)的初始化。由于存储器控制器并不是ARM架构的一部分,不同芯片的实现方式各不相同。由于运算能力和寻址能力的强大,基于ARM内核的微处理器系统一般都需要外扩展各种类型的存储器。对于存储器系统的初始化一般包括如下几个方面:存储器类型、时序和总线宽度的配置存储器地址的配置(1)
10、存储器类型 基于ARM微处理系统的存储器一般有如下几类:SARM,DRAM,Flah,同时,即使同类存储器也有访问速度上的不同。其中,SRAM和Flah属于静态存储器,可以共用存储器端口,而DRAM有动态刷新和地址复用等特征,需要专门的存储器端口。(2)时序 存储器端口的接口时序优化对系统性能影响非常大,因为系统运行的速度瓶颈一般都存在于存储器的访问,因此希望存储器的访问尽可能快,但又要考虑由此带来的系统稳定性问题。(3)总线宽度 ARM微处理器架构支持8/16/32位的数据总线宽度访问存储器和外设,对于特定的存储器来说,需要设定数据总线的宽度。(4)存储器地址的配置 ARM微处理器架构理论上
11、可以支持4GB的地址空间,而对于一个实际的系统来说,配置的物理地址远没有这么多,因此,如何配置存储器的地址,也是一个重要的问题。(5)存储器地址重映射 存储器地址重映射就是可以通过软件配置来改变一块存储器物理地址的方法,是当前许多先进控制器所具有的功能。进行地址重映射的原因:提高系统的运行效率。进行地址重映射的注意:保证程序流程的连续性。有的ARM处理器不具有地址重映射的功能,可以采样代码搬移加跳转的方式完成上述功能。初始化堆栈因 为ARM有7种执行状态,每一种状态的堆栈指针寄存器(SP)都是独立的。因此,对程序中需要用到的每一种模式都要给SP定义一个堆栈地址。方法是改变状 态寄存器内的状态位
12、,使处理器切换到不同的状态,让后给SP赋值。注意:不要切换到User模式进行User模式的堆栈设置,因为进入User模式后就不 能再操作CPSR回到别的模式了,可能会对接下去的程序执行造成影响。这是一段堆栈初始化的代码示例,其中只定义了三种模式的SP指针:MRS R0,CPSRBIC R0,R0,#MODEMASK 安全起见,屏蔽模式位以外的其他位ORR R1,R0,#IRQMODEMSR CPSR_cxfs,R1LDR SP,=UndefStackORR R1,R0,#FIQMODEMSR CPSR_cxsf,R1LDR SP,=FIQStackORR R1,R0,#SVCMODEMSR C
13、PSR_cxsf,R1LDR SP,=SVCStack初始化应用程序执行环境如果使用分散加载描述文件调整堆栈和堆放置,则链接器创建 _user_initial_stackheap() 函数,并使用链接器定义的符号作为这些区域的名称。映像一开始总是存储在ROMFlash里面的,其RO部分即可以在ROMFlash里面执行,也可以转移到速度更快的RAM中执行;而RW和 ZI这两部分是必须转移到可写的RAM里去。所谓应用程序执行环境的初始化,就是完成必要的从ROM到RAM的数据传输和内容清零。下面是在ADS下,一种常用存储器模型的直接实现:LDR r0,=|Image$RO$Limit| 得到RW数据
14、源的起始地址LDR r1,=|Image$RW$Base| RW区在RAM里的执行区起始地址LDR r2,=|Image$ZI$Base| ZI区在RAM里面的起始地址CMP r0,r1 比较它们是否相等BEQ %F10 CMP r1,r3LDRCC r2,r0,#4STRCC r2,r1,#4BCC %B01 LDR r1,=|Image$ZI$Limit|MOV r2,#02 CMP r3,r1STRCC r2,r3,#4BCC %B2程序实现了RW数据的拷贝和ZI区域的清零功能。其中引用到的4个符号是由链接器第一输出的。|Image$RO$Limit|:表示RO区末地址后面的地址,即RW
15、数据源的起始地址|Image$RW$Base|:RW区在RAM里的执行区起始地址,也就是编译器选项RW_Base指定的地址|Image$ZI$Base|:ZI区在RAM里面的起始地址|Image$ZI$Limit|:ZI区在RAM里面的结束地址后面的一个地址程 序先把ROM里|Image$RO$Limt|开始的RW初始数据拷贝到RAM里面|Image$RW$Base|开始的地址,当RAM这边的目 标地址到达|Image$ZI$Base|后就表示RW区的结束和ZI区的开始,接下去就对这片ZI区进行清零操作,直到遇到结束地 址|Image$ZI$Limit|改变处理器模式因为在初始化过程中,许多操
16、作需要在特权模式下才能进行(比如对CPSR的修改),所以要特别注意不能过早的进入用户模式。内核级的中断使能也可以考虑在这一步进行。如果系统中另外存在一个专门的中断控制器,这么做总是安全的。呼叫主应用程序当所有的系统初始化工作完成之后,就需要把程序流程转入主应用程序。最简单的一种情况是:IMPORT mainB main直接从启动代码跳转到应用程序的主函数入口,当然主函数名字可以由用户随便定义。在ARM ADS环境中,还另外提供了一套系统级的呼叫机制。IMPORT _mainB _main_main()是编译系统提供的一个函数,负责完成库函数的初始化和初始化应用程序执行环境,最后自动跳转到mai
17、n()函数。ARM 介绍ARM微处理器的工作状态一般有两种,并可在两种状态之间切换: 第一种为ARM状态,此时处理器执行32位的字对齐的ARM指令; 第二种为Thumb状态,此时处理器执行16位的、半字对齐的Thumb指令。 在程序的执行过程中,微处理器可以随时在两种工作状态之间切换,并且,处理器工作状态的转变并不影响处理器的工作模式和相应寄存器中的内容。但ARM微处理器在开始执行代码时,应该处于ARM状态。 ARM处理器状态 进 入Thumb状态:当操作数寄存器的状态位(位0)为1时,可以采用执行BX指令的方法,使微处理器从ARM状态切换到Thumb状态。此外,当处理器处 于Thumb状态时
18、发生异常(如IRQ、FIQ、Undef、Abort、SWI等),则异常处理返回时,自动切换到Thumb状态。 进入ARM状态:当操作数寄存器的状态位为0时,执行BX指令时可以使微处理器从Thumb状态切换到ARM状态。此外,在处理器进行异常处理时,把PC指针放入异常模式链接寄存器中,并从异常向量地址开始执行程序,也可以使处理器切换到ARM状态。ARM处理器模式 ARM微处理器支持7种运行模式,分别为: 用户模式(usr):ARM处理器正常的程序执行状态。 快速中断模式(fiq):用于高速数据传输或通道处理。 外部中断模式(irq):用于通用的中断处理。 管理模式(svc):操作系统使用的保护模
19、式。 数据访问终止模式(abt):当数据或指令预取终止时进入该模式,可用于虚拟存储及存储保护。 系统模式(sys):运行具有特权的操作系统任务。 定义指令中止模式(und):当未定义的指令执行时进入该模式,可用于支持硬件协处理器的软件仿真。 ARM处理器模式 ARM微处理器的运行模式可以通过软件改变,也可以通过外部中断或异常处理改变。大多数的应用程序运行在用户模式下,当处理器运行在用户模式下时,某些被保护的系统资源是不能被访问的。 除用户模式以外,其余的所有6种模式称之为非用户模式,或特权模式;其中除去用户模式和系统模式以外的5种又称为异常模式,常用于处理中断或异常,以及需要访问受保护的系统资
20、源等情况。 ARM寄存器 ARM处理器共有37个寄存器。其中包括:31个通用寄存器,包括程序计数器(PC)在内。这些寄存器都是32位寄存器。以及6个32位状态寄存器。 关于寄存器这里就不详细介绍了,有兴趣的人可以上网找找,很多这方面的资料。 异常处理 当 正常的程序执行流程发生暂时的停止时,称之为异常,例如处理一个外部的中断请求。在处理异常之前,当前处理器的状态必须保留,这样当异常处理完成之后,当 前程序可以继续执行。处理器允许多个异常同时发生,它们将会按固定的优先级进行处理。当一个异常出现以后,ARM微处理器会执行以下几步操作: 进入异常处理的基本步骤: 将下一条指令的地址存入相应连接寄存器
21、LR,以便程序在处理异常返回时能从正确的位置重新开始执行。将CPSR复制到相应的SPSR中。根据异常类型,强制设置CPSR的运行模式位。 强制PC从相关的异常向量地址取下一条指令执行,从而跳转到相应的异常处理程序处。如果异常发生时,处理器处于Thumb状态,则当异常向量地址加载入PC时,处理器自动切换到ARM状态。 ARM微处理器对异常的响应过程用伪码可以描述为: R14_ = Return Link SPSR_= CPSR CPSR4:0 = Exception Mode Number CPSR5 = 0 ;当运行于 ARM 工作状态时 If = Reset or FIQ then;当响应
22、FIQ 异常时,禁止新的 FIQ 异常 CPSR6 = 1 PSR7 = 1 PC = Exception Vector Address 异常处理完毕之后,ARM微处理器会执行以下几步操作从异常返回: 将连接寄存器LR的值减去相应的偏移量后送到PC中。 将SPSR复制回CPSR中。 若在进入异常处理时设置了中断禁止位,要在此清除每一种异常模式拥有自己的物理的R13。应用程序初始化该R13,使其指向该异常模式专用的栈地址。当进入异常模式时,可以将需要使用的寄存器保存在R13所指的栈中;当退出异常处理程序时,将保存在R13所指的栈中的寄存器值弹出。这样就使异常处理程序不会破坏被其中断程序的运行现场
23、。1。请问cache和write-buffer的关系,在WB和cache同时打开的时候,数据是如何流动的呢?考虑到WB的特性,是不是较大段的数据被写入WB? WB的设计是为了防止处理器流水线被写数据总线操作(写主存,写外围设备寄存器等)拉住。典型写数据总线时机有三种:一是cache处于write through策略下的写操作;二是cache处于write back策略下,dirty数据由于cache行替换或者被程序主动清空而写回主存,三是不经过cache,直接对数据总线的写操作。有了WB之后,被写 回数据总线的内容在进入WB之后,处理器和cache就可以立刻继续使用了。这就是WB和cache的
24、关系。2。还有就是所谓“cachable bit”和“bufferable bit”的问题,这个问题来自于arm-armpartB 的5.4节,那里有一张表,我对这个表死活不理解。这个问题以前也问过,但是没有人回答。ARM各系列的处理器上的cache设计有所不同,所以有的cache只有write through策略,有的cache只有write back策略,还有的cache是write back策略但允许一定的write through行为,因此C和B位的四种组合对这三种cache而言有不同的含义。第一列和第二列分别针对write through型cache和write back型cache
25、进行解释,其含义可以参考我对第1个问题的回答,第三列针对write back策略但允许一定的write through行为的cache,第一行好理解,第二行之所以在B位为0的情况下依然是bufferable,应该是因为硬件上的设计原因(节省硬件资源 或者由于目标设计频率限制),第三行的含义是,当C=1,B=0时,cache使用write through策略,WB开启,第四行的含义是,当C=1,B=1时,cache使用write back策略,WB开启。 可以看到,对第 三种类型的cache,C和B位不再“严格”是其本来控制cacheable和bufferable的含义,而是利用这两位的“组合”
26、来控制cache和 WB的表现行为,这样做比另外再增加一位来选择cache的write back策略和write through策略硬件上节约了资源,效果上却差不多,少了cache和WB几种意义不大的组合,应该说还是挺巧妙的。ARM启动代码设计参考 收藏 基于ARM的芯片多数为复杂的片上系统,这种复杂系统里的多数硬件模块都是可配置的,需要由软件来设置其需要的工作状态。因此在用户的应 用程序之前,需要由专门的一段代码来完成对系统的初始化。由于这类代码直接面对处理器内核和硬件控制器进行编程,一般都是用汇编语言。一般通用的内容包 括: 中断向量表 初始化存储器系统 初始化堆栈 初始化有特殊要求的断口
27、,设备 初始化用户程序执行环境 改变处理器模式 呼叫主应用程序 1.中断向量表 ARM要求中断向量表必须放置在从0地址开始,连续8X4字节的空间内。 每当一个中断发生以后,ARM处理器便强制把PC指针置为向量表中对应中断类型的地址值。因为每个中断只占据向量表中1个字的存储空间,只能放置一条ARM指令,使程序跳转到存储器的其他地方,再执行中断处理。 中断向量表的程序实现通常如下表示: AREABoot,CODE,READONLY ENTRY BResetHandler BUndefHandler BSWIHandler BPreAbortHandler BDataAbortHandler B B
28、IRQHandler BFIQHandler 其中关键字ENTRY是指定编译器保留这段代码,因为编译器可能会认为这是一段亢余代码而加以优化。链接的时候要确保这段代码被链接在0地址处,并且作为整个程序的入口。 2.初始化存储器系统 (1)存储器类型和时序配置 通常Flash和SRAM同属于静态存储器类型,可以合用同一个存储器端口;而DRAM因为有动态刷新和地址线复用等特性,通常配有专用的存储器端口。 存储器端口的接口时序优化是非常重要的,这会影响到整个系统的性能。因为一般系统运行的速度瓶颈都存在于存储器访问,所以存储器访问时序应尽可能的快;而同时又要考虑到由此带来的稳定性问题。 (2)存储器地址
29、分布 一种典型的情况是启动ROM的地址重映射。3.初始化堆栈 因为ARM有7种执行状态,每一种状态的堆栈指针寄存器(SP)都是独立的。因此,对程序中需要用到的每一种模式都要给SP 定义一个堆栈地址。方法是改变状态寄存器内的状态位,使处理器切换到不同的状态,让后给SP赋值。注意:不要切换到User模式进行User模式的堆栈设 置,因为进入User模式后就不能再操作CPSR回到别的模式了,可能会对接下去的程序执行造成影响。 这是一段堆栈初始化的代码示例,其中只定义了三种模式的SP指针: MRSR0,CPSR BICR0,R0,#MODEMASK安全起见,屏蔽模式位以外的其他位 ORRR1,R0,#
30、IRQMODE MSRCPSR_cxfs,R1 LDRSP,=UndefStack ORRR1,R0,#FIQMODE MSRCPSR_cxsf,R1 LDRSP,=FIQStack ORRR1,R0,#SVCMODE MSRCPSR_cxsf,R1 LDRSP,=SVCStack 4.初始化有特殊要求的端口,设备 5.初始化应用程序执行环境 映 像一开始总是存储在ROMFlash里面的,其RO部分即可以在ROMFlash里面执行,也可以转移到速度更快的RAM中执行;而RW和ZI这两部 分是必须转移到可写的RAM里去。所谓应用程序执行环境的初始化,就是完成必要的从ROM到RAM的数据传输和内容
31、清零。 下面是在ADS下,一种常用存储器模型的直接实现: LDRr0,=|Image$RO$Limit|;得到RW数据源的起始地址 LDRr1,=|Image$RW$Base|;RW区在RAM里的执行区起始地址 LDRr2,=|Image$ZI$Base|;ZI区在RAM里面的起始地址 CMPr0,r1;比较它们是否相等 BEQ%F1 0CMPr1,r3 LDRCCr2,r0,#4 STRCCr2,r1,#4 BCC%B0 1LDRr1,=|Image$ZI$Limit| MOVr2,#0 2CMPr3,r1 STRCCr2,r3,#4 BCC%B2 程序实现了RW数据的拷贝和ZI区域的清零功
32、能。其中引用到的4个符号是由链接器第一输出的。 |Image$RO$Limit|:表示RO区末地址后面的地址,即RW数据源的起始地址 |Image$RW$Base|:RW区在RAM里的执行区起始地址,也就是编译器选项RW_Base指定的地址 |Image$ZI$Base|:ZI区在RAM里面的起始地址 |Image$ZI$Limit|:ZI区在RAM里面的结束地址后面的一个地址 程 序先把ROM里|Image$RO$Limt|开始的RW初始数据拷贝到RAM里面|Image$RW$Base|开始的地址,当RAM这边的目 标地址到达|Image$ZI$Base|后就表示RW区的结束和ZI区的开始,
33、接下去就对这片ZI区进行清零操作,直到遇到结束地 址|Image$ZI$Limit| 6.改变处理器模式 因为在初始化过程中,许多操作需要在特权模式下才能进行(比如对CPSR的修改),所以要特别注意不能过早的进入用户模式。 内核级的中断使能也可以考虑在这一步进行。如果系统中另外存在一个专门的中断控制器,这么做总是安全的。 7.呼叫主应用程序 当所有的系统初始化工作完成之后,就需要把程序流程转入主应用程序。最简单的一种情况是: IMPORTmain Bmain 直接从启动代码跳转到应用程序的主函数入口,当然主函数名字可以由用户随便定义。 在ARMADS环境中,还另外提供了一套系统级的呼叫机制。
34、IMPORT_main B_main _main()是编译系统提供的一个函数,负责完成库函数的初始化和初始化应用程序执行环境,最后自动跳转到main()函数。ARM中B _Main和B Main的区别转第一种答案如下: 在软件仿真的情况下,执行“B _main”指令,能使程序跳到C文件的main函数,但用硬件仿真时,还没执行到main函数时就进入了异常中断。 原因是执行“B _main”指令后,程序先跳到_main库函数的入口,再进行一些初始化操作,最后再跳入用户的main函数。但在初始化过程中,由于堆栈或其它原 因造成程序出错。有两种方法可以解决这个问题。第一:将“B _main”指令直接改
35、成“B main”,使程序不进行初始化而直接跳入用户的main()函数。第二:合理初始化堆栈。由于考虑到刚接触ARM和将问题简单化,我选择了第一种方法。第二种答案如下:IMPORT _main. .BL TargetResetInitB _main1、此后流程将跳转到ADS提供的启动代码_main函数处,负责完成库函数的初始化及相关功能,并最终引导处理器入口用户代码main()函数,其代码流程为:嵌入式系统在进入应用主程序之前必须有一个初始化的过程,该过程完成系统的启动和初始化功能,初始化过程可以分成两部分来看: _main负责设置运行映像存储器映射; _rt_entry负责库函数的初始化。_
36、main完成代码和数据的复制,并把ZI数据区清零,这一步当代码和数据区在存储和运行时 处于不同的存储器位置时有意义。接着_main跳进_rt_entry,进行STACK和HEAP等的初始化。最后_rt_entry跳进应用程序的入口 main()。当应用程序执行完时,_rt_entry又将控制权交还给C库函数,函数main()在ADS中有特殊的意义。当一个程序工程项目中存在 main()时,连接器会把_main和_rt_entry中的初始化代码连接进来;如果没有main()函数,初始化过程就不会被连接,初始化过程中一 些标准的C库函数失效。而用户应用程序初始化过程并未显式的完成这些堆栈初始化及数
37、据拷贝操作,应用程序将启动不成功。2、_main()为编译系统提供的一个库函数,使用_main标号引导系统时必须将应用程序的入口数定义为main();3、若希望系统进入应用程序前自动完成系统调用(如库函数的初始化、RW、ZI数据从 Flash到RAM的拷贝(加载映像-执行映像)等)的初始化过程,必须使用_main标号以跳至ADS提供的初始化函数库,这种情况一般 需做一些库函数移植及重定向问题解决;这一点上,应用程序入口必须以main函数标识;4、若所有的初始化步骤都已显式的完成(如堆栈初始化、加载映像到执行映像RW、ZI数据的 拷贝等),应用程序入口函数XXXX()可以任意定义(非main,这
38、样可以避免自动链接上_main,从而跳过_main),初始化完成后直接 B XXXX即可,这一点上,main()函数并不具有特殊性;5、若必须的初始化步骤都未显示的完成,一意孤行的B XXXX,程序运行的结果将不可预料。一个典型的例子是:若用户并未显示完成加载映像-执行映像的数据初始化而直接B XXXX,则应用程序中所有的全局变量并不会被预期的初始化,而所有的ZI变量也不会被预期的清零;6、本人在调试LPC2294 LCD模块时才认识到上述第五点(5、)的重要性:程序指令流并没有错,但LCD上显示的全部是乱码。我用两种方法解决了这一bug:(1)将所有的字库数据定义为const常量,程序运行正
39、确;(2)将B Main 修改成B _main接着将应用入口改为int main(void) 而非int Main(void),程序也得到正确的执行;Initializing memory required by C codeThe initial values for any initialized variables (RW) must be copied from ROM to RAM.All other ZI variables must be initialized to zero. The library initialization code calledat _main per
40、forms the copying and initialization.Using the main functionWhen the compiler compiles a function called main(), it generates a reference to thesymbol _main to force the linker to include the basic C run-time system from the ANSIC library. (The symbol _main is marked as an entry point.)建议和我一样的初学者还是乖
41、乖的B _main接着将应用入口设置为main()函数的好。ARM编程:Scatter文件的编写、分析scatter基本点:1. 编译后输出的映像文件中各段是首尾相连的,中间没有空闲的区域,它们的先后关系是根据链接时参数的先后次序决定的 armlinker -file1.o file2.o 2. scatter用于将编译后的映像文件中的特定段加载到多个分散的指定内存区域3. 有2类域region:执行域(execution region,一般是ram区域)和加载域(load region,一般是rom区域)4. 加载域:就是编译之后得到的二进制文件烧写到rom中的这一段区域,所有的代码RO、预
42、定义变量RW、堆栈之类清不清空无关紧要的大片内存区域ZI,都包括在其中5. 执行域:就是把加载域进行解压缩后的样子。比如:RO没有变动还是在ROM中,RW被移到了SRAM中,而ZI被放置在SDRAM中6. scatter本身并不能对映像实现解压缩,编译器读入scatter文件之后会根据其中的各种地址生成启动代码,实现对映像的加载,而这一段代码就 是* (InRoot$Sections)它是_main()的一部分。这就是在汇编启动代码的最后跳转到_main() 而不是跳向main()的原因之一。7. 起始地址与加载域重合的执行域成为root region,* (InRoot$Sections)必
43、须放在这个执行域中,否则链接的时候会报错。*(+RO)包含了* (InRoot$Sections),所以如果在root region中用到了*(+RO)可以不再指定* (InRoot$Sections),scatter语法:ROM_LOAD 0x00000000 ROM 0x00000000 0x003FFFFF vectors.o (+RO,+FIRST) * (InRoot$Sections) ; All library sections that must be in a root region *(+RO) SRAM 0x00400000 0x003FFFFF * (+RW,+ZI)
44、SDRAM1 0x41000000 UNINIT stack.o (+ZI) ; stack.s中定义了top_of_stack为长度为1的space,指定栈顶地址 SDRAM2 +0 UNINIT heap.o (+ZI) 注解:1. ROM_LOAD是加载域。这里只有一个,也可以有多个(rom地址不连续的情况)2. ROM、SRAM、SDRAM1、SDRAM2是执行域,有多个。第一个执行域必须和加载域地址重合,因为ARM的复位地址就是加载域的起始地址(有bootstrap的话加载域址就是bootstrap执行完后的跳转地址)3. vectors.o (+RO, +FIRST) 中断向量表放在最开头4. ROM 0x00000000 0x003FFFFF; 加载域名 起始地址 最大允许长度;最大允许长度也可以省略,但缺点是编译器不会检查段是否溢出和别的段重叠了。起始地址= +0表示紧接着上一段开始的连续地址。5. * (InRoot$Sections)是复制代码的代码6. UNINT关键字表示不进行初始化清零值得注意的是:在一个scatter文件中,同一个.o文件不能出现2次,即使是在2个不同的加载域中也不可以,否则会报错:Ambiguous select