中止是咱们嵌入式开发很常用到的一种资源和编程手法。这篇文章要点剖析arm的中止处理流程。
首要,中止是反常的一种。当产生一种反常时,处理器会进入不同的作业形式。ARM的反常和相应的形式之间的对应联系见下表:
当一个反常导致形式的改动时,ARM核主动地:
1、把cpsr保存到相应形式下的spsr
2、把pc保存到相应形式下的lr
3、设置cpsr为相应反常形式
4、设置pc为相应反常处理程序的进口地址
关于IRQ或许FIQ而言,还多一项改变:禁用相关的中止IRQ或FIQ,制止同类型的其他中止被触发。(这也是主动完成的,因而正常情况下,ARM中止不行嵌套)
从反常中止处理程序退出时,需求咱们在程序顶用软件完成下面两个操作:
1、从spsr_mode中康复数据到cpsr中
2、从lr_mode中康复内容到pc中,返回到反常中止的指令的下一条政令处履行.
2440默许的有一个反常向量表,即产生某一个反常后,会依据反常向量表设置pc为相应的处理函数进口地址。
地址 |
反常称号 |
指令 |
0x00 |
复位反常 |
B RestHandler |
0x04 |
未定义指令反常 |
B HandlerUndef |
0x08 |
软件中止反常 |
B HandlerSWI |
0x0C |
指令预取反常 |
B HandlerPabort |
0x10 |
数据预取反常 |
B HandlerDabort |
0x14 |
保存 |
|
0x18 |
IRQ中止反常 |
B HandlerIRQ |
0x1C |
FIQ中止反常 |
B HandlerFIQ |
上表中的 指令都是在2440init.s中的程序表明。
下面,咱们结合板子自带的2440test源代码中的2440init.s中的反常处理,来剖析arm中止处理的完成。
首要,在2440init.s中有:
__ENTRYb ResetHandlerb HandlerUndef ;handler for Undefined modeb HandlerSWI ;handler for SWI interruptb HandlerPabort ;handler for PAbortb HandlerDabort ;handler for DAbortb . ;reservedb HandlerIRQ ;handler for IRQ interruptb HandlerFIQ ;handler for FIQ interrupt
这儿便是相应的 反常处理向量表。程序正常发动就跳转到resethandler,如果是产生中止就跳转到handlerIRQ。关于handlerIRQ,它是用一个宏完成的。
MACRO$HandlerLabel HANDLER $HandleLabel$HandlerLabelsub sp,sp,#4 ;decrement sp(to store jump address)stmfd sp!,{r0} ;PUSH the work register to stack(lr doest push because it return to original address)ldr r0,=$HandleLabel;load the address of HandleXXX to r0ldr r0,[r0] ;load the contents(service routine start address) of HandleXXXstr r0,[sp,#4] ;store the contents(ISR) of HandleXXX to stackldmfd sp!,{r0,pc} ;POP the work register and pc(jump to ISR)MEND
上面是宏的声明。下面是详细用到宏的当地。
HandlerFIQ HANDLER HandleFIQHandlerIRQ HANDLER HandleIRQHandlerUndef HANDLER HandleUndefHandlerSWI HANDLER HandleSWIHandlerDabort HANDLER HandleDabortHandlerPabort HANDLER HandlePabort
上面的这段程序在编译的时分会被编译器打开,咱们能够将其间的IRQ相关的打开如下:
HandlerIRQ HANDLER HandleIRQ 会被下面的代码段替换:
HandlerIRQ sub sp,sp,#4 ;decrement sp(to store jump address)stmfd sp!,{r0} ;PUSH the work register to stack(lr doest push because it return to original address)ldr r0,=HandleIRQ ;load the address of HandleXXX to r0ldr r0,[r0] ;load the contents(service routine start address) of HandleXXXstr r0,[sp,#4] ;store the contents(ISR) of HandleXXX to stackldmfd sp!,{r0,pc} ;POP the work register and pc(jump to ISR)
因而,产生中止时,就会b HandlerIRQ,跳转到上面的代码进行履行。依照上面的流程,处理器会把HandleIRQ地址中所存储的数 交给pc指针,作为下一条指令的地址,然后履行。那么HandleIRQ地址中存储的数是什么呢?
在2440init.s中有这样一段程序:
ldr r0,=HandleIRQ ;This routine is neededldr r1,=IsrIRQ ;if there isnt subs pc,lr,#4 at 0x18, 0x1cstr r1,[r0]
从这儿,能够看出,HandleIRQ中存的是IsrIRQ。所以处理器会跳转到isrIRQ中履行。
IsrIRQsub sp,sp,#4 ;reserved for PCstmfd sp!,{r8-r9}ldr r9,=INTOFFSETldr r9,[r9]ldr r8,=HandleEINT0add r8,r8,r9,lsl #2ldr r8,[r8]str r8,[sp,#8]ldmfd sp!,{r8-r9,pc}
在上面的程序中,INTOFFSET表明的是中止号关于EINT0的偏移号。这样核算得到中止向量号之后,跳转到中止函数进行处理。关于,上面的程序古怪的一点是没有看到康复cpsr和pc指针。因而,能够揣度,关于中止函数在ADS中有特别的声明方法,如:static void __irq Uart_DMA_ISR(void)。像这种声明方法,在编译的时分,编译器会主动在函数的结尾增加康复cpsr和pc的句子。别的, 寄存器r0-r12也是需求维护的,由于在中止函数和本来的上下文中都会用到,所以,我以为 编译器在中止处理函数中对r0-r12也进行了维护和康复。
别的,在ucosII中,对IsrIRQ函数进行了修正,咱们后边再进行剖析。
别的,咱们用软件完成了一套中止向量表:
ALIGNAREA RamData, DATA, READWRITE^ _ISR_STARTADDRESS ; _ISR_STARTADDRESS=0x33FF_FF00HandleReset # 4HandleUndef # 4HandleSWI # 4HandlePabort # 4HandleDabort # 4HandleReserved # 4HandleIRQ # 4HandleFIQ # 4;Dont use the label IntVectorTable,;The value of IntVectorTable is different with the address you think it may be.;IntVectorTable;@0x33FF_FF20HandleEINT0 # 4HandleEINT1 # 4HandleEINT2 # 4HandleEINT3 # 4HandleEINT4_7 # 4HandleEINT8_23 # 4HandleCAM # 4 ; Added for 2440.HandleBATFLT # 4HandleTICK # 4HandleWDT # 4HandleTIMER0 # 4HandleTIMER1 # 4HandleTIMER2 # 4HandleTIMER3 # 4HandleTIMER4 # 4HandleUART2 # 4;@0x33FF_FF60HandleLCD # 4HandleDMA0 # 4HandleDMA1 # 4HandleDMA2 # 4HandleDMA3 # 4HandleMMC # 4HandleSPI0 # 4HandleUART1 # 4HandleNFCON # 4 ; Added for 2440.HandleUSBD # 4HandleUSBH # 4HandleIIC # 4HandleUART0 # 4HandleSPI1 # 4HandleRTC # 4HandleADC # 4;@0x33FF_FFA0
之前,咱们在产生中止时,pc指针就跳转到了 HandleIRQ地址中所存储的数 出履行。也便是说在HandleIRQ中存的是反常处理函数的进口地址。这便是反常处理向量表的效果。
所以,咱们能够看出,关于2440init.s完成的反常处理,选用的是两级向量表机制。 榜首级向量表是arm核自己完成的,产生相应的反常时,pc指针跳转到0x18地址中存的数,作为进口地址。 第二级向量表是由Handler宏完成的,持续跳转到 HandleIRQ地址中存的数,持续履行。
而关于IRQ来讲,还有第三级的向量表,在IsrIRQ中,又会依据中止号,比方uart2的中止,跳转到 HandleUart2地址中 存的数,持续履行。
在2440init.s中,并没有给HandleUndef等这些地址处赋值,因而,一旦履行到,程序就会跑飞。