Akos Ledeczi EECE 6354, Fall 2017 Vanderbilt University µC/OS-III Tasks Akos Ledeczi EECE 6354, Fall 2017 Vanderbilt University
Multitasking Multiple sequential tasks (threads) Creates the illusion of having multiple CPUs Pro: Helps in creating modular, better structured code that is simpler to write and understand Con: Brings in unique problems and hard to find bugs
Tasks Run-to-completion Infinite loop Call any function Must call OSTaskDel() at the end Infinite loop Inside the loop it MUST call a RT service (function) to wait for an event and hence, block. Otherwise, it would be the only task running (not necessarily, if there was a higher priority task that becomes ready due to an interrupt. For example, the idle task does not block. But user tasks should not do this. To do something during idle time, you can hook into the idle task itself.) Call any function Reentrant functions
Task Creation OSTaskCreate() 1: stack 2: the OS can initialize the stack with 0s 3: the OS puts the CPU registers on the stack 4: and sets the stack pointer (SP) to this location (top of the stack) and saves it in the TCB 5: the rest of the TCB (Task Control Block) is initialized OSTaskCreateHook() TCB is placed in the Ready list
Task-Resource Interaction Code. A regular C function, but not allowed to return. Priority. Lower number = higher priority OS_PRIO_MAX: no of levels Multiple tasks at the same priority are allowed CPU registers. As if it had the CPU all to itself. Stack. Needed for preemptive kernels. Store local variables, function calls, used by ISRs, etc. Context switch Allocating the stack Global variables. Need protection! IO devices. Need protection!
Stack size Manual stack size calculation: All function call nesting (return address plus all arguments plus all local variables) All ISR nesting (full CPU context and variables used for each) Multiply by 2 to be safe Tedious and unreliable Dynamic stack size calculation Give tasks large stacks and monitor actual usage: Initialize stack with zeros An extra low priority task can “walk” the stack and store highest usage for all other tasks
Stack overflow detection Hardware support: .StkLimitPtr Can be set close to the end of the stack Desired value stored in TCB OS context switch mechanism takes care of setting .StkLimitPtr corresponding to the new Task Software: .OSTaskSWHook() for CPUs with no hardware check Checked at context switch only, so user needs to leave enough slack
Task States (conceptual) Dormant: OS does not know about it (yet or any more) Ready: all, but the highest priority task that are available for running Running: highest priority task available for running Pending: waiting for an event Interrupted: ISR (possibly nested). ISR can call scheduler (OSIntExit(), so the current task may be switched out. ISRs are only allowed to make Post calls.
Task States (actual) Delay: task call one of the delay functions Pend timeout: the task only blocks for a max time limit and then goes back to ready state Suspend: task can suspend itself (or another task) by OSTaskSuspend(). The only way to return from one of the suspended states is by calling OSTaskResume(). This creates four new states.
Task Control Block (TCB) struct os_tcb { CPU_STK *StkPtr; /* Pointer to current top of stack */ void *ExtPtr; /* Pointer to user definable data for TCB extension */ CPU_STK *StkLimitPtr; /* Pointer used to set stack 'watermark' limit */ OS_TCB *NextPtr; /* Pointer to next TCB in the TCB list */ OS_TCB *PrevPtr; /* Pointer to previous TCB in the TCB list */ OS_TCB *TickNextPtr; OS_TCB *TickPrevPtr; OS_TICK_SPOKE *TickSpokePtr; /* Pointer to tick spoke if task is in the tick list */ CPU_CHAR *NamePtr; /* Pointer to task name */ CPU_STK *StkBasePtr; /* Pointer to base address of stack */ OS_TASK_PTR TaskEntryAddr; /* Pointer to task entry point address */ void *TaskEntryArg; /* Argument passed to task when it was created */ OS_PEND_DATA *PendDataTblPtr; /* Pointer to list containing objects pended on */ OS_OBJ_QTY PendDataTblEntries; /* Size of array of objects to pend on */ CPU_TS TS; /* Timestamp */
Task Control Block (TCB) #if OS_MSG_EN > 0u void *MsgPtr; /* Message received */ OS_MSG_SIZE MsgSize; #endif #if OS_CFG_TASK_Q_EN > 0u OS_MSG_Q MsgQ; /* Message queue associated with task */ #if OS_CFG_TASK_PROFILE_EN > 0u CPU_TS MsgQPendTime; /* Time it took for signal to be received */ CPU_TS MsgQPendTimeMax; /* Max amount of time it took for signal to be received */ #if OS_CFG_FLAG_EN > 0u OS_FLAGS FlagsPend; /* Event flag(s) to wait on */ OS_OPT FlagsOpt; /* Options (See OS_OPT_FLAG_xxx) */ OS_FLAGS FlagsRdy; /* Event flags that made task ready to run */ #if OS_CFG_TASK_REG_TBL_SIZE > 0u OS_REG RegTbl[OS_CFG_TASK_REG_TBL_SIZE]; /* Task specific registers */
Task Control Block (TCB) OS_SEM_CTR SemCtr; /* Task specific semaphore counter */ #if OS_CFG_TASK_PROFILE_EN > 0u CPU_TS SemPendTime; /* Time it took for signal to be received */ CPU_TS SemPendTimeMax; /* Max amount of time it took for signal to be received */ #endif #if OS_CFG_TASK_SUSPEND_EN > 0u OS_NESTING_CTR SuspendCtr; /* Nesting counter for OSTaskSuspend() */ CPU_STK_SIZE StkSize; /* Size of task stack (in number of stack elements) */ #if OS_CFG_STAT_TASK_STK_CHK_EN > 0u CPU_STK_SIZE StkUsed; /* Number of stack elements used from the stack */ CPU_STK_SIZE StkFree; /* Number of stack elements free on the stack */ OS_OPT Opt; /* Task options as passed by OSTaskCreate() */
Task Control Block (TCB) /* DELAY / TIMEOUT */ OS_TICK TickCtrPrev; /* Previous time when task was ready */ OS_TICK TickCtrMatch; /* Absolute time when task is going to be ready */ OS_TICK TickRemain; /* Number of ticks remaining for a match (updated at ... */ /* ... run-time by OS_StatTask() */ OS_TICK TimeQuanta; OS_TICK TimeQuantaCtr; #if OS_CFG_TASK_PROFILE_EN > 0u OS_CPU_USAGE CPUUsage; /* CPU Usage of task (0-100%) */ OS_CTX_SW_CTR CtxSwCtr; /* Number of time the task was switched in */ CPU_TS CyclesDelta; /* value of OS_TS_GET() - .CyclesStart */ CPU_TS CyclesStart; /* Snapshot of cycle counter at start of task resumption */ OS_CYCLES CyclesTotal; /* Total number of # of cycles the task has been running */ #endif #ifdef CPU_CFG_INT_DIS_MEAS_EN CPU_TS IntDisTimeMax; /* Maximum interrupt disable time */ #if OS_CFG_SCHED_LOCK_TIME_MEAS_EN > 0u CPU_TS SchedLockTimeMax; /* Maximum scheduler lock time */
Task Control Block (TCB) OS_STATE PendOn; /* Indicates what task is pending on */ OS_STATUS PendStatus; /* Pend status */ OS_STATE TaskState; /* See OS_TASK_STATE_xxx */ OS_PRIO Prio; /* Task priority (0 == highest) */ #if OS_CFG_DBG_EN > 0u OS_TCB *DbgPrevPtr; OS_TCB *DbgNextPtr; CPU_CHAR *DbgNamePtr; #endif };
Idle Task Lowest priority task Not blocking void OS_IdleTask (void *p_arg) { while (DEF_ON) { CPU_CRITICAL_ENTER(); OSIdleTaskCtr++; #if OS_CFG_STAT_TASK_EN > 0u OSStatTaskCtr++; #endif CPU_CRITICAL_EXIT(); OSIdleTaskHook(); } Lowest priority task Not blocking Hook allows the user to put the processor in a low power mode, for example. (interrupts typically wake up CPU.)
Tick Task Hardware timer interrupts current task Tick ISR takes care of hardware and OS related issues and notifies Tick Task (Post) ISR returns, depending on priorities Tick Task runs eventually. Tick Task usually has relatively high priority.
Tick Task cont’d. Tickwheel: stores all tasks waiting for a time event (delayed or pending with a timeout) Optimized to make task handling fast: only one of the spokes needs to be looked at any one time List is ordered by CtrMatch so only first (few) items needs to be looked at Tick Task removes tasks whose delay (timeout) expired
Statistics Task µC/OS-III can provide run time statistics: Overall CPU utilization Per task CPU utilization Per task stack usage OS_CFG_STAT_TASK_EN in os_cfg.h Must call OSStatTaskCPUUsageInit() at the beginning of the first task before creating any more tasks Statistics are stored in each task’s TCB
Timer Task Timers are countdown counters that perform a user-specific action via a callback function OS_CFG_TMR_EN in os_cgf.h Callback is called from the Timer Task context Driven by Tick ISR but typically signaled at a lower rate
ISR Handler Task µC/OS-III can defer event posts from ISRs if the OF_CFG_ISR_POST_DEFERRED_EN is set Used when critical sections are handled by locking/unlocking the scheduler (as opposed to disabling interrupts) ISR stores posts in a queue, the ISR Handler Task actually make the posts Has the highest priority (no other task can have it) Used to minimize interrupt disable time