Real-Time
The parts that must never miss a deadline are written as native Zig tasks. The VM orchestrates around them. A runaway script cannot stop a native task — a preempting timer switches away.
The primitive set
The scheduler carries a hard-RTOS primitive set in arch-neutral code.
| Primitive | Mechanism |
|---|---|
| Priority-inheritance mutex | A blocked high-priority waiter boosts the owner to its own priority, restored on unlock. One mutex per task — nested and chained inheritance are not implemented |
| Deadline timer | The timer is armed at the next event in nanoseconds — the earliest of any sleeper's wake, any periodic release, or a round-robin slice. Not a fixed drum |
| Bounded timed wait | Wake on an event or after n nanoseconds, whichever comes first |
| Deadline-miss detection | A rate-monotonic implicit deadline that counts overruns |
| CPU-time accounting | Run ticks charged to the interrupted task |
| CPU reservation / temporal isolation | Per-task budget and period; a task exceeding its budget is throttled until refill |
| Software watchdog | Heartbeat liveness, so a hung task is detectable |
| RM schedulability analysis | The hyperbolic bound Π(Cᵢ/Tᵢ + 1) ≤ 2 — all integer arithmetic |
| High-resolution clock | x86 TSC (PIT-calibrated), riscv rdtime, aarch64 CNTVCT/CNTFRQ, ARMv6-M the µs TIMER |
| Sub-µs busy-wait | Spin on the nanosecond clock |
x86_64, riscv64 and aarch64 start and run the whole set. The RP2350 starts the deadline-satisfaction pair below — its task table is capped at 8 and the full set wants 12. The RP2040 starts none of them.
The RM analysis is not an admission gate. It is a routine you call to ask whether a task set fits; spawn does not consult it and does not refuse a task set that would not.
The scheduler meets its deadlines
The scheduler's notion of now is a nanosecond clock, and its timer is armed at the next deadline — the earliest of any sleeper's wake, any periodic release, or a round-robin slice. It is not a fixed periodic drum, so no tick sets a floor under the shortest deadline you can ask for. sleep and wait_period take nanoseconds; the tick forms are wrappers over them. All five targets already had the hardware this needs: an absolute one-shot comparator.
The real control law runs as an ordinary periodic task under the scheduler, at 1 kHz on real silicon, and the scheduler holds the deadline:
- RP2350 (Hazard3): 0 misses in 1,741,824 jobs, period 948.9–1056.5 µs — a full delivery flight, the mission and the network sharing the chip.
- RP2040 (Cortex-M0+): 0 misses in 113,664 jobs, period 998–1002 µs — network running, but no VM mission: a 264 KB chip cannot hold both the scheduled RT task and the mission, so it hovers. The ±2 µs band is the 1 µs timer's resolution floor, not a jitter figure.
That is the real control() — the soft-float cascade-PID plus quaternion — with the OS in the loop: scheduling it, accounting for its CPU time, and sharing the chip with the network and the VM flight actors. It is the number that decides whether this is an RTOS — the scheduler releasing a real 1 kHz task on time, on real silicon, with the network and the mission contending for the same chip.
The same control law flies the delivery mission — the island, the drop, the return, the landing — as the hardware-in-the-loop flight controller on the same silicon, over Wi-Fi. That runs at the HIL frame rate. The 1 kHz figure above is the scheduled RT task: two demonstrations of one control law, not one loop.
A synthetic pair — C/T = 2/10 and 5/40, 32.5% utilization, a set the hyperbolic bound calls schedulable — also runs on x86_64, riscv64, aarch64 and the RP2350, meeting every deadline. The analysis prints its verdict before the set runs, so prediction and measurement check each other. But it is a set chosen to pass. The real control law is not.
The measurements
The control law's worst case, measured on both boards. Each board runs control() at boot over a 20,000-input adversarial sweep of the reachable flight envelope, interrupts masked, at 125 MHz — so the two counts are directly comparable:
- Cortex-M0+ (RP2040): 190 µs — a 5.2× margin under a 1 kHz budget.
- Hazard3 RISC-V (RP2350): 98 µs — a 10.2× margin. The Hazard3 has a hardware integer multiply/divide the soft-float law leans on, so it is about twice as fast.
Neither core has a cache, branch prediction, or speculation, so the measured maximum is the engineering WCET — there is no hidden tail. The sweep is deterministic and reproducible on the board.
The scheduler's own response floor, measured on riscv64: ~1.1 µs interrupt-driven with the Sstc timer, ~3.5 µs when the timer costs a firmware call. That is the cost of the OS getting to your task, and it is the number to weigh against your period.
Under deterministic virtual time (-icount shift=0) an absolute-deadline wait shows zero jitter — the mechanism is exact, and the jitter seen on a loaded host is the host, not the OS.
On real x86 the residual is firmware system-management interrupts (non-maskable) plus cache and micro-architectural effects. Below that, determinism needs dedicated hardware or an FPGA soft core.
The control loop's worst-case execution time
A hardware-in-the-loop flight controller runs on Lambda OS. The controller is real — real silicon, real compute, real motor outputs — driven by a simulated plant. This is how flight control is verified before a real airframe: it pins down the deadline behaviour, deterministically and repeatably, which no field flight can. The verification here is HIL-first by design.
The mission it flies is a cargo delivery. The full demo — the fault responses, the pre-flight gate, the HIL rig — is on the Flight Control page.
The number that decides whether a deadline is safe is the worst-case execution time of the control math itself — the soft-float cascade-PID plus quaternion. Each board measures its own at boot, interrupts masked so the delta is pure compute, over a 20,000-input adversarial sweep of the reachable flight envelope: a unit attitude quaternion anywhere on the sphere, ±35 rad/s gyro, ±400 m position error, the full 1.0–1.4 kg mass range. The inputs are chosen so the square root and divide take their data-dependent worst path.
- Cortex-M0+ (RP2040): 190 µs (23,782 cycles, best 180 µs). Hazard3 (RP2350): 98 µs (12,353 cycles, best 85 µs). Same code, same 125 MHz clock — the Hazard3's hardware integer multiply/divide makes it about twice as fast.
- Take the denominator from the control law, not from the demo's link. A real multirotor stabilizes its attitude at 1–2 kHz — it cannot be flown at tens of Hz. Against 1 kHz, that is a 5.2× margin on the Cortex-M0+ and 10.2× on the Hazard3.
- The figure covers the whole cascade in a single call — position → velocity → attitude → rate → motor mixing — which is more than a real controller runs in its fast loop. So it is a conservative bound on the inner loop.
- The worst case is flat: the control loop's internal clamps bound every operand feeding the square root and divide to normalized, O(1) ranges, so no subnormal slow path is reachable — the sweep cannot find a spike a benign mission would miss.
Why a measured maximum can be called the WCET here
The Cortex-M0+ has no cache, no branch prediction, no speculation and no maskable SMIs. There is no hidden tail behind the measurement. The only variance is the data-dependent soft-float cost, which the mission exercises broadly.
On a cached, superscalar chip the measured max sits below the true worst case and you need static analysis; on the M0+ the measured maximum is the engineering WCET. That is why "slow but deterministic" wins for a hard deadline: raw throughput is poor — fib(25) interprets in 24 s — yet the deadline-bearing math has a 190 µs worst case. The slowness is confined to the VM interpreter, deliberately kept off the deadline path.
What these measurements cover
- The controller runs on real silicon against a simulated plant. The compute, the timing and the motor outputs are real; the aircraft dynamics are the simulator's. That is the point of HIL — the deadline behaviour is measured deterministically, which a field flight cannot do.
- The 190 µs is a compute time, measured with interrupts masked — not a response time that includes preemption.
- The 1 kHz deadline result and the flight are separate demonstrations of one control law. The scheduler holds the 1 kHz deadline and the compute fits it (190 µs); the delivery itself flew as a closed loop at the HIL frame rate. A single 1 kHz closed-loop flight is the conjunction of the two, not something shown as one run.
- On the Cortex-M0+, the mission is the one unprivileged, MPU-confined actor; the safety envelope and the control loop are native, so a buggy mission cannot reach them at all.
- Full certification would still pin the soft-float square-root and divide worst case by static analysis, or rewrite the control loop in fixed-point to remove the data-dependence entirely.