Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 14 Current »

Task States

From a µC/OS-III user point of view, a task can be in any one of five states as shown in the figure below. Internally, µC/OS-III does not need to keep track of the dormant state and the other states are tracked slightly differently. The actual µC/OS-III states will be discussed after a discussion on task states from the user’s point of view. The figure below also shows which µC/OS-III functions are used to move from one state to another. The diagram is actually simplified as state transitions are a bit more complicated than this.

Internally, µC/OS-III keeps track of task states using the state machine shown in the figure below. The task state is actually maintained in a variable that is part of a data structure associated with each task, the task’s TCB. The task state diagram was referenced throughout the design of µC/OS-III when implementing most of µC/OS-III’s services. The number in parentheses is the state number of the task and thus, a task can be in any one of eight (8) states (see os.h, OS_TASK_STATE_??? ).

Note that the diagram does not keep track of a dormant task, as a dormant task is not known to µC/OS-III. Also, interrupts and interrupt nesting is tracked differently as will be explained further in the text.

This state diagram should be quite useful to understand how to use several functions and their impact on the state of tasks. In fact, I’d highly recommend that the reader bookmark the page of the diagram.

Task Control Blocks

A task control block (TCB) is a data structure used by kernels to maintain information about a task. Each task requires its own TCB and, for µC/OS-III, the user assigns the TCB in user memory space (RAM). The address of the task’s TCB is provided to µC/OS-III when calling task-related services (i.e., OSTask???() functions). The task control block data structure is declared in os.h as shown in the listing below. Note that the fields are actually commented in os.h, and some of the fields are conditionally compiled based on whether or not certain features are desired. Both are not shown here for clarity.

Also, it is important to note that even when the user understands what the different fields of the OS_TCB do, the application code must never directly access these (especially change them). In other words, OS_TCB fields must only be accessed by µC/OS-III and not the code.

.StkPtr

This field contains a pointer to the current top-of-stack for the task. µC/OS-III allows each task to have its own stack and each stack can be any size. .StkPtr should be the only field in the OS_TCB data structure accessed from assembly language code (for the context-switching code). This field is therefore placed as the first entry in the structure making access easier from assembly language code (it will be at offset zero in the data structure).

.ExtPtr

This field contains a pointer to a user-definable data area used to extend the TCB as needed. This pointer is provided as an argument passed in OSTaskCreate(). This pointer is easily accessible from assembly language since it always follows the .StkPtr. .ExtPtr can be used to add storage for saving the context of a FPU (Floating-Point Unit) if the processor you are using has a FPU.

.NamePtr

This pointer allows a name (an ASCII string) to be assigned to each task. Having a name is useful when debugging, since it is user friendly compared to displaying the address of the OS_TCB. Storage for the ASCII string is assumed to be in user space, either in code memory (ASCII string declared as a const) or in RAM.

.StkLimitPtr

The field contains a pointer to a location in the task’s stack to set a watermark limit for stack growth and is determined from the value of the “stk_limit” argument passed to OSTaskCreate(). Some processors have special registers that automatically check the value of the stack pointer at run-time to ensure that the stack does not overflow. .StkLimitPtr may be used to set this register during a context switch. Alternatively, if the processor does not have such a register, this can be “simulated” in software. However, this is not as reliable as a hardware solution. If this feature is not used then you can set the value of “stk_limit” can be set to 0 when calling OSTaskCreate(). See also Detecting Task Stack Overflows).

.NextPtr and .PrevPtr

These pointers are used to doubly link OS_TCBs in the ready list. A doubly linked list allows OS_TCBs to be quickly inserted and removed from the list.

.TickNextPtr and .TickPrevPtr

These pointers are used to doubly link OS_TCBs in the list of tasks waiting for time to expire or to timeout from pend calls. Again, a doubly linked list allows OS_TCBs to be quickly inserted and removed from the list.

.TickListPtr

