Why should you use a Real-Time Operating System (RTOS), anyway? In this article, Bill Lamie and Yuxin Zhou outline some of the chief advantages of implementing an RTOS in your design.
A Real-Time Operating System (RTOS) manages the processor’s resources (such as memory, I/O, and so forth) as well as the flow of execution. Without an RTOS, there are two common alternatives: a General Purpose Operating System (GPOS), or no operating system at all, which is often referred to as “bare metal.” Figure 1 shows the spectrum (from large to small) of embedded run-time solutions.

The largest embedded systems depend on a general-purpose OS, such as embedded Linux, while the smallest are bare metal. Most of the systems lie in the middle and their functionality depends on a real-time OS.
An RTOS differs from a GPOS in that it is primarily designed for resource-constrained devices. GPOSs, like Embedded Linux, are primarily designed to offer a rich set of services that require significant amounts of memory (typically more than 4MB) and processing power (typically more than 1GHz). The GPOS also requires a hardware Memory Management Unit (MMU) to support virtual addressing, and they are typically not designed for hard real-time requirements.
In sharp contrast, an RTOS is capable of operating in memory constrained environments—in some cases less than 32KB of memory. In addition, an RTOS can efficiently operate on microcontrollers (MCUs) running at less than 48MHz. More importantly, an RTOS is designed to meet hard real-time requirements, typically in the microsecond arena. Table 1 summarizes the primary differences between RTOS and GPOS environments.

As you can see, the primary differences between an RTOS and GPOS highlight differences in the kinds of systaems each operating system is best suited for. If you want a real-time control, an RTOS is the way to go.
Although GPOSs, like Embedded Linux, are not practical for many embedded MCUs, they may be good choices for larger microprocessor (MPU)-based designs that require rich OS services. For resource-constrained MCU-based designs and for MPU designs with hard real-time requirements, an RTOS is generally a better solution.
Even though an RTOS is often a good fit for resource-constrained embedded devices, not all embedded devices benefit from an RTOS. The overhead of an RTOS isn’t appropriate for some very small and single-purpose applications. These environments are typically referred to as bare metal implementations. This begs the question: When is it beneficial to use an RTOS?
The Development Challenges of a “Simple” Control Loop
To illustrate the need for an RTOS, let’s first take a look at how a developer would need to manage a real-time task using a “simple” control loop.
A typical embedded application runs periodic tasks to process input from peripherals (like sensors or communication devices) and generates output (such as sending data through communication devices or controlling an actuator). Listing 1 provides sample pseudocode of a device that performs machine learning (ML) tasks on images taken with an attached camera every 300ms.
LISTING 1
This pseudo code provides an example of a device that performs machine learning tasks on images taken with an attached camera every 300 ms.
int main(…){ while(1) { /* Get the time before the machine learning task. */ time_before = get_time(); /* Perform the task. */ perform_machine_learning(); /* Get time after the task. */ time_after = get_time(); /* Compute the amount of time spent on the task. */ elapsed_time = time_before – time_after; /* Sleep for the rest of the 300ms window. */ millisecond_sleep(300 – elapsed_time); } ……}
The timing diagram in Figure 2 illustrates how the system behaves. Note the main thread performs the ML task every 300ms. When the ML task completes, the main thread goes into idle (or low-power) mode for the remainder of the 300ms cycle.

The timing diagram illustrates how a system running the pseudocode of Listing 1 would behave. As you can see, once the task is complete, the thread idles for the rest of the 300ms cycle.
In this example, a simple bare metal control loop might be warranted, thereby eliminating the memory and processing cycles required by a more robust RTOS solution. However, it’s important to note that all the responsibility for allocating processor cycles and meeting real-time requirements falls squarely on the application code.
Most embedded systems today handle more than one periodic task. Figure 3 offers an example of a device processing two periodic tasks. Suppose our device also needs to read temperature data every 200ms in addition to the existing 300ms ML task, and let’s further assume that it is important to read the temperature at fixed intervals. That means the temperature reading has higher priority over the ML task. The system timing of our enhanced example device now looks like that in Figure 3.

The typical system requires more than one task, with each task following different requirements. Here we have an example of a temperature reading occurring every 200ms at fixed intervals.
Figure 3’s timing diagram shows that some of the events occur concurrently. Specifically, the temperature readings at 0ms, 400ms, 600ms, and 1000ms collide with the ML task execution. Understanding the real-time requirement, the developer might choose to schedule the temperature task before the ML task at 0ms and 600ms. However, this still leaves a problem for how to handle the temperature task at 400ms and 1000ms, which now must run after the ML task, since preemption is not supported.
This overlap of tasks introduces delays (or jitter) in the temperature task processing. If the real-time requirement doesn’t allow this amount of jitter, the system fails. Figure 4 illustrates system behavior when delays/jitter are used to coordinate the two tasks.

A workaround using jitter/delays can be used to ensure that tasks dovetails vs. clash over needed system resources. If the real-time requirements cannot tolerate this kind of delay, then the system fails.
Recognizing that the jitter is caused by allowing the low priority task to run to completion, one could break the ML task into multiple segments, allowing the temperature task to execute between each segment, therefore reducing jitter. This approach increases the complexity of the system, making it more difficult to maintain. However, a bigger drawback is that it doesn’t scale well as the number of periodic events increases with more complex timing requirements.
In addition to the periodic events, an embedded system also handles asynchronous events, typically generated by external interrupts. Building on the same example, let’s assume when a sensor detects a dangerous high-pressure situation, the program invokes a safety task to open a relief valve. For the sake of this example, let’s assume the system must respond to such an event within 30ms to prevent damage from the pressure build-up.
Looking at the timing diagram in Figure 4, we know that the program cannot wait for the ML process to finish before handling such critical event, as it will miss the 30ms deadline. To accomplish this requirement, one solution might be to further break down any long running tasks (such as the ML task) into smaller segments, then build a state machine that allows control to temporarily leave the running task to handle the safety task and temperature task. When the higher-priority tasks are complete, the ML task resumes using the state machine to get back to the unfinished processing.
Although this solution could meet the real-time requirements as stated, the solution has become quite complicated, possibly inefficient, difficult to validate (as various logic units intertwine), hard to maintain, and exponentially more complex as new requirements are added.
Another difficult issue to overcome with a simple control loop design is waiting for peripheral device I/O (blocking). Suppose perform_machine_learning controls an image sensor via peripheral I/O operations and must wait for the peripheral I/O device to complete its operation. Being blocked by I/O in the ML task could adversely affect the higher priority temperature and safety task—especially if the peripheral I/O operation is not deterministic.
In this situation, the application developer would likely be forced to add another state in the state machine inside of perform_machine_learning such that it wouldn’t ever wait, instead setting up a state machine such that it could find its way back to the peripheral I/O device operation to check for completion. This additional state machine processing necessarily adds complexity and overhead.
From the developer team point of view, there is another drawback to developing a complex system using a simple control loop. The project then requires every developer to have precise knowledge of the real-time processing of every component. Not only does this become exponentially more difficult as the complexity and real-time requirements increase, it also doesn’t scale well as more developers are added to the project.
How an RTOS Would Control the Tasks
Let’s now see what the same example device looks like using an RTOS. With the aid of an RTOS, the example device can be restructured using preemptive programming. The program can be partitioned into three threads:
- the ML thread
- the higher priority temperature thread
- the highest-priority pressure-sensor thread
LISTING 2
Here is the implementation of the ML thread, the highest-priority temperature thread, and the highest-priority pressure-sensor thread.
int main(…){ pthread_create(&thread_0, NULL, machine_learning_entry, NULL); pthread_create(&thread_1, NULL, temperature_entry, NULL); pthread_create(&thread_2, NULL, safety_entry, NULL);}void * machine_learning_entry(void *argument){ while(1) { wait_for_300ms_interrupt(); perform_machine_learning(); }}void * temperature_entry(void *argument){ while(1) { Wait_for_200ms_interrupt(); read_temperature(); }}void * safety_entry(void *argument){ while(1) { wait_for_pressure_sensor_interrupt(); open_relief_valve(); }}
Listing 2 demonstrates pseudocode showing the implementation of these three threads. Note that with RTOS support, the periodic tasks rely on timer interrupts to wake them up. Figure 5 shows the timing execution.

Once an RTOS is in place, executions can be interrupted to ensure that real-time tasks take place exactly when they need to. As you can see the temperature reading and the pressure gauge check interrupt the ongoing machine-learning task, which is secondary to the more critical tasks.
Using an RTOS, all the high-priority events meet their timing requirements. Complex systems can be easily broken down into multiple threads, each with its own simple control loop. The priority of each thread determines when each thread executes: when a higher priority thread is ready, it preempts the executing lower priority thread, thereby guaranteeing the higher priority deadline is met.
The beauty of an RTOS-based design is that boundless new functionality can be added without adversely affecting the real-time processing in the safety or temperature threads, assuming that they have a higher priority.
Fundamental Specs and Advantages
In summary, an RTOS is generally a better choice than a GPOS for resource-constrained devices or devices that have hard real-time requirements. At the other end of the spectrum, an RTOS is generally better than a bare metal simple control loop when the device firmware has hard real-time requirements and/or sufficient complexity (more than 32K, network connectivity, device I/O, and so on).
Even in situations where the application could get by with a simple control loop, there is still the benefit of using an RTOS to help future proof the device firmware.
Thus, RTOS:
- is generally better than GPOS for resource-constrained or hard real-time environments;
- enhances application real-time responsiveness;
- reduces complexity and makes development easier;
- more easily divides an application into more manageable pieces;
- enables more features and project developers;
- possibly reduces overhead via elimination of polling and state machines;
- achieves concurrency by enabling other processing while waiting for blocking I/O;
- enables true parallel processing in symmetric multiprocessing RTOS environments.
REFERENCES
[1] Definitions of resonance: http://hyperphysics.phy-astr.gsu.edu/hbase/electric/serres.html
[2] Wikipedia. Inductance. https://en.wikipedia.org/wiki/Inductance#Mutual_
RESOURCES
PX5 | www.px5rtos.com
PUBLISHED IN CIRCUIT CELLAR MAGAZINE • FEBRUARY 2024 #403 – Get a PDF of the issue
Sponsor this ArticleYukin Zhou is the VP of Engineering at PX5 and has been in the deeply embedded space for over 17 years—previously with Express Logic and Microsoft.
Bill Lamie is the President/CEO of PX5 and has been in the commercial RTOS space for over 30 years—first with Accelerated Technology (acquired by Siemens) and then with Express Logic (acquired by Microsoft). Bill was the sole author of Nucleus and ThreadX. Bill’s latest endeavor is PX5, where you can find his latest creation – the PX5 RTOS
