← Back
Home › Systems › Embedded and IoT

medical-device-firmware

safety-critical medical firmware

insulin pumps

Open the medical-device-firmware pagemedical-device-firmw… Fault tree analysisembedded: Write the unwanted top event as AND/OR logic over component failures, reduce it to minimal cut sets, then compute its probability and each failure's share.›Model checking and formal proofembedded: Exhaustively explore a model's reachable states (or prove its invariants with a solver or proof assistant) to find rule violations with a shortest counterexample trace.›Memory protection unitembedded: A handful of base/size regions with permissions, checked on every access by the CPU, give microcontroller tasks isolation and stack-overflow traps without an MMU.›Memory self test marchembedded: March RAM tests, ROM CRCs and CPU self-tests required by safety standardsPreemptive priority schedulingembedded: Always run the highest-priority ready task, switching the instant a higher one becomes ready; selection is O(1) with one FIFO per priority and a bitmap.›Rate monotonic analysisembedded: Schedulability tests and response-time analysis for periodic real-time tasksRedundancy votingembedded: N-modular redundancy and MooN voting (2oo3, TMR) with discrepancy detectionSafety-critical codingstandardsembedded: Which rule sets (MISRA C, CERT C, JPL Power of Ten, Rust lint subsets) apply when software must not fail, what they forbid, and how to enforce them in CI.›Safety monitor architectureembedded: Independent doer/checker safety monitor that forces a safe state when the main channel misbehavesWatchdog timerembedded: A hardware down-counter that resets the chip unless software proves, in time and in a plausible pattern, that it is still healthy.›Deterministic fixed-point mathgame-networking: Bit-identical simulation math across CPUs, compilers and builds: integer fixed-point formats, the float traps (FMA, x87, fast-math, libm), deterministic trig, sqrt and RNG.›CRC32C checksumshardware-os: The Castagnoli CRC-32 with dedicated instructions on x86 (SSE4.2) and ARMv8. It is the standard fast integrity check for storage blocks, logs and network frames.›Model predictive controlrobotics: Each tick, solve a small optimization over a predicted horizon, apply only the first input, repeat. Handles constraints and multiple inputs that a PID loop cannot.›AEAD (AES-GCM,ChaCha20-Poly1305)security: Authenticated encryption that encrypts and detects tampering in one call; the only kind of symmetric encryption you should use.›ECDH key exchange (X25519,P-256)security: Each side multiplies the other's public curve point by its own scalar and both get the same point; always validate the peer key and run the result through a KDF.›Secure boot chainsecurity: Each boot stage verifies the signature of the next before running it, anchored in an immutable ROM key hash, with version counters against downgrade and a TPM log of what ran.›Ble gattsignal-processing: Bluetooth LE link layer, ATT/GATT services and characteristics, connection intervals and pairingWear levelingstorage: Spread erases across flash blocks: dynamic and static wear leveling, read disturb refreshHierarchical state machinesystems: Nest states so a substate inherits its parent's reactions and only overrides what differs, with ordered entry and exit actions along the path.›BFS and DFSgraphs: The two basic graph traversals; BFS gives hop distances and levels, DFS gives discovery/finish order for cycles, components and topological order.›Binary decision diagramsoptimization: A reduced ordered BDD stores a Boolean function as a shared DAG that is unique per variable order, so equivalence is one integer compare and counting is one pass.›Swiss tabledata-structures: Open-addressing hash table that filters 8-16 slots at once with 1-byte hash tags and SIMD, giving fast lookups at 87.5 % load.›CDCL SAT solvingoptimization: Search for a satisfying assignment, learn a new constraint from every dead end, and jump back past irrelevant choices; handles industrial formulas with millions of clauses.›Dpll toptimization: SMT architecture: CDCL over boolean abstraction plus theory solvers, theory propagation, Nelson-OppenCode coverage instrumentationcompilers: Insert counters into compiled code to record which blocks, edges or lines ran; a spanning tree of the control-flow graph cuts the counters needed.›Dataflow analysiscompilers: Compute a fact per program point (live variables, reaching definitions, available expressions) by iterating transfer functions over the CFG to a fixed point.›End to end protection e2eembedded: Per-message counter, ID and CRC over a black channel to detect loss, repetition, corruption, delayPRNGs (PCG, xoshiro)numerics: Small, fast, statistically strong non-cryptographic generators: PCG32 and xoshiro256**, with seeding, parallel streams and unbiased ranges.›SIMD programminghardware-os: Process 4-16 lanes per instruction with AVX2, AVX-512 or NEON via Rust std::arch, with runtime dispatch and a portable fallback.›Fixed-rate control loopembedded: Run a controller at an exact period using absolute deadlines and a fixed dt, so jitter never accumulates and the math stays valid.›Runge-Kutta 4numerics: The classic four-stage, fourth-order ODE step; error drops 16× when the step halves, with no derivatives or past steps needed.›+ 6 moreSparse matrices (CSR)numerics: Compressed sparse row stores only nonzeros as row pointers, column indices and values; SpMV streams them once, so it is memory-bandwidth bound.›Drat proof certificatesoptimization: DRAT and LRAT proofs emitted by SAT solvers and checked independentlyTwo watched literalsoptimization: Unit propagation for SAT that watches just two literals per clause, so backtracking costs nothing and long clauses are rarely touched.›Arena allocatorsystems: Allocate by bumping a pointer through big chunks and free everything at once: a few instructions per allocation for objects sharing a lifetime.›Congruence closurecompilers: Union-find over terms with congruence propagation; decides equality with uninterpreted functionsBit blastingoptimization: Reduce bit-vector constraints to CNF via adder, multiplier and comparator circuitsSimplex methodoptimization: Solves linear programs by walking along the edges of the feasible polytope from vertex to vertex, one basis swap (pivot) at a time; the workhorse behind MIP solvers.›Minimum spanning treegraphs: Cheapest set of edges connecting all vertices; Kruskal sorts edges and joins components with union-find, Prim grows one tree, Boruvka merges in parallel rounds.›Control-flow graphcompilers: Split a linear instruction list into basic blocks and connect them by jumps; the graph every optimizer, SSA builder and dataflow pass walks.›Topological sortgraphs: Order the vertices of a DAG so every edge points forward; the core of build systems, schedulers and dependency resolution, with cycle detection for free.›Dma transfersembedded: DMA controllers: descriptors, circular buffers, cache coherence for zero-CPU data movementPWM generationembedded: A timer's counter, period and compare registers draw the pulse train; preload, dead time and DMA make duty changes glitch-free, shoot-through-free and cheap.›+ 2 moreHash consingdata-structures: Intern structurally equal immutable terms so equality is pointer comparison and sharing is maximalArithmetic circuitshardware-os: Adder and multiplier circuits: carry-lookahead, prefix adders, Booth encoding, Wallace and Dadda treesTseitin transformationoptimization: Linear-size CNF encoding of formulas with auxiliary variablesUnion-findgraphs: Disjoint-set forest with union by size and path compression; merges sets and answers same-set queries in effectively constant time.›Floating-point errornumerics: How IEEE 754 rounding error accumulates and how to contain it: compensated (Kahan/Neumaier) and pairwise summation, error-free transformations with FMA, ULP comparisons.›+ 2 more+ 1 more+ 1 more

