I often see developers struggling to properly write applications that use an RTOS. These struggles range from properly determining how many tasks to have in their system, how to set their priorities, coordinate task behavior, avoid common pitfalls and sometimes just making the application work! Nearly two-thirds of all embedded systems use an RTOS today and this number is only increasing with time as the timing requirements for systems become more complex. In today’s post, we are going to examine five best practices for designing RTOS based applications.
RTOS Best Practice #1 – Identify Tasks through Task Decomposition
The first best practice that developers can follow to make their RTOS application development successful is to use task decomposition to get the right number of tasks in their application. There many techniques that can be used to decompose tasks, but one approach that I like to use that works well for embedded developers is to use the outside-in approach. In this approach, developers follow seven simple steps:
- Identify and list the major system components
- Draw a high-level block diagram
- Label the system inputs
- Label the outputs
- Identify the first-tier tasks
- Determine concurrency levels and dependencies
- Identify second tier tasks (application tasks)
When following this process for an IoT sensor node such as a thermostat, one might end up with a diagram like the following:
In this case, there are six tasks for the system in general where one of those tasks oversees the application code. (Depending on the system complexity, this task could be further broken down).
RTOS Best Practice #2 – Select the Right Scheduling Algorithm to Assign Task Priorities
I’ve observed that quite a few developers working with an RTOS never take the time to decide how they are going to schedule their tasks. They often assume that the RTOS will do it for them and that their tasks will just run successfully based on providing a task priority that they select. The truth is that there are several different ways that developers can schedule tasks.
First, developers can use a tasks response time to schedule the task. In these systems, the task with the shortest response time should be assigned the highest priority.
Second, developers can use a tasks execution time to schedule the task. In these systems, the task with the shortest execution time should be assigned the highest priority.
Finally, developers can use a tasks period to schedule the task. In these systems, the task with the shortest period is the highest priority.
Only after you’ve selected a scheduling methodology can you properly set your tasks priority. (I see a lot of developers just guess).
RTOS Best Practice #3 – Use RMS to Verify a Task is Schedulable
The scheduling algorithm that is used in most embedded systems that use an RTOS is period-based scheduling which also goes by the name Rate Monotonic Scheduling. There has been a lot of research done over the years on how to use RMS to properly schedule tasks. In general, RMS comes with several assumptions that developers need to keep in mind.
First, RMS assumes that tasks are periodic and that they are also independent. This means that if you have a non-periodic task, in an analysis you would assume that it is and provide it with some periodic time. Next, RMS assumes that the RTOS uses preemptive scheduling. It also assumes that all tasks are equal and that the worst-case execution times are constant.
I’ve often found that RMS is great for performing a sanity check as to whether the RTOS application architecture I developed makes sense or if I’m going in the wrong direction. For example, I can take my assumptions about how a system with the following tasks will behave and determine if it can successfully schedule its tasks:
For a system that uses RMS, for a system with an infinite number of tasks, the CPU utilization for all those tasks must be less than 69.3%. For the above system, we can see that the total utilization is 52% which means that they should be schedulable based on the system assumptions.
RTOS Best Practice #4 – Use Synchronization and Data Flow Diagrams
After using the outside-in approach to identify all the tasks that I need in an application, I often will create a synchronization and data flow diagram. The purpose of this diagram is to:
- Map where all the data in the system is coming from
- Map how that data is moving from its source to tasks in the system
- Map how that data may be stored and accessed
- Map how that data is used to generate system outputs
Earlier I used an internet connected thermostat as an example. Below is a data flow and synchronization diagram that we might make for that application.
As you can see, this diagram not only helps us to see how the data is moving through the system but also the RTOS components that we will need in our application such as:
- Stream buffers
Without a diagram like this, a development team is bound to run into development and maintenance issues.
RTOS Best Practice #5 – Leverage the RTOS Best Practices Guide
The last best practice is simply to recognize that there are many more best practices than what we can cover in this article. I’ve put together an RTOS Best Practices Guide that I periodically update that contains best practices for common RTOS such as:
- Task Management
- Memory Management
- Performance Management
- Synchronization and Task Recommendations
- RTOS Issues and Debugging
If you’d like to learn about more RTOS best practices, you can download the RTOS Best Practice Guide by signing up for my Embedded Bytes monthly newsletter here.
RTOSs are being used more than ever before to develop real-time applications. Following these best practices can help you to avoid making mistakes that could delay product delivery and total development costs.
If you would like to learn more about how to use an RTOS when developing a product, you can check-out Jacob’s RTOS Training courses or contact Jacob to help you develop your RTOS Software Architecture.
As usual, well written and very good suggestions.
Very nicely written. It might be good to add a note regarding potential issues with task deadlock and priority inversion in the section regarding task priority assignment.
Thanks for the comment! I will probably write more about those topics in the future. Definitely important to be aware of.
I have a question about the part where you say to assume all non-periodic tasks as periodic tasks. What if my system is mostly comprised of event-driven tasks? Should I assume those periodic? I think that by doing so, the complexity of the RMS analysis would inecessarily increase. Or am I missing something?
It depends. RMA assumes that tasks are periodic. If your events only run occasionally, you need to determine if they are low enough of a cpu load, they may not need to be considered. If they do load the CPU, then you need to assume that they occur at a minimum periodic rate. (It also might turn out depending on the design that it may make no sense to use RMA!).
Thanks really good content as usual!!