And in regulated hardware, hidden complexity is the most dangerous kind.

Most hardware programmes reach a point where the firmware team gets pulled into a conversation that starts like this: “The software is the simple part. We just need the hardware to work first.”

It’s a reasonable-sounding instinct. In early development, firmware can often be scaffolded quickly enough that it doesn’t feel like the long pole. Hardware has lead times, tolerances, regulatory implications. Firmware seems malleable — something you iterate on until it works.

That assumption is expensive. It is how teams end up discovering concurrency bugs at verification. It is how architecture decisions made implicitly in month two become technical debt that takes months to untangle in month fourteen. And in regulated products — medical devices, life science diagnostics, defence systems, industrial platforms — where software process documentation, traceability, and verification evidence are not optional, the cost of a weak firmware architecture is not just schedule. It can compromise the evidence base needed for regulatory submission, clearance, certification, and post-market change control.

The deeper problem is not that firmware is difficult. It is that one of the most common development paradigms — threading models built on traditional real-time operating systems — allows teams to write code that appears to work while hiding structural fragility underneath.

What is firmware architecture in medical devices? Firmware architecture for medical devices defines how embedded software is structured across subsystems, how concurrent behaviour is managed, and how system state is modelled and traced to requirements. In regulated products where IEC 62304 defines the software development lifecycle, architecture decisions directly determine what verification evidence can be generated, how requirements trace to code, and how much post-market design changes cost. It is a programme-level decision with regulatory consequences — not a technical implementation detail.

The RTOS Illusion

Traditional RTOS-based firmware development offers a seductive model. Threads are organized around functions or peripherals. Within a thread, you write sequential, imperative code. When you need to wait for something — an I²C transfer to complete, a sensor to respond, a mechanical operation to finish — you call a blocking function. The RTOS suspends your thread, hands execution to something else, and resumes you when the result arrives.

From the programmer’s perspective, it feels clean. The code is readable. The logic is linear. Blocking functions read almost like synchronous pseudocode.

But the RTOS isn’t eliminating the concurrent behaviour. It is abstracting it. Underneath the surface, multiple threads are executing, sharing resources, waiting on semaphores, and competing for access to buses and peripherals. The complexity is real. The RTOS just makes it invisible.

The invisibility is the problem.

Race conditions in RTOS-based systems are among the most notoriously difficult bugs to find and eliminate. Priority inversion — where a lower-priority thread holding a resource ends up blocking a higher-priority thread that needs it — can stall a system in ways that don’t surface during normal testing. Deadlocks can emerge from patterns that look perfectly reasonable in isolation. The latent bug exists in the code for weeks or months before a specific sequence of events in a specific operating condition exposes it.

In a consumer product, a latent concurrency bug is a quality problem. In a regulated medical device or a diagnostic instrument, it is a design control failure. Your verification plan didn’t catch it because the failure mode wasn’t explicitly modelled. Your design history file doesn’t explain it because you didn’t know it existed.

The traditional RTOS model doesn’t give you complexity for free. It loans it to you with interest, and the bill arrives at the worst possible time.

Making Complexity Visible

None of this means RTOS-based systems are inherently poor architectures. Well-designed thread-based systems can be robust. The issue is that the model makes it easy for architectural coupling and concurrency risk to remain implicit — unless the team deliberately exposes and governs it.

There is an alternative architectural pattern that inverts this dynamic. Rather than hiding concurrent behaviour behind threads and blocking primitives, it makes that behaviour explicit in the structure of the code itself. This is what systems design looks like when applied seriously to the firmware layer — not just defining subsystem interfaces at the hardware boundary, but governing how state, events, and concurrency are modelled across the entire embedded stack.

One mature embedded implementation of this pattern is Quantum Leaps’ QP framework, which organises firmware around active objects: independent entities that each run in their own execution context, and that communicate through asynchronous events. At the application layer, active objects communicate through asynchronous events rather than shared state, mutexes, semaphores, or blocking calls — the patterns that make thread-based designs difficult to reason about and harder to verify.

Critical application behaviours are expressed as state machines. Every state is explicitly defined. Every transition is explicit. Every asynchronous operation — waiting for a sensor response, waiting for a mechanical operation to complete, waiting for a bus acknowledgement — is a state that appears in the model, not a hidden pause buried inside a function call.

This has an important consequence: the complexity of the system is no longer hidden in the runtime behaviour of concurrent threads. It is visible in the architecture.

On a recent point-of-care blood analyzer development programme — a cartridge-based diagnostic instrument with multiple precision electromechanical subsystems, an optical measurement chain, and a real-time control system — Inertia’s firmware team used the QP framework to structure the entire application layer. The director object coordinated high-level instrument workflow. Each major subsystem — cartridge handling, sensor management, optical measurement, display control, communications — was its own active object, with a clean interface and an explicitly modelled state machine.

The cartridge-cinching subsystem alone had a meaningful hierarchy of states. At the top level, the instrument workflow needed to know one thing: is a cartridge ready? But beneath that, the cinching subsystem managed a sequence of motor actuation, mechanical position sensing, cartridge identification, and error handling, each with its own internal states and transitions. The parent state machine didn’t need to know any of that. It received a single event when the subsystem was ready — or a different event if it wasn’t, with enough information to inform the user and decide how to proceed.

That clean interface was only possible because the internal complexity of the subsystem was explicitly modelled, not hidden inside a thread that the rest of the system had to trust.