This pointer points to one of two tick lists: either the list of tasks delayed waiting for time to expire (OSTickListDly) or tasks pending on an object with a timeout (OSTickListTimeout). This field is used to easily remove the OS_TCB from the list.

.StkBasePtr

This field points to the base address of the task’s stack. The stack base is typically the lowest address in memory where the stack for the task resides. A task stack is declared as follows:

CPU_STK MyTaskStk[???];

CPU_STK is the data type you must use to declare task stacks and ??? is the size of the stack associated with the task. The base address is always &MyTaskStk[0].

.TLS_Tbl[]

This field typically contains an array of pointers and is used by compilers for thread safety as described in Chapter 20, “Thread Safety of the Compiler’s Run-Time Library”).

.TaskEntryAddr

This field contains the entry address of the task. As previously mentioned, a task is declared as shown below and this field contains the address of MyTask.

void MyTask (void *p_arg);

.TaskEntryArg

This field contains the value of the argument that is passed to the task when the task starts. As previously mentioned, a task is declared as shown below and this field contains the value of p_arg.

void MyTask (void *p_arg);

.PendNextPtr and .PendPrevPtr

These two pointers are used to doubly link the OS_TCB in a wait list for a kernel object such as a semaphore, event flag group, mutex or message queue.  This is further described in Chapter 10, “Pend Lists”.

.PendObjPtr

Is a pointer to the kernel object pended on.  This is further described in Chapter 10, “Pend Lists”.
 

.PendOn

This field indicates what the task is pending on and contains one of the following values declared in os.h:

OS_TASK_PEND_ON_NOTHING
OS_TASK_PEND_ON_FLAG
OS_TASK_PEND_ON_TASK_Q
OS_TASK_PEND_ON_MUTEX
OS_TASK_PEND_ON_Q
OS_TASK_PEND_ON_SEM
OS_TASK_PEND_ON_TASK_SEM
OS_TASK_PEND_ON_COND_VAR 

.PendStatus

This field indicates the outcome of a pend and contains one of the values declared in os.h:

OS_STATUS_PEND_OK
OS_STATUS_PEND_ABORT
OS_STATUS_PEND_DEL
OS_STATUS_PEND_TIMEOUT
 

.TaskState

This field indicates the current state of a task and contains one of the eight (8) task states that a task can be in. These states are declared in os.h:

OS_TASK_STATE_RDY
OS_TASK_STATE_DLY
OS_TASK_STATE_PEND
OS_TASK_STATE_PEND_TIMEOUT
OS_TASK_STATE_SUSPENDED
OS_TASK_STATE_DLY_SUSPENDED
OS_TASK_STATE_PEND_SUSPENDED
OS_TASK_STATE_PEND_TIMEOUT_SUSPENDED
 

.Prio

This field contains the current priority of a task. .Prio is a value between 0 and OS_CFG_PRIO_MAX-1. In fact, the idle task is the only task at priority OS_CFG_PRIO_MAX-1.

.BasePrio

This field contains the base priority of a task. .BasePrio is a value between 0 and OS_CFG_PRIO_MAX-1. This field is used to correctly propagate mutex owner priorities to prevent priority inversion.

.MutexGrpHeadPtr

This field is a pointer to a list of mutexes that this task owns.

.StkSize

This field contains the size (in number of CPU_STK elements) of the stack associated with the task. Recall that a task stack is declared as follows:

CPU_STK MyTaskStk[???];

.StkSize is the number of elements in the above array.

.Opt

This field saves the “options” passed to OSTaskCreate() when the task is created as shown below. Note that task options are additive.

OS_OPT_TASK_NONE
OS_OPT_TASK_STK_CHK
OS_OPT_TASK_STK_CLR
OS_OPT_TASK_SAVE_FP

.PendDataTblEntries

This field works with the .PendDataTblPtr and indicates the number of objects a task is pending on at the same time.

.TS

