uC/OS II
uC/OS是以function的集合作爲kernel,並沒有和RTXC一樣把所有的Kernel operation包裝在一個 .C檔裏,也就是說uC/OS的kernal API沒有統一的entry point。
uC/OS規定每一個task有一個priority,priority不能重復,這個priority同時會拿來作task id。id越小,priority越高。規定只有64個task.
目前的執行的task id (priority)是放在OSPrioCur,task control block (TCB)是OSTCBCur。
uC/OS的task switch動作在OSCtxSw裏,所有作task switch的function最後都會執行這一段程式,OSCtxSw會switch到task id是OSPrioHighRdy的task,對應的TCB是OSTCBHighRdy,同時upate這兩個變數,所以執行完OSCtxSw後,OSPrioHighRdy=OSPrioCut,OSTCBHighRdy = OSTCBCur。
所以任何有可能做task switch的kernel API,都會找到ready task中priority最高的task,update到OSPrioHighRdy和OSTCBHighRdy裏,再由OSCtxSw作switch。
和其他的OS一樣,有機會作task switch的時機有1.Call kernel API. 2.Hardware interrupt,
uC/OS的system object有: Message, MemoryPartition, Queue, Semaphore, Task, Timer。
中斷服務常式的寫法:
1. 調整r14, 存入Stack[IRQ]
2. 將SPSR存入Stack[IRQ]
3. 清除中斷源
4. 切換SYS Mode,Disable Interrupt
5. 將r0~r12, r14存入Stack[SYS]
6. 呼叫OSIntEnter ( C function )
7. 呼叫 這個中斷的C function
8. 呼叫OSIntExit ( C function )
9. 將r0~r12, r14從Stack取出
10. 切換回IRQ Mode, Enable Interrupt
11. 回應硬體EOI (End Of Interrupt)
12. 將SPSR從Stack取出
13. ldmfd sp!, {pc}^
Timer Interrupt Service Routine
scratch reg: r12
IRQ Mode
1. IRQ 先存 Stack[IRQ]={ lr-4,SPSR,r12 }
2. IRQ 讀取AIC_IVR和TC0_Base[20]
3. IRQ Enable IRQ, Switch to SYS Mode
4. SYS 將所有reg存 Stack[SYS] = {r0~r12, r14}
5. SYS 呼叫 OSIntEnter, OSTimeTick
6. SYS 呼叫OSIntExit: -------------- 如果有需要,切回IRQ Mode,作Context Switch
7. SYS 取回r0 ~ r12, r14 Stack[SYS] = { }
8. SYS Disable IRQ, Switch to IRQ Mode
9. IRQ 讀取AIC_EOICR
10. IRQ 取回r12, r14(SPSR) Stack[IRQ]= {lr-4}
11. IRQ restore SPSR from r14
12. IRQ ldmfd sp!, {pc}^ 回到被中斷程式
OSIntExit
C function,所有Interrupt Service routine都要呼叫這個C function before ending, function主要工作是找出目前Priority最高的Ready Task,並且作Task Switch,會呼叫OSIntCtxSw (Assembly Code)作Context Switch
OSIntCtxSw
Assembly Code,主要工作是Content Switch,程式中負責Task Switch的有兩個,這是其中之一,另一個是OS_TASK_SW,不同的地方是OSIntCtxSw是在Interrupt Service Routine中作Task Switch, OS_TASK_SW是在呼叫System API時作Task Switch使用的。
1. 調整sp,sp = sp - 4
2. switch to IRQ Mode
3. 拿出所所有進入IRQ時Save的Stack, (3個register)
4. 通知EOI
5. Switch to SYS Mode
6. 到OSCtxSw執行
OS_TASK_SW
都是在SYS Mode
1. 存r0~r12, r14 Stack[SYS] = { r0~r12, r14 }
2. ---- OSCtxSw ---- label,以下和OSIntCtxSw共用
3. 存CPSR, SPSR Stack[SYS] = { r0~r12, r14, CPSR, SPSR }
4. OSPrioCur = OSPrioHighRdy
5. 將目前sp保存在tack的TCB結構 OSTCBCur.sp = SP
6. 切換目前的sp到新的task SP = OSTCBHighRdy.sp
7. OSTCBCur = OSTCBHighRdy
8. 從Stack中取回SPSR Stack[SYS] = { r0~r12, r14, CPSR }
9. 從Stack中取回CPSR Stack[SYS] = { r0~r12, r14 }
10. 取回r0~r12, r14 Stack[SYS] = { }
11. bx r14
uCOS_II用一個特殊的方法來找出目前ReadyList中priority最高的Task. 系統最多容許64個task,所以一個task用一個bit來代表,需要8個byte來代表所有Task。
8 bit OSRdyTbl[8] : bit=1代表Ready Task
8 bit OSRdyGrp. : bit=1代表OSRdyGrp的Entry不是0
當要從OSRdyTble[]找到最高priority的bit時(也就是bit order最小的task時)
uCOS_II利用一個Table
INT8U const OSUnMapTbl[] = {
0, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
};
Table有256個entry,代表一個OSRdyTbl Entry (8 bit)的所有組合。這個Table就是”最小bit”的查表,舉例來說13的最bit是
OSUnMap[13]=0 /* 13 = 10011,最小是bit 0 */
所以先從
OSRdyGrp中找到最小不等於0的OSRdyTbl Entry
y = OSUnMapTbl [ OSRdyGrp ]
再由OSRdyTbl找到最小不為0的bit
x = OSUnMapTbl [ OSRdyTbl [ y ] ]
x + ( y <<>
沒有留言:
張貼留言