2009年6月22日 星期一

MicroC/OS-II CH6

Event Control Block

General Operations

Xxx為可代換的字
OSXxxCreate - 從ECB FreeList要一個新ECB
OSXxxDel - 將ECB還回ECB FreeList
OSXxxPend - 沒有resource就等Event發生
OSXxxAccept - 取不到resource就繼續執行自己的,不等Event發生
OSXxxPost - 釋放resource
OSXxxQuery - 將傳入的ECB複製一份回傳

Semantics of ECB

ISR或Task可以去發一個Event的訊號。
只有Task可以去等待Event發生。
指定Timeout表示只等待一段時間,如果這段時間內Event沒發生,那Task會被叫醒。


























The use of TCB

ECB提供的function只有關於怎麼去操作資料結構。
Caller 必須要自己處裡同步化的問題。

ECB data structure
/*
*********************************************************************************************************
* OS_EVENT types
*********************************************************************************************************
*/
#define OS_EVENT_TYPE_UNUSED 0
#define OS_EVENT_TYPE_MBOX 1
#define OS_EVENT_TYPE_Q 2
#define OS_EVENT_TYPE_SEM 3
#define OS_EVENT_TYPE_MUTEX 4
#define OS_EVENT_TYPE_FLAG 5
/*
*********************************************************************************************************
* EVENT CONTROL BLOCK
*********************************************************************************************************
*/


#if (OS_EVENT_EN > 0) && (OS_MAX_EVENTS > 0)
typedef struct {
INT8U OSEventType; /* ECB的型態,為OS_EVENT_TYPE_??? */
INT8U OSEventGrp; /*Tbl對應到的列*/
INT16U OSEventCnt; /* Semaphore Count (其他 EVENT type 用不到*/
void *OSEventPtr; /* 指到message 或 queue structure的指標*/
INT8U OSEventTbl[OS_EVENT_TBL_SIZE];
/*Waiting List 存放正在等待Event發生的Task */

} OS_EVENT;
#endif




















ECB functions

OS_EventWaitListInit() - 初始化ECB
OS_EventTaskRdy() - 將從waiting queue移出,移入ready queue
OS_EventTaskWait() - 將task從ready queue移出,移入waiting queue
OS_EventTO() - 讓有設定TimeOut的task變ready

2009年6月21日 星期日

MicroCOS-II -CH5

Time management

  • OSTimeDly(), OSTimeDlyHMSM()
- 讓一個Task將自己給delay參數為tick的時間。
- 如果要delay至少一個tick,那必須呼叫OSTimeDly(2)。
  • OSTimeDlyResume()
回復一個被delay的task。
  • OSTimeSet(), OSTimeGet()
設定或取得系統時間。


OSTimeTick

每次進來就去將OSTCBList裡所有Task裡OSTCBDly不是0的值減1,
如果OSTCBDly減完後變0且那個Task沒被其他Task suspend的話,
就將他移入ready queue。
如果減成0時有suspend,就將他Dly設1。




















Timer


這裡兩種Timer處理ISR- OSTickISR的實作方式

(1) OSTickISR 直接呼叫OSTimeTick,
OSTickISR裡有實際去處理interrupt發生時的handle。





















(2) OSTickISR沒有直接去處理interrupt的handle,
還是在interrupt發生時送一個訊息給一個TickTask,這個Task收到訊息去呼叫
OSTimeTick。

在這裡我們稱OSTimeTick為top-half,
TickTask為buttom-half。





















補充
※Top half 是在呼叫 Interrupt 時所指定的 interrupt handler 函數,bottom half 則是由 top half 所排程(scheduling),真正負責回應中斷的 task。
Top half 是真正接受中斷請求的 task,因此應避免執行過久。由 top half 的實作原則可以看出,top half 真正要做的工作其實只有排程 bottom half,因此執行的速度將會非常快。

補充參考來源: http://www.jollen.org/blog/2008/03/interrupt_handling_bottom_half.html


MicroCOS-II -CH3(4)

Task Scheduleing
Schedular always schedules the highest-priority task to run.
Task-level scheduling : OSSched() => call OS_TASK_SW()
ISR-level scheduleing: OSIntExit() => call OSIntCtxSw()
-不同點在於對PSW或CPU flags的 saving/restoration

OSSchedLock() : 將schedular鎖加1層,lock>0時不會去呼叫schedular。
OSSchedUnlock() : 將schedular鎖解開一層。

Idle Task
系統裡最低優先權的Task,不能被user-tasks delete或suspend。
如果要進入省電狀態,可以在OSTaskIdelHook裡使用HALT-like的指令。

Statistics Task
系統開始時就創造,每秒會執行去計算CPU的使用率。
優先權為OS_LOWPEST_PRIO-1。
OSStatInit() 是初始化這個Task的函式,這函式必須在OS_TaskStat(Statistic本體)執行前被呼叫。

Interrupt under uC/OS-II


















