The whole truth about the RTOS. Article # 13. Task data structures and unsupported API calls

https://www.embedded.com/design/operating-systems/4458931/Task-data-structures-and-unsupported-API-calls
  • Transfer


In this third and final article on tasks, I will look at the Nucleus SE data structures and describe the RTOS API calls that are not implemented in the Nucleus SE, and also talk about other compatibility issues.

Previous articles in the series:
Article # 12. Task Services
Article # 11. Tasks: configuration and introduction to the API
Article # 10. Scheduler: Additional Features and Saving Context
Article # 9. Scheduler: implementation
Article # 8. Nucleus SE: Internal Design and Deployment
Article # 7. Nucleus SE: Introduction
Article # 6. Other RTOS services
Article # 5. Interaction between tasks and synchronization
Article # 4. Tasks, Context Switching and Interrupts
Article # 3. Tasks and planning
Article # 2. RTOS: Structure and Real Time
Article # 1. RTOS: introduction.

Data structures


Tasks use different data structures (both in RAM and ROM), which, like other Nucleus SE objects, is a set of tables whose size corresponds to the number of selected tasks and parameters.

I strongly recommend that application code refer to these data structures using API functions, rather than directly. This avoids undesirable side effects, incompatibility with future versions of Nucleus SE, and also simplifies porting the application to the Nucleus RTOS. For a better understanding of the work of the service call code and the debugging process, below is a detailed description of the data structures.

Kernel data structures hosted in RAM


These data structures include:

NUSE_Task_Context [] [] - a two-dimensional array of the type ADDR , has one line for each task. The number of columns depends on the controller architecture and is determined by the symbol NUSE_REGISTERS , which is defined in nuse_types.h . This array is used by the scheduler to save the context of each task and was described in detail in the “Saving Context” section of Article # 10. It is not created if the RTC scheduler is used.
NUSE_Task_Signal_Flags [] is an array of type U8 , created when signals are turned on, and contains 8 signal flags for each task. Signals will be discussed in one of the following articles.
NUSE_Task_Timeout_Counter []- an array of type U16 , consists of subtracting counters for each task and is created if the NUSE_Task_Sleep () API call is activated .
NUSE_Task_Status [] is an array of type U8, contains the status of each task - NUSE_READY or the status of suspension. It is created only if task suspension is activated.
NUSE_Task_Blocking_Return [] is an array of type U8, created if the blocking of API calls is activated. It contains the return code that will be used after blocking API calls. It usually contains NUSE_SUCCESS or code indicating that the object was dropped (for example, NUSE_MAILBOX_WAS_RESET ).
NUSE_Task_Schedule_Count [] - type arrayU16 , contains a counter for each task and is created only if the scheduler count has been activated.

NUSE_Task_Context [] [] is initialized mainly with zeros, except for the entries corresponding to the status register (SR), the program counter (PC) and the stack pointer (SP), which are assigned initial values ​​(see in the ROM ”below), and all other data structures NUSE_Init_Task () assigns zeros when running the Nucleus SE. One of the following articles will contain a complete list of Nucleus SE starting procedures with their descriptions.

Below are the definitions of the data structures that are contained in the nuse_init.c file.



User data in RAM


The user must define each task stack (if not using the RTC scheduler). These should be ADDR arrays , which are typically defined in nuse_config.c . Addresses and stack sizes should be placed in task entries NUSE_Task_Stack_Base [] and NUSE_Task_Stack_Size [], respectively (see. Data in ROM).

ROM data


The ROM contains from one to four data structures related to the tasks. The exact number depends on the selected parameters:

NUSE_Task_Start_Address [] is an array of type ADDR , having one entry for each task, which is a pointer to the entry point to the code for the task.
NUSE_Task_Stack_Base [] is an array of type ADDR , having one entry for each task, which is a pointer to the base address of the stack for the task. This array is created if any scheduler other than RTC is used.
NUSE_Task_Stack_Size [] is an array of type U16that has one entry for each task, which shows the stack size for the task (in words). This array is created if any scheduler other than RTC is used.
NUSE_Task_Initial_State [] is an array of type U8 , which has one entry for each task, which shows the initial state of the task. May be NUSE_READY or NUSE_PURE_SUSPEND . This array is created if support for the initial state of the task is selected.

