1. 程式人生 > >μCOS-II移植 - 基於CortexM3

μCOS-II移植 - 基於CortexM3

system ptr sum stk void entry rst arm eve

μCOS-II是一個經典的RTOS。

任務切換對於RTOS來說是最基本也是最核心的部分,除此之外還有任務調度算法。

先來看看基於stm32f107的任務切換代碼:

;********************************************************************************************************
;                                          START MULTITASKING
;                                       void OSStartHighRdy(void)
; ; Note(s) : 1) This function triggers a PendSV exception (essentially, causes a context switch) to cause ; the first task to start. ; ; 2) OSStartHighRdy() MUST: ; a) Setup PendSV exception priority to lowest; ; b) Set initial PSP to 0, to tell context switcher this is first run;
; c) Set the main stack to OS_CPU_ExceptStkBase; ; d) Set OSRunning to TRUE; ; e) Trigger PendSV exception; ; f) Enable interrupts (tasks will run with interrupts enabled). ;********************************************************************************************************
OSStartHighRdy LDR R0, =NVIC_SYSPRI14 ; Set the PendSV exception priority LDR R1, =NVIC_PENDSV_PRI STRB R1, [R0] MOVS R0, #0 ; Set the PSP to 0 for initial context switch call MSR PSP, R0 LDR R0, =OS_CPU_ExceptStkBase ; Initialize the MSP to the OS_CPU_ExceptStkBase LDR R1, [R0] MSR MSP, R1 LDR R0, =OSRunning ; OSRunning = TRUE MOVS R1, #1 STRB R1, [R0] LDR R0, =NVIC_INT_CTRL ; Trigger the PendSV exception (causes context switch) LDR R1, =NVIC_PENDSVSET STR R1, [R0] CPSIE I ; Enable interrupts at processor level OSStartHang B OSStartHang ; Should never get here ;******************************************************************************************************** ; PERFORM A CONTEXT SWITCH (From task level) ; void OSCtxSw(void) ; ; Note(s) : 1) OSCtxSw() is called when OS wants to perform a task context switch. This function ; triggers the PendSV exception which is where the real work is done. ;******************************************************************************************************** OSCtxSw                                   LDR R0, =NVIC_INT_CTRL ; Trigger the PendSV exception (causes context switch) LDR R1, =NVIC_PENDSVSET STR R1, [R0] BX LR ;******************************************************************************************************** ; PERFORM A CONTEXT SWITCH (From interrupt level) ; void OSIntCtxSw(void) ; ; Notes: 1) OSIntCtxSw() is called by OSIntExit() when it determines a context switch is needed as ; the result of an interrupt. This function simply triggers a PendSV exception which will ; be handled when there are no more interrupts active and interrupts are enabled. ;******************************************************************************************************** OSIntCtxSw                                   LDR R0, =NVIC_INT_CTRL ; Trigger the PendSV exception (causes context switch) LDR R1, =NVIC_PENDSVSET STR R1, [R0] BX LR ;******************************************************************************************************** ; HANDLE PendSV EXCEPTION ; void OS_CPU_PendSVHandler(void) ; ; Note(s) : 1) PendSV is used to cause a context switch. This is a recommended method for performing ; context switches with Cortex-M3. This is because the Cortex-M3 auto-saves half of the ; processor context on any exception, and restores same on return from exception. So only ; saving of R4-R11 is required and fixing up the stack pointers. Using the PendSV exception ; this way means that context saving and restoring is identical whether it is initiated from ; a thread or occurs due to an interrupt or exception. ; ; 2) Pseudo-code is: ; a) Get the process SP, if 0 then skip (goto d) the saving part (first context switch); ; b) Save remaining regs r4-r11 on process stack; ; c) Save the process SP in its TCB, OSTCBCur->OSTCBStkPtr = SP; ; d) Call OSTaskSwHook(); ; e) Get current high priority, OSPrioCur = OSPrioHighRdy; ; f) Get current ready thread TCB, OSTCBCur = OSTCBHighRdy; ; g) Get new process SP from TCB, SP = OSTCBHighRdy->OSTCBStkPtr; ; h) Restore R4-R11 from new process stack; ; i) Perform exception return which will restore remaining context. ; ; 3) On entry into PendSV handler: ; a) The following have been saved on the process stack (by processor): ; xPSR, PC, LR, R12, R0-R3 ; b) Processor mode is switched to Handler mode (from Thread mode) ; c) Stack is Main stack (switched from Process stack) ; d) OSTCBCur points to the OS_TCB of the task to suspend ; OSTCBHighRdy points to the OS_TCB of the task to resume ; ; 4) Since PendSV is set to lowest priority in the system (by OSStartHighRdy() above), we ; know that it will only be run when no other exception or interrupt is active, and ; therefore safe to assume that context being switched out was using the process stack (PSP). ;******************************************************************************************************** OS_CPU_PendSVHandler CPSID I ; Prevent interruption during context switch MRS R0, PSP ; PSP is process stack pointer CBZ R0, OS_CPU_PendSVHandler_nosave ; Skip register save the first time SUBS R0, R0, #0x20 ; Save remaining regs r4-11 on process stack STM R0, {R4-R11} LDR R1, =OSTCBCur ; OSTCBCur->OSTCBStkPtr = SP; LDR R1, [R1] STR R0, [R1] ; R0 is SP of process being switched out ; At this point, entire context of process has been saved OS_CPU_PendSVHandler_nosave PUSH {R14} ; Save LR exc_return value LDR R0, =OSTaskSwHook ; OSTaskSwHook(); BLX R0 POP {R14} LDR R0, =OSPrioCur ; OSPrioCur = OSPrioHighRdy; LDR R1, =OSPrioHighRdy LDRB R2, [R1] STRB R2, [R0] LDR R0, =OSTCBCur ; OSTCBCur = OSTCBHighRdy; LDR R1, =OSTCBHighRdy LDR R2, [R1] STR R2, [R0] LDR R0, [R2] ; R0 is new process SP; SP = OSTCBHighRdy->OSTCBStkPtr; LDM R0, {R4-R11} ; Restore r4-11 from new process stack ADDS R0, R0, #0x20 MSR PSP, R0 ; Load PSP with new process SP ORR LR, LR, #0x04 ; Ensure exception return uses process stack CPSIE I BX LR ; Exception return will restore remaining context