How it is built

Architecture overview

Medical device firmware drives a therapy: an insulin pump moves a plunger, a ventilator moves air. A software fault can hurt the patient, and a regulator will ask for evidence that you looked for it. The design therefore does not trust the dosing code to be right. A smaller, independent safety channel checks every actuator command against its own limits and can cut the drive, and every unsafe default (reset, power loss, bad update, lost link) must land in the same place: therapy stopped, patient alerted. The lifecycle rules (IEC 62304 for the software, ISO 14971 for risk management) are named only; they shape the process, the parts below shape the product. The running example is an insulin pump. No commercial pump firmware is open, so the open pieces named below are the RTOS, the bootloader and the phone-side dosing controllers.

Parts:

 phone ──BLE, AEAD──┐
                    ▼
 ┌─ application MCU (RTOS, MPU) ─────────────────┐ requests  ┌─ safety MCU ─────────┐
 │ comms ─► therapy state machine ─► dose plan   ├──────────►│ own clock + watchdog │
 │ alarms ◄───────────┘                  │       │◄──────────┤ limits · dose ledger │
 │                                       │       │ verdicts  │ cutoff decision      │
 └───────────────────────────────────────┬───────┘           └───────────┬──────────┘
                                         ▼                               │ enable
                                  ┌──────────────┐                       ▼
                                  │ motor driver │◄─── cutoff gate ─────AND
                                  └──────┬───────┘
                                         ▼
                             motor ─► plunger ─► reservoir ─► infusion set ─► patient
                                └── encoder counts ─► both MCUs

