This article continues our look at Nucleus SE.
Nucleus SE provides the range of facilities that might be expected in a RTOS.
Firstly, there is the scheduler, which is designed to be simple, but, by having four variants available, maintains flexibility. Support is available for run to completion, round robin, time slice and priority scheduling schemes.
The Nucleus SE API includes nearly 50 service calls, which provide programmer access and control of tasks, memory partitions, signals, event flag groups, semaphores, mailboxes, queues, pipes, system time, application timers and diagnostics.
In addition to simple task scheduling, Nucleus SE (optionally) supports task suspension. This may be “pure” (i.e. as a result of an explicit task suspend API call), it may be a “sleep” function (where the task is suspended for a specific time period) or it may be the result of another API call, where a task is blocked (i.e. conditionally suspended) pending the availability of a kernel resource. Unlike Nucleus RTOS, Nucleus SE does not support timeouts on blocking API calls.
The range of facilities enable a choice to be made from a hierarchy of inter-task synchronization and communication facilities: from semaphores, through signals, event flags, mailboxes and queues/pipes.
By setting the configuration option NUSE_API_PARAMETER_CHECKING , code is included in all API functions to verify parameters – check for null pointers, valid object indexes etc. Since this is extra code, consuming additional memory, it would normally be prudent to activate the option during debug, but turn it off for a production build.
Nucleus SE is designed to be very configurable. This has two facets. Firstly, the kernel can be configured in a very fine-grained way to meet the needs of a specific application – tuning the available functionality and controlling memory utilization are very straightforward. Secondly, the Nucleus SE code is intended to be very portable – between toolkits and between processors.
As clarity and ease of understanding of the Nucleus SE code was a goal, some thought was given to naming conventions. Every symbol in the code has the prefix NUSE_ . What follows this string obeys some simple rules.
Every API call function name in Nucleus SE starts NUSE_ , which is almost always followed by the type of object in question, then the operation to be performed, all in mixed case and separated by underscores; an example is NUSE_Queue_Send() , which places a message in a queue.
Other Functions and Variables
All other functions and (global) variables in the Nucleus SE code continue to use the NUSE_ prefix, but the remainder of the name does not necessarily have any “structure”. This is unimportant to the normal user of the kernel, as the API is sole recommended interface into the code.
Since Nucleus SE is configured by means of #define symbols, these too obey the naming rules. They are also solely in upper case. The API call enablers have exactly the same name as the functions themselves (except for the case); an example is NUSE_QUEUE_SEND .
Other #define Symbols
Any other #define symbols – API call parameter and status return values, for example – that may be used by application code, follow the same conventions; they start with NUSE_ and are in upper case. An example is NUSE_SUCCESS .
All RTOSes maintain a number of data structures that describe kernel objects. In most implementations, these are in the form of C structures, which are typically organized into linked lists – often doubly and maybe circularly linked. This makes sense, as all the relevant data is encapsulated conveniently and list members may be added or removed, as objects are created and deleted.
In Nucleus SE, objects are all static, so arranging the object data structures into a simple list was an obvious optimization. This saved the space and complexity of forward and backward pointers. However, I decided to take the optimization one step further and not use structures at all; in Nucleus SE, all the kernel object data is represented by several simple arrays (often referred to as tables) of various types – one or more for each type of object. There were several reasons for this strategy:
Nucleus SE was intended to be “8-bit friendly”. Most small CPUs do not have an optimal means for a compiler to implement C structures. Simple arrays are much more efficient.
Since a maximum of 16 of each object type is permitted, addressing the elements of each of these arrays requires four bits – often a byte is used. This is more efficient than an address, which are normally 16 or 32 bits.
It is required that object data, which is constant, is kept in ROM, and not copied into RAM. Since a structure cannot (in conventional, portable C) be split between ROM and RAM, each object type might require two structures, which is overly complex. In Nucleus SE, object description tables may each be in either ROM or RAM, as required.
Because of the great configurability of Nucleus SE (“extreme scalability”), some object description data may be optional, depending on the selected facilities. This results in wide use of conditional compilation. A structure definition with conditional compilation directives embedded within it tends to be very hard to understand. Controlling the instantiation of individual arrays this way is quite readable.
All the object data tables adhere to the hierarchical naming convention idea mentioned earlier in this article. So, understanding which tables logically belong together is quite straightforward.
Key Differences from Nucleus RTOS
Although Nucleus SE was designed to offer a high degree of compatibility with Nucleus RTOS, many small and larger differences remain. These will be discussed in detail in the relevant articles, so it will suffice to summarize them here.
In Nucleus RTOS, objects are created and deleted as required. In Nucleus SE all objects are created statically and defined at build time.
An effectively indefinite number of objects of each type may be supported by Nucleus RTOS. Nucleus SE imposes a maximum of sixteen instances of each object type.
Nucleus RTOS allows certain object types to be given textual names, which may be used for debug purposes. This facility is not supported by Nucleus SE.
Task Blocking Mechanism
The Nucleus SE API call task blocking mechanism is quite simplistic. When a resource becomes available, all waiting tasks are resumed and compete (via the scheduler) to obtain the resource. The “losers” are suspended (blocked) again. In Nucleus RTOS, the mechanism is more complex, only resuming the relevant task(s), which is more efficient.
API Call Timeout
When making a blocking API call, Nucleus RTOS allows the programmer to specify a timeout period after which the call will return, even if the resource has not become available. This is not supported by Nucleus SE.
The Nucleus RTOS scheduler is highly efficient, flexible and fully deterministic. Nucleus SE offers a choice of schedulers, each of which is simple and reasonably efficient with the reduced number of tasks (1-16) supported.
A system using Nucleus RTOS can have an effectively indefinite number of tasks, which may utilize 256 priority levels, with multiple tasks at any given priority, if required. Task priority levels may also be changed at runtime. With Nucleus SE, if the priority scheduler is selected, each task must occupy a unique priority level, which cannot be changed dynamically. There is simply one level per task, up to the maximum of sixteen tasks.
Nucleus RTOS supports a sophisticated two-level interrupt service routine architecture, which allows efficient interaction between ISRs and kernel services. Nucleus SE has a simpler approach, which either allows for simple ISRs that do not interact with the kernel (unmanaged interrupts) or ISRs with a full task context save that can use API calls (managed interrupts).
Nucleus RTOS has a well-defined device driver architecture. Nucleus SE does not, leaving it to the user to split device management between task and ISR code.
Nucleus SE Distribution
Nucleus SE source files will be made available as this series of articles progresses. Available files will be listed and an email request required to obtain them. Towards the end of the series, a download facility will be established to facilitate the easy download of all the files.
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 firstname.lastname@example.org