
This article continues the semaphore review.
Semaphore support services
Nucleus RTOS has four API calls providing functionality related to semaphores: resetting a semaphore, obtaining information about a semaphore, obtaining the number of semaphores in an application, and receiving pointers to all semaphores in an application. The first three of these are implemented in the Nucleus SE.
Previous articles in the series:
Article # 19. Semaphores: introduction and basic servicesArticle # 18. Event flag groups: helper services and data structuresArticle # 17. Event flag groups: introduction and basic servicesArticle # 16. SignalsArticle # 15. Memory sections: services and data structuresArticle # 14. Memory sections: introduction and basic servicesArticle # 13. Task data structures and unsupported API callsArticle # 12. Task ServicesArticle # 11. Tasks: configuration and introduction to the APIArticle # 10. Scheduler: additional features and context preservationArticle # 9. Scheduler: implementationArticle # 8. Nucleus SE: Inside and DeploymentArticle # 7. Nucleus SE: introductionArticle # 6. Other RTOS servicesArticle # 5. Interaction between tasks and synchronizationArticle # 4. Tasks, context switching and interruptsArticle # 3. Tasks and planningArticle # 2. RTOS: Structure and Real Time
Article # 1. RTOS: introduction.
Reset semaphore
This API call resets the semaphore to its initial, unused state. This API function is unusual compared to the functions of other kernel objects, since, despite the fact that it performs a reset, it does not just set the counter to the initial value, but a new initial counter value is transmitted in the call. Any task that was suspended on the semaphore is resumed and returns the
NUSE_SEMAPHORE_WAS_RESET code in the Nucleus SE, and in the Nucleus RTOS,
NU_SEMAPHORE_RESET .
')
Call to reset semaphore in Nucleus RTOSService Call Prototype:
STATUS NU_Reset_Semaphore (NU_SEMAPHORE * semaphore, UNSIGNED initial_count);Options:
semaphore - pointer to user-supplied semaphore control block;
initial_count is the value to which the semaphore will be set.
Return value:
NU_SUCCESS - the call was successfully completed;
NU_INVALID_SEMAPHORE - invalid pointer to semaphore.
Call to reset semaphore in Nucleus SEThis API call supports the core Nucleus RTOS API.
Service Call Prototype:
STATUS NUSE_Semaphore_Reset (NUSE_SEMAPHORE semaphore, U8 initial_count);Options:
semaphore - index (ID) of the reset semaphore;
initial_count is the value to which the semaphore will be set.
Return value:
NUSE_SUCCESS - the call was successfully completed;
NUSE_INVALID_SEMAPHORE - incorrect semaphore index.
Implementing a semaphore reset in Nucleus SEThe main task of the
NUSE_Semaphore_Reset () API function is to set the corresponding
NUSE_Semaphore_Counter [] element to the specified value (after checking the parameters).
If task lock is activated, the following code is needed to unlock tasks:
while (NUSE_Semaphore_Blocking_Count[semaphore] != 0) { U8 index; /* check whether any tasks are blocked */ /* on this semaphore */ for (index=0; index<NUSE_TASK_NUMBER; index++) { if ((LONIB(NUSE_Task_Status[index]) == NUSE_SEMAPHORE_SUSPEND) && (HINIB(NUSE_Task_Status[index]) == semaphore)) { NUSE_Task_Blocking_Return[index] = NUSE_SEMAPHORE_WAS_RESET; NUSE_Task_Status[index] = NUSE_READY; break; } } NUSE_Semaphore_Blocking_Count[semaphore]--; } #if NUSE_SCHEDULER_TYPE == NUSE_PRIORITY_SCHEDULER NUSE_Reschedule(NUSE_NO_TASK); #endif
Each suspended task on the semaphore is marked as “finished”, and the task suspension code returns
NUSE_SEMAPHORE_WAS_RESET . After this process is completed, if the Priority scheduler is used, the call initializes
NUSE_Reschedule () , since one or more tasks with a higher priority could go to the ready state and wait for resumption.
Semaphore Information
This service call returns semaphore information. The implementation of this call in the Nucleus SE differs from the Nucleus RTOS in that less information is returned, since the naming of objects and the order of suspension are not supported, and the suspension of tasks itself can be disabled.
Call for information about the semaphore in Nucleus RTOSService Call Prototype:
STATUS NU_Semaphore_Information (NU_SEMAPHORE * semaphore, CHAR * name, UNSIGNED * current_count, OPTION * suspend_type, UNSIGNED * tasks_waiting, NU_TASK ** first_task);Options:
semaphore - a pointer to the semaphore control block about which information is required;
name is a pointer to the 8-character semaphore name, with a zero terminating byte included in this area;
current_count - a pointer to a variable that will accept the current value of the semaphore counter;
suspend_type - a pointer to a variable that will accept the type of task suspension; it can be
NU_FIFO and
NU_PRIORITY ;
task_waiting - a pointer to a variable that will accept the number of suspended tasks in the semaphore;
first_task is a pointer to a variable of type
NU_TASK , which will receive a pointer to the control unit of the first suspended task.
Return value:
NU_SUCCESS - the call was successfully completed;
NU_INVALID_SEMAPHORE - invalid pointer to semaphore.
Call for information about the semaphore in Nucleus SEThis API call supports the core Nucleus RTOS API.
Service Call Prototype:
STATUS NUSE_Semaphore_Information (NUSE_SEMAPHORE semaphore, U8 * current_count, U8 * tasks_waiting, NUSE_TASK * first_task);Options:
semaphore - the index of the semaphore about which you want to provide information;
current_count - a pointer to a variable that will accept the current value of the semaphore counter;
tasks_waiting - a pointer to a variable that will accept the number of tasks suspended on this semaphore (nothing is returned if support for suspending tasks 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 support for suspending tasks is disabled).
Return value:
NUSE_SUCCESS - the call was successfully completed;
NUSE_INVALID_SEMAPHORE - incorrect semaphore index;
NUSE_INVALID_POINTER - one or more pointer parameters are incorrect.
Implementing Semaphore Information in Nucleus SEThe implementation of this API call is quite simple:
NUSE_CS_Enter(); *current_count = NUSE_Semaphore_Counter[semaphore]; #if NUSE_BLOCKING_ENABLE *tasks_waiting = NUSE_Semaphore_Blocking_Count[semaphore]; if (NUSE_Semaphore_Blocking_Count[semaphore] != 0) { U8 index; for (index=0; index<NUSE_TASK_NUMBER; index++) { if ((LONIB(NUSE_Task_Status[index]) == NUSE_SEMAPHORE_SUSPEND) && (HINIB(NUSE_Task_Status[index]) == semaphore)) { *first_task = index; break; } } } else { *first_task = 0; } #else *tasks_waiting = 0; *first_task = 0; #endif NUSE_CS_Exit(); return NUSE_SUCCESS;
The function returns the semaphore status. Then, if the API call blocking functionality is activated, the number of pending tasks and the index of the first one are returned (otherwise, these parameters are assigned the value 0).
Getting the number of semaphores
This service call returns the number of semaphores in the application. In Nucleus RTOS, this value changes over time and the return value corresponds to the current number of semaphores, and in Nucleus SE, the return value is set at the assembly stage and does not change again.
Call for semaphore count in Nucleus RTOSService Call Prototype:
UNSIGNED NU_Established_Semaphores (VOID);Options:
None.
Return value:
The number of semaphores created in the application.
Call for semaphore count in Nucleus SEThis API call supports the core Nucleus RTOS API.
Service Call Prototype:
U8 NUSE_Semaphore_Count (void);Options:
None.
Return value:
The number of configured semaphores in the application.
Implementing Semaphore Counters in Nucleus SEThe implementation of this API call is quite simple: the value of the
#define NUSE_SEMAPHORE_NUMBER symbol is
returned .
Data structures
Semaphores use two or three arrays of data structures (in RAM and ROM), which, like all other objects of Nucleus SE, are a set of tables, the size of which depends on the number of semaphores in the application and the selected 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 will avoid incompatibility with future versions of Nucleus SE and unwanted side effects, as well as simplify porting the application to the Nucleus RTOS. For a better understanding of how the service call code works and for debugging, a detailed overview of the data structures is given below.
Data in ram
This data has the following structure:
NUSE_Semaphore_Counter [] - an array of type
U8 , having one entry for each configured semaphore, stores the value of the counter.
NUSE_Semaphore_Blocking_Count [] is an array of type
U8 that contains counters of blocked tasks on each semaphore. This array exists only if the call API blocking functionality is activated.
NUSE_Semaphore_Counter [] is initialized to the initial value (see “Data in ROM” below), and
NUSE_Semaphore_Blocking_Count [] is zeroed with
NUSE_Init_Semaphore () when you start Nucleus SE. One of the following articles will provide a complete description of the Nucleus SE startup procedures.
The following are the definitions of these data structures in the
nuse_init.c file.
RAM U8 NUSE_Semaphore_Counter[NUSE_SEMAPHORE_NUMBER]; #if NUSE_BLOCKING_ENABLE RAM U8 NUSE_Semaphore_Blocking_Count[NUSE_SEMAPHORE_NUMBER]; #endif
ROM data
Data structure:
NUSE_Semaphore_Initial_Value [] is an array of type
U8 , having one entry for each semaphore, these are the initial values ​​of the semaphores.
This data structure is declared and initialized (statically) in
nuse_config.c :
ROM U8 NUSE_Semaphore_Initial_Value[NUSE_SEMAPHORE_NUMBER] = { /* semaphore initial count values */ };
Memory Capacity for Semaphores
Like all Nucleus SE core objects, the amount of data needed for semaphores is predictable.
The amount of memory in ROM (in bytes) for all semaphores in the application is
NUSE_SEMAPHORE_NUMBER .
The amount of memory in RAM (in bytes) for all semaphores in the application with activated blocking API calls can be calculated as follows:
NUSE_SEMAPHORE_NUMBER * 2
Otherwise, it is
NUSE_SEMAPHORE_NUMBER .
Unrealized API calls
Three API calls for semaphores that are present in the Nucleus RTOS are not implemented in the Nucleus SE.
Creating semaphores
This API call creates a semaphore. It is not necessary for the Nucleus SE, since the semaphores are created statically.
Service Call Prototype:
STATUS NU_Create_Semaphore (NU_SEMAPHORE * semaphore, CHAR * name, UNSIGNED initial_count, OPTION suspend_type);Options:
semaphore is a pointer to a user-provided semaphore control block, it is used to control semaphores in other API calls;
name is a pointer to the 8-character semaphore name, with the terminating zero byte enabled;
initial_count - the initial value of the semaphore;
suspend_type - indicates the principle of suspending the task on the semaphore. It can take the values
NU_FIFO and
NU_PRIORITY , which correspond to the FIFO (First-in-First-Out) principle and the order of priority of task suspension.
Return value:
NU_SUCCESS - the call was successfully completed;
NU_INVALID_SEMAPHORE - says that the pointer to the semaphore control block is null (
NULL ) or is already in use;
NU_INVALID_SUSPEND is an invalid
suspend_type parameter.
Delete semaphore
This API call removes the previously created semaphore. It is not necessary for the Nucleus SE, since the semaphores are created statically and cannot be deleted.
Service Call Prototype:
STATUS NU_Delete_Semaphore (NU_SEMAPHORE * semaphore);Options:
semaphore - pointer to the semaphore control block.
Return value:
NU_SUCCESS - the call was successfully completed;
NU_INVALID_SEMAPHORE - invalid pointer to semaphore.
Pointers to semaphores
This API call generates a sequential list of pointers to all semaphores in the system. It is not necessary for the Nucleus SE, since the semaphores are identified by a simple index, not a pointer.
Service Call Prototype:
UNSIGNED NU_Semaphore_Pointers (NU_SEMAPHORE ** pointer_list, UNSIGNED maximum_pointers);Options:
pointer_list is a pointer to an array of pointers
NU_SEMAPHORE , this array is filled with pointers to semaphores;
maximum_pointers - the maximum number of pointers in the array.
Return value:
The number of
NU_SEMAPHORE pointers in the array.
Nucleus RTOS Compatibility
As with all other Nucleus SE objects, the goal was to ensure maximum compatibility of application code with the Nucleus RTOS. Semaphores 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 the fact that the final code will become clearer and more efficient in terms of the amount of memory required. Otherwise, Nucleus RTOS API calls can be almost directly used as Nucleus SE calls.
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 semaphore 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. Thus, I have defined
NUSE_SEMAPHORE as equivalent to
U8 , the variable (and not the pointer) of this type serves as the semaphore 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 naming semaphores. These names are used only when debugging. I excluded them from the Nucleus SE to save memory.
Counter size
In the Nucleus RTOS, the semaphore counter is of the
unsigned type, which is usually a 32-bit variable. The Nucleus SE counter is 8-bit, but this can be easily changed. Normally, in the RTOS Nucleus, a semaphore overflow check is not performed. Calling the Nucleus SE API will not allow you to assign values ​​higher than 255 to the counter.
Unrealized API calls
Nucleus RTOS supports eight service calls for semaphore. Of these, three are not implemented in the Nucleus SE. The details of these calls, as well as the decision to exclude them from Nucleus SE, have been described above.
The following article will be considered mailboxes.
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.