The whole truth about RTOS. Article # 22. Mailboxes: ancillary services and data structures

Original author: Colin Walls
  • Transfer


This article continues the review of mailboxes, which was started in the previous article in the series The Entire Truth About RTOS.

Previous articles in the series:

Article # 21. Mailboxes: Introduction and Basic Services
Article # 20. Semaphores: auxiliary services and data structures
Article # 19. Semaphores: an introduction and basic services
Article # 18. Event flag groups: ancillary services and data structures
Article # 17. Event flag groups: introduction and basic services
Article # 16. Signals
Article # 15. Memory sections: services and data structures
Article # 14. Memory sections: introduction and basic services
Article # 13. Task data structures and unsupported API calls
Article # 12. Task Services
Article # 11. Tasks: configuration and introduction to the API
Article # 10. Scheduler: additional features and context preservation
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 Mode
Article # 1. RTOS: introduction.


Mailbox Support Services


Nucleus RTOS has four API calls that provide helper functions related to mailboxes: resetting the mailbox, getting information about the mailbox, getting the number of mailboxes in the application, and getting pointers to all the mailboxes in the application. The first three of these functions are implemented in the Nucleus SE.

Mailbox reset


This API service call resets the mailbox to its initial, unused state. The message stored in the mailbox will be lost. Any tasks suspended on the mailbox will resume with the return code NUSE_MAILBOX_WAS_RESET .

Call to reset the mailbox in the Nucleus RTOS
Service Call Prototype:
STATUS NU_Reset_Mailbox (NU_MAILBOX * mailbox);

Parameters:
mailbox - pointer to the mailbox control block.

Return value:
NU_SUCCESS - the call was successfully completed;
NU_INVALID_MAILBOX - invalid mailbox pointer.

Call to reset a mailbox in Nucleus SE
This API service call supports the Nucleus RTOS API core functionality.

Service call prototype:
STATUS NUSE_Mailbox_Reset (NUSE_MAILBOX mailbox);

Parameters:
mailbox - index (ID) of the mailbox to be reset.

Return value:
NUSE_SUCCESS - the call was successfully completed;
NUSE_INVALID_MAILBOX - invalid mailbox index.

Implementing a mailbox reset in Nucleus SE
The option code for the NUSE_Mailbox_Reset function (after checking the parameters) is selected using conditional compilation, depending on whether support for blocking (suspending) tasks is activated or not. We will consider both of these options.

If the lock is not activated, the code for this API function is quite simple. The mailbox is marked as unused by assigning parameter NUSE_Mailbox_Status [] value FALSE .

If the lock is activated, the code becomes more complex:

while (NUSE_Mailbox_Blocking_Count[mailbox] != 0)
{
    U8 index;           /* check whether any tasks are blocked */
                        /* on this mailbox */
    for (index=0; index<NUSE_TASK_NUMBER; index++)
    {
        if ((LONIB(NUSE_Task_Status[index]) ==
            NUSE_MAILBOX_SUSPEND)
            && (HINIB(NUSE_Task_Status[index]) == mailbox))
        {
            NUSE_Task_Blocking_Return[index] =
               NUSE_MAILBOX_WAS_RESET;
            NUSE_Task_Status[index] = NUSE_READY;
            break;
        }
    }
    NUSE_Mailbox_Blocking_Count[mailbox]--;
}
#if NUSE_SCHEDULER_TYPE == NUSE_PRIORITY_SCHEDULER
    NUSE_Reschedule(NUSE_NO_TASK);
#endif

The mailbox is reset to the “Empty” state.

Each suspended task on the mailbox is assigned the status “ready” with the return code NUSE_MAILBOX_WAS_RESET . After this process is completed, if the Priority Scheduler is used, a service call is made to NUSE_Reschedule () , since one or more tasks with a higher priority could become ready and wait for execution permission.

Retrieving Mailbox Information


This service call provides a set of mailbox information. The implementation of this call in the Nucleus SE differs from the Nucleus RTOS in that it returns less information, since the naming of objects and the pause order are not supported, and the suspension of tasks can be disabled.

Call for information about the mailbox in the Nucleus RTOS
This API call supports the core functionality of the Nucleus RTOS API.

Service Call Prototype:
STATUS NU_Mailbox_Information (NU_MAILBOX * mailbox, CHAR * name, OPTION * suspend_type, DATA_ELEMENT * message_present, UNSIGNED * tasks_waiting, NU_TASK ** first_task);

Parameters:

mailbox - a pointer to the mailbox control block;
name- pointer to the 8-character field for the mailbox name. The terminating zero byte is also included in this area;
suspend_type - pointer to the variable that stores the type of task suspension. It can be NU_FIFO and NU_PRIORITY ;
message_present - a pointer to a variable that will take the value NU_TRUE or NU_FALSE , depending on whether the mailbox is full or not;
tasks_waiting - pointer to a variable that will receive the number of tasks suspended on this mailbox;
first_task is a pointer to a task pointer that will take a pointer to the first suspended task.

Return value:

NU_SUCCESS - the call was successfully completed;
NU_INVALID_MAILBOX - invalid mailbox pointer.

