The whole truth about the RTOS. Article # 24. Queues: helper services and data structures

Original author: Colin Walls
  • Transfer


In this article, we will continue to consider the queue.

Queuing support services


Nucleus RTOS has four API calls that provide support functions related to queues: resetting a queue, obtaining queue information, obtaining the number of queues in an application, and receiving pointers to all queues in an application. The first three functions are implemented in the Nucleus SE.

Previous articles in the series:

Article # 23. Queues: Introduction and Basic Services
Article # 22. Mailboxes: ancillary services and data structures
Article # 21. Mailboxes: Introduction and Basic Services
Article # 20. Semaphores: ancillary services and data structures
Article # 19. Semaphores: 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 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.


Queue reset


This API call resets the queue to its original, unused state. Any messages that are stored in the queue will be lost. Any queued tasks will resume with the return code NUSE_QUEUE_WAS_RESET .

Call to reset the queue in the Nucleus RTOS
Service Call Prototype:
STATUS NU_Reset_Queue (NU_QUEUE * queue);

Parameters:
queue - a pointer to the queue control block provided by the user.

Return value:
NU_SUCCESS - the call was successfully completed;
NU_INVALID_QUEUE - invalid pointer to the queue.

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

Service Call Prototype:
STATUS NUSE_Queue_Reset (NUSE_QUEUE queue);

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

Return value:
NUSE_SUCCESS - the call was successfully completed;
NUSE_INVALID_QUEUE is an invalid queue index.

Implementing a Queue Reset in the Nucleus SE
The function code NUSE_Queue_Reset (after checking the parameters) is quite simple. The indexes of the head and tail of the queue, as well as the message counter in the queue, are assigned a zero value.

If task lock is activated, the additional code is responsible for restoring suspended tasks:

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

Each suspended task in the queue is assigned the status “ready” with the return code NUSE_QUEUE_WAS_RESET . After this process is completed, if the Priority Scheduler is used, the NUSE_Reschedule () function is called , since one or more high-priority tasks may be ready to be executed.

Getting Queue Information


This service call provides queue 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, the variable length of the message and the order of pausing tasks are not supported, and the blocking of tasks can be disabled.

Call to get queue information on Nucleus RTOS
Service Call Prototype:

STATUS NU_Queue_Information (NU_QUEUE * queue, CHAR * name, VOID ** start_address, UNSIGNED * available, UNSIGNED * messages, OPTION * message_type, UNSIGNED * message_size * suspend_type, UNSIGNED * tasks_waiting, NU_TASK ** first_task);

Parameters:

queue - a pointer to the queue control block provided by the user;
name- pointer to the 8-character field for the name of the message in the queue;
start_address - a pointer to a pointer, in which the start address of the queue data area will be written;
queue_size is a pointer to a variable for storing the total number of UNSIGNED elements in the queue;
available - a pointer to a variable to store the number of available UNSIGNED elements in the queue;
messages - a pointer to a variable to store the current number of messages in the queue;
message_type is a pointer to a variable to store the type of messages supported by the queue. Valid values ​​are NU_FIXED_SIZE and NU_VARIABLE ;
message_size- pointer to a variable to store the number of data elements of type UNSIGNED in each message queue. If the queue supports variable length messages, this number indicates the maximum message length;
suspend_type - a pointer to a variable to store the type of task suspension. Valid values ​​are NU_FIFO and NU_PRIORITY ;
tasks_waiting - pointer to a variable to store the number of tasks suspended on this queue;
first_task - pointer to the task pointer, into which the pointer of the first suspended task is placed.

Return value:

NU_SUCCESS - the call was successfully completed;
NU_INVALID_QUEUE- invalid pointer to the queue.

Call to get queue information in Nucleus SE
This API call supports the core functionality of the Nucleus RTOS API.

Service call prototype:

STATUS NUSE_Queue_Information (NUSE_QUEUE queue, ADDR * start_address, U8 * queue_size, U8 * available, U8 * messages, U8 * tasks_waiting, NUSE_TASK * first_task);

Parameters:

queue - the index of the queue about which information is requested;
start_address - a pointer to a variable of the type ADDR , in which the address of the beginning of the queue data area will be stored;
queue_size - pointer to a variable of type U8which will store the total number of messages that can fit in the queue;
available - a pointer to a variable of type U8 , in which the number of free places in the queue will be stored;
messages - a pointer to a variable of type U8 , in which the current number of messages in the queue will be stored;
tasks_waiting - pointer to the variable in which the number of tasks suspended on this queue will be stored (nothing is returned if the task lock is disabled);
first_task - a pointer to a variable of type NUSE_TASK , in which the index of the first suspended task will be stored (nothing is returned if the blocking of tasks is disabled).

Return value:

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

Implementing the display of queue information in Nucleus SE

The implementation of this API call is quite simple:

*start_address = NUSE_Queue_Data[queue];
*queue_size = NUSE_Queue_Size[queue];
*available = NUSE_Queue_Size[queue] - NUSE_Queue_Items[queue];
*messages = NUSE_Queue_Items[queue];
#if NUSE_BLOCKING_ENABLE
   *tasks_waiting = NUSE_Queue_Blocking_Count[queue];
   if (NUSE_Queue_Blocking_Count[queue] != 0)
   {
      U8 index;
      for (index=0; index<NUSE_TASK_NUMBER; index++)
      {
         if ((LONIB(NUSE_Task_Status[index]) ==
            NUSE_QUEUE_SUSPEND)
            && (HINIB(NUSE_Task_Status[index]) == queue))
         {
            *first_task = index;
            break;
         }
      }
   }
   else
   {
      *first_task = 0;
   }
   #else
      *tasks_waiting = 0;
      *first_task = 0;
   #endif

The function returns the status of the queue. Then, if the task lock is activated, the number of waiting tasks and the index of the first one are returned (otherwise, both parameters are assigned the value 0).

Getting the number of queues


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

Call to Nucleus RTOS Queue Counter
Service Call Prototype:
UNSIGNED NU_Established_Queues (VOID);

Parameters:
None.

Return Value: The
number of queues created in the system.

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

Service Call Prototype:
U8 NUSE_Queue_Count (void);

Parameters:
None.

Return Value: The
number of queues configured in the application.

The implementation of the queue counter in Nucleus SE
The implementation of this API call is very simple: the value of the #define NUSE_QUEUE_NUMBER symbol is returned .

Data structures


The queues use five or six data structures (which are either in RAM or ROM), which are sets of tables (like other Nucleus SE objects), the number and size of which corresponds to the number of queues in the application and the selected parameters.

Kernel data in ram


This data has the following structure:

NUSE_Queue_Head [] is an array of U8 type pointers , has one entry for each configured queue and points to the head of the message queue. Used as an index of addresses in NUSE_Queue_Data [] (see below);
NUSE_Queue_Tail [] is an array of type U8 , has one entry for each queue configured in the application, and points to the tail of the message queue. Used as an index of addresses in NUSE_Queue_Data [] (see below);
NUSE_Queue_Items [] - array of type U8, has one entry for each configured queue, and is the count of messages in the queue. This data can be considered redundant, since these values ​​can be obtained through the indices of the beginning and end of the queue, but keeping the counter simplifies the code;
NUSE_Queue_Blocking_Count [] - this array of type U8 contains counters of the number of tasks suspended on each queue. This array is created only if task lock support is activated.

These data structures are initialized with zeros by the NUSE_Init_Queue () function when Nucleus SE is started. This is logical, since all queues are created empty (not used).

The following are the definitions of these structures in the nuse_init.c file :

RAM U8 NUSE_Queue_Head[NUSE_QUEUE_NUMBER];
RAM U8 NUSE_Queue_Tail[NUSE_QUEUE_NUMBER];
RAM U8 NUSE_Queue_Items[NUSE_QUEUE_NUMBER];
#if NUSE_BLOCKING_ENABLE
   RAM U8 NUSE_Queue_Blocking_Count[NUSE_QUEUE_NUMBER];
#endif

User data in RAM


The user is responsible for providing an area of ​​RAM for storing each queue. The size of this area must contain an array of type ADDR , in which each record corresponds to one message in the queue

ROM data


This data has the following structure:

NUSE_Queue_Data [] is an array of type ADDR , has one entry for each configured queue and points to the queue data area (see User RAM Data);
NUSE_Queue_Size [] is an array of type U8 , has one entry for each configured queue, and shows the maximum number of messages each queue can receive.

These data structures are declared and initialized (statically) in the nuse_config.c file :

ROM ADDR *NUSE_Queue_Data[NUSE_QUEUE_NUMBER] =
{
   /* addresses of queue data areas ------ */
};
ROM U8 NUSE_Queue_Size[NUSE_QUEUE_NUMBER] =
{
   /* queue sizes ------ */
};

The amount of memory for the queues


As with all Nucleus SE core objects, the amount of memory required for queues is easily predictable.

The amount of data in ROM (in bytes) for all queues in an application can be calculated as follows:
NUSE_QUEUE_NUMBER * (sizeof (ADDR) + 1)

The amount of kernel data in RAM (in bytes) for all queues in an application with activated task locking is calculated as follows:
NUSE_QUEUE_NUMBER * 3

If the lock is disabled:
NUSE_QUEUE_NUMBER * 4

The amount of user data in RAM (in bytes) for the queue with the queue index :
NUSE_Queue_Size [queue] * sizeof (ADDR)

Unrealized API calls


The four API calls that can be found in the Nucleus RTOS are not implemented in the Nucleus SE:

Creating a queue


This API call creates a queue, in Nucleus SE this is not necessary, since the queues are created statically.

Service Call Prototype:
STATUS NU_Create_Queue (NU_QUEUE * queue, char * name, VOID * start_address, UNSIGNED queue_size, OPTION message_type, UNSIGNED message_size, OPTION suspend_type);

