OSTaskStkInit
Description
This function is called by OSTaskCreate()
to setup the stack frame of the task being created. Typically, the stack frame will look as if an interrupt just occurred, and all CPU registers were pushed onto the task’s stack. The stacking order of CPU registers is very CPU specific.
OSTaskStkInit()
is part of the CPU port code and this function must not be called by the application code. OSTaskStkInit()
is actually defined by the µC/OS-III port developer.
Files
os.h/os_cpu_c.c
Prototype
CPU_STK *OSTaskStkInit (OS_TASK_PTR p_task, void *p_arg, CPU_STK *p_stk_base, CPU_STK *p_stk_limit, CPU_STK_SIZE stk_size, OS_OPT opt)
Arguments
p_task
is the address of the task being created (see MyTask()
below). Tasks must be declared as follows:
void MyTask (void *p_arg) { /* Do something with "p_arg" (optional) */ while (DEF_ON) { /* Wait for an event to occur */ /* Do some work */ } }
Or,
void MyTask (void *p_arg) { OS_ERR err; /* Do something with "p_arg" (optional) */ /* Do some work */ OSTaskDel((OS_TCB *)0, &err); }
p_arg
is the argument that the task will receive when the task first start (see code above).
p_stk_base
is the base address of the task’s stack. This is typically the lowest address of the area of storage reserved for the task stack. In other words, if declaring the task’s stack as follows:
CPU_STK MyTaskStk[100];
OSTaskCreate()
would pass &OSMyTaskStk[0]
to p_stk_base
.
p_stk_limit
is the address of the task’s stack limit watermark. This pointer is computed by OSTaskCreate()
prior to calling OSTaskStkInit()
.
stk_size
is the size of the task’s stack in number of CPU_STK
elements. In the example above, the stack size is 100.
opt
is the options passed to OSTaskCreate()
for the task being created.
Returned Value
The new top of stack after the task’s stack is initialized. OSTaskStkInit()
will place values on the task’s stack and will return the new pointer for the stack pointer for the task. The value returned is very processor specific. For some processors, the returned value will point to the last value placed on the stack while, with other processors, the returned value will point at the next free stack entry.
Required Configuration
None
Callers
OSTaskCreate()
.
Notes/Warnings
- Do not call this function from the application.
Example Usage
The pseudo code below shows the typical steps performed by this function. Consult an existing µC/OS-III port for examples. Here it is assumed that the stack grows from high memory to low memory.
CPU_STK *OSTaskStkInit (OS_TASK_PTR p_task, void *p_arg, CPU_STK *p_stk_base, CPU_STK *p_stk_limit, CPU_STK_SIZE stk_size, OS_OPT opt) { CPU_STK *p_stk; p_stk = &p_stk_base[stk_size - 1u]; (1) *p_stk-- = Initialize the stack as if an interrupt just occurred; (2) return (p_stk); (3) }
(1) p_stk
is set to the top-of-stack. It is assumed that the stack grows from high memory locations to lower ones. If the stack of the CPU grew from low memory locations to higher ones, the user would simply set p_stk
to point at the base. However, this also means that it would be necessary to initialize the stack frame in the opposite direction.
(2) The CPU registers are stored onto the stack using the same stacking order as used when an interrupt service routine (ISR) saves the registers at the beginning of the ISR. The value of the register contents on the stack is typically not important. However, there are some values that are critical. Specifically, you need to place the address of the task in the proper location on the stack frame and it may be important to load the value of the CPU register and possibly pass the value of p_arg
in one of the CPU registers. Finally, if the task is to return by mistake, it is a good idea to place the address of OS_TaskReturn()
in the proper location on the stack frame. This ensures that a faulty returning task is intercepted by µC/OS-III.
(3) Finally, your code will need to return the value of the stack pointer at the new top-of-stack frame. Some processors point to the last stored location, while others point to the next empty location. You should consult the processor documentation so that the return value points at the proper location.
Below is an example showing which arguments OSTaskCreate()
passes to OSTaskStkInit()
.
CPU_STK MyTaskStk[100]; OS_TCB MyTaskTCB; void MyTask (void *p_arg) { /* Do something with "parg" (optional) */ } void main (void) { OS_ERR err; : : OSInit(&err); /* Check "err" */ : : OSTaskCreate ((OS_TCB *)&MyTaskTCB, (CPU_CHAR *)"My Task", (OS_TASK_PTR )MyTask, /* "p_task" of OSTaskStkInit() */ (void *)0, /* "p_arg" of OSTaskStkInit() */ (OS_PRIO )prio, (CPU_STK *)&MyTaskStk[0], /* "p_stk_base" of OSTaskStkInit() */ (CPU_STK_SIZE )10, /* "p_stk_limit" of OSTaskStkInit() */ (CPU_STK_SIZE )100, /* "stk_size" of OSTaskStkInit() */ (OS_MSG_QTY )0, (OS_TICK )0, (void *)0, (OS_OPT )(OS_OPT_TASK_STK_CLR + OS_OPT_TASK_STK_CHK), /* "opt" of OSTaskStkInit() */ (OS_ERR *)&err); /* Check "err" */ : : OSStart(&err); /* Check "err" */ : : }