These data structures are declared and initialized (static) in nuse_config.c :



Memory capacity for storing task data (Task Data Footprint)


Like all Nucleus SE core objects, the amount of memory required to store data is predictable.

ROM
size (in bytes) required for all application tasks: NUSE_TASK_NUMBER * sizeof (ADDR)

Plus, if any scheduler other than RTC is
selected : NUSE_TASK_NUMBER * (sizeof (ADDR) +2)

Plus, if support for the initial task state is selected:
NUSE_TASK_NUMBER

For storing data in RAM, the amount of memory (in bytes) is determined by the selected parameters, and it can be zero if none of the parameters are selected.
If a scheduler other than RTC is
selected : 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 activated:
NUSE_TASK_NUMBER * 2

Plus, if task pause is activated:
NUSE_TASK_NUMBER

Plus, if API call blocking is activated:
NUSE_TASK_NUMBER

Plus, if the scheduler counter is activated:
NUSE_TASK_NUMBER_NUMBER

Non-implemented Nucleus SE API calls


Below are seven API calls that are in the Nucleus RTOS, not implemented in the Nucleus SE.

Create Task


This API call creates an application task. In Nucleus SE, this function is not necessary, since tasks are created statically.

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 priorities, UNSIGNED, UNSIGNED, VOID *). auto_start);

Parameters:

task - a pointer to a user task management block, can be used as a handle / reference ("handle") of a task in other API calls;
name - pointers to the name of the task, a 7-character string with a terminating zero;
task_entry - specifies the input function for the task;
argc - UNSIGNEDa data element that can be used to transfer initial information to a task;
argv is a pointer that can be used to pass information to a task;
stack_address - sets the initial memory sector for the task stack;
stack_size - indicates the number of bytes in the stack;
priority - indicates the priority value of the task: from 0 to 255, where lower numbers correspond to the highest priority;
time_slice - indicates the maximum number of time slices that can pass when performing this task. A value of “0” disables time slicing for this task;
preempt - indicates whether the task is being repressed or not. May be NU_PREEMPT andNU_NO_PREEMPT ;
auto_start - shows the initial state of the task. NU_START means that the task is ready for execution, and NU_NO_START - that the task is suspended.

Return value:

NU_SUCCESS - indicates the successful completion of the service;
NU_INVALID_TASK - indicates that the pointer to the task control block is zero ( NULL );
NU_INVALID_ENTRY - indicates that the pointer to the input function of the task is zero ( NULL );
NU_INVALID_MEMORY - indicates that the memory sector assigned by the stack_address parameter is zero ( NULL );
NU_INVALID_SIZE- indicates that the specified stack size is insufficient;
NU_INVALID_PREEMPT - indicates that the preempt parameter is set incorrectly;
NU_INVALID_START - indicates that the auto_start parameter is incorrect.

Delete Task


This API call removes the previously created application task, which should have the status of Finished (terminated) or Terminated (complete suspension). This call is also not necessary in Nucleus SE, since tasks are created statically and cannot be deleted.

Call prototype:

STATUS NU_Delete_Task (NU_TASK * task);

Parameters:

task - a pointer to the task management block

Return value:

NU_SUCCESS - indicates a successful completion of the service;
NU_INVALID_TASK - indicates that the pointer to the task is set incorrectly;
NU_INVALID_DELETE - indicates that the task is not in the “Finished” or “Terminated” state.

Get task pointers (Get Task Pointers)


This API call makes a sequential list of pointers to all tasks in the system. It is not needed in the Nucleus SE, since tasks are identified using a simple index, not a pointer.

Call prototype:

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

Parameters:

pointer_list - a pointer to an array of pointers NU_TASK . This array will be filled with pointers to the tasks installed in the system;
maximum_pointers - the maximum number of pointers that can be placed in an array.

Return value: The

number of NU_TASK pointers placed in the array.

