Real-time operating systems (RTOSs) tend to be a checkbox item for many embedded projects. But is an RTOS always necessary? The answer is application-specific, so understanding what one will deliver is key to determining whether it becomes a requirement or an extravagance.
In general, an RTOS can be used anywhere a non-RTOS is employed. However, it’s rare to find an operating system with a matching RTOS that has exactly the same application programming interface (API). Many of them, though, embed an RTOS within a conventional operating system. For example, Lynux- Works LynxOS and Bluecat Linux share a Linux API. LynxOS is a hard RTOS, while Bluecat inherits its base from Linux.
Linux continues to improve its real-time performance, but its worst-case interrupt latency still doesn’t meet what would be considered hard real time for an RTOS. It all comes down to quality of service (QoS). Platforms like RTLinux Free augment Linux, providing hard real-time class QoS.
It’s important to note that this type of addition often incorporates an RTOS programming environment that’s distinct from the original operating system. An RTOS is typically small compared to a conventional desktop or server OS. They often target more smaller, resource-constrained microcontrollers. For instance, CMX’s CMX-RTX and CMX-Tiny+ can run on 8-bit MCUs up through 64-bit processors.
The increased power and memory capacity of 8-bit processors is making an RTOS more desirable for these platforms. But, an OS or RTOS is usually a requirement in 16-bit platforms and up with RTOS products like Express Logic’s ThreadX, Wind River’s VxWorks, Micrium’s uCOS-II, and Green Hills Software’s velOSity being common selections. Depending on requirements, MontaVista’s Linux meets 16- and 32-bit platform requirements in the low microsecond range.
THE RTOS CORE: SCHEDULING AND PARTITIONING
Most programmers aren’t familiar with RTOS constraints and requirements. Most usually opt for an RTOS due to its performance. Most RTOS products are small and fast, yet an RTOS also adds consistency. Beyond the fact that an RTOS gets the job done quickly, it can guarantee a job will get done.
In many applications, a late result can be catastrophic. Thus, a poor result within the proper timeframe is preferable. These applications are generally called hard real-time systems. Hard real time doesn’t indicate how fast the system may be or how quickly a system may respond. Rather, it refers to how reliably a system can meet the specified requirements.
A hard real-time system may have a fixed cycle time of one minute with a response time of one second. In theory, it’s something almost any operating system could handle. This isn’t always the case, though, as anyone can attest to when waiting for a desktop application to respond within a minute.
Hard real-time systems typically have shorter cycle times and tighter response requirements. Faster processors always help, and multicore platforms can improve response time, too. The trick for developers is to match system requirements to the hardware and software, hence the importance of an RTOS in embedded applications.
An RTOS can implement a range of scheduling policies, and the application will often restrict a programmer’s choices (see the table). Non-preemptive scheduling is trivial to implement but useful in some applications. On the other hand, non-preemptive scheduling within a task can be implemented on top of a preemptive system.
Non-preemptive should not be overlooked, especially in light of new multicore processors. Here, hardware may be tuned to handle an event-based operation in which a thread will wait for an external event to occur. This approach is usually unsuitable for a single-core processor handling multiple threads. On multicore systems with many cores, though, it’s often typical to dedicate one core to handle one peripheral. It then makes sense to have that core idle while waiting for an event to occur.
As a result, preemptive, interrupt-driven RTOS architectures make up the majority of platforms deployed. These platforms have a range of requirements, issues, and solutions (see the figure). Interrupt latency is always an issue, although hardware— multiple register sets, hardware scheduling and task switching, and hierarchical priority interrupt systems—can significantly reduce this overhead.
Several issues coincide with preemption. Most are timing-related, like race conditions, deadlock, starvation, and priority inversion, which occurs when a low-priority task A owns a synchronization resource of a higher-priority task B, and a task C with priority higher than A is running.
Without a feature like priority ceilings, task C can prevent task A and C from running. A priority-ceiling feature changes the priority of task A to that of task C, allowing it to run and eventually release the resource needed by C. At this point, task A’s priority returns to normal and task C can run.
The other timing-related issues, which the programmer must address, are often the sources of bugs that are difficult to locate and correct. Trace tools become valuable assets in locating these kinds of bugs, since symptoms such as blocked tasks are the only indication of the problem.
Continue to page 2
Of the required features for an operating system, one is optional in an RTOS environment—reentrant libraries. In a typical operating system, this is a requirement because the tasks and programs tend to be arbitrary and changing, as well as prone to sharing libraries.
In an embedded environment, there’s usually more control over the programs and tasks running in the system. Often, tasks never share any code except for the operating-system interface, which may or may not be reentrant. Programmers, especially those dealing with device drivers, need to be aware of reentrancy issues.
A plethora of task synchronization mechanisms is out there, from mutexes to messaging systems. From an RTOS perspective, no difference exists between them with respect to synchronization issues, such as race conditions.
The timer tends to be common among microcontrollers and operating systems. Minimally, a timer will be used as a timeof- day clock. But because they’re so useful, they tend to be implemented in a special fashion. The POSIX specification even defines timers as a component. Timers can also provide watchdog support.
In many microcontrollers, a timer can be set up to wake up the system when it’s in sleep mode. Some implementations allow operating systems to use it as a general timer, even though this wakeup feature is independent of the operating system.
Some systems have different types of timers with different characteristics to meet various requirements. Some timers can be synchronized to deliver simultaneous pulse-width-modulation (PWM) streams for motor-control applications. For an RTOS, a single timer will usually provide the necessary support to implement a time-of-day clock as well as providing time-slice support.
Timers also support time slicing. Usually found on time-sharing systems, it gives applications a “fair” amount of time to execute. This type of round-robin scheduling can be implemented for any interrupt level.
Normally, the interval provided by a clock “tick” will be fixed and tasks will be given the same amount of time to execute before being preempted. Of course, this policy is arbitrary and a variety of implementations is possible. For instance, variable time-slice durations would allow time to be allocated on a per task basis, with some tasks receiving more time than others, versus a task priority scheme that would potentially lock out lower-priority tasks.
Many RTOSs feature a fixed scheduler. Others permit replacement or customization, while another segment of RTOSs supports a variety of policies. This flexible approach is what enables operating systems such as Linux to provide real-time support at the same time they’re running a mix of applications in a time-sliced environment. Real-time tasks have a high priority and run before the typical user tasks.
Linux actually has a more complex scheduling system that specifies a task on whether it will relinquish control to another task of the same priority in a roundrobin methodology or continue to run to completion. (For more, see “Hypervisors And Separation Kernels”) Virtualization RTOS platforms like Open Kernel Labs’ OKL4 address this issue.
Some texts separate task synchronization and communication, but generally they’re the same thing. It’s just a matter of what information is exchanged. Nowhere is this more apparent than with messagepassing- based RTOS. Here, the messaging system handles all communication with no distinction between communication and synchronization.
Minimally, an RTOS must provide a mutual exclusion primitive, such as a mutex. Everything else can be built upon this primitive. In many cases, as with a message-passing system, the mutual exclusion support is hidden within the operating system. Only higher-level messaging functions are exposed.
Messaging systems have a range of names, from pipes to queues, and their implementations can span from singleprocessor, single-memory models to multicore clustering systems. Enea’s OSE RTOS and QNX’s Neutrino are two mainline RTOSs based on message passing.
Regardless of the approach or API chosen, the communication system must be integrated into the operating system’s scheduler at some level. Therefore, a task can be removed from the active queue if it must wait for an event. Likewise, a task that initiates an event, causing another task to become active, will result in a scheduling action.
Communication, events, and scheduling may be tied to the hardware, something that the RTOS must also handle. Texas Instruments’ DSP/BIOS is an RTOS designed to run on the DSP side of a dualcore system like TI’s DaVinci line. Among other things, DSP/BIOS handles communication between the ARM core and the DSP core.
Continue to page 3
The move to multicore with larger cores will likely retain the RTOS or an OS. However, smaller cores obviate or limit the possibility of an RTOS. Intellasys’ SEAforth 40C18 chip contains 40 small 18-bit cores that run Forth (see “Are You Migrating To Massive Multicore Yet?” at www.electronicdesign.com, ED Online 19976). The instructions are compact, so each word holds four instructions.
Each core has 64 words of ROM and RAM, and the chip has space for only 10,000 instructions. That’s barely enough to fit a program, let alone an RTOS. Still, enough space exists to place dedicated parts of an operating environment throughout the chip. Likewise, the applications for this platform are often dedicated. Thus, RTOS-style support isn’t required since the hardware is handling core-to-core communication and task dispatch.
What distinguishes an RTOS is its ability to manage resources, including time and memory. Timing issues are associated with interrupt response time, but resource-management timing issues occur, too. Though interrupts address a number of timing issues, applications must still utilize resources.
Consider memory allocation. Many real-time applications forego dynamic memory allocation to make sure it doesn’t become an issue because of the variance that can occur due to allocation and deallocation. Applications that need dynamic memory allocation often split them into real-time and non-real-time portions. The latter handles dynamic memory allocation. Typically, the real-time portion must have enough memory allocated before it’s used.
C and C++ are used in real-time embedded applications because memory and other resource usage is explicit. Real-time tasks need to avoid C and C++. In particular, it’s more difficult with C++, where allocation and deallocation are more easily hidden but the process is still manageable.
The challenge is greater with languages such as Java and C#, which inherently use dynamic memory allocation. The programmer can control memory allocation and deallocation. In some instances, the programming environment can enforce memory allocation and deallocation.
The Real Time Specification for Java (RTSJ) defines ways to create Java applications that do not need garbage collection. RTSJ does so within the Java framework, enabling programmers to gain the advantages of Java without the memory-allocation limitations.
Both Sun an DDC-I have implementations of RTSJ. DDC-I’s supports x86 and PowerPC platforms. Aonix has a similar real-time platform called PERC. These platforms feature real-time, concurrent garbage collectors that make it possible to write real-time applications in Java without the memory-allocation limitations.
However, the real-time requirements aren’t as tight because the system must allow thread switches for the garbage collector. On the other hand, the garbage collector will consume timing resources, though only when real-time tasks can guarantee that certain deadlines are met. Fast is good, but on time is the RTOS credo.
One consideration when looking at realtime platforms is the overall impact memory allocation will have on a system. Many systems can operate with a static allocation that never changes, but more dynamic systems could benefit from real-time garbage collection instead. Studies show that efficiency of garbage collection versus explicit memory allocation is comparable.
Another issue surrounding virtualmachine- style platforms such as Java and C# is the use of a just-in-time (JIT) compiler. Real-time systems based on these types of systems must employ ahead-oftime (AOT) compilers like C and C++.
Designers may choose Java or C# for their improved productivity, lower susceptibility to bugs, and safety. It should come as no surprise, then, that a safety-critical Java specification dubbed JSR-302 is in the works (see “Critical Java,” ED Online 8116).
SECURING AN RTOS
An RTOS is limited by the hardware it runs on. Hardware that lacks memory protection can be secured, but the level of security will be limited. Memory and virtual- machine support usher in a higher level of security, though. Security policies like those in SE Linux, Green Hills Integrity, and LynuxWorks LynxSecure Embedded Hypervisor and LynxOS-SE RTOS offer much more security than a typical RTOS. Cost is higher, however, so developers need to weigh the tradeoff.
Real-time developers will have to contend with policy implementation and boundaries. Security support can take lots of time, depending on where information is coming from and going. This is where partitioned systems step in, so security can be applied at the boundaries and with non-real-time portions of the application in that space.
OS AWARE DEBUGGERS
Debugger support is a key factor to consider when looking at any operating system. This support shows up in two areas: kernel and device driver debugging, and OS awareness.
Kernel debugging is critical to the creation and support of device drivers and kernel enhancements. In many cases, a special debugger is required to handle an RTOS’s kernel. In others, the debugging understands the kernel environment as well as the application environment.
OS awareness provides insights into the operating system. Support can vary from providing information about OS service state to the ability to adjust task scheduling. Likewise, an OS-aware debugger typically lets other applications or threads run while the debugger stops another.