UCOS-II移植ARM的讀書筆記(12.20)續

 
之前剛開始的時候是直接看移植代碼,後來看到後面實在看不下去了,轉過頭回去看了一個星期的內核結構,以前也看過一遍內核結構,但是有點暈暈的,現在重新看了一次清楚多了,相信回過頭來看移植部分也應該更清楚了。
現在先來掌握一下關於軟件中斷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(0x40) void *GetOSFunctionAddr(int Index); /*  獲取系統服務函數入口        */
__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             */
比如在程序運行到調用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指向參數存儲位置
        MRS     R3, SPSR
        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爲第一次任務切換
        BL      SWI_Exception           ;否則進入c編寫的中斷服務函數
       
        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  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
        MSR     CPSR_c, #(NoInt | SYS32Mode)
        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
                                                    ;保存當前任務堆棧指針到當前任務的TCB
        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                              ;設置堆棧指針
        LDMFD   SP!, {R4, R5}                       ;CPSR,OsEnterSum
                                                    ;恢復新任務的OsEnterSum
        LDR     R3, =OsEnterSum
        STR     R4, [R3]
   
        MSR     SPSR_cxsf, R5                       ;恢復CPSR
        LDMFD   SP!, {R0-R12, LR, PC }^             ;運行新任務
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章