Follow some simple steps and you’ll make the right RTOS choice
An RTOS consists of a kernel plus middleware. Middleware is needed to do certain functions efficiently and reliably. If it meets those requirements, it’s of little further consequence. The kernel, however, is central to the embedded design. It strongly influences how the design is done and it’s what embedded programmers interact with the most. Hence, it has become industry practice to refer to “kernels” as “RTOSs.” I adhere to that practice herein.
One problem in picking an RTOS is that it’s often done at the start of a project, before the system design has been completed or even started. At that point, there’s little understanding of what RTOS features are needed. Given the lack of reasons to do otherwise, the tendency of many programmers is to pick a simple, low-cost or free RTOS, then live with its limitations. This obviously is a non-optimum approach, and it can cause wasted man-months of effort and late delivery of the final system.
I’m offering a different approach to picking an RTOS, one that my that have many features you think you don’t need. That requires learning how these features work, and then determining if they are useful for your application. How else can you decide what features are useful to you, unless you actually try them? Let’s begin by dispelling some common misbeliefs concerning simple RTOSs. Then we can discuss how to simultaneously create a system design and pick the RTOS for it, without getting locked into the wrong RTOS, prematurely.
Generally, the main RAM use for an RTOS is for stacks used by tasks. If the RTOS doesn’t provide a system stack for interrupts, then each task’s stack must be large enough to handle the total ISR stack requirement. If interrupt nesting is permitted, worst-case ISR stack loading can add hundreds of bytes to each task’s stack. If there are around 20 tasks, this could easily add up to an additional 5 to 10 KB of RAM. Simple RTOSs usually have this problem. Some RTOSs even allow sharing stacks between tasks.
Modern linkers leave out unused RTOS functions. Hence, they don’t contribute to the ROM footprint. What about missing (i.e., needed, but not there) RTOS features? They must be created in the application code. Not only does this cost time and money, but these features are likely to be implemented less efficiently than in an RTOS, due to schedule pressure, and may even be implemented more than once by different programmers or may be unreliable rush jobs. As a consequence, a simple RTOS may actually require more RAM and ROM than an advanced RTOS and it may result in more problems for the project.
Without question, less code runs faster. Hence services for RTOSs that perform error checking and offer diverse features will be slower than the stripped-down, equivalent services of a simple RTOS. However, how much performance do you need from your RTOS? RTOS calls and task switches are infrequent compared to other processing performed in an embedded system. A well-written RTOS (not GPOS) can achieve 50,000 task switches per second on a “low-end” 50 MHz Cortex-M3 processor. How many do you need? With the tremendous advance in processor performance in the past decade, the important metric has shifted from raw RTOS performance to the utility of the services it offers and how much help it provides to develop a reliable system quickly.
Debug features and error management
Everyone knows that more time is spent debugging code than creating it and that projects are most often late due to stubborn bugs. Yet what do we do to limit this future pain? Simple RTOSs typically provide no more help than does the C library – zilch. If you want sanity and a good family life, this is something you should pay attention to.
Picking an RTOS is a three dimensional problem: size, performance, and features. Together, these characteristics define the embedded space, where your system will reside. On top of these, there are the license and price dimensions, but we will leave these to the business types. Here we will focus on the technical space where we all feel more comfortable.
A single RTOS can’t cover all of the embedded space well. Hence, you must determine where your application lives in order to make a good fit. The best way to do this is to start designing your application from the top down. In another post, Christmas Tree RTOS, I likened this process to decorating a Christmas tree.
Picking a candidate
To start the selection process, review RTOS vendor literature to find a candidate with plenty of interesting features. Then, if it offers an evaluation kit for the processor you favor, you’re ready to begin. Download the eval kit and get it running on the eval board for your processor. Hopefully, this will be a quick process. If not, try another RTOS candidate.
The best way to start is to create a block diagram of the proposed system. Next, relate blocks to threads (a thread is an ISR, a task, or another path of execution). This involves grouping or splitting blocks. The general objectives are to put asynchronous operations into separate threads, to put interrupt-driven operations into ISRs, and to put high-level operations into tasks. Intermediate operations may be put into callback functions or link service routines (LSRs), which are a more structured form of callback functions.
As you progress, the block diagram will quickly become unwieldy. When that happens, it’s a good time to go to code. The objective here is to create skeleton ISRs, tasks, and LSRs. This can be done with any RTOS. Simply use RTOS services to create and control RTOS objects. As you gain insight into what RTOS services you need, you may want to compare how each RTOS candidate does particular jobs and continue with the one you like best.
Put in delay stubs to simulate actual code and its estimated delays. Also put in minimal code to simulate loading data into messages and sending them to other tasks, as well as simulating other operations that your system needs to perform. Use semaphores, mutexes, etc., to achieve order. The delays and simulations need not be accurate to start.
As you progress, build your system frequently and use a debugger to learn how it works. You’ll often be surprised! Note: Stepping is useful, but you need to set breakpoints in many strategic places to see what’s really happening. Otherwise, during a step, an interrupt can occur, multiple tasks can run, and you’ll be wondering how this or that magically happened. A multitasking system tends to take on a life of its own, which may not be the one you expected. Surprises may cause you to drill down deeper into your candidate RTOS to understand better how it works or to find the flaws in its implementation. You may need to consider alternative approaches offered by a different RTOS. It’s better to face system design surprises early than after a lot of code has been written and painfully debugged.
This is also a good time to start measuring critical times for your application such as interrupt latencies, task switching times, and overhead, as well as power usage, and anything else that is important for this system. Of course, to get accurate numbers, you’ll need to begin refining your delays and simulations. A nice by-product is that the estimated delays and RTOS-induced structure put boundaries upon code yet to be written. Delays can be used to determine where optimized code is required, before it’s implemented. If the final code is within its boundaries, it will fit into the system. This gives security to everyone on the project.
What’s good about this?
The beauty of this approach is that early-on you have something that actually runs, others can see it run, and they can run it, too. In addition, you’re getting feedback to figure out what structures work best for what you need to do and in making measurements to confirm your decisions. Yet you have actually put only a little work into code – it’s mostly thinking and study that can be carried over to a different RTOS or to a different system design, if necessary. The focus is more on structure than on function.
Many projects paint themselves into a corner where changing the RTOS or system design is no longer a viable option; too much code has been written to permit changing direction. For better or worse, the project is stuck with whatever it picked. This can be bad news for the bean counters as well as everyone else.
Meanwhile, continuing down the yellow-brick road, you will be learning how your selected RTOS works, what features are useful to you, how to use them, and how its debug and kernel-aware features help you to solve problems. Yet, you aren’t too far from shore to wade back to dry land and pick a different RTOS or system design, if you need to.
Using this approach, you’ll have more confidence that you’ve picked the right RTOS for your project and also that you will have laid a good foundation for the hard programming work that follows. For more information on this design methodology see the smx User’s Guide.