This field is used to store a “time stamp” of when an event that the task was waiting on occurred. When the task resumes execution, this time stamp is returned to the caller.

.SemID

This field is used by third-party tools such TraceAlyzer by Percepio.

.SemCtr

This field contains a semaphore counter associated with the task. Each task has its own built-in semaphore. An ISR or another task can signal a task using this semaphore. .SemCtr is therefore used to keep track of how many times the task is signaled. .SemCtr is used by OSTaskSem???() services.

.TickRemain

This field contains the number of ticks remaining before the task is removed from either the delayed or timeout list. As of V3.04.00, the value of this field is relative (it used to be absolute) since these lists are now implemented as delta-lists. In other words, this field does not contain absolute time remaining until the delay or timeout expires.

.TickCtrPrev

This field is used to keep track of the previous value of OSTickCtr in order to be able to implement the periodic mode for OSTimeDly().

.TimeQuanta and .TimeQuantaCtr

These fields are used for time slicing. When multiple tasks are ready-to-run at the same priority, .TimeQuanta determines how much time (in ticks) the task will execute until it is preempted by µC/OS-III so that the next task at the same priority gets a chance to execute. .TimeQuantaCtr keeps track of the remaining number of ticks for this to happen and is loaded with .TimeQuanta at the beginning of the task’s time slice.

.MsgPtr

When a message is sent to a task, this field contains the message received. This field only exists in a TCB if message queue services (OS_CFG_Q_EN is set to DEF_ENABLED in os_cfg.h), or task message queue services, are enabled (OS_CFG_TASK_Q_EN is set to DEF_ENABLED in os_cfg.h) at compile time.

.MsgSize

When a message is sent to a task, this field contains the size (in number of bytes) of the message received. This field only exists in a TCB if message queue services (OS_CFG_Q_EN is set to DEF_ENABLED in os_cfg.h), or task message queue services, (OS_CFG_TASK_Q_EN is set to DEF_ENABLED in os_cfg.h) are enabled at compile time.

.MsgQ

µC/OS-III allows tasks or ISRs to send messages directly to tasks. Because of this, a message queue is actually built into each TCB. This field only exists in a TCB if task message queue services are enabled at compile time (OS_CFG_TASK_Q_EN is set to DEF_ENABLED in os_cfg.h). .MsgQ is used by the OSTaskQ???() services.

.MsgQPendTime

This field contains the amount of time it took for a message to arrive. When OSTaskQPost() is called, the current time stamp is read and stored in the message. When OSTaskQPend() returns, the current time stamp is read again and the difference between the two times is stored in this variable. A debugger or µC/Probe can be used to indicate the time taken for a message to arrive by displaying this field.

This field is only available if setting OS_CFG_TASK_PROFILE_EN to DEF_ENABLED in os_cfg.h.

.MsgQPendTimeMax

This field contains the maximum amount of time it takes for a message to arrive. It is a peak detector of the value of .MsgQPendTime. The peak can be reset by calling OSStatReset().

This field is only available if setting OS_CFG_TASK_PROFILE_EN to DEF_ENABLED in os_cfg.h.

.RegTbl[]

This field contains a table of “registers” that are task-specific. These registers are different than CPU registers. Task registers allow for the storage of such task-specific information as task ID, “errno” common in some software components, and more. Task registers may also store task-related data that needs to be associated with the task at run time. Note that the data type for elements of this array is OS_REG, which can be declared at compile time to be nearly anything. However, all registers must be of this data type. This field only exists in a TCB if task registers are enabled at compile time (OS_CFG_TASK_REG_TBL_SIZE is greater than 0 in os_cfg.h).

.FlagsPend

When a task pends on event flags, this field contains the event flags (i.e., bits) that the task is pending on. This field only exists in a TCB if event flags services are enabled at compile time (OS_CFG_FLAG_EN is set to DEF_ENABLED in os_cfg.h).

.FlagsRdy

This field contains the event flags that were posted and that the task was waiting on. In other words, it allows a task to know which event flags made the task ready-to-run. This field only exists in a TCB if event flags services are enabled at compile time (OS_CFG_FLAG_EN is set to DEF_ENABLED in os_cfg.h).

.FlagsOpt

When a task pends on event flags, this field contains the type of pend (pend on any event flag bit specified in .FlagsPend or all event flag bits specified in .FlagsPend). This field only exists in a TCB if event flags services are enabled at compile time (OS_CFG_FLAG_EN is set to DEF_ENABLED in os_cfg.h). There can be up to eight main values as shown below plus add-on options. Possible values are:

OS_OPT_PEND_FLAG_CLR_ALL
OS_OPT_PEND_FLAG_CLR_ANY
OS_OPT_PEND_FLAG_CLR_AND
OS_OPT_PEND_FLAG_CLR_OR
OS_OPT_PEND_FLAG_SET_ALL
OS_OPT_PEND_FLAG_SET_ANY
OS_OPT_PEND_FLAG_SET_AND
OS_OPT_PEND_FLAG_SET_OR

You can also ‘add’ OS_OPT_PEND_FLAG_CONSUME and either OS_OPT_PEND_BLOCKING or OS_OPT_PEND_NON_BLOCKING to the above options.

.MonData

Monitor data associated with a task.

.SuspendCtr

This field is used by OSTaskSuspend() and OSTaskResume() to keep track of how many times a task is suspended. Task suspension can be nested. When .SuspendCtr is 0, all suspensions are removed. This field only exists in a TCB if task suspension is enabled at compile time (OS_CFG_TASK_SUSPEND_EN is set to DEF_ENABLED in os_cfg.h).

.CPUUsage

This field is computed by OS_StatTask() if OS_CFG_TASK_PROFILE_EN is set to DEF_ENABLED in os_cfg.h. .CPUUsage contains the CPU usage of a task in percent (0 to 100%). As of version V3.03.00, .CPUUsage is multiplied by 100. In other words, 10000 represents 100.00%.

.CPUUsageMax

This field contains the maximum (i.e., peak detector) CPU usage in percentage for the task and is only available if setting OS_CFG_TASK_PROFILE_EN to DEF_ENABLED in os_cfg.h.

This field is computed by OS_StatTask(). As of version V3.03.00, .CPUUsageMax represents percentage multiplied by 100. In other words, 10000 represents 100.00%.

The peak can be reset by calling OSStatReset().

.CtxSwCtr

This field keeps track of how often the task has executed (not how long it has executed). This field is generally used by debuggers or run-time monitors to see if a task is executing (the value of this field would be non-zero and would be incrementing). The field is enabled at compile time when OS_CFG_TASK_PROFILE_EN is set to DEF_ENABLED in os_cfg.h.

.CyclesDelta

.CyclesDelta is computed during a context switch and contains the value of the current time stamp (obtained by calling OS_TS_GET()) minus the value of .CyclesStart. This field is generally used by debuggers or a run-time monitor to see how long a task had control of the CPU until it got switched out. The field is enabled at compile time when OS_CFG_TASK_PROFILE_EN is set to DEF_ENABLED in os_cfg.h.

.CyclesStart

This field is used to measure how long a task had control of the CPU. .CyclesStart is updated when µC/OS-III performs a context switch. .CyclesStart contains the value of the current time stamp (it calls OS_TS_GET()) when a task is switched-in. The field is enabled at compile time when OS_CFG_TASK_PROFILE_EN is set to DEF_ENABLED in os_cfg.h.

.CyclesTotal

This field accumulates the value of .CyclesDelta, so it contains the total execution time of a task during a set period of time. .CyclesTotal is used by OS_StatTask() to determine CPU usage on a per-task basis. This is typically a 32-bit value because of the accumulation of cycles over time. On the other hand, using a 64-bit value ensures that we can accumulate CPU cycles for almost 600 years even if the CPU is running at 1 GHz! Of course, it’s assumed that the compiler supports 64-bit data types. The field is enabled at compile time when OS_CFG_TASK_PROFILE_EN is set to DEF_ENABLED in os_cfg.h.. .CyclesTotal is used by OS_StatTask() to determine CPU usage on a per-task basis.

.SemPendTime

This field contains the amount of time taken for the semaphore to be signaled. When OSTaskSemPost() is called, the current time stamp is read and stored in the OS_TCB (see .TS). When OSTaskSemPend() returns, the current time stamp is read again and the difference between the two times is stored in this variable. This field can be displayed by a debugger or µC/Probe to indicate how much time it took for the task to be signaled.

This field is only available when setting OS_CFG_TASK_PROFILE_EN to DEF_ENABLED in os_cfg.h.

.SemPendTimeMax

This field contains the maximum amount of time it took for the task to be signaled. It is a peak detector of the value of .SemPendTime. The peak can be reset by calling OSStatReset().

This field is only available if setting OS_CFG_TASK_PROFILE_EN to DEF_ENABLED in os_cfg.h.

.StkUsed and .StkFree

µC/OS-III is able to compute (at run time) the amount of stack space a task actually uses and how much stack space remains. This is accomplished by a function called OSTaskStkChk(). Stack usage computation assumes that the task’s stack is “cleared” when the task is created. In other words, when calling OSTaskCreate(), it is expected that the following options be specified: OS_TASK_OPT_STK_CLR and OS_TASK_OPT_STK_CHK. OSTaskCreate() will then clear all the RAM used for the task’s stack.

µC/OS-III provides an internal task called OS_StatTask() that checks the stack of each of the tasks at run-time. OS_StatTask() typically runs at a low priority so that it does not interfere with the application code. OS_StatTask() saves the value computed for each task in the TCB of each task in these fields, which represents the maximum number of stack bytes used and the amount of stack space still unused by the task. These fields only exist in a TCB if the statistic task is enabled at compile time (OS_CFG_STAT_TASK_STK_CHK_EN is set to DEF_ENABLED in os_cfg.h).

.IntDisTimeMax

This field keeps track of the maximum interrupt disable time of the task. The field is updated only if µC/CPU supports interrupt disable time measurements. This field is available only if setting OS_CFG_TASK_PROFILE_EN to DEF_ENABLED in os_cfg.h and µC/CPU’s CPU_CFG_INT_DIS_MEAS_EN is defined in cpu_cfg.h.

.SchedLockTimeMax

The field keeps track of the maximum scheduler lock time of the task. In other words, the maximum amount of time the task locks the scheduler.

This field is available only if you set OS_CFG_TASK_PROFILE_EN to DEF_ENABLED and OS_CFG_SCHED_LOCK_TIME_MEAS_EN is set to DEF_ENABLED in os_cfg.h.

.DbgNextPtr

This field contains a pointer to the next OS_TCB in a doubly linked list of OS_TCBs. OS_TCBs are placed in this list by OSTaskCreate(). This field is only present if OS_CFG_DBG_EN is set to DEF_ENABLED in os_cfg.h.

.DbgPrevPtr

This field contains a pointer to the previous OS_TCB in a doubly linked list of OS_TCBs. OS_TCBs are placed in this list by OSTaskCreate(). This field is only present if OS_CFG_DBG_EN is set to DEF_ENABLED in os_cfg.h.

.DbgNamePtr

This field contains a pointer to the name of the object that the task is pending on when the task is pending on either an event flag group, a semaphore, a mutual exclusion semaphore or a message queue. This information is quite useful during debugging and thus, this field is only present if OS_CFG_DBG_EN is set to DEF_ENABLED in os_cfg.h.

.TaskID

This field is used by third-party tools such as TraceAlyzer by Percepio.

  • No labels