ARM處理器架構-----異常/中斷處理

中斷是我們嵌入式開發很常用到的一種資源和編程手段。這篇文章重點分析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中有:

__ENTRY
	b	ResetHandler
	b	HandlerUndef	;handler for Undefined mode
	b	HandlerSWI	    ;handler for SWI interrupt
	b	HandlerPabort	;handler for PAbort
	b	HandlerDabort	;handler for DAbort
	b	.		        ;reserved
	b	HandlerIRQ	;handler for IRQ interrupt
	b	HandlerFIQ	;handler for FIQ interrupt

這裏就是相應的 異常處理向量表。程序正常啓動就跳轉到resethandler,如果是發生中斷就跳轉到handlerIRQ。對於handlerIRQ,它是用一個宏實現的。

MACRO
$HandlerLabel HANDLER $HandleLabel
$HandlerLabel
	sub	sp,sp,#4	;decrement sp(to store jump address)
	stmfd	sp!,{r0}	;PUSH the work register to stack(lr does't push because it return to original address)
	ldr     r0,=$HandleLabel;load the address of HandleXXX to r0
	ldr     r0,[r0]	 ;load the contents(service routine start address) of HandleXXX
	str     r0,[sp,#4]      ;store the contents(ISR) of HandleXXX to stack
	ldmfd   sp!,{r0,pc}     ;POP the work register and pc(jump to ISR)
	MEND
上面是宏的聲明。下面是具體用到宏的地方。

HandlerFIQ      HANDLER HandleFIQ
HandlerIRQ      HANDLER HandleIRQ
HandlerUndef    HANDLER HandleUndef
HandlerSWI      HANDLER HandleSWI
HandlerDabort   HANDLER HandleDabort
HandlerPabort   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 does''t push because it return to original address)
	ldr     r0,=HandleIRQ   ;load the address of HandleXXX to r0
	ldr     r0,[r0]	        ;load the contents(service routine start address) of HandleXXX
	str     r0,[sp,#4]      ;store the contents(ISR) of HandleXXX to stack
	ldmfd   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 needed
ldr r1,=IsrIRQ    ;if there isn't 'subs pc,lr,#4' at 0x18, 0x1c
str r1,[r0]
從這裏,可以看出,HandleIRQ中存的是IsrIRQ。所以處理器會跳轉到isrIRQ中執行。

IsrIRQ
	sub	sp,sp,#4       ;reserved for PC
	stmfd	sp!,{r8-r9}

	ldr	r9,=INTOFFSET
	ldr	r9,[r9]
	ldr	r8,=HandleEINT0
	add	r8,r8,r9,lsl #2
	ldr	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函數進行了修改,我們後面再進行分析。

另外,我們用軟件實現了一套中斷向量表:

ALIGN
	AREA RamData, DATA, READWRITE
	^   _ISR_STARTADDRESS		; _ISR_STARTADDRESS=0x33FF_FF00
HandleReset 	#   4
HandleUndef 	#   4
HandleSWI	#   4
HandlePabort    #   4
HandleDabort    #   4
HandleReserved  #   4
HandleIRQ	#   4
HandleFIQ	#   4

;Don''t use the label 'IntVectorTable',
;The value of IntVectorTable is different with the address you think it may be.
;IntVectorTable
;@0x33FF_FF20
HandleEINT0		#   4
HandleEINT1		#   4
HandleEINT2		#   4
HandleEINT3		#   4
HandleEINT4_7	#   4
HandleEINT8_23	#   4
HandleCAM		#   4		; Added for 2440.
HandleBATFLT	#   4
HandleTICK		#   4
HandleWDT		#   4
HandleTIMER0 	#   4
HandleTIMER1 	#   4
HandleTIMER2 	#   4
HandleTIMER3 	#   4
HandleTIMER4 	#   4
HandleUART2  	#   4
;@0x33FF_FF60
HandleLCD 		#   4
HandleDMA0		#   4
HandleDMA1		#   4
HandleDMA2		#   4
HandleDMA3		#   4
HandleMMC		#   4
HandleSPI0		#   4
HandleUART1		#   4
HandleNFCON		#   4		; Added for 2440.
HandleUSBD		#   4
HandleUSBH		#   4
HandleIIC		#   4
HandleUART0 	#   4
HandleSPI1 		#   4
HandleRTC 		#   4
HandleADC 		#   4
;@0x33FF_FFA0
之前,我們在發生中斷時,pc指針就跳轉到了 HandleIRQ地址中所存儲的數 出執行。也就是說在HandleIRQ中存的是異常處理函數的入口地址。這就是異常處理向量表的作用。
所以,我們可以看出,對於2440init.s實現的異常處理,採用的是兩級向量表機制。 第一級向量表是arm核自己實現的,發生相應的異常時,pc指針跳轉到0x18地址中存的數,作爲入口地址。 第二級向量表是由Handler宏實現的,繼續跳轉到 HandleIRQ地址中存的數,繼續執行。 

而對於IRQ來講,還有第三級的向量表,在IsrIRQ中,又會根據中斷號,比如uart2的中斷,跳轉到 HandleUart2地址中 存的數,繼續執行。


在2440init.s中,並沒有給HandleUndef等這些地址處賦值,因此,一旦執行到,程序就會跑飛。



部分內容轉自:http://blog.csdn.net/dndxhej/article/details/7626347

發佈了56 篇原創文章 · 獲贊 12 · 訪問量 15萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章