Two processors are one way to get independence; an isolated second core or a hardware limiter serves the same purpose. What matters is that the checker shares no clock, firmware image or bug with the doer.

How the parts fit together

Kernel and tasks

The tasks are few and fixed at build time: a motor task (the only hard deadline), a sensor task, comms, UI and alarms, a logger. A preemptive priority scheduler runs the highest-priority ready task, so the motor task preempts the rest and its worst-case response time is computable. Whether the set fits is a schedulability question that rate-monotonic-analysis (no page yet) answers: for n periodic tasks the Liu and Layland sufficient bound is a CPU load of n(2^(1/n) − 1) (source: Liu and Layland 1973), about 0.78 for three tasks (arithmetic). Above the bound you run exact response-time analysis, not hope. An RTOS beats a bare loop here because a slow radio call cannot delay a motor step; the sibling RTOS system covers the kernel itself.

Contract to neighbours: tasks exchange small fixed-size messages through bounded queues, not shared variables, and only the motor task touches the driver registers.

Isolation

On a microcontroller without an MMU, a memory protection unit gives each task a few regions with permissions, so an overrun in the comms task faults instead of overwriting the dose schedule. Zephyr's user mode is built on it and is designed for devices with an MPU; it treats user threads as untrusted and the kernel and supervisor threads as trusted (source: Zephyr user-mode documentation). Two limits matter for a safety case. The MPU checks CPU accesses only, so a DMA engine needs its own restrictions. And isolation shows freedom from interference between functions; it does not replace the checker.

Therapy logic

The modes (idle, priming, delivering, suspended, alarm, fault) form a hierarchical state machine: a substate inherits its parent's reactions, so "any fault, from any state, stops delivery" is written once on the root, and leaving "delivering" always disables the driver in its exit action.

Dose arithmetic runs in integers, in a fixed-point format chosen per quantity (units per step, steps per unit). Float would agree on most builds, but "most" is not a claim a reviewer accepts, and the checker must reproduce the number bit-exactly to compare it.

A closed loop adds a model-predictive controller that optimises a short predicted glucose horizon under constraints and applies only the first step; a PID controller is the simpler alternative but handles constraints less directly. Either way the controller proposes and the state machine disposes: output is clipped by limits the controller cannot change. The open OpenAPS controller shows the pattern. It never sets a temporary basal above the lowest of the pump's own maximum basal rate, 3 times the highest programmed hourly basal (default max_daily_safety_multiplier) and 4 times the current hourly basal (default current_basal_safety_multiplier), and max_iob caps insulin on board (source: OpenAPS documentation, preferences and safety settings).

Safety monitor

The safety-monitor-architecture (no page yet) idea is doer and checker. The application MCU plans a delivery and sends a request; the safety MCU holds its own copy of the limits, recomputes the step count, counts encoder pulses itself and keeps a ledger of what was delivered. A request over a single-bolus, hourly or daily limit is refused. The motor driver's enable line is a hardware AND of both channels' permissions, so "drive on" needs two positive signals and either side can stop it.

Where a value must not be wrong, redundancy-voting (no page yet) applies in small form: a second copy stored inverted, or the dose arithmetic run twice by different code. Full 2oo3 voting needs a third channel. Hot-standby takeover (Hot-standby redundancy) fits systems that must keep running; a pump's safe response to a fault is to stop.

Supervision

A watchdog timer resets the chip when software stops proving it is healthy. The proof must be about the application, not about interrupts: one supervisor kicks the hardware watchdog only after every task has reported in. Zephyr's task watchdog gives one software channel per task on a kernel timer, with the hardware watchdog as an optional fallback if the task watchdog or the scheduler fails (source: Zephyr task watchdog documentation). A reset is not a safe state by itself: the driver comes up disabled and the next boot starts in "delivery stopped" until the checker and the ledger agree on what was delivered.

