您好,欢迎访问三七文档
当前位置:首页 > 行业资料 > 其它行业文档 > ARM 启动代码详解
ARM启动代码详解(Vectors.c、Init.s、Target.c、Target.h)2010-05-1516:03启动代码是芯片复位后进入C语言的main()函数前执行的一段代码,主要是为运行C语言程序提供基本运行环境,如初始化存储器系统等。ARM公司只设计内核,不自己生产芯片,只是把内核授权给其它厂商,其它厂商购买了授权且加入自己的外设后生产出各具特色的芯片。这样就促进了基于ARM处理器核的芯片多元化,但也使得每一种芯片的启动代码差别很大,不易编写出统一的启动代码。ADS(针对ARM处理器核的C语言编译器)的策略是不提供完整的启动代码,启动代码不足部分或者由厂商提供,或者自己编写。启动代码划分为4个文件:Vectors.c、Init.s、Target.c、Target.h。Vectors.c包含异常向量表、堆栈初始化及中断服务程序与C程序的接口。Init.s包含统初始化代码,并跳转到ADS提供的初始化代码。Target.c和Target.h包含目标板特殊的代码,包括异常处理程序和目标板初始化程序。这样做的目的是为了尽量减少汇编代码,同时把不需要修改的代码独立出来以减少错误。§4.2.1Vectors.c文件的编写§4.2.1.1中断向量表VectorsLDRPC,ResetAddrLDRPC,UndefinedAddrLDRPC,SWI_AddrLDRPC,PrefetchAddrLDRPC,DataAbortAddrDCD0xb9205f80LDRPC,[PC,#-0xff0]LDRPC,FIQ_AddrResetAddrDCDResetUndefinedAddrDCDUndefinedSWI_AddrDCDSoftwareInterruptPrefetchAddrDCDPrefetchAbortDataAbortAddrDCDDataAbortnouseDCD0IRQ_AddrDCDIRQ_HandlerFIQ_AddrDCDFIQ_Handler异常是由内部或外部源产生的以引起处理器处理的一个事件。ARM处理器核支持7种类型的异常。异常出现后,CPU强制从异常类型对应的固定存储地址开始执行程序。这个固定的地址就是异常向量。向量从上到下依次为复位、未定义指令异常、软件中断、预取指令中止、预取数据中止、保留的异常、IRQ和FIQ。IRQ向量“LDRPC,[PC,#-0xff0]”使用的指令与其它向量不同。在正常情况下这条指令所在地址为0X00000018。当CPU执行这条指令但还没有跳转时,PC的值为0X00000020,0X00000020减去0X00000FF0为0XFFFFF030,这是向量中断控制器(VIC)的特殊寄存器VICVectAddr。这个寄存器保存当前将要服务的IRQ的中断服务程序的入口,用这一条指令就可以直接跳转到需要的中断服务程序中。至于在保留的异常向量“DCD0xb9205f80”位置填数据0xb9205f8是为了使向量表中所有的数据32位累加和为0。§4.2.1.2初始化CPU堆栈InitStackMOVR0,LRMSRCPSR_c,#0xd2;设置中断模式堆栈LDRSP,StackIrqMSRCPSR_c,#0xd1;设置快速中断模式堆栈LDRSP,StackFiqMSRCPSR_c,#0xd7;设置中止模式堆栈LDRSP,StackAbtMSRCPSR_c,#0xdb;设置未定义模式堆栈LDRSP,StackUndMSRCPSR_c,#0xdf;设置系统模式堆栈LDRSP,StackSysMOVPC,R0StackIrqDCD(IrqStackSpace+IRQ_STACK_LEGTH*4-4)StackFiqDCD(FiqStackSpace+FIQ_STACK_LEGTH*4-4)StackAbtDCD(AbtStackSpace+ABT_STACK_LEGTH*4-4)StackUndDCD(UndtStackSpace+UND_STACK_LEGTH*4-4)StackSysDCD(SysStackSpace+SYS_STACK_LEGTH*4-4);/*分配堆栈空间*/AREAMyStacks,DATA,NOINITIrqStackSpaceSPACEIRQ_STACK_LEGTH*4;中断模式堆栈FiqStackSpaceSPACEFIQ_STACK_LEGTH*4;快速中断模式堆栈AbtStackSpaceSPACEABT_STACK_LEGTH*4;中止义模式堆栈UndtStackSpaceSPACEUND_STACK_LEGTH*4;未定义模式堆栈SysStackSpaceSPACESYS_STACK_LEGTH*4;系统模式堆栈因为程序需要切换模式,而且程序退出时CPU的模式已经不再是管理模式而是系统模式LR已经不再保存返回程序地址,所以程序首先把返回地址保存到R0中,同时使用R0返回。然后程序把处理器模式转化为IRQ模式,并设置IRQ模式的堆栈指针。其中变量Stacklrq保存着IRQ模式的堆栈指针的初始值,Irqstackspace是分配给IRQ模式的堆栈空间的开始地址,IRQ_STACK_LEGTH是用户定义的常量,用于设置IRQ模式的堆栈空间的大小。程序使用同样的方法设置FIQ模式堆栈指针、中止模式堆栈指针、未定义堆栈指针和系统模式堆栈指针。程序使用编译器分配的空间作为堆栈,而不是按照通常的做法把堆栈分配到RAM的顶端,之所以这样是因为这样做不必知道RAM顶端位置,移植更加方便;编译器给出的占用RAM空间的大小就是实际占用的大小,便于控制RAM的分配。对于LPC2106来说,中止模式是不需要分配堆栈空间的,这是因为LPC2106没有外部总线,也没有虚拟内存机制,如果出现取数据中止或取指令中止肯定是程序有问题。而一般情况下也不需要模拟协处理器指令或扩充指令,未定义中止也就意味着程序有错误,也不需要分配堆栈空间。§4.2.1.3异常处理代码与C语言的接口程序μC/OS-Ⅱ中断服务子程序流程图如图4-1所示:图4-1中断服务子程序流程图异常处理代码与C语言的接口程序如下:MACRO$IRQ_LabelHANDLER$IRQ_ExceptionEXPORT$IRQ_Label;输出的标号IMPORT$IRQ_Exception;引用的外部标号$IRQ_LabelSUBLR,LR,#4;计算返回地址STMFDSP!,{R0-R3,R12,LR};保存任务环境MRSR3,SPSR;保存状态STMFDSP!,{R3}LDRR2,=OSIntNesting;OSIntNesting++LDRBR1,[R2]ADDR1,R1,#1STRBR1,[R2]BL$IRQ_Exception;调用c语言的中断处理程序MSRCPSR_c,#0x92;关中断BLOSIntExitLDRR0,=OSTCBHighRdyLDRR0,[R0]LDRR1,=OSTCBCurLDRR1,[R1]CMPR0,R1LDMFDSP!,{R3}MSRSPSR_cxsf,R3LDMEQFDSP!,{R0-R3,R12,PC}^;不进行任务切换LDRPC,=OSIntCtxSw;进行任务切换MENDUndefined;未定义指令bUndefinedPrefetchAbort;取指令中止bPrefetchAbortDataAbort;取数据中止bDataAbortIRQ_HandlerHANDLERIRQ_Exception;中断FIQ_Handler;快速中断bFIQ_HandlerTimer0_HandlerHANDLERTimer0;定时器0中断未定义指令异常、取指令中止异常、取数据中止异常均是死循环,其中原因在上一小节已经说明。而快速中断在本应用中并未使用,所以也设置为死循环。LPC2106使用向量中断控制器,各个IRQ中断的人口不一样,所以使用了一个宏来简化中断服务程序与C语言的接口编写。由ARM处理器核的文档可知,处理器进入IRQ中断服务程序时(LR-4)的值为中断返回地址,为了使任务无论在主动放弃CPU时还是中断时堆栈结构都一样,在这里先把LR减4。其它的部分与μC/OS-Ⅱ要求的基本一致。ARM处理核在进入中断服务程序时处理器模式变为IRQ模式,与任务的模式不同,它们的堆栈指针SP也不一样,而寄存器应当保存到用户的堆栈中,为了减少不必要的CPU时间和RAM空间的浪费,本移植仅在必要时将处理器的寄存器保存到用户的堆栈中,其它时候还是保存到IRQ模式的堆栈中。同时,从编译器的函数调用规范可知,C语言函数返回时,寄存器R4—R11、SP不会改变,所以只需要保存CPSR、R0—R3、R12和返回地址LR,在后面保存CPSR是为了必要时将寄存器保存到用户堆栈比较方便。在异常处理代码与C语言的接口程序中没有与中断服务子程序流程图中的判断语句对应的语句。判断语句是为了避免在函数OSIntCtxsw()调整堆栈指针,这个调整量是与编译器、编译器选项、μC/OS-Ⅱ配置选项都相关的变量。在这里进行这些处理相对其它处理器结构可能增加的处理器时间很少,但对于ARM来说,由于中断(IRQ)有独立的堆栈,在这里这样做就需要把所有寄存器从中断的堆栈拷贝到任务的堆栈,需要花费比较多的额外时间。而变量OSIntNesting为0时,并不一定会进行任务切换,所以本移植没有与之对应的程序,而在函数OSIntCtxsw()中做这一项工作。这样,仅在需要时才处理这些事物,程序效率得以提高。在中断调用后,如果需要任务切换,则变量OSTCBHighRdy和变量OSTCBCur的值不同;如果不需要任务切换这两个变量则相同。本移植通过判断这两个变量来决定是进行任务切换,还是不进行任务切换。通过比较,如果需要任务切换则执行“LDRPC,=OSIntCtxSw”跳转到OSIntCtxSw处进行任务切换;如果不需要任务切换则执行“LDMEQFDSP!,{R0-R3,R12,PC}^”中断返回。这里需要对“MSRCPSR_c,#0x92”说明下,这条指令的作用是关IRQ中断。因为中断(IRQ)模式的LR寄存器在处理器响应中断时用于保存中断返回地址,所以在处理器响应中断时中断(IRQ)模式的LR寄存器不能保存有效数据。而BL指令要用LR寄存器保存BL下一条指令的位置,所以在中断(IRQ)模式时,在BL指令之前必须关中断,在保存LR后才能开中断。§4.2.2Target.c文件的编写为了使系统基本能够工作,必须在进人main()函数前对系统进行一些基本的初始化工作,这些工作由函数TargetResetInit()完成。voidTargetResetInit(void){uint32i;uint32*cp1;uint32*cp2;externvoidVectors(void);/*拷贝向量表,保证在flash和ram中程序均可正确运行*/cp1=(uint32*)Vectors;cp2=(uint32*)0x40000000;for(i=0;i2*8;i++){*cp2++=*cp1++;}MEMMAP=0x2;PINSEL0=(PINSEL0&0xFFFF0000)|UART0_PCB_PINSEL_CFG|0x50;PLLCON=1;/*设置系统各部分时钟*/VPBDIV=0;PLLCFG=0x23;PLLFEED=0xaa;PLLFEED=0x55;while((PLLSTAT&(110))==0);PLLCON=3;PLLFEED=0xaa;PLLFEED=0x55;MAMCR=2;/*设置存储器加速模块*/#ifFcclk20000000MAMTIM=1;#else#ifFcclk40000000MAMTIM=2;#elseMAMTIM=3;#endif#endif首先向量表拷贝到RAM底部,加上这部分是为了代码无论从Flash基地址开始编译还是从RAM基地址开始编译程序均运行正确。而把RAM底部映射到向量表“MEMMA
本文标题:ARM 启动代码详解
链接地址:https://www.777doc.com/doc-23587 .html