Call for information about the mailbox Nucleus SE
This API service call supports the basic functionality of the Nucleus RTOS API.

Service call prototype:

STATUS NUSE_Mailbox_Information (NUSE_MAILBOX mailbox, U8 * message_present, U8 * tasks_waiting, NUSE_TASK * first_task);

Parameters:

mailbox - mailbox index about which information is requested;
message_present - a pointer to a variable that will take the value TRUE or FALSE , depending on whether the mailbox is full or not;
tasks_waiting - pointer to a variable that will accept the number of tasks suspended on this mailbox (nothing is returned if task suspension is disabled);
first_task - a pointer to a variable of type NUSE_TASK , which will accept the index of the first suspended task (returns nothing if the suspension of tasks is disabled).

Return value:

NUSE_SUCCESS - the call was successfully completed;
NUSE_INVALID_MAILBOX - invalid mailbox index;
NUSE_INVALID_POINTER - one or more pointer parameters is incorrect.

Implementing mailbox information in Nucleus SE

The implementation of this API call is quite simple:

*message_present = NUSE_Mailbox_Status[mailbox];
#if NUSE_BLOCKING_ENABLE
   *tasks_waiting = NUSE_Mailbox_Blocking_Count[mailbox];
   if (NUSE_Mailbox_Blocking_Count[mailbox] != 0)
   {
      U8 index;
      for (index=0; index<NUSE_TASK_NUMBER; index++)
      {
         if ((LONIB(NUSE_Task_Status[index]) ==
            NUSE_MAILBOX_SUSPEND)
            && (HINIB(NUSE_Task_Status[index]) == mailbox))
         {
            *first_task = index;
            break;
         }
      }
   }
   else
   {
      *first_task = 0;
   }
#else
   *tasks_waiting = 0;
   *first_task = 0;
#endif
return NUSE_SUCCESS;

The function returns the status of the mailbox. Then, if service calls for blocking tasks are activated, the number of suspended tasks and the index of the first one are returned (otherwise, these parameters are assigned the value 0).

Getting the number of mailboxes


This service call returns the number of mailboxes in the application. While in Nucleus RTOS, their number may change over time, and the return value will show the current number of mailboxes, in Nucleus SE, the number of mailboxes is set at the build stage and cannot be changed.

Call to the mailbox counter in Nucleus RTOS
This API call supports the core functionality of the Nucleus RTOS API.

Service Call Prototype:
UNSIGNED NU_Established_Mailboxes (VOID);

Parameters:
None.

Return value: The
number of mailboxes created in the application.

Call to the mailbox counter in Nucleus SE
This API call supports the core functionality of the Nucleus RTOS API.

Service Call Prototype:
U8 NUSE_Mailbox_Count (void);

Parameters:
None.

Return value: The
number of configured mailboxes in the application.

The implementation of the mailbox count in Nucleus SE
The implementation of this API call is extremely simple: the value of the #define NUSE_MAILBOX_NUMBER directive is returned .

Data structures


Mailboxes use two or three data structure arrays (all of them are in RAM), which, like other objects of Nucleus SE, are sets of tables whose size depends on the number of configured mailboxes and their parameters.

I strongly recommend that the application code does not use direct access to these data structures, but access them through the provided API functions. This avoids incompatibility with future versions of the Nucleus SE and unwanted side effects, and also simplifies porting the application to the Nucleus RTOS. Below is a detailed overview of the data structures for a better understanding of how the service call code works and for debugging.

Data in ram


This data has the following structure:

NUSE_Mailbox_Data [] is an array of type ADDR , having one entry for each configured mailbox, it stores mailbox data.
NUSE_Mailbox_Status [] is an array of type U8 , having one entry for each configured mailbox, it tracks mailbox usage. A non-zero value ( TRUE ) indicates that the mailbox is full.
NUSE_Mailbox_Blocking_Count [] is an array of type U8 , it contains a counter of blocked tasks for each mailbox. This array is created only if support for API call blocking functionality is activated.

These data structures are initialized with zeros in the NUSE_Init_Mailbox () function when Nucleus SE is started. This is logical, since each mailbox is created empty (unused).

The following are the definitions of these data structures from the nuse_init.c file .

RAM ADDR NUSE_Mailbox_Data[NUSE_MAILBOX_NUMBER];
RAM U8 NUSE_Mailbox_Status[NUSE_MAILBOX_NUMBER];
#if NUSE_BLOCKING_ENABLE
   RAM U8 NUSE_Mailbox_Blocking_Count[NUSE_MAILBOX_NUMBER];
#endif

Data in the ROM


For the implementation of mailboxes are not used data in the ROM.

Mailbox Memory Capacity


As with all Nucleus SE core objects, the amount of memory required for mailboxes is known in advance.

The amount of memory for data in the ROM for all mailboxes in the application is 0.

The amount of data in RAM for all mailboxes in the application (in bytes) with activated task blocking API calls can be calculated as follows:
NUSE_MAILBOX_NUMBER * (sizeof (ADDR) +2 )

