之前剛開始的時候是直接看移植代碼,後來看到後面實在看不下去了,轉過頭回去看了一個星期的內核結構,以前也看過一遍內核結構,但是有點暈暈的,現在重新看了一次清楚多了,相信回過頭來看移植部分也應該更清楚了。
現在先來掌握一下關於軟件中斷swi的內容,這是我比較發暈的源泉
軟中斷:
中斷不返回形式:void _swi(swi_num) swi_name(arguments)
返回一個結果到R0中 int _swi(swi_num) swi_name(arguments);最多可以返回四個結果R0-R3到一個結構struct type{ int a,b,c,d}中 type(返回類型) _value_in_regs(返回多個結果的修飾符) _swi(swi_num) swi_name(arguments);
在ARM中實現軟中斷的方法我在blog裏面搜了很多文章也沒有看到講的通俗一點的,還是自己看ARM的移植代碼吧
首先定義了一堆軟中斷的中斷號,其中0和1的中斷服務子程序是用匯編編寫的,其他的都是在c語言編寫的中斷服務子程序SWI_Exception中。
__swi(0x00) void OS_TASK_SW(void); /* 任務級任務切換函數 */
__swi(0x01) void _OSStartHighRdy(void); /* 運行優先級最高的任務 */
__swi(0x02) void OS_ENTER_CRITICAL(void); /* 關中斷 */
__swi(0x03) void OS_EXIT_CRITICAL(void); /* 開中斷 */
__swi(0x01) void _OSStartHighRdy(void); /* 運行優先級最高的任務 */
__swi(0x02) void OS_ENTER_CRITICAL(void); /* 關中斷 */
__swi(0x03) void OS_EXIT_CRITICAL(void); /* 開中斷 */
__swi(0x40) void *GetOSFunctionAddr(int Index); /* 獲取系統服務函數入口 */
__swi(0x41) void *GetUsrFunctionAddr(int Index);/* 獲取自定義服務函數入口 */
__swi(0x42) void OSISRBegin(void); /* 中斷開始處理 */
__swi(0x43) int OSISRNeedSwap(void); /* 判斷中斷是否需要切換 */
__swi(0x41) void *GetUsrFunctionAddr(int Index);/* 獲取自定義服務函數入口 */
__swi(0x42) void OSISRBegin(void); /* 中斷開始處理 */
__swi(0x43) int OSISRNeedSwap(void); /* 判斷中斷是否需要切換 */
__swi(0x80) void ChangeToSYSMode(void); /* 任務切換到系統模式 */
__swi(0x81) void ChangeToUSRMode(void); /* 任務切換到用戶模式 */
__swi(0x82) void TaskIsARM(INT8U prio); /* 任務代碼是ARM代碼 */
__swi(0x83) void TaskIsTHUMB(INT8U prio); /* 任務代碼是THUMB */
__swi(0x81) void ChangeToUSRMode(void); /* 任務切換到用戶模式 */
__swi(0x82) void TaskIsARM(INT8U prio); /* 任務代碼是ARM代碼 */
__swi(0x83) void TaskIsTHUMB(INT8U prio); /* 任務代碼是THUMB */
比如在程序運行到調用OS_TASK_SW(void)函數時,就產生軟件中斷,然後就進入中斷服務子程序,按照什麼指令走呢?恩,就按照下面這個代碼,這個代碼是將軟件中斷異常處理程序掛接到內核的作用的,是在啓動代碼中實現的:
LDR PC,SWI_Addr
SWI_Addr DCD SoftwareInterrupt
因此當產生軟中斷之後PC就跳到了SoftwareInterrupt,這時就算真正進入了軟件異常中斷處理部分了,然後就是執行下面的彙編代碼
SoftwareInterrupt
LDR SP, StackSvc ; 重新設置堆棧指針
STMFD SP!, {R0-R3, R12, LR}
MOV R1, SP ; R1指向參數存儲位置
LDR SP, StackSvc ; 重新設置堆棧指針
STMFD SP!, {R0-R3, R12, LR}
MOV R1, SP ; R1指向參數存儲位置
MRS R3, SPSR
TST R3, #T_bit ; 中斷前是否是Thumb狀態,判斷SPSR的T位是不是爲零
TST R3, #T_bit ; 中斷前是否是Thumb狀態,判斷SPSR的T位是不是爲零
LDRNEH R0, [LR,#-2] ; 不爲零即THUMB指令集: 取得Thumb狀態SWI號
BICNE R0, R0, #0xff00 ;在THUMB指令集中軟中斷功能號爲8位,所以取低八位即爲功能號
LDREQ R0, [LR,#-4] ; 爲零即ARM指令集: 取得arm狀態SWI號
BICEQ R0, R0, #0xFF000000 ;在ARM指令集中軟中斷功能號爲24位,所以取低6位即爲功能號
; r0 = SWI號,R1指向參數存儲位置
CMP R0, #1
LDRLO PC, =OSIntCtxSw ;功能號爲0到OSIntCtxSw執行中斷任務切換函數
LDREQ PC, =__OSStartHighRdy ; SWI 0x01爲第一次任務切換
BICNE R0, R0, #0xff00 ;在THUMB指令集中軟中斷功能號爲8位,所以取低八位即爲功能號
LDREQ R0, [LR,#-4] ; 爲零即ARM指令集: 取得arm狀態SWI號
BICEQ R0, R0, #0xFF000000 ;在ARM指令集中軟中斷功能號爲24位,所以取低6位即爲功能號
; r0 = SWI號,R1指向參數存儲位置
CMP R0, #1
LDRLO PC, =OSIntCtxSw ;功能號爲0到OSIntCtxSw執行中斷任務切換函數
LDREQ PC, =__OSStartHighRdy ; SWI 0x01爲第一次任務切換
BL SWI_Exception ;否則進入c編寫的中斷服務函數
LDMFD SP!, {R0-R3, R12, PC}^
StackSvc DCD (SvcStackSpace + SVC_STACK_LEGTH * 4 - 4)
LDMFD SP!, {R0-R3, R12, PC}^
StackSvc DCD (SvcStackSpace + SVC_STACK_LEGTH * 4 - 4)
怎麼進入c編寫的中斷服務子程序SWI_Exception呢?通過下面的申明
IMPORT SWI_Exception ;軟中斷異常處理程序,表示將c程序中的該函數掛接到此段彙編代碼中
同樣的道理
EXPORT __OSStartHighRdy
EXPORT OSIntCtxSw ;中斷退出時的入口,參見startup.s中的IRQ_Handler
EXPORT OSIntCtxSw ;中斷退出時的入口,參見startup.s中的IRQ_Handler
EXPORT SoftwareInterrupt ;軟中斷入口
上面的申明是將該段彙編代碼掛接到外面,因此在外部可以直接調用函數名
繼續看OS_CPU_A.S的其他部分代碼,就是兩個軟件異常中斷處理函數OSIntCtxSw和OSStarHighRdy
OSIntCtxSw代碼是中斷服務子程序使得更高優先級的任務進入就緒狀態後,中斷返回後需要切換到該任務時調用的,這是被切換的任務的CPU寄存器的值已經在響應中斷後存入了堆棧中,因此,這裏不需要重複保存了直接切換任務即可,具體過程看代碼
OSIntCtxSw
;下面爲保存任務環境
;下面爲保存任務環境
;當響應軟件異常中斷後進入了系統模式,在上面的代碼中我們可以看到,進入系統模式時保存的堆棧結構從頂到底依次是:R0,R1,R2,R3,R12,LR,而在用戶模式中任務的堆棧結構應該是:OsEnterSum,CPSR,RO-12,LR,PC,所以在進行軟件中斷任務切換之前先要保存原來任務的堆棧結構。
LDR R2, [SP, #20] ;獲取PC
LDR R12, [SP, #16] ;獲取R12
MRS R0, CPSR
LDR R2, [SP, #20] ;獲取PC
LDR R12, [SP, #16] ;獲取R12
MRS R0, CPSR
MSR CPSR_c, #(NoInt | SYS32Mode)
MOV R1, LR
STMFD SP!, {R1-R2} ;保存LR,PC
STMFD SP!, {R4-R12} ;保存R4-R12
MOV R1, LR
STMFD SP!, {R1-R2} ;保存LR,PC
STMFD SP!, {R4-R12} ;保存R4-R12
MSR CPSR_c, R0
LDMFD SP!, {R4-R7} ;獲取R0-R3
ADD SP, SP, #8 ;出棧R12,PC
MSR CPSR_c, #(NoInt | SYS32Mode)
STMFD SP!, {R4-R7} ;保存R0-R3
LDR R1, =OsEnterSum ;獲取OsEnterSum
LDR R2, [R1]
STMFD SP!, {R2, R3} ;保存CPSR,OsEnterSum
LDMFD SP!, {R4-R7} ;獲取R0-R3
ADD SP, SP, #8 ;出棧R12,PC
MSR CPSR_c, #(NoInt | SYS32Mode)
STMFD SP!, {R4-R7} ;保存R0-R3
LDR R1, =OsEnterSum ;獲取OsEnterSum
LDR R2, [R1]
STMFD SP!, {R2, R3} ;保存CPSR,OsEnterSum
;保存當前任務堆棧指針到當前任務的TCB
LDR R1, =OSTCBCur
LDR R1, [R1]
STR SP, [R1]
LDR R1, =OSTCBCur
LDR R1, [R1]
STR SP, [R1]
BL OSTaskSwHook ;調用鉤子函數
;OSPrioCur <= OSPrioHighRdy
LDR R4, =OSPrioCur
LDR R5, =OSPrioHighRdy
LDRB R6, [R5]
STRB R6, [R4]
;OSTCBCur <= OSTCBHighRdy
LDR R6, =OSTCBHighRdy
LDR R6, [R6]
LDR R4, =OSTCBCur
STR R6, [R4]
OSIntCtxSw_1
;獲取新任務堆棧指針
LDR R4, [R6]
ADD SP, R4, #68 ;17寄存器CPSR,OsEnterSum,R0-R12,LR,SP
LDR LR, [SP, #-8]
MSR CPSR_c, #(NoInt | SVC32Mode) ;進入管理模式
MOV SP, R4 ;設置堆棧指針
;OSPrioCur <= OSPrioHighRdy
LDR R4, =OSPrioCur
LDR R5, =OSPrioHighRdy
LDRB R6, [R5]
STRB R6, [R4]
;OSTCBCur <= OSTCBHighRdy
LDR R6, =OSTCBHighRdy
LDR R6, [R6]
LDR R4, =OSTCBCur
STR R6, [R4]
OSIntCtxSw_1
;獲取新任務堆棧指針
LDR R4, [R6]
ADD SP, R4, #68 ;17寄存器CPSR,OsEnterSum,R0-R12,LR,SP
LDR LR, [SP, #-8]
MSR CPSR_c, #(NoInt | SVC32Mode) ;進入管理模式
MOV SP, R4 ;設置堆棧指針
LDMFD SP!, {R4, R5} ;CPSR,OsEnterSum
;恢復新任務的OsEnterSum
LDR R3, =OsEnterSum
STR R4, [R3]
MSR SPSR_cxsf, R5 ;恢復CPSR
LDMFD SP!, {R0-R12, LR, PC }^ ;運行新任務
;恢復新任務的OsEnterSum
LDR R3, =OsEnterSum
STR R4, [R3]
MSR SPSR_cxsf, R5 ;恢復CPSR
LDMFD SP!, {R0-R12, LR, PC }^ ;運行新任務