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.

Configuring Tasks

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.

API Enables

Every API function (service call) in Nucleus SE has an enabling #define symbol in nuse_config.h . For tasks, these are:

NUSE_TASK_SUSPEND
NUSE_TASK_RESUME
NUSE_TASK_SLEEP
NUSE_TASK_RELINQUISH
NUSE_TASK_CURRENT
NUSE_TASK_CHECK_STACK
NUSE_TASK_RESET
NUSE_TASK_INFORMATION
NUSE_TASK_COUNT

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.

Functionality Enables

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.

Task Control Services

The fundamental controls that may be applied to a task, are suspending indefinitely, resuming, suspending for a fixed time period (sleep) and relinquishing control of the CPU. Nucleus RTOS and Nucleus SE each provide four basic API calls for these operations, which I will discuss here.

Suspending a Task

Nucleus PLUS provides a simple API call to enable a specified task to be suspended indefinitely. Nucleus SE has a call with the same functionality.

Nucleus RTOS Call for Task Suspend
Service call prototype:

STATUS NU_Suspend_Task(NU_TASK *task);

Parameters:

task – pointer to the task control block of the task to be suspended (which may be the current task, the ID of which may be obtained using NU_Current_Task_Pointer() – covered in the next article)

Returns:

NU_SUCCESS – the call was completed successfully

NU_INVALID_TASK – the task pointer is invalid

NU_INVALID_SUSPEND – the specified task has a status of NU_FINISHED or NU_TERMINATED


Nucleus SE Call for Task Suspend
This API call supports the key functionality of the Nucleus PLUS API.

Service call prototype:

STATUS NUSE_Task_Suspend(NUSE_TASK task);

Parameters:

task – the index (ID) of the task to be suspended (which may be the current task, the ID of which may be obtained using NUSE_Task_Current() – covered in the next article)

Returns:

NUSE_SUCCESS – the call was completed successfully

NUSE_INVALID_TASK – the task index is invalid

Nucleus SE Implementation of Task Suspend
The key functionality of the API function is quite simple:

This essentially calls the scheduler utility function NUSE_Suspend_Task() , specifying an unconditional suspend (NUSE_PURE_SUSPEND ). This function will invoke the scheduler if the task being suspended is current.

Resuming a Task

Nucleus RTOS provides a simple API call to enable a specified task, which was previously suspended indefinitely, to be resumed. Nucleus SE has a call with the same functionality.

Nucleus RTOS Call for Task Resume
Service call prototype:

STATUS NU_Resume_Task(NU_TASK *task);

Parameters:

task – pointer to the task control block of the task to be resumed

Returns:

NUSE_SUCCESS – the call was completed successfully

NUSE_INVALID_TASK – the task pointer is invalid

NUSE_INVALID_RESUME – the task was not unconditionally suspended

Nucleus SE Call for Task Resume
This API call supports the key functionality of the Nucleus RTOS API.

Service call prototype:

STATUS NUSE_Task_Resume(NUSE_TASK task);

Parameters:

task – the index (ID) of the task to be resumed

Returns:

NUSE_SUCCESS – the call was completed successfully

NUSE_INVALID_TASK – the task index is invalid

NUSE_INVALID_RESUME – the task was not unconditionally suspended

Nucleus SE Implementation of Task Resume
The key functionality of the API function is quite simple:

This essentially calls the scheduler utility function NUSE_Wake_Task() . This function will invoke the scheduler, if the Priority scheduler is in use, in case the resumed task is higher priority than the current task.

Putting a Task to Sleep

Nucleus RTOS provides a simple API call to enable the current task to be suspended for a specific time period. Nucleus SE has a call with the same functionality.

Nucleus RTOS Call for Task Sleep
Service call prototype:

VOID NU_Sleep(UNSIGNED ticks);

Parameters:

ticks – the time period, in real time clock ticks, for which the task should be suspended

Returns:

Nothing

Nucleus SE Call for Task Sleep
This API call supports the key functionality of the Nucleus RTOS API.

Service call prototype:

void NUSE_Task_Sleep(U16 ticks);

Parameters:

ticks – the time period, in real time clock ticks, for which the task should be suspended

Returns:

Nothing

Nucleus SE Implementation of Task Sleep
The key functionality of the API function is quite simple:

The code loads the delay value into the current task’s entry in NUSE_Task_Timeout_Counter[] . Then the task is suspended, using a call to NUSE_Suspend_Task() specifying sleep suspend (NUSE_SLEEP_SUSPEND ).

The timeout value is utilized by the real time clock interrupt service routine. The code is shown here and will be discussed in more detail in a future article.

Relinquishing the CPU

Nucleus PLUS provides a simple API call to enable a task to relinquish the CPU to any other ready tasks, at the same priority level, on a round robin basis. Nucleus SE has a call with very similar functionality. However, it may not be used with the Priority scheduler, as only one task at each priority level is permitted. An error will result if you attempt to enable this API call with the Priority scheduler. The call works with the Round Robin and Time Slice schedulers and has no effect with the Run to Completion scheduler.

Nucleus RTOS Call for Task Relinquish
This API call supports the key functionality of the Nucleus PLUS API.

Service call prototype:

VOID NU_Relinquish(VOID);

Parameters:

None

Returns:

Nothing

Nucleus SE Call for Task Relinquish
This API call supports the key functionality of the Nucleus PLUS API.

Service call prototype:

void NUSE_Task_Relinquish(void);

Parameters:

None

Returns:

Nothing

Nucleus SE Implementation of Task relinquish
The key functionality of the API function is quite simple:

This essentially calls the scheduler function NUSE_Reschedule() . This function simply invokes the scheduler to run the next task.

The next two articles will continue the review of task-related RTOS service calls, illustrated using Nucleus RTOS and Nucleus SE.


Colin Walls has over thirty years experience in the electronics industry, largely dedicated to embedded software. A frequent presenter at conferences and seminars and author of numerous technical articles and two books on embedded software, Colin is an embedded software technologist with Mentor Embedded [the Mentor Graphics Embedded Software Division], and is based in the UK. His regular blog is located at: http://blogs.mentor.com/colinwalls. He may be reached by email at colin_walls@mentor.com

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.