Otherwise:
NUSE_MAILBOX_NUMBER * (sizeof (ADDR) +1)

Unrealized API calls


The four service calls that can be found in the Nucleus RTOS are not implemented in the Nucleus SE.

Create mailbox


This API service call creates a mailbox. In the Nucleus SE, it is not necessary because the mailboxes are created statically.

Service Call Prototype:
STATUS NU_Create_Mailbox (NU_MAILBOX * mailbox, CHAR * name, UNSIGNED OPTION suspend_type);

Parameters:

mailbox - pointer to the mailbox control unit provided by the user; used to manage mailboxes in other API calls;
name - pointer to the 7-character mailbox name with zero terminating byte;
suspend_type - indicates the principle of suspending the task on the mailbox. May be NU_FIFO and NU_PRIORITY, which means the principle of FIFO (First-In-First-Out) or the principle of priority of the suspension of tasks, respectively.

Return value:

NU_SUCCESS - the call was successfully completed;
NU_INVALID_MAILBOX - null pointer to the mailbox control block ( NULL ), or the pointer is already in use;
NU_INVALID_SUSPEND is an invalid suspend_type parameter .

Deleting a mailbox


This API service call removes a previously created mailbox. In the Nucleus SE, this is not necessary, since mailboxes are created statically and cannot be deleted.

Service call prototype:

STATUS NU_Delete_Mailbox (NU_MAILBOX * mailbox);

Parameters:

mailbox - pointer to the mailbox control block.

Return value:

NU_SUCCESS - the call was successfully completed;
NU_INVALID_MAILBOX - invalid mailbox pointer.

Mailbox Pointers


This API call makes a sequential list of pointers to all mailboxes in the system. In Nucleus SE, it is not necessary, since mailboxes are identified using a simple index, not a pointer.

Service Call Prototype:

UNSIGNED NU_Mailbox_Pointers (NU_MAILBOX ** pointer_list, UNSIGNED maximum_pointers);

Parameters:

pointer_list - pointer to an array of pointers NU_MAILBOX ; this array will be filled with pointers to the mailboxes created in the system;
maximum_pointers - the maximum number of pointers in the array.

Return value: The

number of NU_MAILBOX pointers in the array.

Record message in the mailbox for delivery to all recipients


This service call sends a message to all tasks waiting for messages from a specific mailbox. In Nucleus SE, this service call is not implemented, as it would add unnecessary complexity.

Service call prototype:

STATUS NU_Broadcast_To_Mailbox (NU_MAILBOX * mailbox, VOID * message, UNSIGNED suspend);

Parameters:

mailbox - a pointer to the mailbox control block;
message - a pointer to the message being sent;
suspend - indicates whether the calling task should be suspended if the mailbox already contains a message; can be NU_NO_SUSPEND , NU_SUSPEND, or timeout value.

Return value:

NU_SUCCESS- the call was successfully completed;
NU_INVALID_MAILBOX - invalid mailbox pointer;
NU_INVALID_POINTER - null pointer to the message ( NULL );
NU_INVALID_SUSPEND - an attempt to pause from a thread that is not associated with the task;
NU_MAILBOX_FULL - the mailbox already contains the message;
NU_TIMEOUT - after the timeout expires, the mailbox is still full;
NU_MAILBOX_DELETED - the mailbox was deleted while the task was suspended.
NU_MAILBOX_RESET - the mailbox was reset while the task was suspended.

Nucleus RTOS Compatibility


As with all other Nucleus SE objects, my goal was to ensure maximum compatibility of application code with Nucleus RTOS. Mailboxes are no exception and, from the user's point of view, they are implemented in the same way as in the Nucleus RTOS. There is a certain incompatibility, which I considered to be admissible given the fact that as a result the code will become more understandable and more efficient in terms of the amount of memory required. Otherwise, Nucleus RTOS API calls can be almost directly transferred to the Nucleus SE.

Object IDs


In Nucleus RTOS, all objects are described by data structures (control units) of a particular type. A pointer to this control unit serves as a mailbox identifier. I decided that in Nucleus SE, a different approach is needed to efficiently use memory: 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 configured objects of each type. The specific object identifier is an index in this table. So, I defined NUSE_MAILBOX as equivalent to U8, a variable (not a pointer) of this type serves as a mailbox identifier. This small incompatibility is easy to handle if the code is ported from the Nucleus SE to the Nucleus RTOS and vice versa. Usually no operations are performed on object identifiers, except for moving and storing.

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

Message size and type


In Nucleus RTOS, a mailbox message consists of four 32-bit words. In Nucleus SE, I decided to reduce this value to one variable of type ADDR . This change leads to significant memory savings and reduced task execution time. It also says that the usual use of the mailbox is to send information from one task to another. This incompatibility will not cause major problems when porting applications to the Nucleus RTOS. Nucleus SE can be modified if another message format is needed.

Unrealized API calls


Nucleus RTOS supports nine mailbox service calls. Of these, four are not implemented in the Nucleus SE. The details of these calls, as well as the reasons why they were excluded from the Nucleus SE, are described above.

The next article will be considered queues.

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: