NucleusPlus中斷處理(基於ARM處理器)

1、中斷優先級

1.1、中斷優先級表

NucleusPlus中斷優先級類似一個數組,數組的索引代表優先級,數組元素的值代表該優先級對應的中斷號,0的優先級最高,優先級彙編語言表如下(僅是按中斷號排序,移動中斷號的位置即可調整中斷的優先級):

INT_IRQ_Priority:
    .word       IRQ_EINT0
    .word       IRQ_EINT1
    .word       IRQ_EINT2
    .word       IRQ_EINT3
    .word       IRQ_EINT4_7
    .word       IRQ_EINT8_23
    .word       IRQ_INT_CAM
    .word       IRQ_nBATT_FLT
    .word       IRQ_INT_TICK
    .word       IRQ_INT_WDT_AC97
    .word       IRQ_INT_TIMER0
    .word       IRQ_INT_TIMER1
    .word       IRQ_INT_TIMER2
    .word       IRQ_INT_TIMER3
    .word       IRQ_INT_TIMER4
    .word       IRQ_INT_UART2
    .word       IRQ_INT_LCD
IRQ_PRIORITY_END:

1.2、中斷向量表

中斷向量表按中斷號排序,依次存放中斷處理函數的入口地址,彙編語言定義如下(s3c2440):

INT_IRQ_Vectors:
    .long     INT_Interrupt_Shell             // Vector 0
    .long     INT_Interrupt_Shell             // Vector 1
    .long     INT_Interrupt_Shell             // Vector 2
    .long     INT_Interrupt_Shell             // Vector 3
    .long     INT_Interrupt_Shell             // Vector 4
    .long     INT_Interrupt_Shell             // Vector 5
    .long     INT_Interrupt_Shell             // Vector 6
    .long     INT_Interrupt_Shell             // Vector 7
    .long     INT_Interrupt_Shell             // Vector 8
    .long     INT_Interrupt_Shell             // Vector 9
    .long     INT_Interrupt_Shell             // Vector 10
    .long     INT_Interrupt_Shell             // Vector 11
    .long     INT_Interrupt_Shell             // Vector 12
    .long     INT_Interrupt_Shell             // Vector 13
    .long     INT_Timer_Interrupt             // Vector 14
    .long     INT_Interrupt_Shell             // Vector 15
    .long     INT_Interrupt_Shell             // Vector 16
    .long     INT_Interrupt_Shell             // Vector 17
    .long     INT_Interrupt_Shell             // Vector 18
    .long     INT_Interrupt_Shell             // Vector 19
    .long     INT_Interrupt_Shell             // Vector 20
    .long     INT_Interrupt_Shell             // Vector 21
    .long     INT_Interrupt_Shell             // Vector 22
    .long     INT_Interrupt_Shell             // Vector 23
    .long     INT_Interrupt_Shell             // Vector 24
    .long     INT_Interrupt_Shell             // Vector 25
    .long     INT_Interrupt_Shell             // Vector 26
    .long     INT_Interrupt_Shell             // Vector 27
    .long     INT_Interrupt_Shell             // Vector 28
    .long     INT_Interrupt_Shell             // Vector 29
    .long     INT_Interrupt_Shell             // Vector 30
    .long     INT_Interrupt_Shell             // Vector 31
    .long     INT_Interrupt_Shell             // Vector 32

在此代碼中僅用了INT_Timer_Interrupt用來實現操作系統調度定時器,其他中斷默認調用INT_Interrupt_Shell。

2、中斷入口查找

2.1、中斷向量

硬件中斷向量如下,IRQ處理函數爲INT_Interrupt。

.global _start
_start:
        ldr pc, ResetAddr
        ldr pc, _undefined_instruction
        ldr pc, _software_interrupt
        ldr pc, _prefetch_abort
        ldr pc, _data_abort
        ldr pc, _not_used
        ldr pc, _irq // IRQ中斷向量地址
        ldr pc, _fiq

ResetAddr:
        .word reset
_undefined_instruction:
        .word undefined_instruction
_software_interrupt:
        .word software_interrupt
_prefetch_abort:
        .word prefetch_abort
_data_abort:
        .word data_abort
_not_used:
        .word not_used
_irq:
        .word INT_Interrupt // IRQ中斷處理函數
_fiq:
        .word fiq
_pad:
        .word 0x12345678 /* now 16*4=64 */

.global _end_vect
_end_vect:
        .balignl 16,0xdeadbeef

