Mailboxes: utility services and data structures

This article continues the look at mailboxes discussed in a previous article in this RTOS Revealed series.

Mailbox Utility Services

Nucleus RTOS has four API calls which provide utility functions associated with mailboxes: reset mailbox, return information about a mailbox, return number of mailboxes in the application and return pointers to all mailboxes in the application. The first three of these are implemented in Nucleus SE.

Resetting a Mailbox

This API call restores the mailbox to its initial, unused state. Any message stored in the mailbox is lost. Any tasks which were suspended on the mailbox are resumed and receive a return code of NUSE_MAILBOX_WAS_RESET .

Nucleus RTOS API Call for Resetting a Mailbox

Service call prototype:

STATUS NU_Reset_Mailbox(NU_MAILBOX *mailbox);

Parameters:

mailbox – pointer to the user-supplied mailbox control block

Returns:

NU_SUCCESS – the call was completed successfully
NU_INVALID_MAILBOX – the mailbox pointer is not valid

Nucleus SE API Call for Resetting a Mailbox

This API call supports the key functionality of the Nucleus RTOS API.

Service call prototype:

STATUS NUSE_Mailbox_Reset(NUSE_MAILBOX mailbox);

Parameters:

mailbox – the index (ID) of the mailbox to be reset

Returns:

NUSE_SUCCESS – the call was completed successfully
NUSE_INVALID_MAILBOX – the mailbox index is not valid

Nucleus SE Implementation of Mailbox Reset

The bulk of the code of the NUSE_Mailbox_Reset() API function – after parameter checking – is selected by conditional compilation, dependent on whether support for blocking (task suspend) API calls is enabled. We will look at the two variants separately here.

If blocking is not enabled, the API function code is almost trivial. The mailbox is marked as unused by setting its entry in NUSE_Mailbox_Status[] to FALSE .

When blocking is enabled, 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

Initially the mailbox is marked as empty.

Each task suspended on the mailbox is marked as “ready” with a suspend return code of NUSE_MAILBOX_WAS_RESET . After this process is complete, if the Priority scheduler is in use, a call is made to NUSE_Reschedule() , as one or more higher priority tasks may have been readied and needs to be allowed to run.

Mailbox Information

This service call obtains a selection of information about a mailbox. The Nucleus SE implementation differs from Nucleus RTOS in that it returns less information, as object naming and suspend ordering are not supported and task suspend may not be enabled.

Nucleus RTOS API Call for Mailbox Information

This API call supports the key 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 – pointer to the user-supplied mailbox control block
name – pointer to an 8-character destination area for the mailbox’s name. This includes space for a null terminator
suspend_type – pointer to a variable for holding the task suspend type. Valid task suspend types are NU_FIFO and NU_PRIORITY
message_present – a pointer to a variable, which will receive a value of NU_TRUE or NU_FALSE depending on whether the mailbox is full or not
tasks_waiting – a pointer to a variable which will receive the number of tasks suspended on this mailbox
first_task – a pointer to a task pointer which will receive the pointer to the first suspended task

Returns:

NU_SUCCESS – the call was completed successfully
NUSE_INVALID_MAILBOX – the mailbox pointer is not valid

Nucleus SE API Call for Mailbox Information

This API call supports the key 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 – the index of the mailbox about which information is being requested
message_present – a pointer to a variable, which will receive a value of TRUE or FALSE depending on whether the mailbox is full or not
tasks_waiting – a pointer to a variable which will receive the number of tasks suspended on this mailbox (nothing returned if task suspend is disabled)
first_task – a pointer to a variable of type NUSE_TASK which will receive the index of the first suspended task (nothing returned if task suspend is disabled)
 

Returns:

NUSE_SUCCESS – the call was completed successfully
NUSE_INVALID_MAILBOX – the mailbox index is not valid
NUSE_INVALID_POINTER – one or more of the pointer parameters is invalid

Nucleus SE Implementation of Mailbox Information

The implementation of this API call is quite straightforward:

*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;#endifreturn NUSE_SUCCESS;

The function returns the mailbox status. Then, if blocking API calls is enabled, the number of waiting tasks and the index of the first one are returned (otherwise these two parameters are set to 0).

Obtaining the Number of Mailboxes

This service call returns the number of mailboxes configured in the application. Whilst in Nucleus RTOS this will vary over time and the returned value will represent the current number of mailboxes, in Nucleus SE the value returned is set at build time and cannot change.

Nucleus RTOS API Call for Mailbox Count

This API call supports the key functionality of the Nucleus RTOS API.

Service call prototype:

UNSIGNED NU_Established_Mailboxes(VOID);

Parameters:

None

Returns:

The number of created mailboxes in the application

Nucleus SE API Call for Mailbox Count

This API call supports the key functionality of the Nucleus RTOS API.

Service call prototype:

U8 NUSE_Mailbox_Count(void);

Parameters:

None 

Returns:

The number of configured mailboxes in the application

Nucleus SE Implementation of Mailbox Count

The implementation of this API call is almost trivially simple: the value of the #define symbol NUSE_MAILBOX_NUMBER is returned.

Data Structures