Self-test and integrity

Safety standards ask the device to test itself: memory-self-test-march (no page yet) covers March RAM tests at start-up, ROM checksums and CPU self-tests, sliced to run in idle time. Every record that matters to dosing carries a CRC32C: settings, basal profile, ledger. A mismatch is a fault state, never a silent default. Checksums find accidental corruption only; authenticity comes from signatures and AEAD.

Persistent state

The ledger and settings live in flash, which wears out and can lose power mid-write. wear-leveling (no page yet) spreads erases; a log-structured store does it as a side effect. Zephyr's NVS appends id-data pairs in a ring of sectors, writes the data first and the metadata (with a CRC) second so an interrupted write is ignored at start-up, and keeps one sector free (source: Zephyr NVS documentation). Its lifetime formula is SECTOR_COUNT × SECTOR_SIZE × PAGE_ERASES / (NS × (DS + 8)) minutes. Arithmetic example with assumed values: 4 sectors of 1,024 bytes, the 20,000-erase page the NVS page cites for an nRF51822, one 8-byte ledger record every 3 minutes gives 4 × 1,024 × 20,000 / ((1/3) × 16) ≈ 15.4 million minutes, about 29 years. A record every second gives about two months, so the ledger writes at delivery milestones and keeps fine counts in RAM.

The pump is controlled from a phone over Bluetooth LE (ble-gatt, no page yet: link layer, attribute protocol, connection intervals, pairing). Do not rely on link-layer security alone. The application layer runs its own key agreement (ECDH, with peer-key validation and a KDF) and protects every command with an AEAD that includes a monotonic counter, so a captured command cannot be replayed. The pump enforces its limits on every command regardless of sender; an authenticated app can be wrong or compromised. Device certificates and revocation come from the sibling PKI system.

Boot and update

Secure boot makes the ROM verify the bootloader, and the bootloader verify the application, with a version or security counter that rejects old signed images. MCUboot, an open bootloader, has two image slots and a "test" swap: the new image runs once, and unless it marks itself OK after its own self-test, the next reset swaps the old image back (source: MCUboot design documentation). That rollback keeps a bad update from bricking the device. Downgrade prevention compares a security counter in the signed image with a counter held in non-volatile trusted storage (same source). Updates wait for a safe moment, not during a bolus; the slot swap and signed manifest follow A/B partition updates and Signed update metadata.

Design-time evidence

Before the code exists, fault tree analysis takes the top event ("insulin delivered exceeds the commanded dose") and lists the failure combinations behind it. A one-element cut set is a single point of failure and a design bug; the tree also shows which channels above earn their cost. Model checking covers the small, high-value state machines (therapy modes, the update confirm protocol) where exhaustive search is feasible. The code follows a safety-critical coding standard: bounded loops, no heap after start-up, checked returns, enforced by a tool in CI. Ferrocene, a Rust toolchain, states it is qualified for IEC 62304 Class C among other standards (source: ferrocene.dev, vendor statement).

Data and control flow

A bolus request, end to end

  1. The phone sends "deliver 2.5 U". The comms task authenticates and decrypts it (AEAD, counter check); on failure nothing reaches therapy logic.
  2. The state machine accepts it only in "idle" or "delivering basal" and converts units to motor steps in fixed point. With an assumed 0.05 U per step (arithmetic example), 2.5 U is 50 steps.
  3. It sends (steps, rate, sequence number) to the safety MCU. The checker validates against its own limits, recomputes the steps and records the intent in the ledger with a CRC.
  4. On "go", the motor task steps the driver from a timer interrupt, a fixed-rate loop with absolute deadlines so the rate does not drift. Both MCUs count the encoder pulses.
  5. The checker compares counts with the plan every few steps. A mismatch beyond tolerance (stall, occlusion, runaway) drops the enable line and the application raises an alarm.
  6. At completion the ledger entry is closed, the log record written and the phone gets an authenticated status. Time goes into step 4 (order of seconds to minutes, set by dose and rate); the checks are small by comparison.

