Tasks

TI-RTOS Tasks are equivalent to independent threads that conceptually execute functions in parallel within a single C program. In reality, switching the processor from one task to another helps achieve concurrency. Each Task is always in one of the following modes of execution:

  • Running: task is currently running
  • Ready: task is scheduled for execution
  • Blocked: task is suspended from execution
  • Terminated: task is terminated from execution
  • Inactive: task is on inactive list

One (and only one) task is always running, even if it is only the Idle Task (see Figure 3.). The current running task can be blocked from execution by calling certain Task module functions, as well as functions provided by other modules like Semaphores. The current task can also terminate itself. In either case, the processor is switched to the highest priority task that is ready to run. See the Task module in the package ti.sysbios.knl section of the TI-RTOS Kernel User Guide for more information on these functions.

Numeric priorities are assigned to tasks, and multiple tasks can have the same priority. Tasks are readied to execute by highest to lowest priority level; tasks of the same priority are scheduled in order of arrival. The priority of the currently running task is never lower than the priority of any ready task. The running task is preempted and rescheduled to execute when there is a ready task of higher priority.

Initializing a Task

When a task is initialized, it has its own runtime stack for storing local variables as well as further nesting of function calls. All tasks executing within a single program share a common set of global variables, accessed according to the standard rules of scope for C functions. This set of memory is the context of the task. The following is an example of the application task being constructed.

Listing 13. A TI-RTOS task
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include <xdc/std.h>
#include <ti/sysbios/BIOS.h>
#include <ti/sysbios/knl/Task.h>

#define TASK_STACK_SIZE 512

/* Task's stack */
uint8_t appTaskStack[TASK_STACK_SIZE];

/* Task object (to be constructed) */
Task_Struct task0;

/* Task function */
void taskFunction(UArg arg0, UArg arg1)
{
    /* Local variables. Variables here go onto task stack!! */

    /* Run one-time code when task starts */

    while (1) /* Run loop forever (unless terminated) */
    {
        /*
         * Block on a signal or for a duration. Examples:
         *  ``Sempahore_pend()``
         *  ``Event_pend()``
         *  ``Task_sleep()``
         *
         * "Process data"
         */
    }
}

int main() {

    Task_Params taskParams;

    // Configure task
    Task_Params_init(&taskParams);
    taskParams.stack = appTaskStack;
    taskParams.stackSize = TASK_STACK_SIZE;
    taskParams.priority = TASK_PRIORITY;

    Task_construct(&task0, taskFunction, &taskParams, NULL);

    BIOS_start();
}

The task creation is done in the main() function, before the TI-RTOS Kernel’s scheduler is started by BIOS_start(). The task executes at its assigned priority level after the scheduler is started.

TI recommends using an existing application task for application-specific processing. When adding an additional task to the application project, the priority of the task must be assigned a priority within the TI-RTOS priority-level range, defined in the TI-RTOS configuration file (.cfg).

Tip

Reduce the number of Task priority levels to gain additional RAM savings in the TI-RTOS configuration file (.cfg):

Task.numPriorities = 6;

Ensure the task has a minimum task stack size of 512 bytes of predefined memory. At a minimum, each stack must be large enough to handle normal subroutine calls and one task preemption context. A task preemption context is the context that is saved when one task preempts another as a result of an interrupt thread readying a higher priority task. Using the TI-RTOS profiling tools of the IDE, the task can be analyzed to determine the peak task stack usage.

Note

The term created describes the instantiation of a task. The actual TI-RTOS method is to construct the task. See Creating vs. Constructing for details on constructing TI-RTOS objects.

A Task Function

When a task is initialized, a function pointer to a task function is passed to the Task_construct function. When the task first gets a chance to process, this is the function which the TI-RTOS runs. Listing 13. shows the general topology of this Task function.

In typical use cases, the task spends most of its time in the blocked state, where it calls a _pend() API such as Semaphore_pend(). Often, high priority threads such as Hwis or Swis unblock the task with a _post() API such as Semaphore_post().