Mailboxes utilize two or three data structures – all in RAM – which, like other Nucleus SE objects, are a series of tables, included and dimensioned according to the number of mailboxes 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 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.

RAM Data

These data structures are:

NUSE_Mailbox_Data[] – This is an array of type ADDR , with one entry for each configured mailbox and is where the mailbox data is stored.

NUSE_Mailbox_Status[] – This is an array of type U8 , with one entry for each configured mailbox, which tracks the usage of the mailboxes. A non-zero value (TRUE ) indicates that a mailbox is full.

NUSE_Mailbox_Blocking_Count[] – This type U8 array contains the counts of how many tasks are blocked on each mailbox. This array only exists if blocking API call support is enabled.

These data structures are all initialized to zeros by NUSE_Init_Mailbox() when Nucleus SE starts up. This is logical, as it renders every mailbox as being empty (unused). A future article will provide a full description of Nucleus SE start-up procedures.

Here are the definitions of these data structures in 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

ROM Data

There are no ROM data structures associated with mailboxes.

Mailbox Data Footprint

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

The ROM data footprint for all the mailboxes in an application is 0.

The RAM data footprint (in bytes) for all the mailboxes in an application, when blocking API calls is enabled, may be computed thus:

NUSE_MAILBOX_NUMBER * (sizeof(ADDR) + 2)

Otherwise it is:

NUSE_MAILBOX_NUMBER * (sizeof(ADDR) + 1)

Unimplemented API Calls

Four mailbox API calls found in Nucleus RTOS are not implemented in Nucleus SE:

Create Mailbox

This API call creates a mailbox. It is not needed with Nucleus SE, as mailboxes are created statically.

Service call prototype:

STATUS NU_Create_Mailbox(NU_MAILBOX *mailbox, CHAR *name, UNSIGNED OPTION suspend_type);

Parameters:

mailbox – pointer to a user-supplied mailbox control block; this will be used as a “handle” for the mailbox in other API calls
name – pointers to a 7-character, null-terminated name for the mailbox
suspend_type – specifies how tasks suspend on the mailbox. Valid options for this parameter are NU_FIFO and NU_PRIORITY , which represent First-In-First-Out (FIFO) and priority-order task suspension, respectively
 

Returns:

NU_SUCCESS – indicates successful completion of the service
NU_INVALID_MAILBOX – indicates the mailbox control block pointer is NULL or already in use
NU_INVALID_SUSPEND – indicates that the suspend_type parameter is invalid
 

Delete Mailbox

This API call deletes a previously created mailbox. It is not needed with Nucleus SE, as mailboxes are created statically and cannot be deleted.

Service call prototype:

STATUS NU_Delete_Mailbox(NU_MAILBOX *mailbox);

Parameters:

mailbox – pointer to mailbox control block

Returns:

NU_SUCCESS – indicates successful completion of the service
NU_INVALID_MAILBOX – indicates the mailbox pointer is invalid

Mailbox Pointers

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

Service call prototype:

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

Parameters:

pointer_list – pointer to an array of NU_MAILBOX pointers; this array will be filled with pointers to established mailboxes in the system
maximum_pointers – the maximum number of pointers to place in the array

Returns:

The number of NU_MAILBOX pointers placed into the array

Broadcast to Mailbox

This API call broadcasts a message to all tasks waiting for a message from the specified mailbox. It is not implemented with Nucleus SE, as it would have added excessive complexity.

Service call prototype:

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

Parameters:

mailbox – pointer to mailbox control block
message – pointer to the broadcast message

suspend – specifies whether or not to suspend the calling task if the mailbox already contains a message; valid options for this parameter are NU_NO_SUSPEND , NU_SUSPEND or a timeout value.

Returns:

NU_SUCCESS – indicates successful completion of the service
NU_INVALID_MAILBOX – indicates the mailbox pointer is invalid
NU_INVALID_POINTER – indicates that the message pointer is NULL
NU_INVALID_SUSPEND – indicates that suspend attempted from a non-task thread
NU_MAILBOX_FULL – indicates the mailbox already contains a message
NU_TIMEOUT – indicates the mailbox is still full after the timeout has expired
NU_MAILBOX_DELETED – mailbox was deleted while task was suspended
NU_MAILBOX_RESET – mailbox was reset while the task was suspended

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. Mailboxes 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 mailbox. 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_MAILBOX as being equivalent to U8 ; a variable – not a pointer – of this type then serves as the mailbox 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 mailboxes. These names are only used for target-based debug facilities. I omitted them from Nucleus SE to save memory.

Message Size and Type

In Nucleus RTOS, a mailbox message consists of four 32-bit words. I decided to reduce this to a single variable of type ADDR in Nucleus SE. This change imparts significant increases in memory and execution time efficiency. It also recognizes that a common application for a mailbox is to send a pointer to something from one task to another. This incompatibility will present few challenges when porting application code to Nucleus RTOS. Nucleus SE could be modified quite readily, if a different message format were required.

Unimplemented API Calls

Nucleus RTOS supports nine service calls to work with mailboxes. Of these, four are not implemented in Nucleus SE. Details of these and of the decision to omit them may be found in Unimplemented API Calls earlier in this article.

The next RTOS Revealed article will look at queues.


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.