微內核之多任務定時切換

/###################################################################

任務目標 :通過定時器產生任務切換,其中swi來實現多個任務之間切換

實驗平臺 : 本實驗是基于S3C2440上實現

/####################################################################

工作原理 :

在運行任務程序的時候通過定時器產生中斷,進入任務優先級調度,選擇優先級最高的任務去執行;其中各個任務之間通過SWI軟中斷來實現相互切換。
難點:在中斷模式和SVC模式下任務棧信息保存及恢復,同時在SVC模式下把將要運行的任務棧信息從該任務棧里恢復到寄存器中,最后跳轉到用戶態執行任務。
基本流程:

圖片.png

實現步驟:

請參考微內核之任務切換。http://www.lxweimin.com/p/7792cdc4fd5f

增加與修改部分bug:

主要增加定時器中斷以及任務函數寄存器信息完整保存,使每次任務執行時被中斷后能正確恢復該任務的信息,各個任務被中斷還能從中斷點繼續執行。

1、IRQ模式下任務棧信息保存

*IRQ模式中斷處理函數

/*#####################################################
 * IRQ中斷被監測到,則下面行為被執行:
 * R14_irq = 將被執行的下一條指令地址+4;
 * SPSR_irq = CPSR
 * CPSR[4:0] = 0b10010 ; Enter Supervisor mode 
 * CPSR[5] = 0         ; Execute in ARM state 
 * CPSR[6] is unchanged 
 * CPSR[7] = 1         ;Disable normal interrupts 
 * CPSR[8] = 1         ; Disable Imprecise Data Aborts (v6 only)
 * CPSR[9] = CP15_reg1_EEbit ; Endianness on exception entry 
 * 執行irq處理函數后返回原程序,可以使用如下指令:
 * SUBS PC,R14,#4 ; SPSR_irq中的值自動存儲到CPSR中
#######################################################*/    
HandleIRQ:

   SUB LR, LR, #4                             /*  計算返回地址,中斷模式LR保存被中斷指令的下一條指令 */
  
    STMDB   SP!,    { LR }                    /*  被中斷模式下的指令下一條指令 */
    STMDB   SP!,    { R0-R14}^                /* ^表示保存user模式下R0-R14,只針對usr模式 */ 
                     
    LDR  R0,=IRQ_STACK          /* 保存irq模式下的SP棧,用于切換到SVC模式下*/
    STR SP, [R0]                        /*  恢復保存的寄存器值,用于調試  */

    LDR R0, =LR_VAR                     /*  保存IRQ模式下的LR,用于調試  */
    STR R14, [R0]   

    LDR R0, =pCurTcb               /* 獲取當前任務棧的起始地址 */
    LDR R2, [R0]        
    MRS R0, SPSR               
    STR R0,[R2],#4          /* SPSR存進當前任務棧里 */
    STR R0,[R2],#4          /* CPSR存進當前任務棧里 */ 

    MOV R1, SP
    MOV R3,#16              /* 把IRQ模式下保存寄存器數量R0-R14 */  
 
/*  把IRQ模式下保存寄存器數量R0-R15復制到當前任務棧里  */

IRQ_STACK_COPY:             

    LDR R0,[R1],#4      
    STR R0,[R2],#4      
    SUBS R3,R3,#1       
    BNE IRQ_STACK_COPY                      
        
    LDR LR, =int_return             /* 設置調用ISR即EINT_Handle函數后的返回地址 
    LDR PC, =ISR_Handle             /* 調用中斷服務函數,在interrupt.c中 */

int_return:
   LDMIA   SP!,    { R0-R14 }^     /* ^表示把中斷模式下SP棧里寄存器恢復到usr模式下對應的寄存器中 */
   LDMIA   SP!,    { PC }^         /* 中斷返回, ^表示將spsr的值復制到cpsr */

2、SVC模式下任務棧信息保存

*SVC模式中斷處理函數

