ARM-IRQ異常處理程式碼
IRQ異常處理程式碼分析
在分析IRQ處理程式碼過程首先要說明下MACRO/MEND偽指令的用法,巨集是一段獨立的程式程式碼,它是通過偽指令定義的,在程式中使用巨集指令即可呼叫巨集。當程式被彙編時,彙編程式將對每個呼叫進行展開,用巨集定義取代源程式中的巨集指令。
MACRO、MEND
語法格式:
MACRO
[$ label] macroname{ $ parameter1, $ parameter,…… }
指令序列
MEND
IRQ異常處理程式碼分析
CODE32
AREAIRQ,CODE,READONLY
MACRO
$IRQ_Label HANDLER $IRQ_Exception_Function//
EXPORT$IRQ_Label;//輸出的標號
IMPORT$IRQ_Exception_Function//引用的外部標號
$IRQ_Label
SUBLR, LR, #4//計算返回地址,此時ARM7處於IRQ模式,此處LR為LR_irq
STMFDSP!, {R0-R3, R12, LR}//儲存任務環境,下面的過程使用了R0-R3 R12,LR等暫存器組,該暫存器組內容會發生變化,為了實現無縫返回故需要將該暫存器組進行壓棧儲存
MRSR3, SPSR//儲存狀態到R3
STMFDSP, {R3, SP, LR}^//儲存使用者狀態的R3,SP,LR,注意不能回寫。此處不能回寫的原因首先要了解^的作用(:如果暫存器組沒有PC的話,暫存器組的內容為使用者模式下的,有PC的話為自動更新CPSR。)由於暫存器組中沒有PC故括號內的暫存器為使用者模式下的,此時回寫SP的話,回寫的是使用者模式下的SP,覆蓋掉了IRQ模式的SP。第一個SP為SP_irq第二個SP為SP_usr。此處再一次壓棧的作用是儲存使用者態下的暫存器,用來實現中斷的巢狀
LDRR2,=OSIntNesting//OSIntNesting++
LDRBR1, [R2]
ADDR1, R1, #1
STRBR1, [R2]
SUBSP, SP, #4*3//由於壓入IRQ堆疊了3個暫存器而沒有回寫SP,故需要認為的調整SP指向最後壓入的資料。
MSRCPSR_c, #(NoInt | SYS32Mode)//切換到系統模式,為了實現中斷的巢狀進入系統模式,此時中斷是關閉的
CMPR1, #1
LDREQSP, =StackUsr
BL$IRQ_Exception_Function//呼叫c語言的中斷處理程式,在中斷處理函式過程中呼叫了OS_EXIT_CRITICAL()開啟中斷,此處就可以實現中斷的巢狀
MSRCPSR_c, #(NoInt | SYS32Mode)//切換到系統模式,此處應該是為了防止中斷巢狀後模式的改變,強制返回系統模式,但是我覺得即使中斷巢狀發生但最終還是會返回系統模式的,這一句有點多餘(我是菜鳥個人感覺的)
LDRR2, =OsEnterSum//OsEnterSum置一,使OSIntExit退出時中斷關閉為什麼置一可以使其關中斷將在後面的OSINTexit中分析說明
MOVR1, #1
STRR1, [R2]
BLOSIntExit
LDRR2, =OsEnterSum//因為中斷服務程式要退出,所以OsEnterSum=0
MOVR1, #0
STRR1, [R2]
MSRCPSR_c, #(NoInt | IRQ32Mode)//切換回irq模式
LDMFDSP, {R3, SP, LR}^//恢復使用者狀態的R3,SP,LR,注意不能回寫
//如果回寫的是使用者的SP
LDRR0, =OSTCBHighRdy
LDRR0, [R0]
LDRR1, =OSTCBCur
LDRR1, [R1]
CMPR0, R1
ADDSP, SP, #4*3//由於不能回寫SP故需要認為調整SP的指標
MSRSPSR_cxsf, R3
LDMEQFD SP!, {R0-R3, R12, PC}^//不進行任務切換
LDRPC, =OSIntCtxSw//進行任務切換
MEND
END
ØOsEnterSum全域性變量表示關中斷的次數
Ø中斷級的切換呼叫OSIntCtxSw而不是OS_TASK_SW()是因為ISR已經將CPU的暫存器壓入過棧了,不需要做第二次壓棧。
Ø存在一個疑問在呼叫OSIntExit的過程中已經進行了任務切換的判斷和執行為什麼在下邊組合語言中又判斷執行了一次。我在網上查了下是說OSIntExit只是簡單的查了優先順序表,而沒有做具體的任務切換,而具體工作交由後面的彙編程式執行,但是我沒看明白,誰來給解釋下
Ø中斷巢狀的實現是依靠了一:壓入使用者模式的CPSR/LR/SP。二:在ISR中開啟中斷實現的。
Ø由於在壓入sp_usr時,不能回寫sp_irq(回寫則儲存的是sp_usr覆蓋掉sp_irq)故需要人為的調整SP的指向
OSIntExit ()函式分析
voidOSIntExit (void)
{
#if OS_CRITICAL_METHOD == 3
OS_CPU_SRcpu_sr;
#endif
if (OSRunning == TRUE) {//只有多工排程開始才可以進行
OS_ENTER_CRITICAL();
if (OSIntNesting > 0){ //引數驗證
OSIntNesting--;//中斷退出中斷巢狀層數減一
}
if ((OSIntNesting == 0) && (OSLockNesting == 0)) { //只有在中斷結束和排程器開鎖的情況下才可以進行任務排程(ISR中不允許任務排程)
OSIntExitY= OSUnMapTbl[OSRdyGrp];//此處使用了全域性變數於OS_IntCtxSw的不同
OSPrioHighRdy = (INT8U)((OSIntExitY << 3) + OSUnMapTbl[OSRdyTbl[OSIntExitY]]);//獲得最高優先順序
if (OSPrioHighRdy != OSPrioCur) {//如果最高優先順序和當前優先順序不一樣的話,那麼進行任務切換
OSTCBHighRdy= OSTCBPrioTbl[OSPrioHighRdy];//獲得最高優先順序任務TCB
OSCtxSwCtr++; //任務切換次數加一
OSIntCtxSw();呼叫任務切換}
}
OS_EXIT_CRITICAL();退出中斷,此處於OSEntersum關中斷計數變數有關了開中斷函式是靠軟中斷實現的即
case 0x03:/* 開中斷函式OS_EXIT_CRITICAL(),參考os_cpu.h檔案 */
if (--OsEnterSum == 0)
{
__asm
{
MRSR0, SPSR
BICR0, R0, #NoInt
MSRSPSR_c, R0
}
}
break;
}
如果在IRQ中斷中沒有將OSENTERSUM變數置一的話,在OS_ENTER_CRITICAL中OsEnterSum加一到退出時--OsEnterSum == 0則中斷開啟。所以為了防止此處中斷開啟,故需要將OsEnterSum置一,再呼叫OS_ENTER_CRITICAL()中OsEnterSum++,此時OsEnterSum==2,而在呼叫OS_EXIT_CRITICAL()中--OsEnterSum == 1,中斷不開啟,只是將中斷巢狀層數減一。
}
Ø任務排程不能發生在中斷的過程中
ØOS_ENTER_CRITICAL(),OS_EXIT_CRITICAL()是由軟中斷實現的
IRQ_Exception_Function分析
改寫μC/OSII 核心中 HANDLER 巨集可以實現ARM的中斷巢狀,這樣做雖然提高了系統的實時性,但損害了系統執行的穩定性和可移植性。通過對中斷過程的分析,下面給出一種編寫中斷服務程式的模板,充分利用ISR執行在特權模式——系統模式這一特點來實現中斷巢狀的條件。中斷服務程式模板如下:
void ISR(void)
{
OS_ENTER_CRITICAL();//在中斷服務程式中關中斷
/*清中斷標誌*/ //防止沒有清中斷標誌使得中斷多次進入
S_EXIT_CRITICAL(); //在中斷服務程式中開中斷
VICVectAddr=0; //將中斷服務程式的入口地址置0
/*使用者的C語言程式碼*/ //進行使用者在中斷中要做的工
}
Ø由於Handler巨集中已將LR、SPSR、返回地址和發生中斷前的堆疊指標等暫存器入棧儲存,所以接下來要做的就只剩下開關中斷的工作。由於在進入C中斷處理程式之前進入的是關中斷系統模式,所以必須在C語言中重新開啟中斷,而C語言是不能進行暫存器操作的,因此必須呼叫軟中斷 OS_EXIT_CRITICAL()重新開啟中斷。在開中斷之前,要判斷將全域性變數OsEnterSum減1後是否為0,所以必須在呼叫開中斷之前呼叫軟中斷OS_ENTER_CRITICAL()將OsEnterSum變成1。在臨界區中可以進行一些處理,如清中斷標誌、關低優先順序中斷等。進行C語言中斷服務程式之後要將VICVectAddr置位為0,這是ARM7處理器核的要求必須進行這樣的編寫,否則會導致一些錯誤(如不能第2次進入中斷等)。