OSPendSV()是 PendSV Handler 的中斷處理函數(的名稱),它實現了上下文切換。這種實現
方式對於 ARM Cortex-M3 來說是強烈推薦的。這是因為對於任何異常,ARM Cortex-M3 可以
自動的保存(進入異常)和恢復上下文(退出異常)的 一部分內容。因此 PendSV handler 只
需要保存和恢復 R4-R11 和堆棧指針這些剩余的上下文。使用了 PendSV 的異常機制,意味
著,無論是由任務觸發還是由中斷或異常觸發的上下文切換都可以用同一種方法實現。
置 註意你必須在異常向量表的位置 14 處設置一個指針指向 OSPendSV()。
PendSV handler 的偽代碼如下:

OSPendSV:
if (PSP != NULL) { (1)
Save R4-R11 onto task stack; (2)
OSTCBCur->OSTCBStkPtr = SP; (3)
}
OSTaskSwHook(); (4)
OSPrioCur = OSPrioHighRdy; (5)
OSTCBCur = OSTCBHighRdy; (6)
PSP = OSTCBHighRdy->OSTCBStkPtr; (7)
Restore R4-R11 from new task stack; (8)
Return from exception; (9)


(0) 註意當 OSPendSV 被 CPU 運行,CPU 會自動地保存 xPSR、PC、LR、R12 和 R0-R13 到
任務的堆棧。當將部分上下文保存到相應的任務的堆棧後,CPU 要切換堆棧指針了,
轉而使用 SP_main 來運行剩下的中斷程序。
(1) 這裏我們檢查 SP_process 堆棧指針是否為 NULL。重申:OSStartHighRdy()函數將
SP_process 置 NULL 來達到避免當運行第一個任務時保存任務的上下文的目的。
(2) 如果 PendSV()確實被觸發來實現一個完整的任務切換,則我們簡單的保存上下的寄
存器的值(R4-R11)
(3) 一旦正在被切換的任務的上下文被保存,我們簡單的保存任務的堆棧指針
(SP_process)到正在被切換的任務的 OS_TCB。
(4) 接下來我們調用 UCOS 上下文切換的鉤子函數(參見 OS_CPU_C.C)。
(5) 像其他硬件平臺上移植 UCOS 的做法一樣,我們需要賦值新的高優先級的任務的指
針到當前的任務的指針。
(6) 同上,我們需要復制 OSTCBHighRdy 進 OSTCBCur。
(7) 接下來,我們恢復我們希望切換的那個任務的當前棧頂指針。重申,棧頂指針保存
在變量 OSTCBHighRdy->OSTCBStkPtr 中。方便起見,UCOS 總是將.OSTCBStkPtr 放在結
構體 OS_TCB 的開頭,這樣避免了去查找 SP 的 offset,offset 總是 0。
(8) 我們從任務的堆棧結構中恢復任務的上下文,為任務的執行做準備。
(9) 實現一個異常的返回,這將促使 ARM Cortex-M3 自動從相應的任務堆棧結構中恢復
R3-R12、LR、PC 和 xPSR 寄存器的值。至此便正在運行新任務了。
註意:PendSV Handler 是不可搶占的是不可被中斷的,以此保證上下文自動切換。如果上下
文切換時發生中斷,則新任務恢復後(上下文切換完畢後)立即執行相應的中斷服務程序。

OSStartHighRdy將PSP置零,設置並觸發了PendSV,在OS_CPU_PendSVHandler中,會跳過PSP和r4-r11的壓棧,這是因為是第一次執行任務切換,之前沒有有意義的任務需要保存。

同時可以發現任務級別的切換和中斷級別的切換是一樣的。

對於移植μCOS-II,基本需要專為操作系統而生的SYSTICK提供的OS心跳、任務切換和禁止使能中斷就可以了。

μCOS-II移植 - 基於CortexM3