Semaphores in Nucleus SE: Overview, Configuration, and API Essentials
View the RTOS Revealed series
Semaphores are a foundational mechanism for synchronizing access to shared resources in embedded systems. They allow tasks to safely obtain and release control over a resource without conflicts.
Using Semaphores
In Nucleus SE, semaphore support is enabled during the build process. A single application can configure up to 16 semaphores. If you opt not to use any semaphores, the related data structures and API code are excluded from the final binary, reducing footprint.
A semaphore is simply an 8‑bit counter whose value is protected so that multiple tasks can manipulate it safely. Tasks call Obtain to decrement the counter and Release to increment it. If a task attempts to obtain a semaphore whose counter is zero, the outcome depends on the API options and the system configuration: the call may return an error or suspend the task until the semaphore becomes available.
Configuring Semaphores
Number of Semaphores
Semaphores are defined via #define statements in nuse_config.h. The key macro is NUSE_SEMAPHORE_NUMBER, which specifies how many semaphore slots the application will use. The default value is 0, meaning no semaphore support. Valid values range from 0 to 16. An invalid value triggers a compile‑time error generated by nuse_config_check.h (included through nuse_config.c).
Setting NUSE_SEMAPHORE_NUMBER to a non‑zero value activates the semaphore subsystem: data structures are sized accordingly, and API enabling symbols are processed.
API Enables
Each Nucleus SE API call is guarded by a dedicated enabling macro in nuse_config.h. For semaphores, the relevant symbols are:
NUSE_SEMAPHORE_OBTAINNUSE_SEMAPHORE_RELEASENUSE_SEMAPHORE_RESETNUSE_SEMAPHORE_INFORMATIONNUSE_SEMAPHORE_COUNT
By default, all these macros are set to FALSE, which disables the corresponding service calls and excludes their implementation from the build. To enable a particular API, set its macro to TRUE.
Below is a snippet from the default nuse_config.h file:
#define NUSE_SEMAPHORE_NUMBER 0 /* Number of semaphores in the system – 0‑16 */ #define NUSE_SEMAPHORE_OBTAIN FALSE /* Service call enabler */ #define NUSE_SEMAPHORE_RELEASE FALSE /* Service call enabler */ #define NUSE_SEMAPHORE_RESET FALSE /* Service call enabler */ #define NUSE_SEMAPHORE_INFORMATION FALSE /* Service call enabler */ #define NUSE_SEMAPHORE_COUNT FALSE /* Service call enabler */
Enabling an API call without configuring any semaphores (except NUSE_Semaphore_Count(), which is always available) results in a compile‑time error. If you call an API that has not been enabled, a link‑time error occurs because the implementation was omitted.
Semaphore Service Calls
Nucleus SE offers eight semaphore‑related service calls. The first five are fully implemented; the remaining three are placeholders for future extensions.
- Obtain –
NUSE_Semaphore_Obtain() - Release –
NUSE_Semaphore_Release() - Reset –
NUSE_Semaphore_Reset() - Information –
NUSE_Semaphore_Information() - Count –
NUSE_Semaphore_Count() - Create – not implemented in Nucleus SE
- Delete – not implemented in Nucleus SE
- List All – not implemented in Nucleus SE
Obtain and Release Operations
The core semaphore operations are obtaining (decrementing) and releasing (incrementing). Nucleus RTOS provides a flexible obtain API with optional suspension and timeouts. Nucleus SE mirrors this behavior, though task suspension is optional and timeouts are not supported.
Nucleus RTOS Obtain API
Prototype:
STATUS NU_Obtain_Semaphore(NU_SEMAPHORE *semaphore, UNSIGNED suspend);
Parameters:
semaphore– pointer to the semaphore control block.
suspend– can beNU_NO_SUSPEND,NU_SUSPEND, or a timeout value.
Return values:
NU_SUCCESS– operation succeeded.
NU_UNAVAILABLE– semaphore value was zero.
NU_INVALID_SEMAPHORE– invalid semaphore pointer.
NU_INVALID_SUSPEND– suspend attempted from a non‑task context.
NU_SEMAPHORE_WAS_RESET– semaphore reset while task was suspended.
Nucleus SE Obtain API
Prototype:
STATUS NUSE_Semaphore_Obtain(NUSE_SEMAPHORE semaphore, U8 suspend);
Parameters:
semaphore– the semaphore index.
suspend– can beNUSE_NO_SUSPENDorNUSE_SUSPEND.
Return values:
NUSE_SUCCESS– operation succeeded.
NUSE_UNAVAILABLE– semaphore value was zero.
NUSE_INVALID_SEMAPHORE– invalid semaphore index.
NUSE_INVALID_SUSPEND– suspend attempted from non‑task or blocking APIs disabled.
NUSE_SEMAPHORE_WAS_RESET– semaphore reset during suspension.
Implementation Highlights
When blocking is disabled, the implementation simply checks if the counter is non‑zero, decrements it, and returns NUSE_SUCCESS. If the counter is zero, it returns NUSE_UNAVAILABLE.
if (NUSE_Semaphore_Counter[semaphore] != 0) {
NUSE_Semaphore_Counter[semaphore]--;
return_value = NUSE_SUCCESS;
} else {
return_value = NUSE_UNAVAILABLE;
}
When blocking is enabled, the routine enters a do…while loop that attempts to acquire the semaphore. If the counter is zero and suspend is NUSE_SUSPEND, the task is blocked. Upon release, the first waiting task is woken and the loop retries the acquisition.
do {
if (NUSE_Semaphore_Counter[semaphore] != 0) {
NUSE_Semaphore_Counter[semaphore]--;
return_value = NUSE_SUCCESS;
suspend = NUSE_NO_SUSPEND;
} else {
if (suspend == NUSE_NO_SUSPEND) {
return_value = NUSE_UNAVAILABLE;
} else {
NUSE_Semaphore_Blocking_Count[semaphore]++;
NUSE_Suspend_Task(NUSE_Task_Active, semaphore << 4 | NUSE_SEMAPHORE_SUSPEND);
return_value = NUSE_Task_Blocking_Return[NUSE_Task_Active];
if (return_value != NUSE_SUCCESS) {
suspend = NUSE_NO_SUSPEND;
}
}
}
} while (suspend == NUSE_SUSPEND);
Nucleus RTOS Release API
Prototype:
STATUS NU_Release_Semaphore(NU_SEMAPHORE *semaphore);
Parameters:
semaphore– pointer to the semaphore control block.
Return values:
NU_SUCCESS– operation succeeded.
NU_INVALID_SEMAPHORE– invalid semaphore pointer.
Nucleus SE Release API
Prototype:
STATUS NUSE_Semaphore_Release(NUSE_SEMAPHORE semaphore);
Parameters:
semaphore– the semaphore index.
Return values:
NUSE_SUCCESS– operation succeeded.
NUSE_INVALID_SEMAPHORE– invalid semaphore index.
NUSE_UNAVAILABLE– counter already at 255; cannot increment.
Implementation Highlights
The release routine first checks that the counter is below its maximum (255). If so, it increments the counter. If blocking is enabled and there are tasks waiting on this semaphore, the first one is awakened.
NUSE_CS_Enter();
if (NUSE_Semaphore_Counter[semaphore] < 255) {
NUSE_Semaphore_Counter[semaphore]++;
return_value = NUSE_SUCCESS;
#if NUSE_BLOCKING_ENABLE
if (NUSE_Semaphore_Blocking_Count[semaphore] != 0) {
NUSE_Semaphore_Blocking_Count[semaphore]--;
for (U8 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_SUCCESS;
NUSE_Wake_Task(index);
break;
}
}
}
#endif
} else {
return_value = NUSE_UNAVAILABLE;
}
NUSE_CS_Exit();
return return_value;
When tasks are suspended on a semaphore, releasing it wakes the first waiting task, allowing it to continue execution.
The next article will explore event flag groups and their related data structures.
Colin Walls has over thirty years of experience in the electronics industry, primarily focused on embedded software. He frequently presents at conferences, writes technical articles, and authored 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 blog is at https://blogs.mentor.com/colinwalls. Reach him by email at colin_walls@mentor.com.
Embedded
- Stainless Steel Explained: Composition, Production, and Global Impact
- Diodes and Rectifiers: Fundamentals, Operation, and Key Parameters
- Understanding Conductors and Insulators: From Quantum Mechanics to Practical Applications
- C# Fundamentals: Input and Output Essentials
- Mailboxes in Nucleus SE: A Practical Guide to Configuration and Usage
- Semaphores in Nucleus RTOS: Utility Services & Data Structures
- Event Flag Groups in Nucleus SE: An Introductory Guide to Configuration and API Usage
- Partition Memory in Nucleus RTOS/SE: Utility Services and Data Structures
- Partition Memory in Nucleus SE: Configuration, APIs, and Best Practices
- Queues in Nucleus SE: Introduction and Core Service Calls