Parameters:

queue - a pointer to the control unit provided by the user, used to manage queues in other API calls;
name - pointer to the 7-character queue name with zero terminating byte;
start_address - address of the beginning of the queue;
message_type is the message type supported by the queue. Can be NU_FIXED_SIZE or NU_VARIABLE_SIZE;
message_size — If the queue supports fixed-length messages, this parameter specifies the exact length of each message, otherwise, if the queue supports variable-length messages, this value is the maximum message length;
suspend_type - defines the type of suspension of tasks in the queue. It can take the values NU_FIFO and NU_PRIORITY , which means the principle of FIFO (First-In-First-Out) or the principle of priority of task suspension, respectively.

Return value:

NU_SUCCESS - the call was successfully completed;
NU_INVALID_QUEUE - null pointer to the queue management block ( NULL ), or the pointer is already in use;
NU_INVALID_MEMORY - invalid memory location specified in start_address ;
NU_INVALID_MESSAGE - incorrect message_type parameter ;
NU_INVALID_SIZE — the queue does not support messages of this length, or the queue size and / or message length is 0;
NU_INVALID_SUSPEND is an invalid suspend_type parameter .

Deleting a queue


This API call deletes the queue created earlier. This is not necessary in the Nucleus SE, since the queues are created statically and cannot be deleted.

Service Call Prototype:
STATUS NU_Delete_Queue (NU_QUEUE * queue);

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

Return value:
NU_SUCCESS - the call was successfully completed;
NU_INVALID_QUEUE - invalid pointer to the queue.

Queue pointers


This API call builds a sequential list of pointers to all queues in the system. In the Nucleus SE, this is not necessary, as the queues are identified using a simple index, not a pointer.

Service Call Prototype:
UNSIGNED NU_Queue_Pointers (NU_QUEUE ** pointer_list, UNSIGNED maximum_pointers);

Parameters:
pointer_list - a pointer to an array of pointers NU_QUEUE . This array will be filled with pointers to the queues created in the system;
maximum_pointers - the maximum number of pointers in the array.

Return value: The
number of NU_QUEUE pointers in the array.

Broadcast to Queue


This API call sends a message to all tasks suspended in the queue that are waiting for messages from the specified queue. This feature is not implemented in the Nucleus SE because it adds excessive complexity.

Service call prototype:
STATUS NU_Broadcast_To_Queue (NU_QUEUE * queue, VOID * message, UNSIGNED size, UNSIGNED suspend);

Parameters:

queue - a pointer to the queue control block;
message - pointer to the message being sent;
size - the number of elements of type UNSIGNEDin the message. If the queue supports variable length messages, this parameter must be equal to or less than the length of the message supported by the queue. If the queue supports fixed-length messages, this parameter must be equal to the length of the message supported by the queue;
suspen - indicates whether the calling task should be suspended if the queue is already full. It can be NU_NO_SUSPEND , NU_SUSPEND, or timeout value.

Return value:

NU_SUCCESS - the call was successfully completed;
NU_INVALID_QUEUE - invalid pointer to the queue;
NU_INVALID_POINTER - null pointer to the message ( NULL );
NU_INVALID_SIZE- the specified message length is not compatible with the length specified when creating the queue;
NU_INVALID_SUSPEND - an attempt to pause a task from a thread not associated with a task;
NU_QUEUE_FULL - there is not enough space in the queue for a message;
NU_TIMEOUT — the queue is still full after the timeout expires;
NU_QUEUE_DELETED - the queue was deleted while the task was suspended;
NU_QUEUE_RESET — The queue 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. Queues 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 acceptable, given 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 a data structure (control units) that has a specific data type. A pointer to this control unit serves as a queue identifier. I decided that in the Nucleus SE, a different approach is needed for effective memory use: 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 identifier of a particular object is the index in this table. So, I defined NUSE_QUEUE as equivalent to U8, a variable (not a pointer) of this type serves as an identifier of a queue. 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 naming queues. These names are used only when debugging. I excluded them from the Nucleus SE to save memory.

Message size and type


In Nucleus RTOS, the queue can be configured to process messages consisting of any number of unsigned elements . In Nucleus SE, Queues are simplified and only support single ADDR messages . Data channels in the Nucleus SE are a bit more flexible and can be a useful alternative to queuing in some cases. Channels will be reviewed in the next two articles of this series.

Nucleus SE also supports variable-length queues in which only the maximum message length is specified when creating. Variable length messages are not supported by Nucleus SE

Queue size


In Nucleus SE, the maximum number of messages in a queue is 256, since all variables and constants are of type U8 . Nucleus RTOS has no such restrictions.

Unrealized API calls


Nucleus RTOS supports ten service calls for queuing. Of these, four are not implemented in the Nucleus SE. The details of these calls, as well as the reasons for this decision, can be found in this article above, in the section "Unrealized API calls".

The following article will discuss data transfer channels.

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: