The whole truth about RTOS. Article # 19. Semaphores: introduction and basic services

Original author: Colin Walls
  • Transfer


Semaphores have been mentioned in a previous article (# 5). Their main task is to control access to resources.

Previous articles in the series:
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 Mode
Article # 1. RTOS: introduction.

Using semaphores


In Nucleus SE, semaphores are defined at the assembly stage. An application can have up to 16 semaphores. If semaphores are not specified, the code for service calls and data structures is not included in the application.

A semaphore is a U8 type counter , access to which is controlled in such a way that several tasks can use it. A task can reduce the semaphore count (capture) or increase it (release). Attempting to capture a semaphore with a null value may result in an error or a suspension of the task, depending on the selected API call parameters and the configuration of the Nucleus SE.

Setting up semaphores


Number of semaphores


As with most Nucleus SE objects, the semaphore setting is determined by the #define directives in nuse_config.h . The main parameter is NUSE_SEMAPHORE_NUMBER , which determines the number of semaphores in the application. By default, the parameter is set to 0 (semaphores are not used in the application) and can take any value up to 16. An incorrect value will result in a compilation error, which will be generated by checking in nuse_config_check.h (this file is included in nuse_config.c , which means together with this module), as a result the #error directive will work .

The choice of a non-zero value serves as the main activator for semaphores. This parameter is used when defining data structures and their size depends on its value (for more details, read later in this article). In addition, a non-zero value activates the API settings.

Activate API calls


Each API function (service call) in the Nucleus SE is activated by the #define directive in nuse_config.h . For semaphores, these include: By default, they are assigned the value FALSE , thus disabling each service call and blocking the inclusion of the code implementing them. To set up semaphores, you need to select the necessary API calls and set the corresponding directives to TRUE . The following is an excerpt from the default nuse_config.h file .

NUSE_SEMAPHORE_OBTAIN
NUSE_SEMAPHORE_RELEASE
NUSE_SEMAPHORE_RESET
NUSE_SEMAPHORE_INFORMATION
NUSE_SEMAPHORE_COUNT






#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 */

An activated API function if there are no semaphores in the application will result in a compilation error (except NUSE_Semaphore_Count () , which is always enabled). If your code uses an API call that has not been activated, a build error will occur because the implementation code was not included in the application.

Service calls semaphores


Nucleus RTOS supports eight service calls that provide the following functionality:

  • Capture semaphore. In Nucleus SE, it is implemented in the function NUSE_Semaphore_Obtain () .
  • Semaphore release. The Nucleus SE is implemented as a function in NUSE_Semaphore_Release () .
  • Returning the semaphore to an unused state with the release of all suspended tasks (reboot). In Nucleus SE, implemented in NUSE_Semaphore_Reset () .
  • Providing information about a specific semaphore. In Nucleus SE, implemented in NUSE_Semaphore_Information () .
  • Returns the number of configured semaphores in the application. In Nucleus SE, NUSE_Semaphore_Count () is implemented .
  • Adding a new semaphore to the application. Nucleus SE is not implemented.
  • Remove semaphore from application. Nucleus SE is not implemented.
  • Return pointers to all semaphores. Nucleus SE is not implemented.

The implementation of each service call is described in detail below.

Service calls capture and release of semaphores


The fundamental operations that can be performed on semaphores are capture and release (decrease and increase in value). Nucleus RTOS and Nucleus SE provide two basic API calls for these operations.

Semaphore capture


The Nucleus RTOS service call to capture a semaphore is very flexible and allows you to suspend tasks implicitly or with a specific timeout if the operation cannot be performed at the moment, for example, if you try to capture a semaphore with a zero value. Nucleus SE provides the same functions, only task suspension is optional, and timeout is not implemented.

Call to capture a semaphore in the Nucleus RTOS
Service Call Prototype:
STATUS NU_Obtain_Semaphore (NU_SEMAPHORE * semaphore, UNSIGNED suspend);

Parameters:

semaphore - a pointer to the semaphore control block provided by the user;
suspend - the option to pause the task; it can be NU_NO_SUSPEND or NU_SUSPENDas well as the timeout value.

Return value:

NU_SUCCESS - the call was successfully completed;
NU_UNAVAILABLE - the semaphore had a zero value;
NU_INVALID_SEMAPHORE - invalid pointer to semaphore;
NU_INVALID_SUSPEND - an attempt to suspend a stream that is not associated with a task;
NU_SEMAPHORE_WAS_RESET - The semaphore was dropped while the task was suspended.

Call to capture the semaphore in Nucleus SE
This API call supports the key functionality of the Nucleus RTOS API.

Service Call Prototype:

STATUS NUSE_Semaphore_Obtain (NUSE_SEMAPHORE semaphore, U8 suspend);

Parameters:

semaphore- the index (ID) of the semaphore used;
suspend - the option to suspend the task; it can be NUSE_NO_SUSPEND or NUSE_SUSPEND .

Return value:

NUSE_SUCCESS - the call was successfully completed;
NUSE_UNAVAILABLE - the semaphore had a zero value;
NUSE_INVALID_SEMAPHORE - incorrect semaphore index;
NUSE_INVALID_SUSPEND - attempt to suspend from a thread that is not associated with the task or when the API blocking functionality is disabled;
NUSE_SEMAPHORE_WAS_RESET - the semaphore was dropped while the task was suspended;

Implementing Semaphore Capture in Nucleus SE
Function Code OptionNUSE_Semaphore_Obtain () (after checking the parameters) is selected using conditional compilation depending on whether support for blocking (suspending) tasks is activated or not. Consider both options.

If the lock is not activated, the logic of this API call is quite simple:

if (NUSE_Semaphore_Counter[semaphore] != 0) /* semaphore available */
{
    NUSE_Semaphore_Counter[semaphore]--;
    return_value = NUSE_SUCCESS;
}
else/* semaphore unavailable */
{
    return_value = NUSE_UNAVAILABLE;
}

The value of the semaphore counter is checked, and if it is not zero, decreases.

If task lock is activated, the logic becomes more complex:

do
{
    if (NUSE_Semaphore_Counter[semaphore] != 0) /* semaphore available */
    {
        NUSE_Semaphore_Counter[semaphore]--;
        return_value = NUSE_SUCCESS;
        suspend = NUSE_NO_SUSPEND;
    }
    else/* semaphore unavailable */
    {
        if (suspend == NUSE_NO_SUSPEND)
        {
            return_value = NUSE_UNAVAILABLE;
        }
        else
        {                                   /* block task */
            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);

Some explanations may be helpful.

The code is placed in a do ... while loop , which runs until the suspend parameter is NUSE_SUSPEND .

If the semaphore has a nonzero value, it is reduced. The variable suspend is assigned the value NUSE_NO_SUSPEND , and the API call ends and returns the value NUSE_SUCESS .

If the semaphore is null and the suspend variable is set to NUSE_NO_SUSPEND , the API call returns NUSE_UNAVAILABLE . If the suspension was assigned the value NUSE_SUSPEND, the task is suspended. After the call is completed (for example, when the task is resumed), if the return value is NUSE_SUCCESS (which indicates that the task was resumed after the semaphore was released, and not after the reset), the cycle starts from the beginning.

Semaphore release


The Nucleus RTOS API service call to release a semaphore is quite simple: the value of the semaphore counter increases and a success message is returned. Nucleus SE provides the same features, only with an additional check for overflow.

Call to release semaphores in Nucleus RTOS
Service Call Prototype:

STATUS NU_Release_Semaphore (NU_SEMAPHORE * semaphore);

Parameters:

semaphore - a pointer to a semaphore control block provided by the user.

Return value:

NU_SUCCESS - the call was successfully completed;
NU_INVALID_SEMAPHORE - invalid pointer to semaphore.

A call to release a semaphore in the Nucleus SE
This API call supports the core functionality of the Nucleus RTOS API.

Service call prototype:

STATUS NUSE_Semaphore_Release (NUSE_SEMAPHORE semaphore);

Parameters:

semaphore - index (ID) of the semaphore being released.

Return value:

NUSE_SUCCESS - the call was successfully completed;
NUSE_INVALID_SEMAPHORE - incorrect semaphore index;
NUSE_UNAVAILABLE - the semaphore is 255 and cannot be incremented.

Implementing Semaphore Release in Nucleus SE NUSE_Semaphore_Release ()
Function Code(after checking the parameters) general, regardless of whether the task lock is activated or not. The value of the semaphore counter is checked, and if it is less than 255, it increases.

Further code is selected using conditional compilation, if support for blocking API calls (task suspension) is activated:

NUSE_CS_Enter();
if (NUSE_Semaphore_Counter[semaphore] < 255)
{
    NUSE_Semaphore_Counter[semaphore]++;
    return_value = NUSE_SUCCESS;
    #if NUSE_BLOCKING_ENABLEif (NUSE_Semaphore_Blocking_Count[semaphore] != 0)
        {
            U8 index;       /* check whether a task is blocked *//* on this semaphore */
            NUSE_Semaphore_Blocking_Count[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_SUCCESS;
                    NUSE_Wake_Task(index);
                    break;
                }
            }
        }
    #endif
}
else
{
    return_value = NUSE_UNAVAILABLE;
}
NUSE_CS_Exit();
return return_value;

If any tasks are suspended on this semaphore, the first one is resumed.

The next article will describe additional API calls related to semaphores and their data structures.

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: