Modern Embedded Systems

2025 04 01 head

Embedded software engineers that have a proper understanding of the corresponding hardware are hard to find. When application developers were moving to newer languages like Java and C++, embedded programmers were still moving from assembly language to C.

The reason for slower technology adoption is having a lesser number of embedded programmers, who need to thoroughly understand the hardware platform on which their code runs.

The key driver of RTOS adoption is application complexity.

An RTOS will often be used when there are more interrupt sources, more functions, and more standard communications interfaces that need to be supported.

Why RTOS?

The key advantages of a realtime operating system are:

  • Asynchronous message-based System. Actor pattern is implemented with this mechanism.

  • Preemptive multitasking design paradigm

  • Pre-tested and pre-integrated communications stacks and drivers

  • Application portability [1]

  • More efficient use of CPU resources

Simple loop-based run-times typically do a lot of polling to check if interrupts have occurred. Response-time is often abysmal because the interrupt delivered data is not processed accordingly to expected priority. The received data is processed in the next loop iteration of the main scheduler [2].

As a result, a great deal of processor time is occupied doing nothing. Because multitasking RTOS-based applications are interrupt-driven, it is possible to largely eliminate polling from the application. This frees up processor resources for useful work and enables power-saving modes to be invoked during idle periods.

Adversaries of RTOS feud that RTOS are too expensive to deploy in devices.

Below some empirical data of the freeRTOS realtime operating system.

The memory usage is:

Item Bytes Used

Scheduler Itself

236 bytes (can easily be reduced by using smaller data types).

For each queue you create

add 76 bytes + queue storage area (see FAQ Why do queues use that much RAM?)

For each task you create

add 64 bytes (includes four characters for the task name) + the task stack size.

The RTOS kernel itself required about 5 to 10 KBytes of ROM space. The boot cost is the area of a few milliseconds. A small set of static variables will be initialized in this phase.

The cost of a context switch depends on the port, compiler, and configuration. A context switch time of 84 CPU cycles was obtained under the following test conditions:

  • FreeRTOS ARM Cortex-M3 port for the Keil compiler

  • Stack overflow checking turned off

  • Trace features turned off

  • Run-time stats feature turned off

  • Compiler set to optimize for speed

  • configUSE_PORT_OPTIMISED_TASK_SELECTION set to 1 in FreeRTOSConfig.h

Why C++?

With the continued improvements in the language, most C++ features have no impact on code size or on speed [1, 2, 3, 4]. Others have a small impact that is generally worth paying for. To use C++ effectively in embedded systems, you need to be aware of what is going on at the machine code level, just as in C. Armed with that knowledge, the embedded systems programmer can produce code that is smaller, faster and safer than is possible without C++.

One property of C++ is so obvious that it is often overlooked. This property is that C++ is almost exactly a superset of C. If you write a code fragment in the C subset, the compiler will usually act like a C compiler. The machine code generated will be what you would get from a C compiler.

Because of this simple fact, anything that can be done in C can also be done in C++. Existing C code can typically be re-compiled as C++ with about the same amount of difficulty that adopting a new C compiler entails. This also means that migrating to C++ can be done gradually, starting with C and working in new language features at your own pace. Although this is not the best way to reap the benefits of object-oriented design, it minimizes short-term risk and provides a basis for iterative changes.

Many C++ features are strictly front-end issues. [3]. They have no effect on the code generation [4]. The benefits conferred by these features are therefore free of cost at runtime.

Default arguments to functions are an example of a cost-free front end feature. The compiler inserts default arguments to a function call where the source specifies none.

A less obvious front end feature is function name overloading. Namespaces provide additional scopes and the same name can be used in multiple contexts.

Common myths about C++

  • C++ is slow.

  • C++ produces bloated machine code. C++ code is at least as efficient as similar C code. C++ compiler inlines per default all concrete class methods.

  • Objects are large.

  • Virtual functions are slow.

  • C++ binary files are not ROMable.

  • Class libraries make large binaries.

  • Abstraction leads to inefficiency.

We are aware of two factual arguments why you should not use C++ in your project.

  1. No C++ toolchain is available for your target platform. ARM, x86_64 and RISC V platforms have multiple C++ toolchains.

  2. Your developers are not fluent with object-oriented approaches and the C++ language. Your organization is also not willing to invest into training and coaching.

These are clear reasons why your development group must stay with C. Beware that multiple organizations including the Linux kernel developers are, albeit slowly, moving away from C.

References

[1] B. Stroustrup, Tour of C++, Third. Pearson Education, Limited Edition, 2021, p. 320 [Online]. Available: https://www.amazon.com/dp/B0B8S35JWV

[2] S. Meyers, Effective Modern C: 42 Specific Ways to Improve Your Use of C11 and C++14. O’Reilly Media, p. 334 [Online]. Available: https://www.amazon.com/dp/B00PGCMGDQ

[3] R. Grimm, C++ Core Guidelines. Pearson Education, Limited, 2021 [Online]. Available: https://www.amazon.com/dp/B09NSGQ4JK

[4] J. Davidson and K. Gregory, Beautiful C++. Pearson Education, Limited, 2021978-0136875673, p. 352 [Online]. Available: https://www.amazon.com/dp/B09HTH1X38


1. A specific RTOS provides support for a wide range of hardware platforms. The effort to move the application to another supported platform is low.
2. You can try to slice the main routine in smaller increment. The approach is hardcoded and must be changed when timing constraints change.
3. I often hear complaints about the extensions for threads and concurrency. But critics never told me how we should support modern processors with multiple cores using another approach. I agree that the support for concurrency e.g. message queues could be better in the current C++ 20 and 23 library. And the current features still solve most of the concurrency problems encountered in regular and embedded applications.
4. I agree that templates and exceptions have an impact on the generated code. And you have to acknowledge that you can write complex C++ programs without using such constructs.