Oftentimes, when we’re using a microcontroller, we’re whipping up some very specific code focused on executing a particular task. The device is set up to execute code that does exactly what we want with minimal overhead. However, sometimes, there are scenarios where it pays to go with a somewhat heavier setup, wherein the microcontroller runs an operating system for the benefits that offers.
Federico Terraneo came to Hackaday Europe to discuss this very topic. He talks about kernel architecture, real-time operating systems, and how to best use C++ in the world of microcontrollers.
Microcontrolling
The talk begins in a helpful place. Federico starts by explaining what an operating system actually is. Basically, it’s the software that exists between the hardware and the applications that run upon it. Breaking it down into parts, an operating system typically consists of a main kernel, atop which sits things like the basic system services, libraries, and device drivers, along with utility programs necessary to maintain and work with the system. The user interface sits on top of all that, which allows the user to select and run applications and generally use the operating system to get things done.

Of course, different operating systems differ in the specifics of their architectures. Monolithic kernels, such as Linux relies on, keep a split between kernelspace and userspace. This is where where the kernel has full hardware access running on the CPU in system mode, while the applications run in user mode without such direct access. Monolithic kernels typically only run on architectures with memory management units (MMU)—think full-scale computers with proper CPUs, like your laptop or desktop. Unikernel operating systems, like FreeRTOS, are a little different, where applications and the operating system are collapsed down into a single executable binary that runs with raw hardware access. There is no abstraction, no memory protection, or anything like that, which makes the architecture easier to run on typical microcontrollers. There are also microkernels, which aim to minimize the amount of code that runs in system mode, pushing things like drivers and filesystem access into userspace. This architecture still needs an MMU, and is mostly only seen in niche uses where high security and/or attention to safety is critical.

When it comes to microcontrollers, unikernels are the most relevant architecture to think about. However, they have limitations–in stability, in security, in the fact that there is no run-time code loading or any way to easily partially upgrade the system. The fluid kernel, which Federico came to explain, aims to solve some of these issues. It hopes to offer a scalable operating system solution that works across the world of embedded computing, where sometimes microcontroller resources are limited and where memory management units seldom exist. It’s also intended to be compatible with standard APIs—think POSIX, C++ standard libraries, and all that. Federico calls it the “UNIX on a chip” concept.
The fluid kernel aims to exist at the intersection of the monolithic kernel and the unikernel. It allows hosting applications in kernelspace or in userspace as needed. A fluid kernel is also built to be POSIX compliant twice—with the same API whether you’re operating in kernelspace or userspace. The fluid kernel concept is designed around achieving process abstraction via the hardware Memory Protection Unit (MPU) common in modern 32-bit microcontrollers. It’s not quite an MMU, and can’t do all the same fancy virtual memory tricks, but it’s enough to provide a basic level of memory protection on a microcontroller platform. The fluid kernel can also become a unikernel if so desired as a compile-time option, which takes away process support while reducing code size significantly. It allows for unikernel devleopment that can be upgraded into a fluid kernel later by flipping the compile-time option the other way.
Federico does a great job of explaining the pros and cons of the fluid kernel architecture, and explores the security implications inherent in going this route. The Miosix RTOS is discussed as the practical implementation of this philosophy, and there’s even a helpful diversion into the efficient use of C++ on microcontrollers. If you’re getting serious about embedded development, or you just want to learn about a new architecture you might find useful one day, it’s a great talk to dive into on your next lunch break.

Original author here, here’s the links from the presentation:
the paper (no paywall): https://ieeexplore.ieee.org/abstract/document/11173649
the Miosix kernel website: https://miosix.org
the thermal camera example: https://github.com/fedetft/thermal_camera
the compiler repository for the last part of the presentation: https://sr.ht/~fedetft/ch32-cpp-runtime
This project and CMRX are both interesting alternatives to the more popular RTOS options out there. Will be interested to see how this progresses.
Fascinating work by Federico. Bridging the gap between rigid unikernels (like FreeRTOS) and heavy monolithic OSs using MPUs feels like the logical evolution for Cortex-M and RISC-V setups. Maintaining POSIX API parity across both kernelspace and userspace makes porting standard C++ libraries significantly cleaner. I’d love to see a benchmark comparison between Miosix’s MPU context-switching overhead versus microkernel approaches like seL4 or Zephyr’s user-mode threads
Running on microcontrollers C++ libraries (or C libraries making use of POSIX such as filesystem support) is one of the goals of the project. Skyward (https://skywarder.eu/departments/swd/), one of our long-time users, for their rockets relies on Kalman filters written using Eigen (a popular C++ library) running in kernelspace for best real-time performance.
Why the obsession with isolating parts of firmware in embedded!
It’s not a PC! In most use cases user won’t load custom application on them..
And you verify if app is coming from vendor.
32bit microcontrollers today are as powerful as PCs 30 years ago.
I think your reluctance to isolation is due to other RTOS APIs for using the MPU being overcomplicated and shifting the burden of isolation to the developer. With a fluid kernel it’s different. Your isolated process is coded just like a program in Linux, you start from a main.c (or main.cpp) and write your application there. You build it separately and deploy it to /bin. The OS does the rest.
As for why you would want to do it:
– divide et impera, breaking a complex firmware in multiple programs allows you to write simpler code
– if a process crashes, it does not crash the rest of your embedded system
– you can use privileges to limit damage if a process gets compromised/hacked
– you can download and execute new code without rebooting
Also note that in a fluid kernel all this is optional. If you want to write entire applications in kernelspace you can. The OS does what you want, not the other way around.