Signals

In this RTOS Revealed, I am going to look at signals, which are the simplest method of inter-task communication supported by Nucleus SE. They provide a very low cost means of passing simple messages between tasks.

Using Signals

Signals are different from all the other types of kernel object in that they are not autonomous – signals are associated with tasks and have no independent existence. If signals are configured for an application, each task has a set of eight signal flags.

Any task can set the signals of another task. Only the owner task can read the signals. The read is destructive – i.e. the signals are cleared by the process of reading. No other task can read or clear a task’s signals.

There is a facility in Nucleus RTOS that enables a task to nominate a function that is run when another task sets one or more of its signal flags. This is somewhat analogous to an interrupt service routine. This capability is not supported in Nucleus SE; tasks need to interrogate their signal flags explicitly.

Configuring Signals

As with most aspects of Nucleus SE, the configuration of signals is primarily controlled by #define statements in nuse_config.h . The key setting is NUSE_SIGNAL_SUPPORT , which enables the facility (for all tasks in the application). There is no question of specifying the number of signals – there is simply a set of eight flags for each task in the application.

Setting this enable parameter is the “master enable” for signals. This causes a data structure to be defined and sized accordingly, of which more later in this article. It also activates the API enabling settings.

API Enables

Every API function (service call) in Nucleus SE has an enabling #define symbol in nuse_config.h . For signals, these are:

NUSE_SIGNALS_SENDNUSE_SIGNALS_RECEIVE

By default, both of these are set to FALSE , thus disabling each service call and inhibiting the inclusion of any implementation code. To configure signals for an application, you need to select the API calls that you want to use and set their enabling symbols to TRUE .

Here is an extract from the default nuse_config.h file:

#define NUSE_SIGNAL_SUPPORT  FALSE   /* Enables support for signals */#define NUSE_SIGNALS_SEND    FALSE   /* Service call enabler */#define NUSE_SIGNALS_RECEIVE FALSE   /* Service call enabler */

A compile time error will result if a signals API function is enabled and the signals facility has not been enabled. If your code uses an API call, which has not been enabled, a link time error will result, as no implementation code will have been included in the application. Of course, the enabling of the two API functions is somewhat redundant, as there would be no point in enabling signals support and not having these APIs available. The enables are included for compatibility with other Nucleus SE features.

Signals Service Calls

Nucleus RTOS supports four service calls that appertain to signals, that provide the following functionality:

  • Send signals to a specified task. Implemented by NUSE_Signals_Send() in Nucleus SE.

  • Receive signals. Implemented by NUSE_Signals_Receive() in Nucleus SE.

  • Register a signal handler. Not implemented in Nucleus SE.

  • Enable/disable (control) signals. Not implemented in Nucleus SE.

The implementation of each of these service calls is examined in detail.

Signals Send and Receive Services

The fundamental operations, that can be performed on a task’s set of signals, are sending data to it (which may be done by any task) and reading data from it (and thus clearing the data, which may only be done by the owner). Nucleus RTOS and Nucleus SE each provide two basic API calls for these operations, that will be discussed here.

Since signals flags are bits, they are best visualized as binary numbers. As standard C does not historically support a representation of binary constants (only octal and hexadecimal), the Nucleus SE distribution includes a useful header file – nuse_binary.h – which contains #define symbols of the form b01010101 for all 256 8-bit values. Here is an extract from the nuse_binary.h file:

#define b00000000 ((U8) 0x00)#define b00000001 ((U8) 0x01)#define b00000010 ((U8) 0x02)#define b00000011 ((U8) 0x03)#define b00000100 ((U8) 0x04)#define b00000101 ((U8) 0x05)

Sending Signals

Any task may send signals to any other task in the application. Sending signals involves setting one or more of the signal flags. This is an OR operation that has no effect upon flags set previously.

Nucleus RTOS API Call for Sending Signals

Service call prototype:

STATUS NU_Send_Signals(NU_TASK *task, UNSIGNED signals);

Parameters:

task – pointer to control block of the task that owns the signal flags to be set

signals – the value of the signal flags to be set

Returns:

NU_SUCCESS – the call was completed successfully

NU_INVALID_TASK – the task pointer is invalid

Nucleus SE API Call for Sending Signals

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

Service call prototype:

STATUS NUSE_Signals_Send(NUSE_TASK task, U8 signals);

Parameters:

task – the index (ID) of the task that owns the signal flags to be set

signals – the value of the signal flags to be set

Returns:

NUSE_SUCCESS – the call was completed successfully

NUSE_INVALID_TASK – the task index is invalid

Nucleus SE Implementation of Sending Signals

Here is the complete code for the NUSE_Signals_Send() function:

STATUS NUSE_Signals_Send(NUSE_TASK task, U8 signals){    #if NUSE_API_PARAMETER_CHECKING        if (task >= NUSE_TASK_NUMBER)        {            return NUSE_INVALID_TASK;        }    #endif    NUSE_CS_Enter();    NUSE_Task_Signal_Flags[task] |= signals;    NUSE_CS_Exit();    return NUSE_SUCCESS;}

The code is very simple. After any parameter checking, the signal values are ORed into the specified task’s signal flags. Task blocking is not relevant to signals.

Receiving Signals

A task may only read its own set of signal flags. The process of reading them is destructive; i.e. it also results in the flags being cleared.

Nucleus RTOS API Call for Receiving Signals

Service call prototype:

UNSIGNED NU_Receive_Signals(VOID);

Parameters: none

Returns: the signals flags value

Nucleus SE API Call for Receiving Signals

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

Service call prototype:

U8 NUSE_Signals_Receive(void);

Parameters: none

Returns: the signal flags value

Nucleus SE Implementation of Receiving Signals

Here is the complete code for the NUSE_Signals_Receive() function:

U8 NUSE_Signals_Receive(void){    U8 signals;    NUSE_CS_Enter();    signals = NUSE_Task_Signal_Flags[NUSE_Task_Active];    NUSE_Task_Signal_Flags[NUSE_Task_Active] = 0;    NUSE_CS_Exit();    return signals;}

The code is very simple. The flags value is copied, the original value cleared and the copy returned by the API function. Task blocking is not relevant to signals.

Data Structures

Since signals are not stand-alone objects, their memory usage really appertains to the task by which they are owned, but some notes are included her for completeness. Signals utilize a single data structure – in RAM – which, like other Nucleus SE objects, is a table, dimensioned according to the number of tasks configured and only included if signals support was selected.

I strongly recommend that application code does not access this data structure 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

The data structure is:

NUSE_Task_Signal_Flags[] – This is an array of type U8 , with one entry for each configured task and is where the signal flags are stored.

This data structure is initialized to zeros by NUSE_Init_Task() when Nucleus SE starts up.

ROM Data

There are no ROM data structures associated with signals.

Signals Data Footprint

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

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

The RAM data footprint (in bytes) for all the signals in an application is simply the number of configured tasks (NUSE_TASK_NUMBER ). Again, this data really “belongs” to the tasks and is included in a previous article about tasks.

Unimplemented API Calls

Two signals API calls found in Nucleus RTOS are not implemented in Nucleus SE:

Register a Signal Handler

This API call establishes a signal handling routine (function) for the current task. It is not needed with Nucleus SE, as signal handlers are not supported.

Service call prototype:

STATUS NU_Register_Signal_Handler(VOID(*signal_handler)(UNSIGNED));

Parameters:

signal_handler – function to be called whenever signals are received

Returns:

NU_SUCCESS – successful completion of the service

NU_INVALID_POINTER – the signal handler pointer is NULL

The signal handler is executed in the context of the current task.

Control (Enable/Disable) Signals

This service enables and/or disables signals for the current task. There are 32 signals available for each task. Each signal is represented by a bit in signal_enable_mask . Setting a bit in signal_enable_mask enables the corresponding signal, while clearing a bit disables the corresponding signal.

Service call prototype:

UNSIGNED NU_Control_Signals(UNSIGNED enable_signal_mask);

Parameters:

enable_signal_mask – bit pattern representing valid signals

Returns:

The previous signal enable/disable mask.

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. Signals 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, that 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.

Signal Handlers

In Nucleus SE, signal handlers were not implemented in order to maintain simplicity.

Signal Availability and Number

In Nucleus RTOS, tasks each have 32 signal flags. In Nucleus SE, I decided to reduce this to eight signal flags per task, as this is likely to be sufficient for simpler applications and saves RAM. Signals may be disabled entirely, if they are not required.

Unimplemented API Calls

Nucleus RTOS supports four service calls to work with signals. Of these, two 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.


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.