(1) 存下所有的CPU register到被中斷的task stack
(2) Call OSIntEnter() 來將OSintNesting counter加1
(3) 如果進到第一層ISR
(4) 將目前的sp位置存進目前執行的Task的OSTCBStkPtr
-為避免context-switch可能會發生
(5) 清除 interrupt source 以保留讓 interrupt 可以再進入的機會
(6) 再開啟 interrupt
(7) user 定義的 interrupt service
(8) Call OSIntExit() 減少 ineterrupt nesting counter,當 counter 為0時表示所有巢狀 interrupt 都做完時,要去看看有沒有 HPT 被 ISR (某一層) 叫醒,如果有的話就要呼叫 context-switch,然後回到 HPT 而不是被中斷的 task。
(9) 如果被中斷的task還是目前最高優先的task,那OSIntExit()會繼續回到ISR,將所有的CPU register回存。
(10) 跳回被中斷的task被中斷的那行,如果schdular被disable那uC/OS-II也會回到被中斷的task。






















Starting of uC/OS-II
OSInit() 初始化所有的資料結構然後創造OS_TaskIdle和OS_TaskStat
OSStart() pops 出 HPT ready task 的 CPU registers,然後執行一個return從ISR回來
-不會回到 OSStart

2009年6月20日 星期六

MicroCOS-II -CH3(3)

Ready List

一種bitmaps能反應出目前有哪些task處於ready state
  • 每個task在bitmap裡是以優先權來作辨識
一個設計ready list必須被考慮的問題就是這個ready list能多快存取到最高優先權的ready task
  • 可以用rom的空間來換取較好效能
存取到最高優先權ready task的位置的時間
  • 如果以linear list去實作的話,需要花O(n)。
  • 如果採用heap結構需要O(log n)
  • uCOS-II的實作裡,只需要花O(1)
#這個方法會比上面兩個方法消耗空間的多,也會要依賴bus的寬度。
 structure\time insert     delete   min  range
 Heap             O(logn)     O(logn)   O(1)  -∞~+∞
 uCOS-II       O(1)      O(1)  O(1)  0~63
(read list)
Ready List的架構




















共64個task,prio 0~63,數越低優先權越高
Y = task prio/8 是Task在OSRdyTbl裡哪一列,對應到OSRdyGrp的一個位置=>TCB裡的OSTCBY
X = task prio%8 是Task在OSRdyTbl裡哪一行
=>TCB裡的OSTCBX





















OSMapTbl是把一個Task屬於OSRdyTbl(X)或OSRdyGrp(Y)的位置,
對應成他被加入或移除OSRdyTbl或OSRdyGrp時,需要設定的bit。
OSMapTbl[Y] => TCB裡的OSTCBBitY
OSMapTbl[X] => TCB裡的OSTCBBitX


快速尋找Highest Priority Task




















先存好這個OSUnMapTbl Table
然後用當下Ready List裡的值查兩次這個表算出最高優先權的Task。

MicroCOS-II -CH3(2)

Task

uCOS-II最多可以64個優先權。
  • 每個Task必須被指定一個獨立的優先權,因此最多只能有64個Task同時存在再系統裡。
  • 63和62被保留為Idle和Statistic Task。

Task States
Dormant: 程序會在RAM/ROM上,只是程式碼,還不能被稱作是Task,除非被用OSTaskCreate()系列的函數去創造他。
Ready: 一個Task沒有延遲或在等某個event發生。
Running: 一個Task被排程器排到正好取得CPU的執行時間。
Waiting: 一個Task正在等待某個特定的event發生。

ISR: 一個Task被ISR給搶走CPU時間。





















Task Control Block(TCB)


一個存在在主記憶體上面的資料結構用來維護Task的狀態,特別是當他被搶先時。
每個Task都有自己對應的TCB。
  • TCB是用一個雙向鏈結來實作的。 => 方便刪除
  • Free的TCB會被鏈結在一個free list裡。
TCB的內文會被儲存和重新載回當contxt-switch發生時。
  • Task的優先權、delay的計數、等待的event、stack的位置。
  • CPU的暫存器會被存在Stack裡,而非TCB裡。

以下是uCOS-II的TCB結構程式碼
typedef struct os_tcb {
OS_STK *OSTCBStkPtr; /*指向stack位置的指標*/

#if OS_TASK_CREATE_EXT_EN > 0
void *OSTCBExtPtr; /*指向使用者定義的延伸資料結構 */
OS_STK *OSTCBStkBottom; /*指向stack底部的指標*/
INT32U OSTCBStkSize; /*Stack的大小*/
INT16U OSTCBOpt; /* OSTaskCreateExt() 參數傳進來的Task的選項 */
INT16U OSTCBId; /* Task ID (0..65535) */
#endif

struct os_tcb *OSTCBNext; /*指標,指向下一個在TCB list裡的TCB*/
struct os_tcb *OSTCBPrev; /*指標,指向前一個在TCB lsit裡的TCB*/

#if ((OS_Q_EN > 0) && (OS_MAX_QS > 0)) || (OS_MBOX_EN > 0) || (OS_SEM_EN > 0) || (OS_MUTEX_EN > 0)
OS_EVENT *OSTCBEventPtr; /* 指向正在等待的ECB*/
#endif

#if ((OS_Q_EN > 0) && (OS_MAX_QS > 0)) || (OS_MBOX_EN > 0)
void *OSTCBMsg; /* 從OSMboxPost() 或 OSQPost() 收到的訊息 */
#endif

#if (OS_VERSION >= 251) && (OS_FLAG_EN > 0) && (OS_MAX_FLAGS > 0)
#if OS_TASK_DEL_EN > 0
OS_FLAG_NODE *OSTCBFlagNode; /* 指向event flag node*/
#endif
OS_FLAGS OSTCBFlagsRdy; /* 讓task準備開始執行的Event flags*/
#endif

INT16U OSTCBDly; /*task要delay的tick數,或是等待enevt的time out*/
INT8U OSTCBStat; /* Task狀態*/
INT8U OSTCBPrio; /* Task優先權(0 == 最高, 63 == 最低) */

INT8U OSTCBX; /* 根據task的優先權決定的在group裡的bit位置(0..7) */
INT8U OSTCBY; /*對應到優先權在ready table裡的索引*/
INT8U OSTCBBitX; /* Bit mask用來去存取到task在ready list裡的bit位置*/
INT8U OSTCBBitY; /* Bit mask用來去存取到task在ready group裡的bit位置 */

#if OS_TASK_DEL_EN > 0
BOOLEAN OSTCBDelReq; /*暗示是否一個Task需要刪除掉自己 */
#endif
} OS_TCB;

OSTCBY、OSTCBX、OSTCBBitY、OSTCBBitX這四個是在一個Task的TCB建立時預先算好的值,以便之後可以快速對ReadyList作修改。

2009年6月19日 星期五

MicroCOS-II -CH3(1)

Kernel Strucure

Critical-Sections
critial-section是一段程式碼,在共享資源的情況下可能會有race condition的發生。

uC/OS-II裡關於critical-section的實作是定義在
OS_ENTER_CRITIAL() 和 OS_EXIT_CRITIAL() 兩個marco裡

關於這兩個macro uC/OS-II有三種實作方式
1. 中斷開啟和關閉的組語指令。
2. 儲存中斷狀態在stack上或從stack上載回中斷狀態。
3. PSW(Processor Status Word) 存入記憶體的變數或從記憶體變數載回。


OS_CRITICAL_METHOD == 1

這是最簡單的作法OS_ENTER_CRITICAL()用Intel組語DI,OS_EXIT_CRITICAL()用EI。
缺點是當有一個程式先DI之後他去呼叫一個function,然後那個function再return卻使用了EI,這樣就造成了進入funtionc前後,Interrupt狀態不一致的,而我們希望他是一致的,比較方便程式撰寫和除錯。


OS_CRITICAL_METHOD == 2

第二種方法是將OS_ENTER_CRITICAL()PSW放進stack裡後再DI,藉此將interrupt status紀錄下來,
OS_EXIT_CRITICAL()再去將PSW pop回去, 回到之前的status。

#define OS_ENTER_CRITICAL () \
asm(" PUSH PSW ") \
asm(" DI ")
#define OS_EXIT_CRITICAL() \
asm(" POP PSW ")

缺點是如果compiler不是很支援in line code的最佳化時,compiler可能不會知道SP有改變,
或是如果在OS_EXIT_CRITICAL前SP的指標被換了,那可能就會造成錯誤效果。

OS_CRITICAL_METHOD == 3

有些compiler有提供方法(C function)去取得PSW的值,然後將它存在local variable裡。
之後離開critical section後再從variable裡resotre回PSW裡。
這種方法解決的前兩種方法可能會遇到的問題。

void uCOS_II_Service(arguments)
{
OS_CPU_SR cpu_sr;
.
cpu_sr = get_processor_psw();
disabl_interrupts();
.
/* Critical section */
.
set_processor_psw(cpu_sr);
}

MicroCOS-II -CH1

Getting Started with uC/OS-II
Introduction:
一個非常小的即時核心。
preemptible的以優先權驅動的排程,優先權是數字越小優先權越高。
架空DOS的interrupt,然後把自己的掛上去,uC/OS-II可直接往下層存取到DOS或硬體。

Example1: Multitasking

程式架構圖




















main
OSInit: 建立ready list、priority table、TCB、Free pools
Install context switch handler: 將OSCtxSw掛到IVT的0x80
OSTaskCreate: 建立TaskStart的TCB並初始化它
OSStart: 執行context-switch到TasStart

TaskStart
prio: 0
Install tickISR: 將OSTickISR掛到0x08上
Create other task: 建立10個Task的TCB,優先權照建立的順序給
Exit?: 判斷有沒有按下ESC離開,離開會longjmp回到DOS
OSTimeDyHMSM: 沒按下ESC就呼叫Dly睡1秒,去讓其他Task執行

Task1~10
每個Task的prio為他的編號
OSSemPend: 取得Semaphore
random: random取得0~9的數字
OSSemPost: 釋放掉Semaphore
OSTimeDly: Dly自己讓別的Task去執行

執行結果: