2440(ARM9)中Ucos移植相關
Keil 中irq作用
如果在中斷函數上使用__irq,編譯器會自動將一些寄存器壓棧,等該中斷函數返回時自動出棧。關於keil中__irq說明參考網址:http://www.keil.com/support/man/docs/armccref/armccref_babicjbc.htm。
針對手動保存寄存器的函數,不用使用__irq。參考:http://hi.baidu.com/chongxing01/item/3c23c1ca9ad3183399b49827。
Ucos中是手動保存中斷寄存器內容,支持中斷嵌套。因此中斷函數不使用__irq。若使用__irq會使程序運行錯誤。
Note:
2440系統剛起時是管理SVC模式。針對ucos各個任務,網上的程序一般都運行在SVC模式。但我覺得在user/system模式應該也是可以的。可參考uCOS-II的中斷-ARM7實現中斷嵌套的方法探究。但得修改os_cpu_c.s中斷函數的代碼。
2440啓動後默認是小端模式,但可以改成大端模式
ARM9部分命令解釋
在理解文件之前,應該先簡要記住下面幾個在文件中用到的命令。
MRS:Move PSR to register,將程序狀態寄存器的值複製到通用寄存器
例:MRS R0,CPSR; 傳送CPSR 的內容到 R0
MSR:Move Register to PSR,將通用寄存器的值複製到到程序狀態寄存器
例:MSR CPSR_c,R0; 傳送 R0 的內容到 SPSR,但僅僅修改 CPSR 中的控制位域
LDR:Load word,裝載一個字數據(立即數/內容)到一個寄存器
例:LDR R0, [R1,R2]!; 將存儲器地址爲 R1+R2 的字數據讀入R0,並將新地址R1+R2 寫入 R1
LDRB:Load byte,裝載一個字節數據(立即數/內容)到一個寄存器
例:LDRB R0,[R1,#8]; 將存儲器地址爲R1+8的字節數據讀入R0,並將R0的高 24 位清零
STR:store,裝載寄存器的字數據到地址的內容裏
例:STR R0,[R1], #8; 將R0中的字數據寫入R1爲地址的存儲器中,並將新地址R1+8 寫入 R1
STRB:Store byte,裝載寄存器的字節數據到地址的內容裏
例:STRB R0, [RS#8] ;將寄存器 R0 中的字節數據寫入以 R1+8 爲地址的存儲
器中
LDM:load multiple,將寄存器地址的內容順序恢復(出棧)到一系列寄存器組中。
例:LDMFD R13!,{R0,R4-R12, PC}^;R13即SP。將堆棧內容順序彈出(此時堆棧遞增)到寄存器R0,R4-R12,LR。後綴!表示最後的地址寫會到R13內,末尾^意味這cpsr的值將從spsr中得到恢復,這只有在pc同時也被裝載的情況下才有效。如果pc沒有被裝載,那麼^只恢復用戶模式的備份寄存器組。
STM:store multiple,將一系列寄存器值順序存入(壓棧)到某一寄存器地址的內容中
STMFD R13!, {R0, R4-R12, LR}; 將寄存器列表中的寄存器LR, R12-R4, R0
順序壓入(此時堆棧遞減)堆棧。後綴!表示最後的地址寫回到R13中。
BL:Branch with link,帶返回的跳轉指令。
例:BL Label; 當程序無條件跳轉到標號Label處執行時,同時將當前的PC值保存到R14(LR)中
Arm9寄存器
ARM9共有37個32位寄存器,其中,有31個通用寄存器,1個CPSR,5個SPSR。
http://bdxnote.blog.163.com/blog/static/8444235201121413413947/。
ARM9處理器的運行模式有7種,用戶模式user,系統模式sys,管理模式svc,中止模式abt,未定義模式und,中斷模式irq,快速中斷模式fiq。各種模式下所擁有的寄存器信息如下圖:
寄存器的別名:摘自《ARM ProcedureCall Standard for the ARM® Architecture》
關於CPSR和SPSR(http://blog.sina.com.cn/s/blog_641a37da01017cjm.html)
CPSR:程序狀態寄存器(current program status register) (當前程序狀態寄存器),在任何處理器模式下被訪問。它包含了條件標誌位、中斷禁止位、當前處理器模式標誌以及其他的一些控制和狀態位。CPSR在用戶級編程時用於存儲條件碼。
SPSR:程序狀態保存寄存器(saved program status register),每一種處理器模式下都有一個狀態寄存器SPSR,SPSR用於保存CPSR的狀態,以便異常返回後恢復異常發生時的工作狀態。當特定的異常中斷髮生時,這個寄存器用於存放當前程序狀態寄存器的內容。在異常中斷退出時,可以用SPSR來恢復CPSR。由於用戶模式和系統模式不是異常中斷模式,所以他沒有SPSR。當用戶在用戶模式或系統模式訪問SPSR,將產生不可預知的後果。
最高優先級任務運行
os_cpu_a.s文件解釋
先從OSStartHighRdy開始,OSStartHighRdy完成的是,將OSRunning置爲TRUE,將最高優先級任務棧裏面的數據順序恢復(出棧)到各個寄存器(包括PC)中。
分析:
OSStartHighRdy
;----------------------------------------------------------------------------------
; OSRunning = TRUE;
;----------------------------------------------------------------------------------
MSR CPSR_cxsf,#SVCMODE:OR:NOINT ;Switch to SVC mode with IRQ&FIQ disable
;解釋:將SVCMODE:OR:NOINT的值存到CPSR中,其中cxsf(字母必須小寫)意爲c是控制域屏蔽字節[0-7],x爲擴展域屏蔽字節[8-15],s爲狀態域屏蔽字節[16-23],f爲狀態域屏蔽字[24-31]。可參考http://19831028.blog.51cto.com/1333653/411832。
此句主要意爲進入SVC模式並屏蔽中斷。NOINT爲0xc0,屏蔽IRQ和FRQ中斷。
BL OSTaskSwHook ;Call user define Task switch hook
;解釋:調用OSTaskSwHook函數,並將下一條指令地址存入LR(R14)寄存器中。BL指令在編譯時,是以當前指令地址爲基準相對跳轉。由於指令中地址區域爲16位,其中1位作前後標誌,剩下15位作爲跳轉範圍。所以跳轉地址範圍爲當前地址前後32MB地址。
LDR R0,=OSRunning ; OSRunning =TRUE
MOV R1, #1
STRB R1, [R0]
;解釋:第1句將OSRunning地址放入到R0;第2句將立即數1存入R1;第3句將R1的字節數據存入到以R0爲地址的存儲器中。一句話解釋就是OSRunning=1
;----------------------------------------------------------------------------------
; SP =OSTCBHighRdy->OSTCBStkPtr;
;----------------------------------------------------------------------------------
LDR R0,=OSTCBHighRdy
LDR R0, [R0]
LDR SP, [R0]
;解釋:OSTCBHighRdy是指針。第1句將指針OSTCBHighRdy的地址存入R0;第2句將OSTCBHighRdy本身存入R0;第3句就是將OSTCBHighRdy指向的內容存入SP。OSTCBHighRdy和OSTCBHighRdy->OSTCBStkPtr屬於同一地址,因此就是將OSTCBHighRdy->OSTCBStkPtr值存入SP。
一句話就是另SP =OSTCBHighRdy->OSTCBStkPtr;
OSTCBHighRdy->OSTCBStkPtr的值是多少?是在Os_cpu_c.c的OSTaskStkInit賦值的,在這個函數中最後返回的值就是OSTCBHighRdy->OSTCBStkPtr初值。
;----------------------------------------------------------------------------------
; Prepare to return to proper mode
;----------------------------------------------------------------------------------
LDMFD SP!,{R0}
MSR SPSR_cxsf, R0
LDMFD SP!,{R0-R12, LR, PC}^
;解釋:第1句將SP的值彈出到R0中。根據OSTaskStkInit函數可知,SP最後的一個值爲CPSR。第2句就是將R0存入到SPSR寄存器。第3句是順序將SP的值彈出到R0-R12, LR, PC。PC值在OSTaskStkInit被設置爲當前任務的指針。後綴!表示最後的SP地址還會寫回到SP內。末尾^意味這cpsr的值將從spsr中得到恢復,這只有在pc同時也被裝載的情況下才有效。如果pc沒有被裝載,那麼^只恢復用戶模式的備份寄存器組。《ARM嵌入式系統開發:軟件設計與優化》9章-異常和中斷處理,例9.8。
Ucos的任務切換有兩種方式,一種是任務級上下文切換,一種是中斷級上下文切換。參考:http://blog.csdn.net/zhanglianpin/article/details/7350712
Ucos中任務上下文切換會發生在兩種情況下。
第一是任務調用了ucos的一些系統函數,比如:延時函數,pend,post函數等等,此時會發生任務切換。這叫做任務級的上下文切換;
第二是有更高優先級的任務進入就緒狀態,在Time tick中斷或者其他中斷髮生時,在中斷裏進行任務切換。這叫做中斷級的上下文切換。
任務級上下文切換
任務級的上下文切換可總結爲(摘自《嵌入式實時操作系統ucos-II原理及應用第2版 任哲》 第三章 第4節):
1> 把被中止任務的斷點指針保存到任務堆棧中;
2> 把CPU通用寄存器的內容保存到任務堆棧中;
3> 把被中止任務的任務堆棧指針當前值保存到該任務的任務控制塊的OSTCBStkPtr中;
4> 獲得待運行任務的任務控制塊;
5> 使CPU通過任務控制塊獲得待運行任務的任務堆棧指針;
6> 把待運行任務堆棧中通用寄存器的內容恢復到CPU的通用寄存器中;
7> 使CPU獲得待運行任務的斷點指針(該指針是待運行任務在上一次被調度器中止運行時保留在任務堆棧中的)。
代碼如下,可對照上面步驟進行理解。
OSCtxSw
;解釋:此時SP表示的是當前(被中止)任務的堆棧指針
STMFD SP!, {LR} ;PC
;解釋:將LR壓入(遞減)SP內。後綴!表示SP遞減後寫回到SP中。一句話:--SP=LR
STMFD SP!, {R0-R12,LR} ;R0-R12 LR
;解釋:將寄存器列表中的寄存器LR, R12-R0, 順序壓入(堆棧遞減)堆棧。後綴!表示SP遞減後寫回到SP中。可表示:--SP=LR;--SP=R12;--SP=R11;…………;--SP=R0;
MRS R0, CPSR ;Push CPSR
;解釋:將CPSR的值存入R0
STMFD SP!, {R0}
;解釋:將R0(此時爲CPSR的值)值壓入SP,即:--SP=R0
;可以看出入棧的順序爲LR(PC),LR,R12-R0,CPSR,和OSTaskStkInit入棧的順序完
;全一致。
;----------------------------------------------------------------------------------
; OSTCBCur->OSTCBStkPtr= SP
;----------------------------------------------------------------------------------
LDR R0,=OSTCBCur
LDR R0, [R0]
STR SP, [R0]
;解釋:將當前SP的值存入到當前任務堆棧指針中,即:OSTCBCur->OSTCBStkPtr= SP
;----------------------------------------------------------------------------------
; OSTaskSwHook();
;---------------------------------------------------------------------------------
BL OSTaskSwHook
;解釋:調用OSTaskSwHook函數,並將下一條指令地址存入LR(R14)寄存器中
;----------------------------------------------------------------------------------
; OSTCBCur = OSTCBHighRdy;
;----------------------------------------------------------------------------------
LDR R0, =OSTCBHighRdy
LDR R1,=OSTCBCur
LDR R0, [R0]
STR R0, [R1]
;解釋:當前任務控制塊指針等於最高優先級任務控制塊指針,即:OSTCBCur = OSTCBHighRdy
;----------------------------------------------------------------------------------
; OSPrioCur = OSPrioHighRdy;
;----------------------------------------------------------------------------------
LDR R0,=OSPrioHighRdy
;解釋:OSPrioHighRdy地址存入R0
LDR R1,=OSPrioCur
;解釋:OSPrioCur地址存入R1
LDRB R0, [R0]
;解釋:將地址爲R0對應的內容(字節)存入R0寄存器中
STRB R0, [R1]
;解釋:將R0的內容(字節)存入以R1爲地址對應的內容中
;解釋:當前任務任務優先級等於最高優先級,即:OSPrioCur = OSPrioHighRdy
;----------------------------------------------------------------------------------
; OSTCBHighRdy->OSTCBStkPtr;
;----------------------------------------------------------------------------------
LDR R0,=OSTCBHighRdy
LDR R0, [R0]
LDR SP, [R0]
;解釋:將最高優先級任務堆棧指針賦值給SP,即:SP= OSTCBHighRdy->OSTCBStkPtr
;----------------------------------------------------------------------------------
;Restore New task context
;----------------------------------------------------------------------------------
LDMFD SP!, {R0} ;POP CPSR
;解釋:將SP的值彈出到R0,此時SP指向的是最高優先級任務的堆棧。可表示爲:R0=SP++
MSR SPSR_cxsf, R0
;解釋:各個任務堆棧裏面最頂層存的是當前模式下的CPSR,因此首先彈出的值爲CPSR,先放到SPSR中。
LDMFD SP!,{R0-R12, LR, PC}^
;解釋:順序將SP的值彈出到R0-R12, LR, PC。後綴!表示最後的SP地址還會寫回到SP內。可表示:R0=SP++;R1=SP++;……;R12=SP++;LR=SP++;PC=SP++。
;末尾^意味這cpsr的值將從spsr中得到恢復,這只有在pc同時也被裝載的情況下才有效。如果pc沒有被裝載,那麼^只恢復用戶模式的備份寄存器組。《ARM嵌入式系統開發:軟件設計與優化》9章-異常和中斷處理,例9.8。
中斷級上下文切換
首先必須要知道的是ARM9怎麼去找到相對應的中斷服務函數。當已經使能的IRQ中斷髮生時,會跳轉到下面的OS_CPU_IRQ_ISR處,這是因爲已經與地址0x00000018相關聯了。
中斷級任務切換簡要流程爲:進入中斷,調用中斷函數,調用中斷退出函數進行任務切換(如果最高優先級任務不等於當前任務)。
下面代碼中實現的IRQ中斷流程,
1> 將R1-R3寄存器值壓入IRQ堆棧;
2> 切換到SVC模式,按照OSTaskStkInit初始化堆棧的順序將寄存器順序壓入當前任務堆棧;其中LR存的是被中斷函數中斷處的下一條指令;
3> OSIntNesting加1後與1比較,若等於1,則將當前SP的值存入當前任務控制塊的堆棧指針中;
4> 切換到IRQ模式,執行真正的中斷函數;
5> 切換到SVC模式,調用OSIntExit函數。若OSIntNesting-1==0且最高優先級任務不爲當前任務優先級,調用OSIntCtxSw執行任務切換,此處即刻返回到被中斷函數中斷運行的下一條地址;
6>若OSIntNesting-1!=0或者最高優先級任務等於當前任務優先級,不動作,退出OSIntExit,接着返回到被中斷函數中斷運行的下一條指令處。
OS_CPU_IRQ_ISR
;解釋:此處SP的值爲ISR的堆棧首地址
STMFD SP!, {R1-R3} ; Wewill use R1-R3 as temporary registers
;----------------------------------------------------------------------------
; R1--SP
; R2--PC
; R3--SPSR
;------------------------------------------------------------------------
;解釋:將R3-R1壓入(遞減)SP內。後綴!表示SP遞減後寫回到SP中。一句話:--SP=R3;--SP=R2;--SP=R1; --SP等於SP-4,因爲寄存器是32位的。
MOV R1, SP
;解釋:將SP的值存入R1
ADD SP, SP,#12 ;Adjust IRQ stack pointer
;解釋:SP=SP+12,將SP的值調回到ISR的堆棧首地址
SUB R2, LR, #4 ;Adjust PC for return address totask
;解釋:R2=LR-4,LR-4表示的是被中斷函數中下一條該運行的地址,爲什麼是LR-4?
;《ARM嵌入式系統開發:軟件設計與優化》第9章表9.4列出了各個模式返回時的PC地址。
MRS R3, SPSR ; Copy SPSR (TaskCPSR)
;解釋:R3=SPSR,將切換到ISR模式前運行模式下的CPSR狀態存入R3。因爲在進入ISR模式時已經將之前模式下的CPSR存入到了當前模式的SPSR中。
;R1存的是ISR模式下寄存器入棧後的SP值
;R2存的是被中斷函數中下一條該運行的地址
;R3存的是之前模式下的CPSR值。
MSR CPSR_cxsf,#SVCMODE:OR:NOINT ;Change to SVC mode
;解釋:切換到SVC模式並禁止中斷,修改CPSR的值就可以切換到對應的模式。
;切換到SVC模式後,SP指向的是被中斷任務的堆棧指針。每個任務都運行在SVC模式下。
; SAVE TASK''S CONTEXT ONTO OLD TASK''S STACK
STMFD SP!, {R2} ; Push task''s PC
;解釋:--SP=R2,R2存的是被中斷函數中下一條該運行的地址,將其壓入當前任務的堆棧。
STMFD SP!, {R4-R12,LR} ; Push task''s LR,R12-R4
;解釋:--SP=LR,--SP=R12,…….,--SP=R4;將這些寄存器值壓入當前任務堆棧
LDMFD R1!, {R4-R6} ; Load Task''s R1-R3 from IRQstack
;解釋:R4=R1++;R5=R1++;R6=R1++;此處R1存的是ISR模式下寄存器入棧後的SP值,在ISR模式下最後入棧的是R1的值,其次是R2,最先入棧的是R3,因此將壓入在ISR堆棧的參數彈出到R4(R1),R5(R2),R6(R3)。
STMFD SP!, {R4-R6} ; Push Task''s R1-R3 to SVCstack
;解釋:--SP=R6(R3),--SP=R5(R2),--SP=R4(R0),將這三個寄存器壓入當前任務堆棧
STMFD SP!, {R0} ; Push Task''s R0 to SVC stack
;解釋:--SP=R0,R0入棧,R0一直未使用,因此也不用恢復。
STMFD SP!, {R3} ; Push task''s CPSR
;解釋:--SP=R3。R3存的是中斷之前模式的CPSR。
;此處可以看到壓入堆棧的寄存器順序爲PC,LR,R12-R0,CPSR,跟OSTaskStkInit函數的入棧順序完全一致。
;另:此處被中斷之前運行的那個任務所有寄存器參數已經全部入棧。
LDR R0,=OSIntNesting ;OSIntNesting++
;解釋:R0=&OSIntNesting,將OSIntNesting地址存入R0中
LDRB R1,[R0]
;解釋:R1=OSIntNesting&0x0FF,將以R0爲地址的字節內容存入R1
ADD R1,R1,#1
;解釋:R1=R1+1
STRB R1,[R0]
;解釋:OSIntNesting=R1&0x0FF,將R1以字節形式存入以R0爲地址的內容
CMP R1,#1 ;if(OSIntNesting==1){
;解釋:將寄存器 R1 的值與1相減,並根據結果設置CPSR 的標誌位。若1==R1,z=0,若1!=R1,z=1。
BNE %F1
;解釋:
LDR R4,=OSTCBCur ;OSTCBHighRdy->OSTCBStkPtr=SP;
LDR R5,[R4]
STR SP,[R5] ;}
;解釋:將當前SP的位置保存到當前任務堆棧指針。OSTCBHighRdy->OSTCBStkPtr=SP。
;此處,之前被中斷之前運行的那個任務各個寄存器參數已經全部保存。
1
MSR CPSR_c,#IRQMODE:OR:NOINT ;Change to IRQ mode to use IRQ stack to handle interrupt
;解釋:切換到ISR模式並禁止中斷,修改CPSR的值就可以切換到對應的模式。
;切換到ISR模式後,SP指向的是ISR模式下的堆棧,此時SP的值指向的是ISR的堆棧首地址
LDR R0, =INTOFFSET
LDR R0, [R0]
;解釋:INTOFFSET= 0x4A000014,中斷偏移寄存器。中斷偏移寄存器中的值表明了是哪個IRQ模式的中斷請求在INTPND寄存器中。此位可以通過清除SRCPND和INTPND自動清除。FIQ模式中斷不會影響INTOFFSET寄存器因爲該寄存器只對IRQ模式中斷有效。此時R0存的是INTOFFSET寄存器的內容。
LDR R1, IRQIsrVect
;解釋,IRQIsrVect =HandleEINT0=0x33FFFF20,0x33FFFF20地址處放的是EINT0中斷函數,HandleEINT1=0x33FFFF24地址處方的是EINT1中斷函數,往後依次4個字節存的是各個中斷函數。
MOV LR, PC ; Save LR befor jump tothe C function we need return back
;解釋:ARM9處理器是五級流水線。即:取指--譯碼--執行--緩衝/數據--回寫。在執行第1句指令的時候,PC指向的是第3條指令,即PC始終指向要取指令的地址。這一點可以參考《Uboot中start.S源碼的指令級的詳盡解析》3.4。即:PC=PC+8。此句執行後LR裏存的是下面MSR CPSR_c,#SVCMODE:OR:NOINT指令的地址。
LDR PC, [R1, R0, LSL #2] ;Call OS_CPU_IRQ_ISR_handler();
;解釋:R1是中斷函數的基地址,R0存的是INTOFFSET寄存器的內容,左移2即乘以4是因爲每4個字節存一箇中斷函數。一句話,就是將對應的中斷函數放入PC,執行完這一句後就會跳轉到對應的中斷函數。
;解釋:中斷函數運行完後,會根據LR返回到下面的這條指令
MSR CPSR_c,#SVCMODE:OR:NOINT ;Change to SVC mode
;解釋:切換到SVC模式並禁止中斷,修改CPSR的值就可以切換到對應的模式。
;切換到SVC模式後,SP指向的是被中斷任務的堆棧指針。每個任務都運行在SVC模式下。
;這條指令的執行同時也說明了此時切換到了被中斷之前運行的那個任務,並且該任務的各個寄存器參數也已經全部入棧。
BL OSIntExit ;Call OSIntExit
;解釋:BL是Branch Link,跳轉到OSIntExit,並將下面的指令地址放入LR寄存器,以備函數返回時使用。在OSIntExit函數中,若OSIntNesting-1不等於0,則退出此函數;否則查找最高優先級任務,若最高優先級與當前任務優先級相同,則此函數不進行任務切換;若最高優先級任務不等於當前任務優先級,則跳轉到OSIntCtxSw函數進行任務切換。若調用OSIntCtxSw進行任務切換,下面的代碼就不會被執行,在OSIntCtxSw函數裏就將PC指針返回到被中斷函數中斷運行的下一條代碼,開始執行。
LDMFD SP!,{R4} ;POP the task''s CPSR
;解釋:R4=SP++;
MSR SPSR_cxsf,R4
;解釋:SPSR=R4;
LDMFD SP!,{R0-R12,LR,PC}^ ;POP new Task''s context
;解釋:順序將SP的值彈出到R0-R12, LR, PC。後綴!表示最後的SP地址還會寫回到SP內。可表示:R0=SP++;R1=SP++;……;R12=SP++;LR=SP++;PC=SP++。
;末尾^意味這cpsr的值將從spsr中得到恢復,這只有在pc同時也被裝載的情況下才有效。如果pc沒有被裝載,那麼^只恢復用戶模式的備份寄存器組。《ARM嵌入式系統開發:軟件設計與優化》9章-異常和中斷處理,例9.8。
;運行到此處說明了不需要進行任務切換。被中斷函數之前那個任務的所有寄存器參數已經全部壓棧,因此現在只要全部出棧即可恢復到被中斷函數之前那個任務運行的地址,上面3條指令實現的就是這個功能。
OSIntCtxSw代碼如下,和OSCtxSw代碼部分相同,可參考。
OSIntCtxSw
;----------------------------------------------------------------------------------
; Call OSTaskSwHook();
;----------------------------------------------------------------------------------
BL OSTaskSwHook
;----------------------------------------------------------------------------------
; OSTCBCur = OSTCBHighRdy;
;----------------------------------------------------------------------------------
LDR R0,=OSTCBHighRdy
LDR R1,=OSTCBCur
LDR R0,[R0]
STR R0,[R1]
;----------------------------------------------------------------------------------
; OSPrioCur = OSPrioHighRdy;
;----------------------------------------------------------------------------------
LDR R0,=OSPrioHighRdy
LDR R1,=OSPrioCur
LDRB R0,[R0]
STRB R0,[R1]
;----------------------------------------------------------------------------------
; SP= OSTCBHighRdy->OSTCBStkPtr;
;----------------------------------------------------------------------------------
LDR R0,=OSTCBHighRdy
LDR R0,[R0]
LDR SP,[R0]
;----------------------------------------------------------------------------------
; Restore New Task context
;----------------------------------------------------------------------------------
LDMFD SP!,{R0} ;POP CPSR
MSR SPSR_cxsf,R0
LDMFD SP!,{R0-R12, LR, PC}^
爲什麼中斷級上下文切換複雜?(待寫)
爲什麼中斷級上下文切換複雜?這個中斷級上下文切換目前不支持中斷嵌套,怎麼修改使其支持中斷嵌套?。這個等有時間再去分析。