What this is. Opinion + Experience + Fact (25% opinion · 20% experience · 55% fact). Written in collaboration with AI — I discuss, I do not outsource.
An RTOS is built to be portable. The layer above it — your architecture — is what decides whether that portability actually pays off. Here is what that layer is, why it is the higher-leverage decision, and how to design it so it travels to the next chip with you.
Choosing an RTOS gets careful, deliberate attention. Teams compare schedulers, tick rates, memory footprints, and driver support, and eventually make a solid call. That work is real, and it matters.
There is a second decision that sits one layer above it, and in twenty-plus years of shipping firmware it is the one that has paid off the most: the shape of the application itself — how your modules talk, who owns each piece of state, what happens on a fault. The RTOS leaves that to you. Which means it is fully in your control, and it is the part that carries your product from one chip to the next.
Let me walk through why the RTOS is the portable layer, why the architecture above it is where the leverage lives, and how to design that layer so a chip change becomes a swap instead of a rewrite.
1. The RTOS is the portable layer
An RTOS does one well-defined job: it manages concurrency and timing. It gives you tasks, semaphores, queues, and a scheduler, and it does that job well.
Those primitives are near-identical across the field. FreeRTOS, Zephyr, ThreadX, and even a hand-rolled bare-metal loop expose the same handful of concepts — a way to run concurrent work and coordinate it in time. That similarity is exactly why an RTOS moves to new silicon so cleanly: the port is mostly a board-support layer and a recompile. The scheduler you spent weeks choosing was always designed to be swappable.
▸ First principle. The RTOS is portable by design — so that part of the problem is already solved.
2. The architecture is the layer that carries your product
Your architecture is the set of decisions the RTOS does not make for you: how a sensor module hands data to a comms module, which module owns a given piece of state, what the system does when something faults. These are the choices that define how the firmware actually behaves as it grows.
Because the RTOS leaves them open, they are yours to make explicitly — named modules, typed interfaces between them, one clear home for each piece of state, events as the way blocks talk. Design them that way and two good things follow: the next engineer can read the system without a guided tour, and the design lifts off the specific board it was born on. The architecture stops being wired to one BSP and starts being an asset you carry forward.
▸ First principle. An explicit architecture is an asset you carry forward; an implicit one is rebuilt each time.
3. The pattern that makes an architecture portable
The good news is that the shape of a portable architecture is well understood, and it has held up for decades. The essentials are simple: modules that own their own state and share nothing directly, events as the way they communicate, non-blocking run-to-completion work inside each block, and observability designed in from the first line rather than added after the first field issue.
This is not a new invention, and it would be dishonest to present it as one. Miro Samek has advanced this model as Active Objects since 2005, in the QP framework and his book Practical UML Statecharts in C/C++; his talk Modern Embedded Software Goes Beyond the RTOS makes the case directly. Parallel takes exist too: AUTOSAR's Classic platform standardized a heavy version of it for automotive, and newer stacks like Infuse-IoT solve it inside a specific niche. Different builders, arriving at the same shape independently — that convergence is the signal that it is the right layer.
▸ First principle. A portable architecture is a known shape: shared-nothing blocks, events between them, observability built in.
4. Made reusable, and open to adopt
For twenty-plus years I built a version of this foundation on product after product. Rather than rebuild it each time, I distilled it into a clean, RTOS-agnostic form and open-sourced it as EmbedIQ — so a team can adopt the foundation and spend its time on the product instead. Its Functional Blocks are Active Objects in the shared-nothing sense; the architecture is designed to sit above any RTOS, so it carries over even when the silicon underneath changes.
Let me be precise about the boundary, because precision matters: EmbedIQ is one way to adopt this pattern, not the only one. If you already run QP, an in-house Active-Object layer, or a Zephyr-native stack that gives you the same guarantees, you are on the same road. The point is not the brand — it is that the layer above the RTOS is worth designing on purpose and writing down, so the next engineer and the next chip both inherit it.
▸ First principle. The fastest architecture to adopt is one already written down, tested, and shared.
5. Architecture-first, chip-second
Here is the practical version — four decisions to make explicitly, before the RTOS comparison spreadsheet, so the design travels with you:
| Decision | Make it explicit as… | Why it carries over |
|---|---|---|
| Module boundaries | Named blocks, each owning its own responsibility | Boundaries are logical, not tied to a BSP |
| Interfaces | Typed contracts + events between blocks — no shared globals | The contract holds on any MCU |
| State ownership | One clear home per piece of state | No hidden coupling to rediscover after a port |
| Observability | Logging and telemetry hooks designed in from line one | You can see the system on any board, day one |
Answer those four and the RTOS choice becomes almost mechanical: pick whichever scheduler supports the architecture you have already designed. FreeRTOS, Zephyr, ThreadX, or bare-metal all work fine underneath a layer that was built to be explicit.
▸ First principle. Design the layer above the RTOS with intent, and the next chip becomes a swap instead of a rewrite.
The RTOS was always going to survive the move to new silicon — that is what it was built for. The real leverage is one layer up, in the architecture you design on purpose and write down. Do that, and the next chip is a swap, the next engineer is productive on day one, and the product you built carries forward intact.
Sources
Active Objects / QP framework — Miro Samek, Practical UML Statecharts in C/C++ (publicly developed since 2005), and the talk Modern Embedded Software Goes Beyond the RTOS (Embedded Online Conference, 2020).
AUTOSAR Classic Platform — application architecture and the Complex Device Drivers escape hatch.
Infuse-IoT (Embeint) — a Zephyr-based IoT application layer solving the same "missing layer" in a specific niche.
EmbedIQ — the open-source, RTOS-agnostic application layer described here; the code and links live on my profile.
FAQ
Is an RTOS the same as a software architecture?
No. An RTOS manages concurrency and timing — tasks, queues, and a scheduler. A software architecture defines how your modules communicate, who owns each piece of state, and what happens on a fault. The RTOS leaves those choices to you, which is why two teams on the same RTOS can end up with very different systems.
Why does architecture matter more than the RTOS choice?
Because the RTOS is portable by design and swaps cleanly between chips, while the architecture is what actually shapes how the codebase grows and whether it survives a move to new silicon. The higher-leverage decision is the one the RTOS does not make for you.
What makes a firmware architecture portable across MCUs?
A few well-understood choices: modules that own their state and share nothing directly, events as the way they communicate, non-blocking run-to-completion work inside each block, and observability built in from the start. Kept explicit and written down, that shape lifts off any specific board.
What are Active Objects?
Active Objects are an event-driven, shared-nothing concurrency model — each object owns its state, communicates by events, and runs to completion without blocking. Miro Samek has advanced the model in the QP framework since 2005. EmbedIQ's Functional Blocks are Active Objects in this sense.
What is EmbedIQ?
EmbedIQ is an open-source, RTOS-agnostic application layer for firmware — the layer above the RTOS. It gives teams shared-nothing blocks, typed events, single-owner state, and built-in observability so the architecture is explicit and portable, and can be adopted rather than rebuilt on each product.
The short version of this is on my LinkedIn. The framework is open source — it's called EmbedIQ — and the architecture, the code, and the links live on my profile.
Stay in the loop
New essays on embedded systems, firmware quality, and engineering craft. No noise.
Discussion
No comments yet. Be the first to share your thoughts.
Leave a comment