ARM启动代码详细注释syd1682009-07-232410Init.s包括了板子上电后的初始化过程,具体有以下几个步骤:1.屏蔽所有中断,关看门狗。2.根据工作频率设置PLL寄存器3.初始化存储控制相关寄存器4.初始化各模式下的栈指针5.设置缺省中断处理函数6.将数据段拷贝到RAM中,将零初始化数据段清零7.跳转到C语言Main入口函数中;=========================================;NAME:2410INIT.S;DESC:Cstartupcodes;Configurememory,ISR,stacks;InitializeC-variables;HISTORY:;2002.02.25:kwtark:ver0.0;2002.03.20:purnnamu:AddsomefunctionsfortestingSTOP,POWER_OFFmode;2002.04.10:SJS:subinterruptdisable0x3ff-0x7ff;=========================================GEToption.s;GET相当INCLUDE将一个源文件包含到当前源文件,这里表示包含option.s,并在当前位置进行汇编GETmemcfg.sGET2410addr.sBIT_SELFREFRESHEQU(122);SDRAM/DRAM刷新控制器bit22REFMD位0:CBR/AUTOREFRESH1:SHIFREFRESH;下面是对arm处理器模式寄存器对应值的常数定义,arm处理器中有一个CPSR程序状态寄存器,后五位决定目前的处理器模式。;Pre-definedconstants;PSR模式位USERMODEEQU0x10;用户模式FIQMODEEQU0x11;FIQ快速中断模式IRQMODEEQU0x12;中断模式SVCMODEEQU0x13;超级用户模式ABORTMODEEQU0x17;中止模式UNDEFMODEEQU0x1b;未定义指令模式MODEMASKEQU0x1f;系统模式NOINTEQU0xc0;禁止IRQ和FIQ中断;;定义各模式堆栈地址_STACK_BASEADDRESS在option.s中,_STACK_BASEADDRESSEQU(SDRAM_END-0x8000);0x33ff8000UserStackEQU(_STACK_BASEADDRESS-0x3800);0x33ff4800~;用户模式堆栈SVCStackEQU(_STACK_BASEADDRESS-0x2800);0x33ff5800~0x3ff47ff;超级用户模式堆栈4kUndefStackEQU(_STACK_BASEADDRESS-0x2400);0x33ff5c00~0x3ff57ff;未定义指令模式堆栈1kAbortStackEQU(_STACK_BASEADDRESS-0x2000);0x33ff6000~0x3ff5bff;中止模式堆栈1kIRQStackEQU(_STACK_BASEADDRESS-0x1000);0x33ff7000~0x3ff5fff;中断模式堆栈4kFIQStackEQU(_STACK_BASEADDRESS-0x0);0x33ff8000~0x33ff6fff;快速中断模式堆栈4k;arm处理器有两种工作状态;1.arm:32位这种工作状态下执行字对齐的arm指令;2.Thumb:16位这种工作状;态执行半字对齐的Thumb指令;因为处理器分为16位32位两种工作状态程序的编译器也是分16位和32两种编译方式;所以下面的程序用于根据处理器工作状态确定编译器编译方式;code16伪指令指示汇编编译器,后面的指令为16位的thumb指令;code32伪指令指示汇编编译器,后面的指令为32位的arm指令;这段是为了统一目前的处理器工作状态和软件编译方式(16位编译环境使用tasm.exe编译);===========================================================================================;Checkiftasm.exe(armasm-16...@ADS1.0)isused.检查是否是用tasm.exe进行16位编译GBLLTHUMBCODE;声明一个全局变量并初始化为{FALSE}[{CONFIG}=16;ifconfig=16这里表示用16位编译方式THUMBCODESETL{TRUE};SETL给全局变量赋值,;设置THUMBCODE为trueCODE32;转入32位编译模式|;|等同ELSETHUMBCODESETL{FALSE};设置THUMBCODE为false];]等同ENDIF;==========================================================================================MACRO;MACRO定义宏MOV_PC_LR;宏名MOV_PC_LR[THUMBCODE;ifTHUMBCODE=truebxlr;THUMBCODE模式上返回ARM状态|;elsemovpc,lr;ARM状态下返回];endifMEND;宏结束;================================================================MACRO;宏定义开始MOVEQ_PC_LR;宏名为MOVEQ_PC_LR[THUMBCODE;ifTHUMBCODE=truebxeqlr;EQ相等则跳转回ARM状态|;elsemoveqpc,lr;????];endifMEND;宏结束;==================================================================;注意下面这段程序是个宏定义;下面包含的HandlerXXXHANDLERHandleXXX将都被下面这段程序展开;这段程序用于把中断服务程序的首地址装载到pc中,有人称之为“加载程序”。;本初始化程序定义了一个数据区(在文件最后),34个字空间,存放相应中断服务程序的首地址。每个字,即4字节;空间都有一个标号,以Handle***命名。;在向量中断模式下使用“加载程序”来执行中断服务程序。;这里就必须讲一下向量中断模式和非向量中断模式的概念;向量中断模式是当cpu读取位于0x18处的IRQ中断指令的时候,系统自动读取对应于该中断源确定地址上的指令取代0x18处的指令,;通过跳转指令系统就直接跳转到对应地址;函数中节省了中断处理时间提高了中断处理速度标例如ADC中断的向量地址为0xC0,则在0xC0处放如下代码:;ldrPC,=HandlerADC当ADC中断产生的时候系统会自动跳转到HandlerADC函数中;非向量中断模式处理方式是一种传统的中断处理方法,当系统产生中断的时候,系统将interruptpending寄存器中对应标志位置位;然后跳转到位于0x18处的统一中断;函数中;该函数通过读取interruptpending寄存器中对应标志位来判断中断源,并根据优先级关系再跳到;对应中断源的处理代码中;大致作用是把宏的第一个参数$HandlerLabel转变为一个标号,然后让程序跳转到第二个参数$HandleLabel(为一个地址)对应的值的地址去。下面一句一句分析一下,为了便于分析,假设sp=0x33ff8000,$HandleLabel=0x33ffff00,[0x33ffff00]=0x10000000,r0=0x56001234:$HandlerLabelHANDLER$HandleLabel说明宏名字叫HANDLER,有一个参数$HandlerLabel,它定义了一个标号subsp,sp,#4把栈顶指针减4,留出一个字的空间(用于保存跳转地址的值),sp=0x33ff7ffcstmfdsp!,{r0}首先把sp减4(sp=0x33ff7ff8),然后把将要使用的r0入栈,此时[0x33ff7ff8]=0x56001234ldrr0,=$HandleLabel给寄存器r0赋值,r0=0x33ffff00ldrr0,[r0]给寄存器r0赋值,r0=0x10000000strr0,[sp,#4]把寄存器r0保存到0x33ff7ffc(sp=0x33ff7ff8+4),此时[0x33ff7ffc]=0x10000000ldmfdsp!,{r0,pc}把栈顶的两个字弹出,分别保存到r0、pc,此时sp=0x33ff8000,r0=0x56001234,pc=0x10000000,通过比较不难发现,sp和r0在执行前后都没有变化,程序就跳转到0x10000000处执行;所以,通过上面的分析可以看出,$HandlerLabelHANDLER$HandleLabel是让PC跳转到$HandleLabel中存放的地址执行。MACRO;宏定义$HandlerLabelHANDLER$HandleLabel;$HandlerLabel行号展开时可替换成相应的符号;HANDLER宏名;$HandleLabel宏的形参$HandlerLabel;展开时可替换成相应的符号subsp,sp,#4;sp-sp-4,空出的空间用于存放跳转地址假如sp原来为0x100,则现在sp为0x96stmfdsp!,{r0};sp=sp-4,并将r0入栈,现在sp=0x92r0存入0x96ldrr0,=$HandleLabel;获取存放跳转地址的标号HandleLabel到r0,假如HandleLabel为0x1000,则现在r0为0x1000ldrr0,[r0];将HandleLabel中存放的跳转地址送给r0,r0为0x1000中的值,假如为0x3000strr0,[sp,#4];将r0存放到sp+4的地方,ldmfdsp!,{r0,pc};将中断函数首地址出栈,放入程序指针中,系统将跳转到对应中断处理函数MEND;宏定义结束;==================================================================;一个ARM由RO,RW,ZI三个段组成,其中RO(只读)为代码段,RW(读写)是已经初始化的全局变量,ZI是未初始化的全局变量;对应于GNU工具的概念是TEXT,DATA,BSS;bootloader要将RW段复制到RAM中,并将ZI段清零编译器使用下列方式来记录各段的起始和结束地址;|Image$$RO$$Base|;RO段起始地址;|Image$$RO$$Limit|;RO段结束地址加1;|Image$$RW$$Base|;RW段起始地址;|Image$$RW$$Limit|;RW段结束地址加1;|Image$$ZI$$Base|;ZI段起始地址;|Image$$ZI$$Limit|;ZI段结束地址加1;这些标号的值是通过编译器设定来确定的如编译软件中对ro-base和rw-base的设定分别为:ro-base=0xc000000rw-base=0xc5f0000IMPORT|Image$$RO$$Limit|;EndofROMcode(=startofROMdata)IMPORT|Image$$RW$$Base|;BaseofRAMtoinitialiseIMPORT|Image$$ZI$$Base|;BaseandlimitofareaIMPORT|Image$$ZI$$Limit|;tozeroinitialise;下面语句是声明引用外部函数IMPORTMain;C程序(在2410test.c)Themainentryofmonprogram;===================================================AREAInit,CODE,READONLY;