ARM启动过程

基于ARM的芯片多数为复杂的片上系统，这种复杂系统里的多数硬件模块都是可配置的，需要由软件来设置其需要的工作状态. 因此在用户的应用程序之前，需要由专门的一段代码来完成对系统的初始化. 由于这类代码直接面对处理器内核和硬件控制器进行编程，一般都是用汇编语言. 一般 通用的内容包括： 中断向量表 初始化存储器系统 初始化堆栈 初始化有特殊要求的端口，设备 初始化用户程序执行环境 改变处理器模式 呼叫主应用程序 1. 中断向量表 ARM要求中断向量表必须放置在从0地址开始，连续8X4字节的空间内. 每当一个中断发生以后，ARM处理器便强制把PC指针置为向量表中对应中断类型的地址值. 因为每个中断只占据向量表中1个字的存储空间，只能放置一条ARM指令，使程序跳转到存储器的其他地方，再执行中断处理. 中断向量表的程序实现通常如下表示： AREA Boot ,CODE, READONLY ENTRY B ResetHandler B UndefHandler B SWIHandler B PreAbortHandler B DataAbortHandler B B IRQHandler B FIQHandler 其中关键字ENTRY是指定编译器保留这段代码，因为编译器可能会认为这是一段亢余代码而加以优化. 链接的时候要确保这段代码被链接在0地址处，并且作为整个程序的入口. 2. 初始化存储器系统 (1)存储器类型和时序配置 通常Flash和SRAM同属于静态存储器类型，可以合用同一个存储器端口；而DRAM因为有动态刷新和地址线复用等特性，通常配有专用的存储器端口. 存储器端口的接口时序优化是非常重要的，这会影响到整个系统的性能. 因为一般系统运行的速度瓶颈都存在于存储器访问，所以存储器访问时序应尽可能的快；而同时又要考虑到由此带来的稳定性问题. (2)存储器地址分布 一种典型的情况是启动ROM的地址重映射. 3. 初始化堆栈 因为ARM有7种执行状态，每一种状态的堆栈指针寄存器（SP）都是独立的. 因此，对程序中需要用到的每一种模式都 要给SP定义一个堆栈地址. 方法是改变状态寄存器内的状态位，使处理器切换到不同的状态，让后给SP赋值. 注意：不要切换到User模式进行User模式 的堆栈设置，因为进入User模式后就不能再操作CPSR回到别的模式了，可能会对接下去的程序执行造成影响. 这是一段堆栈初始化的代码示例，其中只定义了三种模式的SP指针： MRS R0,CPSR BIC R0,R0,#MODEMASK 安全起见，屏蔽模式位以外的其他位 ORR R1,R0,#IRQMODE MSR CPSR_cxfs,R1 LDR SP,=UndefStack ORR R1,R0,#FIQMODE MSR CPSR_cxsf,R1 LDR SP,=FIQStack ORR R1,R0,#SVCMODE MSR CPSR_cxsf,R1 LDR SP,=SVCStack 4. 初始化有特殊要求的端口，设备 5. 初始化应用程序执行环境 映像一开始总是存储在ROM／Flash里面的，其RO部分即可以在ROM／Flash里面执行，也可以转移到速度 更快的RAM中执行；而RW和ZI这两部分是必须转移到可写的RAM里去. 所谓应用程序执行环境的初始化，就是完成必要的从ROM到RAM的数据传输和内 容清零. 下面是在ADS下，一种常用存储器模型的直接实现： LDR r0,=|Image$$RO$$Limit| ;得到RW数据源的起始地址 LDR r1,=|Image$$RW$$Base| ;RW区在RAM里的执行区起始地址 LDR r2,=|Image$$ZI$$Base| ;ZI区在RAM里面的起始地址 CMP r0,r1 ;比较它们是否相等 BEQ %F1 0 CMP r1,r3 LDRCC r2,[r0],#4 STRCC r2,[r1],#4 BCC %B0 1 LDR r1,=|Image$$ZI$$Limit| MOV r2,#0 2 CMP r3,r1 STRCC r2,[r3],#4 BCC %B2 程序实现了RW数据的拷贝和ZI区域的清零功能. 其中引用到的4个符号是由链接器第一输出的. 程序先把ROM里|Image$$RO$$Limt|开始的RW初始数据拷贝到RAM里 面|Image$$RW$$Base|开始的地址，当RAM这边的目标地址到达|Image$$ZI$$Base|后就表示RW区的结束和ZI区的开始， 接下去就对这片ZI区进行清零操作，直到遇到结束地址|Image$$ZI$$Limit| 6. 改变处理器模式 因为在初始化过程中，许多操作需要在特权模式下才能进行（比如对CPSR的修改），所以要特别注意不能过早的进入用户模式. 内核级的中断使能也可以考虑在这一步进行. 如果系统中另外存在一个专门的中断控制器，这么做总是安全的. 7. 呼叫主应用程序 当所有的系统初始化工作完成之后，就需要把程序流程转入主应用程序. 最简单的一种情况是： IMPORT main B main 直接从启动代码跳转到应用程序的主函数入口，当然主函数名字可以由用户随便定义. 在ARM ADS环境中，还另外提供了一套系统级的呼叫机制. IMPORT __main B__ main __main是编译系统提供的一个函数，负责完成库函数的初始化和初始化应用程序执行环境，最后自动跳转到main函数
 * Image$$RO$$Limit|：表示RO区末地址后面的地址，即RW数据源的起始地址
 * Image$$RW$$Base|：RW区在RAM里的执行区起始地址，也就是编译器选项RW_Base指定的地址
 * Image$$ZI$$Base|：ZI区在RAM里面的起始地址
 * Image$$ZI$$Limit|：ZI区在RAM里面的结束地址后面的一个地址

link：bootloader

from http://mcs.szu.edu.cn/user/qmtsang/Article_53723