UCOS-II中OS_CPU_IRQ_ISR移植過程分析
OS_CPU_IRQ_ISR
STMFD SP!, {R1-R3} ; We will use R1-R3 as temporary registers
MOV R1, SP
ADD SP, SP, #12 ;Adjust IRQ stack pointer
SUB R2, LR, #4 ;Adjust PC for return address to task
MRS R3, SPSR ; Copy SPSR (Task CPSR)
MSR CPSR_cxsf, #SVCMODE|NOINT ;Change to SVC mode
; SAVE TASKS CONTEXT ONTO OLD TASKS STACK
STMFD SP!, {R2} ; Push tasks PC
STMFD SP!, {R4-R12, LR} ; Push tasks LR,R12-R4
LDMFD R1!, {R4-R6} ; Load Tasks R1-R3 from IRQ stack
STMFD SP!, {R4-R6} ; Push Tasks R1-R3 to SVC stack
STMFD SP!, {R0} ; Push Tasks R0 to SVC stack
STMFD SP!, {R3} ; Push tasks CPSR
LDR R0,=OSIntNesting ;OSIntNesting++
LDRB R1,[R0]
ADD R1,R1,#1
STRB R1,[R0]
CMP R1,#1 ;if(OSIntNesting==1){
BNE %F1
LDR R4,=OSTCBCur ;OSTCBHighRdy->OSTCBStkPtr=SP;
LDR R5,[R4]
STR SP,[R5] ;}
1 MSR CPSR_c,#IRQMODE|NOINT ;Change to IRQ mode to use IRQ stack to handle interrupt
LDR R0, =INTOFFSET
LDR R0, [R0]
LDR R1, IRQIsrVect
MOV LR, PC ; Save LR befor jump to the C function we need return back
LDR PC, [R1, R0, LSL #2] ; Call OS_CPU_IRQ_ISR_handler();
MSR CPSR_c,#SVCMODE|NOINT ;Change to SVC mode
BL OSIntExit ;Call OSIntExit
LDMFD SP!,{R4} ;POP the tasks CPSR
MSR SPSR_cxsf,R4
LDMFD SP!,{R0-R12,LR,PC}^ ;POP new Tasks context
IRQIsrVect DCD HandleEINT0
這個函數是irq中斷的通用處理形式,我對其中的代碼做一下簡要的分析和討論。
首先我需要簡要的分析一下中斷處理過程中我們應該完成的任務,首先cpu會自動完成一些操作,其中包括中斷的關閉已經返回地址的保存,還有模式的切換等。我們程序員則需要完成一些寄存器的保存工作以及跳轉到具體的處理函數中,最后完成返回控制。
需要注意的是,本文中的UC/OS-II任務都運行在SVC模式下,而不是SYS模式下。任務棧中保存的寄存器也是這種模式下對應的值。
我按照注釋號對代碼進行解釋:
2、STMFD SP!, {R1-R3}; 主要是完成幾個寄存器的壓棧操作,為什么這么做呢?因為我們接下來將要使用這寫寄存器。為什么需要呢?這是因為當前CPU工作的irq模式下,因此這里的SP并不是SVC模式下的SP指針,但是在UC/OS-II的移植過程中通常將各個任務工作在SVC模式下,同時還需要對寄存器的保存,在中斷發(fā)生以后,需要保存所有的寄存器以及CPSR的值,而當前的SP并不是任務的棧,此時除了SP、R14的值發(fā)生了變化以外,其他的寄存器并沒有發(fā)生變化。但是由于后面需要使用這些寄存器,因此需要壓棧。
3、MOV R1, SP; 將R14_irq的值保存到R1中,這個SP的保存主要是為了通過這個值訪問IRQ模式下的堆棧空間,實現對數據的訪問。
4、ADD SP, SP, #12; 調整IRQ模式下的堆棧指針SP_irq,將這個指針指向IRQ堆棧的開始位置,方便下一次中斷的處理操作。
5、SUB R2, LR, #4 ;這個操作主要是調整返回地址,了解異常返回地址的理解其中的含義,這時候的返回地址實質上就是任務的返回地址,也就是將來需要加載到PC中的值。
6、MRS R3, SPSR;因為發(fā)生了IRQ中斷,此時CPU進入IRQ模式中,這時的SPSR_irq中保存了svc模式下的CPSR狀態(tài)。而任務堆棧中保存的剛好是SVC模式下的狀態(tài)寄存器,因此需要將SVC模式下的狀態(tài)寄存器首先讀出來,然后保存進任務的堆棧中,因此用R3來保存CPSR值。
7、MSR CPSR_cxsf, #SVCMODE|NOINT; 因為任務的堆??臻g位于SVC模式下,因此首先需要將CPU的狀態(tài)切換到SVC模式下,然后進行任務情景的切換操作。
9、STMFD SP!, {R2}; 這時候的SP是指在SVC模式下的R13_svc。中斷發(fā)生以后,SVC模式下的SP并沒有發(fā)生改變,返回以后,該值仍然存在,仍然指向任務的棧頂位置。根據任務??臻g的分布,首先需要保存PC值,然后是R14-R0,CPSR的值,最后是保存SP到任務中,這種分布狀態(tài)是和堆棧初始化過程的分布一致的。而通過調整的返回地址剛好就保存在了R2中,因此需要壓棧保存PC值。
10、STMFD SP!, {R4-R12, LR};因為在之前的一系列操作中,并沒有對R4-R12,R14的值進行破壞,因此可以直接進行壓棧操作,實現任務堆棧中R4-R12,LR的保存操作。
接下來的幾句代碼是重點:實現了在對其他模式下堆??臻g的訪問問題。
11、LDMFD R1!, {R4-R6};是指將R1地址處加載一些數據到R4-R6,這三個寄存器的值已經被壓入棧中,對他們的修改并不會導致錯誤的產生,因此這三個寄存器實質上是作為中間地址。其中加載的順序滿足高地址對應高的高編號的寄存器值。R1我在前面就強調了用來訪問IRQ模式堆??臻g的參考地址。從這個地址向上分別保存了壓入棧中的R1-R3寄存器的值,也就是被中斷任務R1-R3的值。
12、STMFD SP!, {R4-R6};也就是完成了對任務寄存器R1-R3的壓棧操作。
13、STMFD SP!, {R0} ;前面的代碼中并沒有修改R0的值,因此其中的值仍然是任務的R0值,因此也需要壓棧操作。通過上面的幾句代碼就完成了任務的R0-R15所有寄存器的保存操作。接下的就應該完成CPSR的保存。
14、STMFD SP!, {R3};當前的R3中保存了實際上是前面所說的SPCR_irq中的值,也就是SVC模式下的狀態(tài)寄存器的值,因此可以認為就是完成了任務的狀態(tài)寄存器的保存。
評論