"上一篇文章" 介紹了S3c2440的中斷體繫結構,今天我們來分析一下GNU uC/OS II在S3c2440上中斷的實現。 首先找到IRQ的中斷的向量,位於 2440init.S : OK ,我們通過名字找到了這個函數: ~~~~ OS_CPU_IRQ_ISR: STMFD SP!, {R1 R3 ...
上一篇文章介紹了S3c2440的中斷體繫結構,今天我們來分析一下GNU-uC/OS-II在S3c2440上中斷的實現。
首先找到IRQ的中斷的向量,位於 2440init.S :
OK ,我們通過名字找到了這個函數:
OS_CPU_IRQ_ISR:
STMFD SP!, {R1-R3}
MOV R1, SP
ADD SP, SP, #12
SUB R2, LR, #4
MRS R3, SPSR
MSR CPSR_cxsf, #SVCMODE|NOINT
STMFD SP!, {R2}
STMFD SP!, {R4-R12, LR}
LDMFD R1!, {R4-R6}
STMFD SP!, {R4-R6}
STMFD SP!, {R0}
STMFD SP!, {R3}
LDR R0,=OSIntNesting
LDRB R1,[R0]
ADD R1,R1,#1
STRB R1,[R0]
CMP R1,#1
BNE 1f
LDR R4,=OSTCBCur
LDR R5,[R4]
STR SP,[R5]
1:
MSR CPSR_c,#IRQMODE|NOINT
LDR R0, =INTOFFSET
LDR R0, [R0]
LDR R1, =INTMSK
LDR R1, [R1]
MOV R3, #1
LSL R4, R3,R0
TST R4, R1
BNE 2f
LDR R1, =IRQ_STARTADDRESS
MOV LR, PC
LDR PC, [R1, R0, LSL #2]
2:
LDR R0, =INTOFFSET
LDR R0, [R0]
MOV R1, #1
MOV R1, R1, LSL R0
LDR R0, =SRCPND
LDR R2, [R0]
ORR R2, R1,R2
STR R2, [R0]
LDR R0, =INTPND
LDR R2, [R0]
ORR R2, R1,R2
STR R2, [R0]
MSR CPSR_c,#SVCMODE|NOINT
BL OSIntExit
LDMFD SP!,{R4}
MSR SPSR_cxsf,R4
LDMFD SP!,{R0-R12,LR,PC}^
有點長,我們挑關鍵的部分分析。
看這三行代碼:
LDR R1, =IRQ_STARTADDRESS
MOV LR, PC
LDR PC, [R1, R0, LSL #2]
IRQ_STARTADDRESS 是這樣定義的:
.equ IRQ_STARTADDRESS , 0x33ffff00
當前PC保存到LR,然後PC直接跳到了 R1 + R0 * 4 這個地址處(LSL #2 代表左移2位,相當於 *4)。
R0的值來自這個寄存器:
LDR R0, =INTOFFSET
LDR R0, [R0]
上一篇文章中我們也說過了,INTOFFSET 這個寄存器可以查出當前是哪個中斷源在發生請求。
假如現在Timer1請求中斷,那麼 INTOFFSET 的值就是 11.
R1 + R0 * 4 的結果計算得出就是 0x33FFFF2C 。
這個地址處是什麼數據呢?
我們在Timer初始化程式中找到了這個:
pIRQ_TIMER1 = (uint32)OSTickISR;
頭文件中找到了這個:
OK,這樣就在 0x33FFFF2C 地址處存儲了 OSTickISR 的入口地址,從而就執行了Timer1的中斷服務程式。
執行完ISR之後,彙編的那段程式之後又完成了清中斷的操作。
其實在這個過程中有一個要點容易被忽視,IRQ異常發生時,PC跳轉到異常向量處,那麼IRQ異常向量應該存儲在 0x18 這個位置處的啊,可我們一開始說到的是執行 "b OS_CPU_IRQ_ISR" 這條語句,它的地址應該是0x30000018呀(我們的程式在SDRAM中運行,如圖定義):
可為什麼執行的是它呢?
這個時候MMU這位大將就要派上用場了,程式中通過這條語句將地址做了映射,
MMU_SetMTT(0x00000000,0x03f00000,(uint32)_start,RW_CB)
所以找到的就是0x30000018位置處的向量啦。