Task data structures and unsupported API calls - Embedded.com

Task data structures and unsupported API calls

In this, the third and last article to focus on tasks, I will look at the relevant data structures in Nucleus SE, and outline the Nucleus RTOS API calls that are not supported by Nucleus SE along, with a summary other compatibility issues.

Data Structures

Tasks utilize a number of data structures – in RAM and ROM – which, as is the case with other Nucleus SE objects, are a series of tables, included and dimensioned according to the number of tasks configured and options selected.

I strongly recommend that application code does not access these data structures directly, but uses the provided API functions. This avoids incompatibility with possible future versions of Nucleus SE and unwanted side-effects, and simplifies porting of an application to Nucleus RTOS. The details of data structures are included here to facilitate easier understanding of the working of the service call code and for debugging.

Kernel RAM Data

These data structures are:

NUSE_Task_Context[][] – This is a 2-dimensional array of type ADDR , with one row for each configured task. The number of columns is chip-specific and determined by the symbol NUSE_REGISTERS , which is defined in nuse_types.h . This array is used by the scheduler to save the context for each task and was described in detail under Context Saving in a previous article. It does not exist if the RTC scheduler is in use.

NUSE_Task_Signal_Flags[] – This array of type U8 is created if signals are enabled and contains the 8 signal flags for each task. Signals will be discussed in a future article.

NUSE_Task_Timeout_Counter[] – If the NUSE_Task_Sleep() API call is enabled, this U16 array is created to contain the down counters for each task.

NUSE_Task_Status[] – This U8 array contains the status of each task – NUSE_READY or a suspended status. It is only created is task suspend is enabled.

NUSE_Task_Blocking_Return[] – If API call blocking is enabled, this U8 array is created. It carries the return code which will be used after an API call has been blocked. Normally it will contain NUSE_SUCCESS or a code to indicate the object was reset (e.g. NUSE_MAILBOX_WAS_RESET ).

NUSE_Task_Schedule_Count[] – This U16 array only exists if schedule counting has been enabled and contains the counts for each task.

NUSE_Task_Context[][] is initialized mostly to zeros, except for the entries for status register (SR), program counter (PC) and stack pointer(SP), which are set to initial values (see ROM Data below) and all the other data structures are set to zeros by NUSE_Init_Task() when Nucleus SE starts up. A future article will include a full description of Nucleus SE start-up procedures.

Here are the definitions of these data structures in nuse_init.c file.

User RAM Data

It is the user’s responsibility to define a stack for each task (unless the RTC scheduler is in use). These should be arrays of type ADDR and are normally defined in nuse_config.c . The addresses and sizes of the stacks need to be placed the tasks’ entries in NUSE_Task_Stack_Base[] and NUSE_Task_Stack_Size[] respectively (see ROM Data below).

ROM Data

Between one and four data structures appertaining to tasks are stored in ROM. The exact number depends upon which options have been selected:

NUSE_Task_Start_Address[] – This is an array of type ADDR , with one entry for each configured task; this is a pointer to the entry point of the code for the task.

NUSE_Task_Stack_Base[] – This is an array of type ADDR , with one entry for each configured task; this is a pointer to the base address of the stack for the task. This array only exists if a scheduler other than RTC is selected.

NUSE_Task_Stack_Size[] – This is an array of type U16 , with one entry for each configured task; this is the size of the stack (in words) for the task. This array only exists if a scheduler other than RTC is selected.

NUSE_Task_Initial_State[] – This is an array of type U8 , with one entry for each configured task; this is the initial (start-up) state for the task. It may have values of NUSE_READY or NUSE_PURE_SUSPEND . This array only exists if initial task state support is selected.

These data structures are declared and initialized (statically, of course) in nuse_config.c , thus:

Task Data Footprint

Like all kernel objects in Nucleus SE, the amount of data memory required for tasks is readily predictable.

The ROM data footprint (in bytes) for all the tasks in an application is:

NUSE_TASK_NUMBER * sizeof(ADDR)

Plus, if the scheduler is not RTC:

NUSE_TASK_NUMBER * (sizeof(ADDR) + 2)

Plus, if initial task state support is selected:

NUSE_TASK_NUMBER

The RAM data footprint (in bytes) for all the tasks in an application is totally governed by selected options and may be zero if none of them are selected.

If the scheduler is not RTC:

NUSE_TASK_NUMBER * NUSE_REGISTERS * sizeof(ADDR)

Plus, if support for signals is selected:

NUSE_TASK_NUMBER

Plus, if the NUSE_Task_Sleep() API call is enabled:

NUSE_TASK_NUMBER * 2

Plus, if task suspend is enabled:

NUSE_TASK_NUMBER

Plus, if API call blocking is enabled:

NUSE_TASK_NUMBER

Plus, if schedule counting is enabled:

NUSE_TASK_NUMBER * 2

Unimplemented API Calls

Seven task API calls found in Nucleus RTOS are not implemented in Nucleus SE:

Create Task

This API call creates an application task. It is not needed with Nucleus SE, as tasks are created statically.

Service call prototype:

STATUS NU_Create_Task(NU_TASK *task, CHAR *name,
VOID (*task_entry)(UNSIGNED, VOID *),

UNSIGNED argc, VOID *argv,

VOID *stack_address, UNSIGNED stack_size,

OPTION priority, UNSIGNED time_slice,

OPTION preempt, OPTION auto_start);

Parameters:

task – pointer to a user-supplied task control block; this will be used as a “handle” for the task in other API calls

name – pointers to a 7-character, null-terminated name for the task

task_entry – specifies the entry function for the task

argc – an UNSIGNED data element that may be used to pass initial information to the task

argv – a pointer that may be used to pass information to the task