2.2、查找中斷處理函數

    .globl  INT_Interrupt
INT_Interrupt:
    /* This Code is used to correctly handle interrupts and
        is necessary due to the nature of the ARM7 architecture  */
    STMDB   sp!, {r0-r4}

    SUB     lr,lr,#4

    LDR     r3, =0x4a000010             // INTPND(Interrupt request status)
    LDR     r2, [r3, #0]
    LDR     r3, =INT_IRQ_Priority

IRQ_VECTOR_LOOP:
    LDR     r0, [r3,#0]                 // Load first vector to be checked from priority table
    MOV     r1, #1                      // Build mask
    MOV     r1, r1, LSL r0              // Use vector number to set mask to correct bit position
    
    TST     r1, r2                      // Test if pending bit is set
    BNE     IRQ_VECTOR_FOUND            // If bit is set, branch to found section...
    
    ADD     r3, r3, #4                  // Move to next word in the priority table
    LDR     r0, =IRQ_PRIORITY_END       // Load the end address for the priority table
    CMP     r0, r3                      // Make sure not at the end of the table (shouldn't happen!)
    BNE     IRQ_VECTOR_LOOP             // Continue to loop if not at the end of the table

    // No bits in pending register set, restore context and exit interrupt servicing
    ADD     sp,sp,#4                    // Adjust sp above IRQ enable value
    LDMIA   sp!,{r0-r4}                 // Restore r0-r4
    STMDB   sp!,{lr}
    LDMIA   sp!,{pc}^
    MOV     pc,lr                       // return to the point of the exception

IRQ_VECTOR_FOUND:
    LDR     r3,=INT_IRQ_Vectors         // Get IRQ vector table address
    MOV     r2, r0, LSL #2              // Multiply vector by 4 to get offset into table
    ADD     r3, r3, r2                  // Adjust vector table address to correct offset
    LDR     r2, [r3,#0]                 // Load branch address from vector table

    MOV     PC, r2                      // Jump to correct branch location based on vector table

    /* END: INT_Interrupt */

第5行 保存通用寄存器r0-r4到中斷棧裏面;

第7行 修正中斷返回地址;

第9~10行 讀取s3c2440中斷掛起寄存器的值(正等待處理的中斷,每bit代表一箇中斷,bit0代表中斷號0,bit1代表中斷號1),r2 = INTPND;

第11行 獲取中斷優先級表INT_IRQ_Priority的地址,r3 = &INT_IRQ_Priority;

第14行 獲取r3指向的優先級的中斷號(r3用於從前往後遍歷中斷優先級表),r0 = 當前優先級的中斷號;

第15~16行 計算當前優先級中斷號對應的INTPND bit位,例如中斷3發生了中斷請求,那麼INTPND[3]應爲1(1<<3);

第18行 判斷當前優先級中斷對應的INTPND bit位是否設置(是否有中斷請求),r1 & r2並且設置標記位;

第19行 根據前一結果進行跳轉,r1 & r2結果爲0,表示中斷對應的bit位爲0(沒有中斷請求),否則爲1(有中斷請求),跳轉到IRQ_VECTOR_FOUND處理中斷;

第21~24行 r3指針移動一個優先級,並判斷r3是否已經移動到中斷優先級表的末尾,沒有的話,跳轉到IRQ_VECTOR_LOOP接着判斷下一優先級的中斷是否產生;

第27~30行 恢復中斷,"STMDB   sp!,{lr}"主要是用於下一條指令"LDMIA   sp!,{pc}^","LDMIA   sp!,{pc}^"帶有pc寄存器,執行時將恢復spsr到cpsr;

第34~37行 獲取中斷號r0對應的中斷函數地址,註釋比較詳細就不介紹每一行代碼。

第39行 跳轉到中斷號對應的中斷處理函數,即調用中斷處理函數。

3、中斷處理

3.1、定時器中斷處理函數(INT_Timer_Interrupt)

       .globl  INT_Timer_Interrupt
INT_Timer_Interrupt:
    ldr     r1, =0x4a000000     // Interrupt Controller Address
    ldr     r2, [r1, #0x14]     // r2 = INTOFFSET
    mov     r3, #1
    lsl     r2, r3, r2          // r2 = (1 << INTOFFSET)
    ldr     r3, [r1, #0]        // r3 = SRCPND
    orr     r3, r3, r2
    str     r3, [r1, #0]        // SRCPND |= (1 << INTOFFSET)
    str     r3, [r1, #0x10]     // INTPND |= (1 << INTOFFSET)

    mov     r4,lr               // Put IRQ return address into r4

    bl      TCT_Interrupt_Context_Save

    bl      TMT_Timer_Interrupt              // Call the timer interrupt
                                                 //   processing.
    b       TCT_Interrupt_Context_Restore
    /* End of INT_Timer_Interrupt */

第3~10行 用於清除s3c2440定時器中斷,清除中斷方式參考s3c2440手冊;

第12行 保存中斷返回地址lr(pc),後面的bl指令會修改lr,需要先保存,另外函數內部默認不保存r0-r3的值,函數內部直接修改r0-r3,因此lr不能保存在r0-r3的寄存器裏面;

第14行 保存中斷上下文;

第16行 調用操作系統定時器處理函數;(任務時間片計數、timer計時...)

第18行 恢復中斷上下文。

3.2、中斷上下文保存(TCT_Interrupt_Context_Save)

//VOID  TCT_Interrupt_Context_Save(INT vector)
//{
        .globl  TCT_Interrupt_Context_Save
TCT_Interrupt_Context_Save:

    /* This routine is designed to handle ARM60/THUMB IRQ interrupts.  The IRQ
       stack is used as a temporary area.  Actual context is saved either
       on the interrupted thread's stack or the system stack- both of which
       are in the Supervisor (SVC) mode.  Note:  upon entry to this routine
       r0-r3 are saved on the current stack and r3 contains the original
       (interrupt return address) lr value.  The current lr contains the
       ISR return address.  */

    /* Determine if this is a nested interrupt.  */
        LDR     r1,Int_Count                // Pickup address of interrupt count
        LDR     r2,[r1, #0]                 // Pickup interrupt counter
        ADD     r2,r2,#1                    // Add 1 to interrupt counter
        STR     r2,[r1, #0]                 // Store new interrupt counter value
        CMP     r2,#1                       // Is it nested?
        BEQ     TCT_Not_Nested_Save         // No

/* Nested interrupt.  Save complete context on the current stack.  */
TCT_Nested_Save:


       /* 1.  Save another register on the exception stack so we have enough to work with */
        STMDB   sp!,{r5}

//       STMFD   sp!,{r0-r1}
//       LDR     R0,=NestX
//       ldr     r1,[r0]
//       add     r1,r1,#1
//       str     r1,[r0]
//       LDMFD   sp!,{r0-r1}

       /* 2.  Save the necessary exception registers into r1-r3 */
        MOV     r1,sp                       // Put the exception sp into r1
        MOV     r2,lr                       // Move the return address for the caller
                                            //  of this function into r2
        MRS     r3,spsr                     // Put the exception spsr into r3

       /* 3.  Adjust the exception stack pointer for future exceptions */
        ADD     sp,sp,#24                   // sp will point to enable reg value when done

       /* 4.  Switch CPU modes to save context on system stack */
        MRS     r5,CPSR                     // Pickup the current CPSR
        BIC     r5,r5,#MODE_MASK            // Clear the mode bits
        ORR     r5,r5,#SUP_MODE             // Change to supervisor mode (SVD)
        MSR     CPSR_cxsf,r5                // Switch modes (IRQ->SVC)

       /* 5.  Store the SVC sp into r5 so the sp can be saved as is. */
        MOV     r5,sp

       /* 6.  Save the exception return address on the stack (PC). */
        STMDB   r5!,{r4}

       /* 7.  Save r6-r14 on stack */
        STMDB   r5!,{r6-r14}

       /* 8.  Switch back to using sp now that the original sp has been saved. */
        MOV     sp,r5

       /* 9.  Get r5 and exception enable registers off of exception stack and
              save r5 (stored in r4) back to the system stack. */
//        LDMIA   r1!,{r4-r5}
        LDMIA   r1!,{r4}

        STMDB   sp!,{r4}
//        MOV     r4,r5                       ; Put exception enable value into r4

       /* 10. Get the rest of the registers off the exception stack and
              save them onto the system stack. */
        LDMIA   r1!,{r5-r8,r11}             // Get r0-r4 off exception stack
        STMDB   sp!,{r5-r8,r11}             // Put r0-r4 on system stack

       /* 11. Store the exception enable value back on the exception stack. */
//        STMDB   r1!,{r4}

       /* 12. Save the SPSR on the system stack (CPSR) */
        STMDB   sp!,{r3}


       /* 13. Re-enable interrupts */
        MRS     r1,CPSR
        BIC     r1,r1,#(IBIT|FBIT)
        MSR     CPSR_cxsf,r1


        BX      r2                          // Return to calling ISR
//    }
//    else
//    {
TCT_Not_Nested_Save:

       /* Determine if a thread was interrupted.  */
//       if (TCD_Current_Thread)
//       {

        LDR     r1,Current_Thread           // Pickup current thread ptr address
        LDR     r1,[r1, #0]                 // Pickup the current thread pointer
        CMP     r1,#0                       // Is it NU_NULL?
        BEQ     TCT_Idle_Context_Save       // If no, no real save is necessary


            /* Yes, a thread was interrupted.  Save complete context on the
               thread's stack.  */

       /* 1.  Save another register on the exception stack so we have enough to work with */
        STMDB   sp!,{r5}

       /* 2.  Save the necessary exception registers into r1-r3 */
        MOV     r1,sp                       // Put the exception sp into r1
        MOV     r2,lr                       // Move the return address for the caller
                                            //  of this function into r2
        MRS     r3,spsr                     // Put the exception spsr into r3

       /* 3.  Adjust the exception stack pointer for future exceptions */
        ADD     sp,sp,#24                   // sp will point to enable reg value when done

       /* 4.  Switch CPU modes to save context on system stack */
        MRS     r5,CPSR                     // Pickup the current CPSR
        BIC     r5,r5,#MODE_MASK            // Clear the mode bits
        ORR     r5,r5,#SUP_MODE             // Change to supervisor mode (SVD)
        MSR     CPSR_cxsf,r5                // Switch modes (IRQ->SVC)

       /* 5.  Store the SVC sp into r5 so the sp can be saved as is. */
        MOV     r5,sp

       /* 6.  Save the exception return address on the stack (PC). */
        STMDB   r5!,{r4}

       /* 7.  Save r6-r14 on stack */
        STMDB   r5!,{r6-r14}

       /* 8.  Switch back to using sp now that the original sp has been saved. */
        MOV     sp,r5

       /* 9.  Get r5 and exception enable registers off of exception stack and
              save r5 (stored in r4) back to the system stack. */
//        LDMIA   r1!,{r4-r5}
        LDMIA   r1!,{r4}
        STMDB   sp!,{r4}
//        MOV     r4,r5                       ; Put exception enable value into r4

       /* 10. Get the rest of the registers off the exception stack and
              save them onto the system stack. */
        LDMIA   r1!,{r5-r8,r11}             // Get r0-r4 off exception stack
        STMDB   sp!,{r5-r8,r11}             // Put r0-r4 on system stack

       /* 11. Store the exception enable value back on the exception stack. */
//        STMDB   r1!,{r4}

       /* 12. Save the SPSR on the system stack (CPSR) */
        STMDB   sp!,{r3}

       /* 13. Save stack type to the task stack (1=interrupt stack) */
        MOV     r1,#1                       // Interrupt stack type
        STMDB   sp!,{r1}


        LDR     r1,Current_Thread           // Pickup current thread ptr address
        LDR     r3,[r1, #0]                 // Pickup current thread pointer
        STR     sp,[r3, #0x2c]               // Save stack pointer

            /* Switch to the system stack.  */
//            REG_Stack_Ptr =  TCD_System_Stack;

        LDR     r1,System_Stack             // Pickup address of stack pointer
        LDR     r3,System_Limit             // Pickup address of stack limit ptr
        LDR     sp,[r1, #0]                 // Switch to system stack
        LDR     r10,[r3, #0]                // Setup system stack limit




       /* Re-enable interrupts */
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
        MRS     r1,CPSR
        BIC     r1,r1,#(IBIT|FBIT)             //;;;;;;;now don't open the interrupt
        MSR     CPSR_cxsf,r1
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

            /* Return to caller ISR.  */
        BX      r2                          // Return to caller ISR

//       }

第15~20行 主要對中斷嵌套計數器TCD_Interrupt_Count加1,並判斷中斷嵌套是否發生(TCD_Interrupt_Count不爲1表示此次是中斷嵌套,即之前正在執行中斷處理函數過程中,發生了新的中斷並且中斷允許,之前的中斷處理被打斷),中斷沒有嵌套則跳轉到TCT_Not_Nested_Save執行;

第27行 r5入棧(INT_Interrupt已經保存了r0-r4),此時棧裏面的內容爲{r5, r0-r4} (低地址到高地址);

第37~40行 IRQ棧保存到r1寄存器,TCT_Interrupt_Context_Save返回地址lr保存到r2寄存器,中斷前的cpsr保存到r3(這幾個寄存器都是專用的,模式切換後就不能直接訪問了);

第43行 恢復IRQ的棧指針(中斷上下文的r5,r0-r4保存在IRQ棧裏面,共4 * 6 = 24個字節空間),至此IRQ棧已經恢復到中斷前;

第46~49行 切換到SVC模式

第52行 SVC的棧指針保存到r5寄存器(中斷處理程序最終是在SVC模式下執行的),對於中斷嵌套,此處的sp是前一中斷處理函數的sp,緊跟的代碼不直接修改sp,通過r5間接操作SVC棧,第58行會把中斷前的sp入棧;

第55行 INT_Timer_Interrupt函數保存的r4(lr中斷返回地址)入棧,SVC棧裏面的內容爲{lr(pc)};

第58行 r6-r14入棧,SVC棧裏面的內容爲{r6-r14, lr(pc)} (低地址到高地址)

第61行 更新棧sp指針(sp已經保存了,此後可以通過sp直接操作SVC棧);

第66~68行 r5出IRQ棧入SVC棧(r1指向IRQ保存中斷上下文棧的地址),SVC棧裏面的內容爲{r5,r6-r14, lr(pc)} (低地址到高地址);

第73~74 r0-r4出IRQ棧入SVC棧,SVC棧裏面的內容爲{r0-r4,r5,r6-r14, lr(pc)} (低地址到高地址);

第80行 第40行保存的cpsr入棧,SVC棧裏面的內容爲{cpsr,r0-r4,r5,r6-r14, lr(pc)} (低地址到高地址);

第84~89行 使能中斷並返回TCT_Interrupt_Context_Save上一級函數,至此已經保存完了嵌套中斷的上下文,第89行之後的爲非中斷嵌套上下文保存;

 

第99~102行 判斷中斷前允許的任務是否爲Idle任務,是的話跳轉到TCT_Idle_Context_Save保存Idle任務的上下文,第108行起保存被中斷的任務的上下文;

第109行 r5入IRQ棧,SVC棧裏面的內容爲{r5,r0-r4} (低地址到高地址);

第112~154行 模式切換,中斷上下文保存到SVC棧(此時的棧正好是被中斷任務的棧),出入棧代碼與中斷嵌套出入棧代碼一樣;

第157~158行 1入任務棧棧頂,任務棧裏面的內容爲{1,cpsr,r0-r4,r5,r6-r14, lr(pc)} (低地址到高地址);

第161~163行 任務棧保存到TCD_Current_Thread->tc_stack_pointer裏面(與中斷嵌套不一樣,中斷恢復是一層層恢復棧,任務棧是下次調度時恢復,中斷處理之後,當前任務可能被換出cpu,需要知道任務的上下文保存在哪裏,因此將任務棧保存在任務控制塊的一個變量裏面);

第168~171行 設置棧、棧幀指針(保存完中斷上下文後的代碼運行在System_Stack裏面,這有個好處,中斷進入時都設置一下,中斷退出的時候不用記得恢復棧,下次進入又會重新設置,因此不會造成棧內存泄漏);

第176~184行 使能中斷並返回TCT_Interrupt_Context_Save上一級函數接着調用中斷處理函數;

3.3、Idle任務上下文保存

Idle任務其實並不存在的,以單任務爲例,任務進入睡眠狀態,首先任務保存自己的上下文,然後調用TCT_Control_To_System,使用新的棧不斷檢測是否有任務需要運行,中斷返回時仍沒有任務需要運行,又會通過其他路徑重新設置任務棧,每次Idle都重新執行,因此Idle任務棧的保存TCT_Idle_Context_Save僅僅恢復IRQ棧及使能中斷。

TCT_Idle_Context_Save:

        MOV     r2,lr                       // Save lr in r2
//        LDR     r3,[sp]                     ; Get exception enable value from stack
        ADD     sp,sp,#20                   // Adjust exception sp for future interrupts
//        STR     r3,[sp]                     ; Put exception enable value back on stack


        MRS     r1,CPSR                     // Pickup current CPSR
        BIC     r1,r1,#MODE_MASK            // Clear the current mode
        BIC     r1,r1,#(IBIT|FBIT)     // Re-enable interrupts
        ORR     r1,r1,#SUP_MODE             // Prepare to switch to supervisor
                                            //   mode (SVC)
        MSR     CPSR_cxsf,r1                // Switch to supervisor mode (SVC)


        BX      r2                          // Return to caller ISR
//    }
//}

3.4、中斷上下文恢復

//VOID  TCT_Interrupt_Context_Restore(void)
//{
        .globl  TCT_Interrupt_Context_Restore
TCT_Interrupt_Context_Restore:

    /* It is assumed that anything pushed on the stack by ISRs has been
       removed upon entry into this routine.  */

    /* Decrement and check for nested interrupt conditions.  */
//    if (--TCD_Interrupt_Count)
//    {

        LDR     r1,Int_Count                // Pickup address of interrupt count
        LDR     r2,[r1, #0]                 // Pickup interrupt counter
        SUB     r2,r2,#1                    // Decrement interrupt counter
        STR     r2,[r1, #0]                 // Store interrupt counter
        CMP     r2,#0
        BEQ     TCT_Not_Nested_Restore

        /* Restore previous context.  */

        LDR     r1,[sp], #4                 // Pickup the saved CPSR
        MSR     SPSR_cxsf,r1                // Place into saved SPSR
        LDMIA   sp,{r0-r15}^                // Return to the point of interrupt

//    }
//    else
//    {

TCT_Not_Nested_Restore:

        /* Determine if a thread is active.  */
//        if (TCD_Current_Thread)
//        {

        LDR     r1,Current_Thread           // Pickup current thread ptr address
        LDR     r0,[r1, #0]                 // Pickup current thread pointer
        CMP     r0,#0                       // Determine if a thread is active
        BEQ     TCT_Idle_Context_Restore    // If not, idle system restore

            /* Clear the current thread pointer.  */
//            TCD_Current_Thread =  NU_NULL;

        MOV     r2,#0                       // Build NU_NULL value
        STR     r2,[r1, #0]                 // Set current thread ptr to NU_NULL

            /* Determine if a time slice is active.  If so, the remaining
               time left on the time slice must be saved in the task's
               control block.  */
//            if (TMD_Time_Slice_State == 0)
//            {

        LDR     r3,Slice_State              // Pickup time slice state address
        LDR     r1,[r3, #0]                 // Pickup time slice state
        CMP     r1,#0                       // Determine if time slice active
        BNE     TCT_Idle_Context_Restore    // If not, skip time slice reset

                /* Pickup the remaining portion of the time slice and save it
                   in the task's control block.  */
//                REG_Thread_Ptr -> tc_cur_time_slice =  TMD_Time_Slice;
//                TMD_Time_Slice_State =  1;

        LDR     r2,Time_Slice               // Pickup address of time slice left
        MOV     r1,#1                       // Build disable time slice value
        LDR     r2,[r2, #0]                 // Pickup remaining time slice
        STR     r1,[r3, #0]                 // Disable time slice
        STR     r2,[r0, #0x20]               // Store remaining time slice

//            }
//        }
TCT_Idle_Context_Restore:

        /* Reset the system stack pointer.  */
        LDR     r1,System_Stack             // Pickup address of stack pointer
        LDR     r2,System_Limit             // Pickup address of stack limit ptr
        LDR     sp,[r1, #0]                 // Switch to system stack
        LDR     r10,[r2, #0]                // Setup system stack limit

        /* Return to scheduler.  */

        B       TCT_Schedule                // Return to scheduling loop

//    }
//}

第13~18行 中斷嵌套計數器TCD_Interrupt_Count減1並判斷是否是中斷嵌套,非中斷嵌套跳轉到TCT_Not_Nested_Restore恢復任務上下文(此次被中斷的是任務);

第22~24行 取出中斷前的cpsr,恢復被嵌套的中斷上下文,接着處理未處理完的中斷代碼;

第36~39行 獲取TCD_Current_Thread,判斷中斷時是否有任務在運行,沒有任務在運行則跳轉到TCT_Idle_Context_Restore;

第44~45行 設置TCD_Current_Thread = NULL;

第53~56行 檢查時間片狀態是否激活,激活情況,每個時鐘中斷會對任務時間片減1,沒有激活,那麼當前任務的時間片不用處理;

第63~67行 去激活時間片狀態,保存當前任務已經運行的時間片,下次再次調度該任務時,時間片繼續按當前時間執行,否則如果每次都從一個完整時間片運行,那麼經常被中斷的任務擁有的執行時間可能就會很長;(去激活時間片後到保存時間片前可能被中斷,去激活時間片後,任務的時間片不再更新)

第73~81行 設置System棧、棧幀指針,跳轉到TCT_Schedule執行任務調度。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章