AT91SAM9261启动代码分析

来源:互联网 发布:java 冒号用法 编辑:程序博客网 时间:2024/06/06 05:38

作者:杨硕华清远见嵌入式学院讲师。

T91SAM9261是atmel公司生产的以ARM926EJ-S ARM Thumb处理器为核心的完全片上系统(SOC),在工业现场控制,智能仪表,工业控制终端,车载电子中有广泛的应用。下面我们逐段分析它的启动代码SAM9261.S(安装了ARM公司的RealView MDK工具后可以在C:/Keil/ARM/Boards/Atmel/AT91SAM9261-EK下找到),熟悉它的启动流程将有助于我们对它的后续开发。

一.定义代码入口点:

这一段定义了启动代码的入口点:只读代码段,CPU复位后执行。请注意一开始的PRESERVE8,它表示在函数调用时,系统必须保证堆栈指针8byte对齐,即每次进栈或者出栈的寄存器数目必须为偶数。这是为了能够更加高效的使用STM与LDR指令对“double”或者“long long”类型的数据进行访问。IF和ENDIF这两个伪指令之间分别导入了只读代码段和读写数据段的镜像大小。

二.建立异常向量表:

ARM架构将异常向量表放置在0x0地址起始的地方,CPU启动正是从0x0开始取指执行的,而0x0地址放置的正是复位异常处理指令:LDR PC,Reset_Addr,这样就可以跳转到下面的第三部分代码执行。另外如果我们定义了SIZE_INFO,那么就在预留的异常向量地址0x14处放置代码的镜像大小。

我们还注意到这条指令:LDR PC,[PC,#-0xF20],这是IRQ对应异常处理指令,它比较特殊,如果我们熟悉s3c2410或者s3c2440,我们知道这两款芯片的启动代码中IRQ对应异常处理指令都是这样写的:LDR     PC, IRQ_Addr,这样很好理解。那么AT91SAM9261为什么不这样用?这其实是因为AT91SAM9261内置的先进中断控制器(AIC)采用硬件中断向量化,硬件中断向量化可以使到达中断处理入口所需的指令减少到只有一条,即位于地址0x18处的LDR PC,[PC,#-0xF20],当执行这条指令时,由ARM流水线结构,此时的 PC = 0x18 + 2*4 = 0x20,因此执行之后PC被赋予地址:0x20 – 0xF20 = 0xFFFFF100里的内容,即IRQ向量寄存器AIC_IVR的内容,程序跳转到相应的中断例程中。AIC_IVR被定义在AT91SAM9261.h:

AT91SAM9261的AIC有32个中断源,它将每个中断源的中断处理程序的地址存储在寄存器AIC_SVR1到AIC_SVR31(源向量寄存器1到31)。当处理器读取中断向量寄存器AIC_IVR时,其返回的就是当前中断被写入到对应的AIC_SVR的值。

三.进入复位处理部分代码,完成各项硬件初始化工作:

这部分汇编代码负责初始化必要的硬件设备,给程序运行提供一个合适的硬件环境。这里面包括SRAM控制器、总线矩阵、外部总线接口、SDRAM控制器、电源管理控制器以及看门狗这些硬件设备,具体的代码可以参考芯片手册来看,这里就不做具体分析了。

四.如果定义了RAM_INTVEC,则拷贝异常向量表到片上RAM

如果定义了RAM_INTVEC,则将异常向量表从Flash拷贝到片上RAM,当然,拷贝之后要做一下重映射,将片上RAM的起始地址重映射到0x0地址。也就是下面一步要做的事情。

五.如果定义了REMAP,则将片上RAM重映射到0x0地址:

这段代码将片上RAM重映射到0x0地址。那么什么情况需要做重映射呢?我们知道CPU启动是从0x0开始的,也就是异常向量表的起始地址。但是当我们调试程序的时候,通常是将程序放在片上的SRAM中,在AT91SAM9261中,片上SRAM的地址是从0x300000开始的(片上SRAM的初始化文件会把0x300000直接赋给PC)。当调试的程序需要进入中断的时候,PC会自动跳转到异常向量表去,但是实际上物理地址0x0上并没有异常向量表,所以需要重映射,将片上RAM重映射到0x0。否则如果调试过程中产生中断,PC跳到0x0地址找不到异常向量表,程序就会跑飞。

六.初始化Cache

这段代码负责使能指令Cache。

七.初始化紧密耦合存储器:

ARM926EJ-S处理器有一个紧密耦合存储器(TCM)接口,该接口使得处理器能直接访问独立的指令和数据TCM(ITCM和DTCM)。TCM被用于存储实时性和性能要求极高的代码,它还提供一个DMA支持机制。不像AHP访问外部存储器,访问TCM是快速的,确定的,不造成总线负荷。
这段代码负责初始化TCM,使能ITCM和DTCM。

八.为每种模式设置堆栈指针:

这段代码负责依次进入ARM的每一种模式设置该模式下的堆栈指针,因为每种模式都需要有自己的栈空间。注意因为User Mode和System Mode共用一套寄存器,所以设置一种就可以了。

九.跳转到C代码去执行:

IMPORT伪指令用于通知编译器要使用的标号在其他源文件中定义,但要在当前文件中引用,而且无论当前源文件是否引用该标号,此标号都会被加入到当前源文件的符号表中。

注意此处的__main并不是C代码中的main,而是编译器提供的专门用于启动用户程序的库函数,负责完成库函数的初始化和初始化应用程序执行环境,调用__user_initial_stackheap以初始化C代码运行需要的堆栈,初始化运行时库,最后自动跳转到main()执行。