Mitch English, Inertia’s Manager of Mechatronic Systems, discussed this programme in depth on the Agile Embedded Podcast — including the QP framework adoption, the architectural decisions made along the way, and what the team would approach differently on the next programme.

IEC 62304 and the Verification Dividend

The downstream effects of an explicit, event-driven architecture are not limited to code quality. They show up directly in verification and validation — and for teams building under IEC 62304-aligned software development processes, this is where architecture decisions made months earlier either pay forward or come due.

When firmware behaviour is expressed as state machines, it can be inspected, reviewed, and verified against requirements at an architectural level. You can show a customer — or a regulatory auditor — a state diagram that represents actual system behaviour, not an abstraction of it. You can trace requirements to specific states and transitions. You can demonstrate that every defined input to a state is handled explicitly, and that every unhandled event fails in a defined way.

For medical-device software developed under an IEC 62304-aligned process, that traceability is not merely a documentation exercise. It is a design input. Unit tests for active objects can be written on the host against the state machine logic, without hardware in the loop, because the application behaviour is decoupled from the hardware layer by design. Those tests generate the software unit verification records that the quality system requires. The architecture makes verification evidence easier to produce, inspect, and maintain.

In practice, this matters in ways teams don’t always anticipate until they’re deep in a verification campaign. When a software unit test catches a state transition that doesn’t match the requirement, fixing it is a contained change. When a concurrency bug surfaces during system-level testing in an RTOS-based architecture — when you don’t know which thread is involved, which shared resource is in contention, or under what exact sequence of events the problem reproduces — you’re not fixing a unit. You’re re-examining architectural assumptions.

One honest post-mortem from the QP programme is worth naming here. Early in the work, the team made a structural choice to use direct posting between active objects in a number of places rather than the publish-subscribe model the framework supports. The reasoning was intuitive: if the director depends on the cartridge subsystem, why not post directly? The coupling is logical. It exists whether you acknowledge it or not.

What that decision produced was unit tests that had to mock the specific partner object in every test scenario. As the system grew and the number of inter-object interactions increased, the test setup overhead grew with it. In retrospect, using the pub-sub model from the beginning — where objects publish events to a kernel without knowing who is subscribing — would have kept the test surface clean. It would also have made adding a system-level event logger trivially easy instead of requiring a structural change. It was premature optimization in the wrong direction, and the team recognized it.

That kind of post-mortem is only possible when the architecture is explicit enough to reason about. In a complex RTOS system, it is often not obvious which coupling decisions produced which test complexity. You can see the symptoms. The root cause is buried in the threading model.

Why This Matters More in Regulated Domains

The stakes are asymmetric in regulated hardware in a way that makes firmware architecture decisions consequential far beyond their immediate cost.

In medical devices and life science diagnostics, the software is part of the regulatory submission. IEC 62304 software classes determine the rigour required for each unit. Risk controls tied to software functions must be verified and traced. Design changes post-clearance trigger regulatory processes. A firmware architecture that produces clean, traceable, testable software units doesn’t just reduce development cost — it reduces the cost and risk of every subsequent change, every post-market modification, every next-generation derivative.

In defence applications, where system reliability under adverse conditions is a hard requirement and software documentation must support through-life support and modification, explainability of system behaviour at the architectural level is not a preference. It is a programme requirement.

In industrial robotics and automation, where complex multi-axis coordination and safety-critical control loops must be demonstrably correct, the ability to reason about system state at any point in time — not as an after-the-fact debug exercise, but as a first-order design property — is what separates architectures that scale from architectures that become intractable at the edges of their operating envelope.

The argument for event-driven, actor-model firmware architecture is not that it eliminates complexity. Complex systems are complex. The argument is that it puts the complexity where you can see it, reason about it, test against it, and trace it to requirements. For products that have to be verified, cleared, and trusted in real operating environments, the difference between visible complexity and hidden complexity is not academic.

The Architectural Decision Is a Programme Decision

If there is a single practical implication here, it is this: firmware architecture is not a decision that should be deferred until the firmware team is ready to start writing code. It is a programme-level decision that should be made during systems design, when the consequences are still visible and the options are still open.

That decision determines how the software development plan is structured. It determines what unit test strategy is feasible. It determines how requirements trace to code. It determines how design changes propagate through the system. It determines what the verification evidence looks like and how much it costs to generate.

Made late, or made implicitly by defaulting to the path of least resistance, it also determines how much of the development budget goes into untangling problems that a different architectural choice would have prevented.

Firmware complexity doesn’t disappear. In a traditional RTOS-based system, it is loaned to you at favourable early terms. In event-driven, explicitly modelled architectures, it appears up front — in the state diagrams, the event tables, the active object interfaces. That upfront visibility feels like more work. It is actually less, because you are doing the work once, in the place where it can be governed, instead of repeatedly, in the places where it surfaces as failure.

Inertia Group is a Toronto-based product strategy, engineering, and manufacturing partner for complex hardware, medical devices, life science diagnostics, defence systems, and advanced electromechanical products. We work with scaling hardware companies, regulated product manufacturers, and OEMs to turn complex product challenges into manufacturable, launch-ready outcomes. If firmware architecture decisions are on your critical path, we should talk.

Related Articles

Product Manufacturing
Where the product proves it can scale.
Capabilities
Product Development
Where the product becomes a working system.
Capabilities
Product Innovation
Where the right product gets defined.
Capabilities
Our Work
See the hardware we've taken from concept to production.
Capabilities