ARM處理器架構(gòu)異常/中斷處理
首先,中斷是異常的一種。當(dāng)發(fā)生一種異常時(shí),處理器會(huì)進(jìn)入不同的工作模式。ARM的異常和相應(yīng)的模式之間的對(duì)應(yīng)關(guān)系見(jiàn)下表:
當(dāng)一個(gè)異常導(dǎo)致模式的改變時(shí),ARM核自動(dòng)地:
1、把cpsr保存到相應(yīng)模式下的spsr
2、把pc保存到相應(yīng)模式下的lr
3、設(shè)置cpsr為相應(yīng)異常模式
4、設(shè)置pc為相應(yīng)異常處理程序的入口地址
對(duì)于IRQ或者FIQ而言,還多一項(xiàng)變化:禁用相關(guān)的中斷IRQ或FIQ,禁止同類型的其他中斷被觸發(fā)。(這也是自動(dòng)實(shí)現(xiàn)的,因此正常情況下,ARM中斷不可嵌套)
從異常中斷處理程序退出時(shí),需要我們?cè)诔绦蛑杏密浖?shí)現(xiàn)下面兩個(gè)操作:
1、從spsr_mode中恢復(fù)數(shù)據(jù)到cpsr中
2、從lr_mode中恢復(fù)內(nèi)容到pc中,返回到異常中斷的指令的下一條政令處執(zhí)行.
2440默認(rèn)的有一個(gè)異常向量表,即發(fā)生某一個(gè)異常后,會(huì)根據(jù)異常向量表設(shè)置pc為相應(yīng)的處理函數(shù)入口地址。
地址 | 異常名稱 | 指令 |
0x00 | 復(fù)位異常 | B RestHandler |
0x04 | 未定義指令異常 | B HandlerUndef |
0x08 | 軟件中斷異常 | B HandlerSWI |
0x0C | 指令預(yù)取異常 | B HandlerPabort |
0x10 | 數(shù)據(jù)預(yù)取異常 | B HandlerDabort |
0x14 | 保留 | |
0x18 | IRQ中斷異常 | B HandlerIRQ |
0x1C | FIQ中斷異常 | B HandlerFIQ |
上表中的 指令都是在2440init.s中的程序表示。
下面,我們結(jié)合板子自帶的2440test源代碼中的2440init.s中的異常處理,來(lái)分析arm中斷處理的實(shí)現(xiàn)。
首先,在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
這里就是相應(yīng)的 異常處理向量表。程序正常啟動(dòng)就跳轉(zhuǎn)到resethandler,如果是發(fā)生中斷就跳轉(zhuǎn)到handlerIRQ。對(duì)于handlerIRQ,它是用一個(gè)宏實(shí)現(xiàn)的。
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上面的這段程序在編譯的時(shí)候會(huì)被編譯器展開(kāi),我們可以將其中的IRQ相關(guān)的展開(kāi)如下:
HandlerIRQ HANDLER HandleIRQ 會(huì)被下面的代碼段替換:
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)
因此,發(fā)生中斷時(shí),就會(huì)b HandlerIRQ,跳轉(zhuǎn)到上面的代碼進(jìn)行執(zhí)行。按照上面的流程,處理器會(huì)把HandleIRQ地址中所存儲(chǔ)的數(shù) 付給pc指針,作為下一條指令的地址,然后執(zhí)行。那么HandleIRQ地址中存儲(chǔ)的數(shù)是什么呢?
在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。所以處理器會(huì)跳轉(zhuǎn)到isrIRQ中執(zhí)行。
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表示的是中斷號(hào)對(duì)于EINT0的偏移號(hào)。這樣計(jì)算得到中斷向量號(hào)之后,跳轉(zhuǎn)到中斷函數(shù)進(jìn)行處理。對(duì)于,上面的程序奇怪的一點(diǎn)是沒(méi)有看到恢復(fù)cpsr和pc指針。因此,可以推斷,對(duì)于中斷函數(shù)在ADS中有特殊的聲明方式,如:static void __irq Uart_DMA_ISR(void)。像這種聲明方式,在編譯的時(shí)候,編譯器會(huì)自動(dòng)在函數(shù)的末尾添加恢復(fù)cpsr和pc的語(yǔ)句。另外, 寄存器r0-r12也是需要保護(hù)的,因?yàn)樵谥袛嗪瘮?shù)和原來(lái)的上下文中都會(huì)用到,所以,我認(rèn)為 編譯器在中斷處理函數(shù)中對(duì)r0-r12也進(jìn)行了保護(hù)和恢復(fù)。
另外,在ucosII中,對(duì)IsrIRQ函數(shù)進(jìn)行了修改,我們后面再進(jìn)行分析。
另外,我們用軟件實(shí)現(xiàn)了一套中斷向量表:
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之前,我們?cè)诎l(fā)生中斷時(shí),pc指針就跳轉(zhuǎn)到了 HandleIRQ地址中所存儲(chǔ)的數(shù) 出執(zhí)行。也就是說(shuō)在HandleIRQ中存的是異常處理函數(shù)的入口地址。這就是異常處理向量表的作用。
所以,我們可以看出,對(duì)于2440init.s實(shí)現(xiàn)的異常處理,采用的是兩級(jí)向量表機(jī)制。 第一級(jí)向量表是arm核自己實(shí)現(xiàn)的,發(fā)生相應(yīng)的異常時(shí),pc指針跳轉(zhuǎn)到0x18地址中存的數(shù),作為入口地址。 第二級(jí)向量表是由Handler宏實(shí)現(xiàn)的,繼續(xù)跳轉(zhuǎn)到 HandleIRQ地址中存的數(shù),繼續(xù)執(zhí)行。
而對(duì)于IRQ來(lái)講,還有第三級(jí)的向量表,在IsrIRQ中,又會(huì)根據(jù)中斷號(hào),比如uart2的中斷,跳轉(zhuǎn)到 HandleUart2地址中 存的數(shù),繼續(xù)執(zhí)行。
在2440init.s中,并沒(méi)有給HandleUndef等這些地址處賦值,因此,一旦執(zhí)行到,程序就會(huì)跑飛。
評(píng)論