Tasks - configuration and API introduction
The concept of a task was introduced in earlier articles. In most respects, Nucleus SE conforms to expectations in terms of how a task is represented at the conceptual level. Fundamentally, a task is just a set of register values, which may be currently loaded into the processor’s registers (for the executing task) or may be stored ready for a context switch to a task at some later time. In most cases, a task also has a stack space associated with it.
With a run to completion scheduler of course, there is no context switch and a task can be considered to be just a program counter (code entry point) value.
The definition of a task does not encompass the actual code. Of course, a task needs to execute code, but does not “own” it. Functions may be shared between multiple tasks. Multiple tasks may even share all their code. Shared code must almost always be written to be reentrant. Most compilers accommodate this with little problem, but care may be required with library functions, as they may not have been designed for a multi-tasking application.
This definition drives the design of task data structures and API functions that are described in this article. I am going to take a look at how tasks are configured in Nucleus SE and begin to detail the service calls (the Application Program Interface – API) that appertain to tasks in both Nucleus SE and Nucleus RTOS.
Number of Tasks
As with most aspects of Nucleus SE, the configuration of tasks is primarily controlled by #define statements in nuse_config.h. The key setting is NUSE_TASK_NUMBER, which determines how many tasks are configured for the application. The default setting is 1 (i.e. a single task in use) and you can set it to any value up to 16. An erroneous value will result in a compile time error, which is generated by a test in nuse_config_check.h (this is included into nuse_config.c and hence compiled with this module) resulting in a #error statement being compiled. This setting results in some data structures being defined and sized accordingly, of which more shortly.
In Nucleus SE, except with the RTC scheduler, it is essential that at least one task is always ready to run. With the Priority scheduler, you should simply ensure that the lowest priority task is never suspended; think of it as a “background task”.
Unlike some other real-time kernels, Nucleus SE does not utilize any “system tasks”, so all 16 tasks are available for use by user application code or middleware.
Every API function (service call) in Nucleus SE has an enabling #define symbol in nuse_config.h. For tasks, these are:
By default, all of these are set to FALSE, thus disabling each service call and inhibiting the inclusion of any implementation code. To configure tasks for an application, you need to select the API calls that you want to use and set their enabling symbols to TRUE.
Here is an extract from the default nuse_config.h file.
If your code uses an API call, which has not been enabled, a link time error will result, as no implementation code will have been included in the application.
In Nucleus SE, a number of aspects of tasks’ functionality may also be optionally enabled. Again, symbols in the nuse_config.h file are utilized:
NUSE_SUSPEND_ENABLE – this enables tasks to be placed into a suspended state. If this option is not set, all tasks are always ready to be scheduled. Clearly, setting this option is mandatory when the Priority scheduler is in use.
NUSE_BLOCKING_ENABLE – this enables tasks to be suspended on a number of API function calls. If this option is selected, NUSE_SUSPEND_ENABLE is also required.
NUSE_INITIAL_TASK_STATE_SUPPORT – this enables the start-up state of a task to be specified. If this option is not selected, all tasks start up ready to be scheduled.
Task Service Calls
Nucleus RTOS supports 16 service calls (APIs) that appertain to tasks, which provide the following functionality:
- Suspend a task: NU_Suspend_Task(). Implemented by NUSE_Task_Suspend() in Nucleus SE.
- Wake up a task (resume): NU_Resume_Task(). Implemented by NUSE_Task_Resume() in Nucleus SE
- Put a task to sleep for a specified period: NU_Sleep(). Implemented by NUSE_Task_Sleep() in Nucleus SE
- Relinquish control of the processor: NU_Relinquish(). Implemented by NUSE_Task_Reliquish() in Nucleus SE
- Obtain the current task's ID: NU_Current_Task_Pointer(). Implemented by NUSE_Task_Current() in Nucleus SE
- Check available stack space: NU_Check_Stack(). Implemented by NUSE_Task_Check_Stack() in Nucleus SE
- Restore a task to the unused state (reset): NU_Reset_Task(). Implemented by NUSE_Task_Reset() in Nucleus SE
- Provide information about a specified task: NU_Task_Information(). Implemented by NUSE_Task_Information() in Nucleus SE
- Return a count of how many tasks are (currently) configured for the application: NU_Established_Tasks(). Implemented by NUSE_Task_Count() in Nucleus SE
- Add a new task to the application (create): NU_Create_Task(). Not implemented in Nucleus SE.
- Remove a task from the application (delete): NU_Delete_Task(). Not implemented in Nucleus SE.
- Return pointers to all the tasks (currently) in the application: NU_Task_Pointers(). Not implemented in Nucleus SE.
- Change a task’s preemption posture: NU_Change_Preemption(). Not implemented in Nucleus SE.
- Change a task’s priority: NU_Change_Priority(). Not implemented in Nucleus SE.
- Change a task’s time slice: NU_Change_Time_Slice(). Not implemented in Nucleus SE.
- Terminate a task: NU_Terminate_Task(). Not implemented in Nucleus SE.
The implementation of each of these service calls is examined in detail below and in the next couple of RTOS Revealed articles.