Finding Solutions for Real-time Embedded Software Development
Learn the common problems and potential solutions for typical embedded real-time operating systems (RTOS) applications, along with Standardization and reusability issues and examples porting RTOS code in applications.
Embedded processors have grown into complex and powerful devices that can often fulfill various requirements in a small physical package. As applications grow ever more complex, engineers must keep pace to manage the resulting increase in software complexity. In industrial applications, this software often runs for many years (if not decades), and managing embedded applications over their entire lifecycle is no trivial task.
In practice, a few overarching issues affect all non-trivial software projects, regardless of whether they rely on a RTOS or not. Examples of such problems include managing a build system over the entire lifecycle of an application, portability considerations, logging, and a shell mechanism. Below in Figure 1, you can see an example RTOS with customizable component sets.
Figure 1. A customizable component set within an example RTOS. Image used courtesy of NXP
This article introduces common problems as well as tasks for an RTOS. It then analyzes the need for standardization and reusability across systems for embedded software development before examining the role of Zephyr OS in an example application.
Time Consuming RTOS Challenges
Nearly every non-trivial software project requires a reliable build system, regardless of whether the project contains a real-time component or not. Maintaining such a build system over the entire lifecycle of an application, which can span multiple years, is not a simple task. Seemingly minor updates and changes in included components and external libraries can quickly lead to time-consuming error hunts that occupy a developer's time.
Software and Module Updates
Without a repository management tool, developers not only have to check for updates of the main RTOS core, but they also have to hunt down every change in every single external module used in their projects. However, it's essential to keep in mind that some modules depend on (or are based on) external libraries and modules, which the developers then have to track as well. Missing updates in these sub-modules can potentially break components built on top of the modules, leading to time-consuming error searches. Managing these chains of dependencies is no trivial task, and a repository or dependency management tool saves engineers lots of time they can instead spend focusing on implementing their embedded applications.
Porting a project from one device to another can quickly become a complicated and lengthy procedure. Even if engineers decide to employ different devices from the same manufacturer, the process might involve many time-consuming re-configuration tasks. Some fixes and implementations might work on one system while they don’t function as intended when using other hardware.
The cause for such problems can be:
- Different memory layouts
- Changes in hardware addresses
- Differing hardware features
- Different driver interfaces
Take, as an example, a program that writes values to flash memory in a system. In their original design, engineers employed a microcontroller unit (MCU) that contained on-chip flash memory and a flash controller. However, due to supply shortages, the design team switched the design to a different MCU without built-in flash memory and an external flash memory module. As the software contains hardware-specific code for accessing the on-chip flash memory, the team can't easily port the application to the new MCU platform without redesigning significant parts of the codebase.
This problem can quickly lead to multiple similar code bases for different devices, which result in more severe problems further down the line—for example, when implementing bug fixes that affect all codebases. Library organization and configuration management further increase the complexity of such re-configuration tasks.
Status and Error Logging
Typically, more complex projects require some logging mechanism to output debug and status messages or a shell that lets developers and external systems interact with the implemented software. However, these facilities are not always part of RTOS, and developers have to implement them or port a previously implemented solution to their current project. Custom implementations must also ensure thread safety and, therefore, must be evaluated and tested extensively before including them in the production version of the software.
Common RTOS Solutions
In light of the problems and tasks discussed above, many conventional RTOS' offer a real-time scheduler, synchronization support, and memory management features. Below, we provide an examination of several popular options (FreeRTOS, Azure RTOS, and Zephyr OS) and their potential benefits and drawbacks.
FreeRTOS started as a simple real-time kernel offering threads, synchronization, and memory allocation mechanisms. The lightweight nature of the project made it appealing for various embedded applications. As of this article's publication, the project is maintained by Amazon. The developers focus on adding additional cloud service integrations, such as support for the Amazon IoT core and other AWS services. The MIT license ensures that FreeRTOS stays free.
Additionally, the lightweight core scheduler is easy to integrate into projects, and the OS is still among the most popular RTOS' today. However, unlike ThreadX, FreeRTOS is not designed to be used with safety-critical systems. For such systems, engineers will have to fall back on using a commercially licensed product called SafeRTOS.
Microsoft Azure RTOS, formerly known as ThreadX, is an alternative to FreeRTOS. Overall, Azure RTOS grants better hard real-time capabilities than FreeRTOS, and it also conforms to various safety-relevant standards. However, there are a few overarching problems that neither of these options manages to solve efficiently.
One problem is how both FreeRTOS and Azure OS have been acquired by large companies that shape their futures. Since Amazon and Microsoft offer proprietary cloud services, they will likely make it easy for developers to connect to their specific cloud services. However, the companies could try to make integrating a different cloud service more cumbersome for developers.
In contrast, Zephyr OS is a relatively new project in the RTOS segment that aims to solve the problem mentioned above. It introduces standardized parts that developers can utilize in several projects across various supported platforms with minimal to no reconfiguration effort. Zephyr OS is a community-governed, open-source project that offers vendor-independent solutions that engineers can use without paying licensing fees. Due to this vendor-independent and open-source nature of the project, it's unlikely that a single company dramatically determines how well Zephyr OS integrates with other products and services. Figure 2 shows a block diagram for the Zephyr OS.
Figure 2. Block diagram of Zephyr OS structure. Image used courtesy of NXP
The publicly available source code of Zephyr OS and the extensive online documentation also ensure that embedded engineers can learn all the details about Zephyr they need to make critical decisions without reverse engineering any source files. In addition, open-source projects governed by many developers often have better security implementations compared to entirely closed-source solutions. Furthermore, practically any developer and company can add support for new architectures and hardware.
Example Solution—the Zephyr Project
The Zephyr project (Figure 3) features multiple, discrete blocks that work to streamline the build process and link disparate libraries through standardized components.
Figure 3. The main features of the Zephyr project. Image used courtesy of NXP.
In general, the Zephyr build system offers engineers the freedom to choose how they want to implement specific options and which built-in facilities they’d like to use. While the SDK includes many advantageous features, most of them are entirely optional. Engineers are free to utilize them in their projects or implement features how they’ve always done it.
The built-in peripheral and driver interface is another example of this approach. A standardized application programming interface (API) allows engineers to re-use lots of code for standard communication options such as I2C and serial peripheral interface (SPI). A universal asynchronous receiver-transmitter (UART) driver ensures that the built-in logging facility works right out of the box.
Zephyr Package Manager
Zephyr’s built-in package manager—called West—pulls external packages from public or private repositories and kicks off the entire build process of an application. It is also responsible for flashing the MCU and can further generate a bill of materials (BOM).
Additionally, Zephyr keeps code that is not part of the Zephyr core in separate external repositories. These external repositories include reusable IoT application building blocks such as:
- Vendor HALs
- Filesystem implementations
- Public libraries (like OpenAMP and OpenThread)
Additionally, West can also manage other external libraries and code held in private repositories. These external components and third-party libraries have their own release schedule and CI/CD tool usage, completely independent of Zephyr. This meta-tool within Zephyr ensures that developers don't have to think about how they include external libraries in their projects. In addition, the team can focus on building their embedded application rather than tracking changes and dependencies across all external third-party and official software modules that were added to a Zephyr project. Under the hood, West uses CMake for managing the build process.
Borrowing from Linux
The Zephyr SDK borrows a few concepts from Linux, two of which are Kconfig and device trees.
Within Zephyr, Kconfig offers a simple way of linking libraries to a project without exactly having to know which source files and build macros to use. The Zephyr SDK includes a simple implementation of Linux device trees, which allows developers to document what hardware is present in the system. However, in contrast to dynamic device trees (Figure 4) in Linux, Zephyr uses them more like a data structure that describes the hardware at compile time.
Figure 4. This image compares the device trees for the two evaluation boards used in this example. The highlighted segments show the differences between the two files. The label is marked because it is required by littlefs, the filesystem used in this example. Image used courtesy of NXP
This description remains static and doesn't change during runtime.
Example Use Cases for Zephyr
Let’s take a closer look at two example use cases—each utilizing an MCU’s GPIO to control the states of some pins—to illustrate how these features come together from the vantage point of designers actually working within this space.
Porting Across MCU Platforms
In this first example, an original board that used an LPC55S69 MCU offered a sufficient number of usable GPIO pins for an industrial I/O panel application. A later iteration of the design, however, employed an S32K118 MCU (from another hardware family, with a comparable number of usable I/O pins).
This new design comprised more external components and the MCU didn’t offer enough accessible GPIO pins. Therefore, the engineers added an SPI-to-GPIO expander to compensate for the missing channels, and they needed to share as much source code between the two projects as possible.
Using Zephyr’s already-included driver (which allowed the SPI-to-GPIO converter to appear to the system as regular MCU GPIO pins), the developers didn’t have to change the source code. Instead, they only had to update the device tree for the newer board design. This lets the designers avoid needing multiple codebases, complicated adaptations to the source code, and a lengthy regression-testing and porting process. This example further highlights that engineers should rely on tried and tested simple implementations rather than quick fixes and hacks to maintain an application’s reliability and security.
Porting Across Different Packages and Pinouts
Even though Zephyr is very board-specific, developers don’t need to write a new device tree source file for each custom board of a family. In other words, developers can utilize an evaluation kit for testing an MCU they want to use in a product, such as the LPC55S69. For the prototype, they can use the LPC55S69-EVK and the DST supplied by the manufacturer—in this case, NXP. This can be shown in Figure 5.
Figure 5. Engineers only need to make minor adjustments to the Zephyr device tree structure and the pinmux.c file to port an application from an EVK to a custom board that uses the same chip in a different package. Image used courtesy of NXP.
Once the developers verify that the code works on the evaluation kit, they only need to create a custom device tree overlay (DTO) for their specific custom board. The overlay file describes the particular hardware of the custom board so that the Zephyr build system can interface it.
Pushing RTOS' to the Next Level
This article has examined several overarching problems endemic to the use of conventional embedded RTOS'. First, managing a software product over its entire lifecycle is no trivial task. The problems begin with maintaining and updating third-party and official external libraries. Developers often have to keep track of updates made to those libraries. Updating those referenced libraries always comes at a risk, as doing so potentially leads to invalid or broken dependencies and version incompatibilities.
Security concerns and potential vulnerabilities plague practically all larger software systems, and real-time operating systems are no exception. Even established protocols and products can get compromised even after many years of reliable operation. However, closed-source and proprietary software products are at greater risk, as fewer developers can inspect the code and test possible security shortcomings.
Open-source systems like Zephyr provide an accessible way for developers to ensure standardization and reusability in their designs from the ground up. Learn how to make the most of your RTOS solution with NXP’s MCUs here.
Featured image used courtesy of NXP
Industry Articles are a form of content that allows industry partners to share useful news, messages, and technology with All About Circuits readers in a way editorial content is not well suited to. All Industry Articles are subject to strict editorial guidelines with the intention of offering readers useful news, technical expertise, or stories. The viewpoints and opinions expressed in Industry Articles are those of the partner and not necessarily those of All About Circuits or its writers.