stack_address – designates the starting memory location of the task’s stack

stack_size – specifies the number of bytes in the stack

priority – specifies the task’s priority value; between 0 and 255 with low numbers indicating highest priority

time_slice – indicates the maximum number of timer ticks that can expire while executing this task; 0 disables time slicing for this task

preempt – indicates whether the task is preemptable; valid values are NU_PREEMPT and NU_NO_PREEMPT

auto_start – indicates the initial state of the task; NU_START makes is Ready ; NU_NO_START makes it suspended

Returns:

NU_SUCCESS – indicates successful completion of the service

NU_INVALID_TASK – indicates the task control block pointer is NULL

NU_INVALID_ENTRY – indicates the task’s entry function pointer is NULL

NU_INVALID_MEMORY – indicates the memory area specified by the stack_ address is NULL

NU_INVALID_SIZE – indicates the specified stack size is not large enough

NU_INVALID_PREEMPT – indicates that the preempt parameter is invalid

NU_INVALID_START – indicates the auto_start parameter is invalid

Delete Task

This API call deletes a previously created application task, which must be Finished or Terminated . It is not needed with Nucleus SE, as tasks are created statically and cannot be deleted.

Service call prototype:

STATUS NU_Delete_Task(NU_TASK *task);

Parameters:

task   – pointer to task control block

Returns:

NU_SUCCESS – indicates successful completion of the service

NU_INVALID_TASK – indicates the task pointer is invalid

NU_INVALID_DELETE – indicates the task is not in a Finished or Terminated state

Get Task Pointers

This API call builds a sequential list of pointers to all established tasks in the system. It is not needed with Nucleus SE, as tasks are identified by a simple index, not a pointer, and it would be redundant.

Service call prototype:

UNSIGNED NU_Task_Pointers(NU_TASK **pointer_list,
UNSIGNED maximum_pointers);

Parameters:

pointer_list – pointer to an array of NU_TASK pointers; this array will be filled with pointers to established tasks in the system

maximum_pointers – the maximum number of pointers to place in the array

Returns:

The number of NU_TASK pointers placed into the array

Change Task Priority

This API assigns a new priority to a task. It is not needed with Nucleus SE as tasks’ priorities are fixed.

Service call prototype:

OPTION NU_Change_Priority(NU_TASK *task, OPTION new_priority);

Parameters:

task – pointer to task control block

new_priority – specifies a priority from 0 to 255

Returns:

The task’s previous priority value

Change Task Preemption

This API call changes the preemption posture of the currently executing task. It is not needed with Nucleus SE as a simpler scheduling scheme is utilized.

Service call prototype:

OPTION NU_Change_Preemption(OPTION preempt);

Parameters:

preempt – new preemption posture: NU_PREEMPT or NU_NO_PREEMPT

Returns:

The task’s previous preemption posture

Change Task Time Slice

This API call changes the time slice of the specified task. It is not needed with Nucleus SE as task time slice durations are fixed.

Service call prototype:

UNSIGNED NU_Change_Time_Slice(NU_TASK *task, UNSIGNED time_slice);

Parameters:

task – pointer to the task control block

time_slice – the maximum amount of timer ticks that can expire while executing this task; a value of zero in this field disables time slicing for this task

Returns:

The task’s previous time slice value.

Terminate Task

This API call terminates the specified task. It is not needed with Nucleus SE as the Terminated state is not supported.

Service call prototype:

STATUS NU_Terminate_Task(NU_TASK *task);

Parameters:

task – pointer to the task control block

Returns:

NU_SUCCESS – indicates successful completion of the service

NU_INVALID_TASK – indicates the task pointer is invalid

Compatibility with Nucleus RTOS

With all aspects of Nucleus SE, it was my goal to maintain as high a level of applications code compatibility with Nucleus RTOS as possible. Tasks are no exception and, from a user’s perspective, they are implemented in much the same way as in Nucleus RTOS. There are areas of incompatibility, which have come about where I determined that such an incompatibility would be acceptable, given that the resulting code is easier to understand, or, more likely, could be made more memory efficient. Otherwise, Nucleus RTOS API calls may be almost directly mapped onto Nucleus SE calls. A future article will include further information on using Nucleus SE for users of Nucleus RTOS.

Object Identifiers

In Nucleus RTOS, all objects are described by a data structure – a control block – which has a specific data type. A pointer to this control block serves as an identifier for the task. In Nucleus SE, I decided that a different approach was needed for memory efficiency, and all kernel objects are described by a number of tables in RAM and/or ROM. The size of these tables is determined by the number of each object type that is configured. The identifier for a specific object is simply an index into those tables. So, I have defined NUSE_TASK as being equivalent to U8 ; a variable – not a pointer – of this type then serves as the task identifier. This is a small incompatibility, which is easily handled if code is ported to or from Nucleus RTOS. Object identifiers are normally just stored and passed around and not operated on in any way.

Nucleus RTOS also supports naming of tasks. These names are only used for target-based debug facilities. I omitted them from Nucleus SE to save memory.

Task States

In Nucleus RTOS, tasks may in one of a number of states: Executing , Ready , Suspended (which may be indefinite, sleep or API call blocked), Terminated or Finished .

Nucleus SE always supports Executing and Ready . All three variants of Suspended are optionally supported. Terminated and Finished are not supported. There are no API calls appertaining to task termination. A task’s outer function must never return, either explicitly or by “falling off the end” (this would result in the Finished state in Nucleus RTOS).

Unimplemented API Calls

Nucleus RTOS supports sixteen service calls to work with tasks. Of these, seven are not implemented in Nucleus SE. Details of these and of the decision to omit them was outlined above.

The next RTOS Revealed article will start to look at RTOS memory management.


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.