Boot after a reset

  1. ROM verifies the bootloader, the bootloader verifies the application and picks the slot.
  2. The application reads the reset cause, runs start-up self-tests and brings the driver up disabled.
  3. It loads settings and ledger, verifying every CRC, and asks the checker whether both agree on the last delivery. If an entry was open (intent recorded, completion not), it compares encoder counts with the plan; if it cannot reconcile them, it stays stopped and alarms until the user confirms.

Scaling

The first limits are not throughput, in the order you would pull the levers:

  1. Worst-case response time of the motor task. Measure on hardware, then rerun the analysis after every change to a higher-priority task.
  2. Flash endurance. The ledger's wear budget is a product-life limit; run the NVS formula first.
  3. Power. Radio duty cycle, motor drive and always-on self-tests share one small battery; measure each and run the tests in idle slots.
  4. Verification effort. Every feature must be in the fault tree and the test plan. The checker must stay simpler than the doer; a checker that grows into a second doer shares its bugs.
  5. Fleet. Each device has its own keys and logs; roll updates out in stages with the revert path as the brake, and feed field logs to the sibling error-tracking and audit-log systems.

Failure modes

FailureSymptomCauseFix
Over-delivery after a bit flipDose larger than ordered, no fault raisedCritical value held once, unchecked, in RAM or flashCRC32C on every critical record, inverted second copy, checker recomputes from its own limits
Runaway motorDrive keeps stepping after the plan endedDriver stuck on, or the motor task in a bad stateChecker counts encoder pulses and drops the AND gate; driver defaults to disabled on reset
Watchdog fed by the wrong codeDevice hung but never resetsKick in a timer interrupt or a dedicated kick taskOne supervisor kicks after every task checked in; task watchdog channels with hardware fallback
Boot loop after updatePump resets forever, no therapySigned image that crashes at start-upTest swap with confirm-after-self-test; reset counter drops to safe mode and alarms
Stack overflow in a low taskRandom faults in neighbouring tasksOne flat address spaceMPU regions and a no-access guard region below each stack
Torn write at power lossLedger or settings unreadableBattery removed during a flash writeData before metadata, CRC per record, spare sector (log-structured store)
Command replay or forgeryUnwanted bolus from a nearby radioLink-layer security onlyAEAD with a monotonic counter above the link; pump-side limits on every command
Silent alarmFault, patient not toldThe alarm output was never testedAnnunciator self-test at boot, alarm priorities and escalation (Alarm management)

Real implementations

namewhat it isrepolicencenotes
ZephyrRTOS with user mode (MPU), task watchdog, NVS, MCUboot integrationzephyrproject-rtos/zephyrApache-2.0Safety group aims at IEC 61508 SIL 3 for a limited source scope: a stated goal, not a granted certificate (safety overview)
FreeRTOS kernelSmall preemptive RTOS; MPU-enabled ports for Cortex-MFreeRTOS/FreeRTOS-KernelMITMPU ports under portable/GCC/ARM_CM4_MPU and portable/ARMv8M
MCUbootSecure bootloader: signed images, swap/test/revert, downgrade preventionmcu-tools/mcubootApache-2.0Needs a port to the part's flash and a security-counter store
SAFERTOSCommercial safety RTOS derived from the FreeRTOS functional modelhighintegritysystems.com/safertos—Vendor states pre-certification to IEC 61508 SIL 3, ISO 26262 ASIL D and IEC 62304
FerroceneQualified Rust toolchainferrocene/ferroceneMIT OR Apache-2.0Vendor lists IEC 62304 Class C among its qualifications
oref0 (OpenAPS)Open closed-loop dosing algorithm that runs outside the pumpopenaps/oref0MITSource of the safety-limit pattern above
LoopiOS closed-loop app for supported pumps and CGMsLoopKit/LoopMIT (with noted exceptions for frameworks and graphics)Community project, not an approved medical product by itself
AndroidAPSAndroid closed-loop appnightscout/AndroidAPSAGPL-3.0Community project

Sources

Similar systems

Share the most building blocks with this one.

In-house systems

Named systems of this kind at large companies, with their paper or write-up.