Change Task Priority


This API call assigns a new priority to the task. It is not required in Nucleus SE, as task priorities are constant.

Call prototype:

OPTION NU_Change_Priority (NU_TASK * task, OPTION new_priority);

Parameters:

task - pointer to the task management block;
new_priority - sets the priority from 0 to 255.

Return value:
Previous value of the task priority.

Change Task Preemption Algorithm


This API call reverses the order of the executing task. In Nucleus SE, it is not needed, since a simpler scheduling algorithm is used.

Call prototype:
OPTION NU_Change_Preemption (OPTION preempt);

Parameters:
preempt - new preemption algorithm, takes the values NU_PREEMPT or NU_NO_PREEMPT

Return value:
Previous algorithm for preempting a task.

Change Task Time Slice (Change Task Time Slice)


This API call changes the time slot of a specific task. In the Nucleus SE, it is not necessary, since the time quanta of the tasks are fixed.

Call prototype:
UNSIGNED NU_Change_Time_Slice (NU_TASK * task, UNSIGNED time_slice);

Parameters:
task - a pointer to the task management block;
time_slice - the maximum number of time slices that can pass when performing this task, the zero value of this field disables the time slicing for this task.

Return value: The
previous value of the task time slice.

Terminate Task


This API call completes a specific task. This is not necessary in the Nucleus SE, as the Terminated state is not supported.

Call prototype:
STATUS NU_Terminate_Task (NU_TASK * task);

Parameters:
task - a pointer to the task management block.

Return value:
NU_SUCCESS - indicates the successful completion of the service;
NU_INVALID_TASK - indicates that the task pointer is set incorrectly.

Nucleus RTOS Compatibility


When developing the Nucleus SE, one of the main tasks was to ensure a high level of code compatibility with Nucleus RTOS. Tasks are no exception, and, from the user's point of view, they are implemented in much the same way as in the Nucleus RTOS. There are some incompatible areas where I came to the conclusion that such incompatibility would be acceptable, given that the final code is easier to understand and can use memory more efficiently. However, besides these incompatibilities, the other Nucleus RTOS API calls can be almost directly used as Nucleus SE calls. One of the following articles will contain more detailed information on the transition from Nucleus RTOS to Nucleus SE

Object IDs


In Nucleus RTOS, all objects are described by a data structure (control units) that have a specific type. A pointer to this control unit serves as an identifier for the task. In Nucleus SE, I decided that a different approach was needed to use memory efficiently. All kernel objects are described by a set of tables in RAM and / or ROM. The size of these tables is determined by the number of object types. The identifier of a particular object is the index in these tables. Therefore, I have defined NUSE_TASK as equivalent to U8 . A variable of this type (not a pointer) serves as a task identifier. This is a small incompatibility that is easy to figure out if the code is ported from or to Nucleus RTOS. Object identifiers are usually stored and transmitted unchanged.

Nucleus RTOS also supports task naming. These names are used only when debugging. I excluded them from the Nucleus SE to save memory.

Task states


In Nucleus, RTOS tasks can be in one of several states: Executing , Ready , Suspended (which leads to uncertainty: the task is in standby mode or blocked by an API call), Terminated or Finished.

Nucleus SE also supports Executing and Ready states . All three Suspended options are supported optionally. Terminated and Finished are not supported. No API calls to complete tasks. The external task function should never return a value either explicitly or implicitly (this will lead to a Finished state in the Nucleus RTOS).

Unrealized API calls


Nucleus RTOS supports 16 task calls. Of these, 7 are not implemented in the Nucleus SE. Their description, as well as the reason for their exclusion, is described above.

In the next article, we’ll start looking at RTOS memory management.

About the author: Colin Walls has been working in the electronics industry for more than thirty years, spending a significant amount of time on embedded software. He is now an embedded software engineer in Mentor Embedded (a division of Mentor Graphics). Colin Walls often speaks at conferences and seminars, author of numerous technical articles and two books on embedded software. Lives in the UK. Colin's professional blog , e-mail: colin_walls@mentor.com.

Also popular now: