There are many different trade-offs when using hardware and software to achieve real-time behavior in a system. The following sections discuss the various ways hardware and software are deployed in real-time systems you might encounter. Also, note that it is possible to have combinations of the following systems working together as subsystems. These different subsystems can occur at the product, board, or even chip level (this approach is discussed in Chapter 15, Multi-Processor and Multi-Core Systems).
Hardware-only
The original real-time system is hardware-only, and it is often the best solution when there are extremely tight tolerances and/or fast timing requirements. It can be implemented with discrete digital logic, analog components, programmable logic, or an application-specific integrated component (ASIC). Other options for the hardware include programmable logic devices (PLDs), complex programmable logic devices (CPLDs), and field-programmable gate arrays (FPGAs). Hardware-based real-time systems can cover anything from analog filters, closed-loop control, and simple state machines to complex video codecs. When implemented with power saving in mind, ASICs can be made to consume less power than an MCU-based solution. In general, hardware has the advantage of performing operations in parallel and instantly (this is, of course, an over-simplification), as opposed to a single-core MCU, which only gives the illusion of parallel processing.
The downsides for real-time hardware development generally include the following:
- The inflexibility of non-programmable devices
- The expertise required is generally less commonly available than software/firmware developers
- The cost of full-featured programmable devices (for example, large FPGAs)
- The high cost of developing a custom ASIC
Bare-metal firmware
Bare-metal firmware is considered (for our purposes) to be any firmware that doesn’t use a preexisting RTOS. Instead, the designer must write firmware for the features that an RTOS would typically provide. Some engineers take this a step further, arguing that true bare-metal firmware can’t use any preexisting libraries (such as vendor-supplied hardware-abstraction libraries)—there is some merit to this view as well. A bare-metal implementation has the advantage that the user’s code has total control of all aspects of the hardware. The only way for the main-loop code execution to be interrupted is if an interrupt occurs, giving control over to an ISR. In this case, the only way for anything else to take control of the CPU is for the existing ISR to finish or for a higher-priority interrupt to occur.
Bare-metal firmware implementations excel when there is a small number of relatively simple tasks to perform—or one monolithic task. If the firmware is kept focused and best practices are followed, deterministic performance is generally easy to measure and guarantee due to the relatively small number of interactions between ISRs (or in some cases, a lack of ISRs).
In some extreme cases for heavily loaded MCUs (or MCUs that are highly constrained in ROM/RAM), bare-metal is the only option. Back when bare-metal development first started, the MCUs were highly constrained, and programming bare-metal was a necessity. However, as the silicon industry has matured, more and more memory fits inside a tiny MCU. There are now many devices available, with plenty of memory, and at a low cost, which makes it unnecessary to limit yourself to bare-metal programming. Often, it makes sense to use an RTOS, which we will explore next.
RTOS-based firmware
As bare-metal implementations become more elaborate when dealing with events asynchronously, they start to overlap with the functionality provided by an RTOS. An important consideration to keep in mind is that by using an RTOS—rather than attempting to roll your own thread-safe system—you automatically benefit from all of the testing the RTOS provider has put in.
You’ll also have the opportunity to use code that has the power of hindsight behind it—many of the RTOSes available today have been around for several years. The authors have adapted and added functionality the entire time to make the RTOSes robust and flexible for different applications. Using one of these mature RTOSes provides a high-quality starting point for your application.
RTOS-based firmware differs from bare-metal firmware in that it runs on top of a scheduling kernel on an MCU. For RTOS-based firmware, the term firmware signifies a type of RTOS that is very limited in regard to its features – typically it operates out of limited flash memory (e.g., 64KB, 256KB, etc.), and it provides a scheduling kernel for use by tasks. It is like bare-metal firmware in that the programmer often has to be very aware of the hardware peripherals and registers offered by the MCU. However, the RTOS often abstracts away some of those details and also provides timing mechanisms that the application firmware can take advantage of.
With an RTOS, having a scheduler and some RTOS primitives allows tasks to operate under the illusion that they have the processor to themselves (discussed in detail in Chapter 6, Understanding RTOS Tasks). Using an RTOS enables the system to remain responsive to the most important events while performing other complex tasks in the background.
There are a few downsides to having several tasks running on the processor. Sharing data between tasks requires the proper use of synchronization methods to avoid problems such as deadlock and priority inversion. Later in the book, we will discuss some of these synchronization methods, although it adds complexity to the code. Interrupts will generally use task signaling to take care of the interrupt as quickly as possible and defer as much processing to a task as possible. If handled properly, this solution is excellent for keeping complex systems responsive, despite many complex interactions.
RTOS-based software
RTOS-based software differs from RTOS-based firmware in that this software runs on a more full-featured RTOS. The features are similar to those found in a general-purpose OS and may include things such as hardware drivers, bootloaders, applications, filesystems, etc. In fact, some general-purpose OSes offer real-time variants (e.g., RTLinux).
These RTOSes typically require significantly more memory (e.g., 1 MB or more) and processors with specific features. For example, the RTOS may make use of the processor’s memory management unit (MMU). Also, the RTOS may provide features such as application loading and a defined driver framework. The software applications for an RTOS can be highly complex, requiring many different interactions between various internal and external systems. The advantage of using this type of RTOS is all of the capabilities that come along with it—both hardware and software.
In addition, on the hardware side, a more complex processor is often used to provide additional event-handling capacity (bandwidth) to the application. For example, there may be multiple CPU cores available running at higher clock rates. There can be GBs of RAM and persistent memory available. Adding peripheral hardware can be as simple as the addition of a card (provided there are pre-existing drivers).
On the software side, there is a plethora of open-source and vendor-proprietary solutions for networking stacks, UI development, file handling, and so on. The availability of these solutions means that certain features of the end product will require less development time to implement. Underneath all of these capabilities and options, the kernel is still implemented in such a way that the critical tasks won’t be blocked for an indefinite period of time, which is possible with a general-purpose OS. Because of this, getting deterministic performance is still within reach, just like with RTOS-based firmware.
Carefully crafted OS software
Similar to RTOS-based software, a standard OS has all of the libraries and features a developer could ask for. What’s missing, however, is a strict focus on meeting timing requirements. Generally speaking, systems implemented with a traditional OS are going to have much less deterministic behavior (and none that can be truly counted on in a safety-critical situation). For example, how many times have you sat at your PC and had a program indicate “not responding”? Now imagine if your PC also happened to be controlling the timer on your toaster – you might end up with some burnt toast!
If there is a lax real-time requirement without catastrophic consequences if a wishy-washy deadline isn’t met on time, a standard OS can be made to work, as long as care is taken in choosing what software stacks are running and their resource use is kept in check. The Linux kernel with PREEMPT_RT
patches is a good example of this type of RTOS.
So, now that several of the options for achieving a real-time system with hardware and software have been laid out, it’s time to define exactly what we mean when we refer to an RTOS, specifically an MCU-based RTOS.