/******************************************************************************
 * SWI中斷進入到SVC(超級用戶模式),當SWI被執行時,下面行為被執行:
 * R14_svc = SWI指令的下一條指令;
 * SPSR_svc = CPSR
 * CPSR[4:0] = 0b10011 ; Enter Supervisor mode 
 * CPSR[5] = 0         ; Execute in ARM state 
 * CPSR[6] is unchanged 
 * CPSR[7] = 1         ;Disable normal interrupts 
 * CPSR[8] is unchanged 
 * CPSR[9] = CP15_reg1_EEbit ; Endianness on exception entry 
 * 執行SWI函數后返回原程序,可以使用如下指令:
 * MOVS PC,R14 ; SPSR_svc中的值自動存儲到CPSR中
 ******************************************************************************/

HandleSWI:

    STMDB   sp!,    {R14}           /* svc模式下R14(LR)保存user模式下的下一條指令地址PC; */
    LDR R0, =LR_VAR                 /* 保存SVC模式下的LR,用于調試 */
    STR R14, [R0]
    
    STMDB   sp!,    { r0-r14}^      /* ^表示user模式下的R0-R14到SVC模式的SP中,只針對user模式 */                 

    MRS     R0, SPSR                /* 不同模式之間切換,cpsr被保存在SPSR中 */    
    STMDB   sp!, {R0}               /* CPSR位置 */    
    STMDB   sp!, {R0}               /* SPSR位置 */    


    LDR R0, =SVC_STACK              /* 保存SVC模式下的SP棧,用于恢復和調試 */
    STR sp, [R0]


    /******************************************************************************
    * 下面代碼用于監測irq中斷觸發的SWI中斷,以便跳轉到taskSched函數執行最高優先級任務
    ******************************************************************************/

    sub  R0, R14, #4                 /* lr -4 為指令SWI XXX的地址,低24位是軟件中斷號 */
    nop
    ldr  R1,[R0,#0]                  /* lr -4 為指令SWI XXX的地址,低24位是軟件中斷號 */
    nop
    bic  R1,R1,#0XFF000000           /* 取得arm指令的低24位立即數 */
    cmp  R1,#255                     /* 判斷24位立即數,如果是255,調用timer0_taskSched函數 */
    beq  timer0_taskSched

    /******************************************************************************
    * 把寄存器值保存到當前任務棧里,把SVC模式下保存的當前任務棧信息保存到當前任務棧
    * tasknTcb中。以備下次調用時在恢復該任務的棧信息,是否可以直接存在棧里呢?
    ******************************************************************************/

    LDR   R0, =pCurTcb            /* 獲取當前任務棧的起始地址 */
    LDR   R1, [R0] 

    CMP   R1,#0                     /* 把pCurTcb值==0;說明是taskStart函數;跳轉到taskBefore處執行 */
    BEQ   taskBefore
    
    LDR   R0,=SVC_STACK             /* 恢復異常時保存SVC模式下的SP棧指針到R2 */
    LDR   R2,[R0] 
    
    MOV   R3,#18                    /* 把SVC模式下棧信息保存到當前任務棧地址,保存SPSR/CPSR/R0-R14寄存器 */    
STACK_COPY: 
    LDR   R0,[R2],#4 
    STR   R0,[R1],#4 
    SUBS  R3,R3,#1 
    BNE   STACK_COPY 
    
taskBefore:

    sub lr, lr, #4                  /* lr -4 為指令SWI XXX的地址,低24位是軟件中斷號 */
    ldr R3,[lr,#0]                  /* lr -4 為指令SWI XXX的地址,低24位是軟件中斷號 */
    bic R3,R3,#0XFF000000           /* 取得arm指令的低24位立即數 */
    
    cmp R3,#0                       /* 判斷24位立即數,如果是0,調用usrModeSpSetup函數 */
    beq  usrModeSpSetup

    cmp R3,#1                       /* 判斷24位立即數,如果是1,調用task1Tcb函數 */
    ldr R0,=task1Tcb
    ldr R0,[R0]
    beq  taskTcbSwitch

    cmp R3,#2                       /* 判斷24位立即數,如果是2,調用task2Tcb函數 */
    ldr R0,=task2Tcb
    ldr R0,[R0]
    beq  taskTcbSwitch

    cmp R3,#3                       /* 判斷24位立即數,如果是3,調用task3Tcb函數 */
    ldr R0,=task3Tcb
    ldr R0,[R0]
    beq  taskTcbSwitch

    bne  END 
taskTcbSwitch:
    ldr pc, =taskSwitch

/******************************************************************************
* 建立用戶模式棧空間
******************************************************************************/
usrModeSpSetup:
    mrs R0, CPSR                    /* Read the CPSR */
    bic R0, R0, #0xdF               /* Clear the mode irq fiq bits  */
    orr R0, R0, #0x10               /* Set the mode bits to FIQ mode */
    msr cpsr_c, R0                  /* 進入用戶態,并設置用戶態的SP,使能中斷 */
    ldr sp, =0x33e00000             /* 設置用戶棧指針起始值 */
    ldr R0, =USER_STACK                 /* 保存IRQ模式下的LR,用于調試 */
    str sp, [R0]

    ldr pc, =rootTask               /* rootTask開始用戶模式 */

timer0_taskSched:
    ldr pc, =taskSched              /* 在SVC模式下進入taskSched函數調度函數 */
            
END:
    
    movne R0, #-1                   /* 沒有該軟中斷對應函數,出錯返回-1 */


3、任務切換函數taskswitch修改


/***********************************************************************************
函數功能: 實現任務切換,調用新得任務執行,并把控制權交給新任務.
入口參數: pTcb: 即將運行的任務的TCB指針.
返 回 值: none.
***********************************************************************************/


int taskSwitch(TCB * pTcb)
{

    /****************************************************************************** 
    * 即將運行任務的寄存器組地址, 匯編語言通過這個變量恢復寄存器 
    ******************************************************************************/
    nextTaskSp = &pTcb->strStackReg;

    /* 即將運行任務的TCB指針 */
    pCurTcb = pTcb;
    __asm__(

    /******************************************************************************
    * 獲取將要運行任務的棧信息并運行新任務
    ******************************************************************************/
    " LDR    R0, =nextTaskSp \n\t"
    " LDR    R1, [R0] \n\t"

    " LDMIA  R1!, {R0} \n\t"      /* Spsr位置 */
    " MSR    SPSR, R0  \n\t"
    " LDMIA  R1!, {R0} \n\t"      /* cpsr位置 */

    " MOV  R13, R1 \n\t"          /* 開始恢復原用戶態任務棧信息 */

    " LDMIA  R13!, {R0-R12}^ \n\t"  /* ^表示把任務棧里R0-R14恢復到user模式下的R0-R14中,只針對用戶模式哦 */
    " ADD R13,R13,#0x4 \n\t"        /* 跳過恢復用戶態的R13  */
    
    " LDMIA  R13!, {R14}^ \n\t"     /* ^表示把任務棧里R0-R14恢復到user模式下的R0-R14中,只針對用戶模式哦 */

    " nop \n\t"
    " LDMIA  R13!, {R14} \n\t"      /* ^表示把任務棧里R15恢復到SVC模式下的R14中,只針對用戶模式哦 */
    " nop \n\t"

    " LDR R13, =SVC_STACK \n\t"     /* 保存SVC模式下的SP棧,用于恢復和調試 */  
    " LDR R13, [R13] \n\t"  
    " ADD R13,R13,#0x48 \n\t"       /* 恢復SVC模式下的SP棧;這里可以釋放掉SVC模式SP棧,因為要跳到用戶模式 */

    " MOVS   R15,  R14 \n\t"        /* 進入用戶模式, SVC模式下的SPSR值恢復到USR模式下的CPSR中 */
 
);
    return 0;
}


最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容