ARM启动代码详解.docx
《ARM启动代码详解.docx》由会员分享,可在线阅读,更多相关《ARM启动代码详解.docx(10页珍藏版)》请在冰豆网上搜索。
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中断向量表
Vectors
LDRPC,ResetAddr
LDRPC,UndefinedAddr
LDRPC,SWI_Addr
LDRPC,PrefetchAddr
LDRPC,DataAbortAddr
DCD0xb9205f80
LDRPC,[PC,#-0xff0]
LDRPC,FIQ_Addr
ResetAddrDCDReset
UndefinedAddrDCDUndefined
SWI_AddrDCDSoftwareInterrupt
PrefetchAddrDCDPrefetchAbort
DataAbortAddrDCDDataAbort
nouseDCD0
IRQ_AddrDCDIRQ_Handler
FIQ_AddrDCDFIQ_Handler
异常是由内部或外部源产生的以引起处理器处理的一个事件。
ARM处理器核支持7种类型的异常。
异常出现后,CPU强制从异常类型对应的固定存储地址开始执行程序。
这个固定的地址就是异常向量。
向量从上到下依次为复位、未定义指令异常、软件中断、预取指令中止、预取数据中止、保留的异常、IRQ和
FIQ。
IRQ向量“LDRPC,[P(#-OxffO]”使用的指令与其它向量不同。
在正常情况下这条指令所在地址为0X00000018。
当CPU执行这条指令但还没有跳转时,
PC的值为0X00000020,0X00000020减去0X00000FF0为0XFFFFF030,这是向量中断控制器(VIC)的特殊寄存器VICVectAddr。
这个寄存器保存当前将要服务的IRQ的中断服务程序的入口,用这一条指令就可以直接跳转到需要的中断服务程序中。
至于在保留的异常向量“DCD0xb9205f80”位置填数据
0xb9205f8是为了使向量表中所有的数据32位累加和为0。
§4.2.1.2初始化CPU堆栈
InitStack
MOVR0,LR
MSRCPSR_c,#0xd2;设置中断模式堆栈
LDRSP,StackIrq
MSRCPSR_c,#0xd1;设置快速中断模式堆栈
LDRSP,StackFiq
MSRCPSR_c,#0xd7;设置中止模式堆栈
LDRSP,StackAbt
MSRCPSR_c,#0xdb;设置未定义模式堆栈
LDRSP,StackUnd
MSRCPSR_c,#0xdf;设置系统模式堆栈
LDRSP,StackSys
MOVPC,R0
StackIrqDCD(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,NOINIT
IrqStackSpaceSPACEIRQ_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语言的接口程序
[iC/OS-U中断服务子程序流程图如图4-1所示:
51kaifa#com
[保存宅部匚ijp奇存器
调用OShrrtEm前或OSmtNetmgM接加1
Y
工
中新开中新
|执行用户代阿做中断服歹
丽除中断源
OSTCBCur-OSTC'BStkl'tr=£F
|恢复所有&PTJ爵存需「
执行中斬逅回指令
图4-1中断服务子程序流程图
异常处理代码与C语言的接口程序如下:
MACRO
$IRQ_LabelHANDLER$IRQ_Exception
EXPORT$IRQ_Label;输出的标号
IMPORT$IRQ_Exception;引用的外部标号
[03恤BCD
ApdMBinaO丄SO-033CH1御uisoi日
H申沃-乙6X0#,o_dSd9dSI/\lMM-uo!
id8ox3_o^i$-|a
[乙3113日3丄sL#UGCI\/[乙513日BCD++6u!
1son1U|SO-buqsoNluisO-乙33CI1{SdJ^dSadlAI丄S鞏#川-dSdStdSdlAI驾曲幺马型制-{3丁乙L3£3-03}'idSGzll/\l丄SWWUW#44•^dn'dians
|9qe-|031$
LDRR1,[R1]
CMPR0,R1
LDMFDSP!
{R3}
MSRSPSR_cxsf,R3
LDMEQFDSP!
{R0-R3,R12,PCF;不进行任务切换
LDRPC,=OSIntCtxSw;进行任务切换
MEND
Undefined;未定义指令
bUndefined
PrefetchAbort;取指令中止
bPrefetchAbort
DataAbort;取数据中止
bDataAbort
IRQ_HandlerHANDLERIRQ_Exception;中断
FIQ_Handler;快速中断
bFIQ_Handler
Timer0_HandlerHANDLERTimer0;定时器0中断
未定义指令异常、取指令中止异常、取数据中止异常均是死循环,其中原因在上一小节已经说明。
而快速中断在本应用中并未使用,所以也设置为死循环。
LPC2106使用向量中断控制器,各个IRQ中断的人口不一样,所以使用了一个宏来简化中断服务程序与C语言的接口编写。
由ARM处理器核的文档可知,处理器进入IRQ中断服务程序时(LR-4)的值为中断返回地址,为了使任务无论在主动放弃CPU时还是中断时堆栈结构都一样,在这里先把LR减4。
其它的部分与yC/OS-U要求的基本一致。
ARM处理核在进入中断服务程序时处理器模式变为IRQ模式,与任务的模式不同,它们的堆栈指针SP也不一样,而寄存器应当保存到用户的堆栈中,为了减少不必要的CPU时间和RAM空间的浪费,本移植仅在必要时将处理器的寄存器保存到用户的堆栈中,其它时候还是保存到IRQ模式的堆栈中。
同时,从编译器的函数调用规范可知,C语言函数返回时,
寄存器R4—R11、SP不会改变,所以只需要保存CPSR、R0—R3、R12和返回地址LR,在后面保存CPSR是为了必要时将寄存器保存到用户堆栈比较方便。
在异常处理代码与C语言的接口程序中没有与中断服务子程序流程图中的判断语句对应的语句。
判断语句是为了避免在函数OSIntCtxsw()调整堆栈指针,这个调整量是与编译器、编译器选项、yC/OS-U配置选项都相关的变量。
在这里进行这些处理相对其它处理器结构可能增加的处理器时间很少,但对于ARM来说,由于中断(IRQ)有独立的堆栈,在这里这样做就需要把所有寄存器从中断的堆栈拷贝到任务的堆栈,需要花费比较多的额外时间。
而变量
OSIntNesting为0时,并不一定会进行任务切换,所以本移植没有与之对应的程序,而在函数OSIntCtxsw()中做这一项工作。
这样,仅在需要时才处理这些事物,程序效率得以提高。
在中断调用后,如果需要任务切换,则变量OSTCBHighRdy和变量
OSTCBCur的值不同;如果不需要任务切换这两个变量则相同。
本移植通过判断这两个变量来决定是进行任务切换,还是不进行任务切换。
通过比较,如果需要任务切换则执行“LDRPC,=OSIntCtxSw”跳转到txSw处进行任务
切换;如果不需要任务切换则执行
回。
LDMEQF
中断返
这里需要对“MSRCPSR_c,#0x92”说明下,这条指令的作用是RQ中
断。
因为中断(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;i<2*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&(1<<10))==0);
PLLCON=3;
PLLFEED=0xaa;
PLLFEED=0x55;
MAMCR=2;/*设置存储器加速模块*/
#ifFcclk<20000000
MAMTIM=1;
#else
#ifFcclk<40000000
MAMTIM=2;
#else
#endif
#endif
首先向量表拷贝到RAM底部,加上这部分是为了代码无论从Flash基地址开始编译还是从RAM基地址开始编译程序均运行正确。
而把RAM底部映射到向量表“MEMMAP=0x2”也是为了同一个目的。
至于复制16个字而不是8个字,是因为后8个字存储跳转的地址是通过PC指针间接寻址的,它们与对应指令(在向量表中)相对位置是不能变化的。
因为在进入多任务环境前使用了一些外设,部分外设使用了芯片的引脚,而LPC2106的所有引脚都是多功能的,所以需要设置引脚功能。
同时串口也进行了设置。
时钟是芯片各部分正常工作的基础,虽然时钟可以在任何时候设置,但为了避免混乱,最好在进入main()函数前设置。
程序首先使能PLL但不连接PLL,然后设置外设时钟(VPB时钟pclk)与系统时钟(cclk)的分频比。
接着设置PLL的乘因子和除因子。
设置完成后,使用
“PLLFEED=0xaa;PLLFEED=0x55;”的访问序列把数据正确写人硬件,并等待
PLL跟踪完成。
最后,使能PLL,并使PLL联上系统。
本应用外接的晶振频率(Fosc)
为11.0592MHz,倍增器的值M=4,所以处理器时钟(Fcclk)为44.2368MHz。
为了使电流控制振荡器频率(Fcco)满足156-320MHZ,所以分频器的值P=2,使得Fcco=FcclkX2XP=176.9472MHzMP取分频器的分频值为1/4,所以外设时钟(Fpclk)=Fcclk/4=11.0592MHz,则记数周期为0.09042g,定时0.2ms,则记数值为2212个,这些时钟的定义都在config.h文件中。
用户程序最终是要在Flash中运行的,而系统复位时Flash是以最低速度运行,这对发挥芯片的性能极其不利。
虽然存储器加速模块可以在任何时候设置,
但为了避免混乱,最好在进入main()函数前设置。
首先使存储器加速模块全速工作,然后根据系统主时钟利用条件编译将Flash的访问时钟设置到合适的值。
§423Init.s文件的编写
由于LPC2106微控制器的存储系统比较简单,所以系统初始化代码也比较
简单,代码如下:
Reset
BLInitStack;初始化堆栈
BLTargetResetInit;目标板基本初始化
B__main;跳转到c语言入口
在芯片复位在芯片复位时程序会跳转到标号Reset处,程序首先调用
Initstack初始化各种模式的堆栈,然后调用TargetResetlnit对系统进行基本初始化,最后跳转到ADS提供的启动代码__main。
—main是ADS提供的启动代码起始位置,它初始化库并最终引导CPU进入main函数。
类别:
Arm|转贴忙‘|添加到搜藏|分享到i贴吧|浏览(428)|评论(0)®
篇:
pic单片机键盘程序下一篇:
LPC系列ARM7startup.s启动代…