Real-Time Embedded Systems
1. At a glance
A real-time embedded system is software running on a dedicated microcontroller, DSP, or SoC that must respond to external events within bounded time. The qualifier bounded is the entire discipline: a real-time system is not one that runs fast, it is one whose worst-case response time is provable a priori. A loop that averages 10 ms but occasionally takes 200 ms is worse than a loop that always takes 100 ms for any controller, brake, autopilot, or pacemaker built around it.
Two flavours of bounded matter in industry. Hard real-time treats a missed deadline as a system failure with safety consequences — anti-lock brakes that release a wheel 5 ms too late, a pacemaker that paces 50 ms late, a flight-control surface command that arrives one frame late. Hard-real-time systems are subject to formal certification (DO-178C avionics, ISO 26262 automotive, IEC 62304 medical, IEC 61508 industrial). Soft real-time treats a missed deadline as a quality-of-service degradation: a video frame dropped, a packet retransmitted, an audio buffer underrun. Both flavours require deterministic infrastructure (RTOS, scheduling discipline, ISR latency budget); only hard real-time additionally requires certification evidence and worst-case execution time (WCET) analysis.
Every car built since 1995 contains 50–100 embedded systems coordinating safety (ABS, ESC, ACC, airbag, EPS, BMS) on CAN-FD and Automotive Ethernet backbones. Every airliner since the A320 uses hard-real-time fly-by-wire computers running ARINC 653 partitioned RTOSs. Every modern medical infusion pump, ventilator, and dialysis machine is hard-real-time IEC 62304 Class C. Every drone running PX4 or ArduPilot is hard-real-time on top of NuttX or ChibiOS. The discipline sits between two siblings: above it lies digital control (the loop math, see [[Engineering/digital-control]]); below it lies microcontrollers and SoCs (the silicon substrate, see [[Engineering/microcontrollers]]).
The price of getting real-time wrong is documented and historical. The Therac-25 radiation-therapy machine killed six patients between 1985–87 from a race condition between a high-priority cursor task and a setup-mode task. The Ariane 5 maiden flight in 1996 lost USD 370 M to an unhandled overflow in inertial-navigation code carried over from Ariane 4. The Mars Pathfinder mission in 1997 suffered repeated unexplained resets in flight from priority inversion in VxWorks pipe semaphores. The Toyota unintended-acceleration cases in 2009–10 were linked by expert testimony to single-event-upset stack-overflow in a non-MPU-guarded engine ECU. The 737 MAX MCAS crashes in 2018–19 had a single-sensor architecture and a controller that did not cross-check redundant data within real-time bounds. Real-time engineering is the discipline of making these mistakes provably impossible.
2. First principles
Real-time ≠ fast
The defining property of a real-time system is predictability, not speed. A 1 kHz control loop on a 16 MHz AVR with a known WCET of 800 µs is real-time. A 100 kHz loop on a 2 GHz Cortex-A78 running Linux with no rt-preempt patch is not, even though it is 100× faster on average — the worst-case can stretch to tens of milliseconds when a page fault, scheduler tick, or cache eviction lands inside the loop.
Time domains: BCET, ACET, WCET, jitter
For a fixed code path:
- Best-case execution time (BCET) — the fastest path through the code (cache hot, branches favourable, no contention).
- Average-case execution time (ACET) — what a profiler measures empirically.
- Worst-case execution time (WCET) — the slowest possible path (cache cold, worst-branch, peripheral contention, DMA stalls). This is the only number that matters for schedulability proof.
- Jitter = WCET − BCET. Lower is better. Jitter inflates a control loop’s effective dead-time and eats phase margin.
WCET is not a profiler measurement — profilers report what they saw, never what they did not see. Provable WCET comes from static analysis tools (aiT, RapiTime, Bound-T) that trace every basic block and compute the cumulative worst path under a documented hardware model, or from end-to-end testing across all input combinations (rarely tractable above small functions).
Periodic, aperiodic, sporadic tasks
A task is characterised by a tuple (C, T, D) where C is its WCET, T is its period, D is its deadline (D ≤ T is the standard assumption). Utilisation U = C/T ∈ [0, 1] is the fraction of CPU the task consumes.
- Periodic — released every T seconds; the dominant workload of control systems (sample, compute, actuate, repeat). T is constant.
- Aperiodic — irregular release pattern, no minimum-inter-arrival guarantee. Treated as background work or scheduled via aperiodic servers (deferrable server, sporadic server).
- Sporadic — irregular, but with a documented minimum inter-arrival time T_min. Analysable like a periodic task at T = T_min.
Determinism
Same input + same state ⇒ same output and same timing. Cache misses, branch prediction, speculative execution, DMA bus contention, and shared peripherals all break determinism in subtle ways. Cortex-M0/M0+/M3 are determinism-friendly (no cache, simple pipeline). Cortex-M7 with cache enabled has variable timing — the cache helps average but inflates WCET unless flushed/locked. Cortex-A class is non-deterministic by design; using one for hard real-time requires isolating critical cores (cpusets / cpu isolation), pinning code in locked cache lines, and disabling SMT.
Schedulability vs feasibility
A task set is feasible if some scheduling algorithm exists that meets all deadlines. It is schedulable under algorithm X if X specifically meets all deadlines. The two are different: a feasible task set may fail under RM but succeed under EDF. The engineering question is therefore always paired: “schedulable under what?” Industrial practice fixes the scheduler choice (almost always fixed-priority preemptive with RM/DM priorities) and then verifies schedulability of the task set against it, because the scheduler is a fixed property of the certified RTOS — the engineer adjusts task parameters (C, T, D, priorities), not the algorithm.
Release jitter
The release of a periodic task does not always happen at the ideal instant t = kT. Tick-aligned releases are quantised to the system tick; ISR-triggered releases inherit ISR-dispatch jitter. Release jitter J reduces the schedulable utilisation: response-time analysis becomes R_i = C_i + Σ ⌈(R_i + J_j)/T_j⌉ · C_j. Always source critical periodic tasks from a hardware timer rather than the RTOS software tick if jitter matters more than a few microseconds.
3. Scheduling theory
Rate-monotonic (RM, Liu & Layland 1973)
Fixed-priority preemptive scheduling. Priority is assigned inversely to period — shortest period gets highest priority. Liu and Layland proved (in the classic J. ACM paper “Scheduling Algorithms for Multiprogramming in a Hard-Real-Time Environment”) that RM is optimal among fixed-priority algorithms for periodic tasks with D = T.
Schedulability under RM is guaranteed if:
ΣU_i ≤ n · (2^(1/n) − 1)
where n is the task count. The bound decreases monotonically: 1.0 (n = 1), 0.828 (n = 2), 0.779 (n = 3), 0.756 (n = 4), and → ln 2 ≈ 0.693 as n → ∞. This is the Liu-Layland bound — a sufficient but not necessary condition. Many task sets with ΣU > 0.69 are still RM-schedulable (testable by response-time analysis below).
Deadline-monotonic (DM, Leung & Whitehead 1982)
Generalises RM to D ≤ T. Priority is assigned inversely to deadline, not period. When D = T the two reduce to the same rule. DM is optimal among fixed-priority algorithms for D ≤ T.
Earliest-deadline-first (EDF, Liu & Layland 1973)
Dynamic-priority preemptive scheduling. At every scheduling decision the task with the nearest absolute deadline runs. EDF is optimal for uniprocessor preemptive scheduling — if any algorithm can schedule the task set, EDF can.
Schedulability under EDF (D = T):
ΣU_i ≤ 1
Strictly better than RM. The catch is overload behaviour: under transient overload RM degrades predictably (lowest-priority tasks miss first); EDF can cascade-miss because the late task’s deadline pulls it ahead of newly released tasks. Domino effect. Most certified RTOSs (VxWorks, Integrity, SafeRTOS) default to fixed-priority RM/DM-style scheduling for this reason. EDF appears in research, some open-source RTOSs (Litmus-RT, SCHED_DEADLINE in Linux), and a small set of industrial systems.
Cyclic executive (static cyclic scheduler)
Offline-computed schedule: a “major cycle” partitioned into “minor cycles”, each minor cycle a fixed sequence of task invocations. No preemption, no priorities, no surprises. ARINC 653 partitioned-scheduling for avionics is a cyclic executive at the partition level. Pros: bit-for-bit deterministic, certifiable. Cons: every change requires re-computing the schedule by hand; no graceful response to sporadic events.
Response-time analysis (Joseph & Pandya 1986)
Necessary-and-sufficient schedulability test for fixed-priority preemptive scheduling. For task i with higher-priority set hp(i), the worst-case response time R_i is the fixed point of:
R_i = C_i + Σ_{j ∈ hp(i)} ⌈R_i / T_j⌉ · C_j
Iterate from R_i^(0) = C_i; convergence (or R_i > D_i) typically within 5–10 iterations. The task is schedulable iff the iteration converges with R_i ≤ D_i. Worked example in §6 below.
Priority inversion and Mars Pathfinder
Priority inversion occurs when a low-priority task L holds a mutex needed by a high-priority task H; H blocks; an unrelated medium-priority task M preempts L and runs to completion, indirectly blocking H for an unbounded interval. Classic bug. Pathfinder’s 1997 reset cascade was caused by an information-bus pipe locked by a meteorological task (low priority), pre-empted by a long-running communications task (medium), while the bus-management task (high priority) waited — watchdog timed out and reset the spacecraft. JPL diagnosed by enabling VxWorks priority inheritance on the offending semaphore over the deep-space link — a real-time-engineering legend.
Priority inheritance protocol (PIP, Sha, Rajkumar & Lehoczky 1990) — when H blocks on L’s mutex, L’s priority is temporarily elevated to H’s; M cannot preempt L; L finishes its critical section quickly and releases the mutex; H resumes. Bounds the inversion to the longest critical section.
Priority ceiling protocol (PCP, same paper) — each mutex has a ceiling = max priority of any task that locks it. A task locks a mutex only if its priority exceeds the ceilings of all other currently-locked mutexes. Eliminates deadlocks (provably) and chained blocking; tighter blocking bound than PIP. Immediate-ceiling PCP (ICPP) is the industrial form used in Ada and SafeRTOS — simpler implementation: on lock, raise the locking task’s priority immediately to the ceiling.
4. RTOS landscape
| RTOS | Vendor / steward | Licence | Footprint | Pre-emptive | Tickless | Highest cert |
|---|---|---|---|---|---|---|
| FreeRTOS / Amazon FreeRTOS | Real Time Engineers / AWS | MIT | 5–10 kB | yes | yes | none in base; SafeRTOS variant up to ASIL-D |
| SafeRTOS | Wittenstein High-Integrity Systems | commercial | 8–15 kB | yes | yes | IEC 61508 SIL-3, IEC 62304 Class C, ISO 26262 ASIL-D |
| Zephyr | Linux Foundation | Apache 2.0 | 8–50 kB | yes | yes | IEC 61508 + ISO 26262 in progress |
| ThreadX / Azure RTOS | Microsoft (formerly Express Logic) | open since 2023 | 2–15 kB | yes | yes | DO-178B Level A, IEC 61508 SIL-4, ISO 26262 ASIL-D |
| VxWorks | Wind River | commercial | varies | yes | yes | DO-178C Level A, IEC 61508 SIL-3, ASIL-D |
| QNX Neutrino | BlackBerry | commercial | varies | yes | yes | ISO 26262 ASIL-D, IEC 61508 SIL-3, IEC 62304, FDA |
| Integrity / MULTI | Green Hills | commercial | varies | yes | yes | DO-178C, EAL 6+, FACE, IEC 61508 SIL-4 |
| µC/OS-III | Micrium / Silicon Labs | commercial | 6–24 kB | yes | yes | DO-178 Level A, IEC 61508 SIL-3, ASIL-D |
| RTEMS | OAR / community | RTEMS Licence (modified GPL) | 32 kB | yes | yes | informally used in DO-178; NASA / DOE deployments |
| NuttX | Apache | Apache 2.0 | 32–256 kB | yes | yes | PX4 autopilot, UAS; no formal cert |
| ChibiOS | community | GPL/commercial | 1–6 kB | yes | no | PX4 alternative; no cert |
| RT-Thread | RT-Thread Foundation | Apache 2.0 | 4–32 kB | yes | yes | IEC 61508 in progress |
| embOS | SEGGER | commercial | 4–16 kB | yes | yes | IEC 61508, DO-178B, IEC 62304 |
| Keil RTX5 | Arm | BSD (CMSIS-RTOS2 ref) | 5 kB | yes | no | IEC 61508 / 62304 |
| AUTOSAR Classic OS | AUTOSAR consortium | spec; impls by Vector, EB, Elektrobit | varies | yes | yes | ASIL-D (OSEK/VDX heritage) |
CMSIS-RTOS2 is not an RTOS; it is an Arm-standard wrapper API implemented by Keil RTX5 directly and by FreeRTOS/Zephyr through shims. Writing application code against CMSIS-RTOS2 keeps it RTOS-portable on Cortex-M.
5. RTOS primitives
Tasks (threads)
Each task has a stack, a priority, a state (READY / RUNNING / BLOCKED / SUSPENDED), and a control block (TCB). FreeRTOS supports 1–32+ priority levels (configurable); Zephyr has unified preemptive (positive) and cooperative (negative) priority bands. Preemption means a higher-priority READY task immediately displaces the running task; cooperation requires the running task to yield voluntarily.
Stack sizing is the single most common firmware bug: 256 B is plausible for a tiny periodic task, 16 kB is needed for ISRs that touch FPU registers, deep call stacks, or stdio. FreeRTOS provides uxTaskGetStackHighWaterMark() to measure peak usage; Zephyr provides k_thread_stack_space_get(). Always size at 1.5× measured peak in development, never below.
A task context switch on Cortex-M4 takes ~150–250 cycles depending on FPU state (lazy stacking saves 17 stack words if the inbound task does not touch the FPU). On Cortex-M7 with cache, first context switch into cold code is ~600–1200 cycles. FreeRTOS uses PendSV (lowest-priority exception) to perform the actual context save/restore — this guarantees the switch happens with all higher-priority ISRs allowed to run first, minimising worst-case ISR latency to a switch-independent 12 cycles + ISR body.
ISR design patterns
Top-half / bottom-half (deferred-interrupt processing). The ISR proper does the minimum: read peripheral status, copy raw data into a buffer, post a semaphore or queue message, return. The heavy work (decoding, filtering, decision-making) runs in a normal task at the appropriate priority. Keeps ISR latency bounded and ISR re-entry safe. FreeRTOS pattern: xSemaphoreGiveFromISR() + portYIELD_FROM_ISR(). Zephyr: k_work_submit() for system workqueue, or signal a thread directly.
Hardware-accelerated DMA path. For peripherals with autonomous DMA (UART RX, I²S, SPI block transfer, ADC scan group), configure DMA to fill a circular buffer and fire the half-transfer complete and transfer complete interrupts. The ISR consumes the back half while DMA fills the front half — true double-buffering. Decouples CPU from sample-rate jitter entirely; common pattern for ≥ 1 MSPS ADC capture into a control loop.
ISR priority discipline. Cortex-M NVIC uses lower number = higher priority. Configurable priorities split into preemption priority and sub-priority via BASEPRI masking. Industrial pattern: control ISRs at the highest preemption level (priority 0–3), communication ISRs at mid-level (priorities 4–7), low-rate housekeeping at low (priorities 8–14), RTOS PendSV / SysTick at the lowest user-configurable level (priority 15). The pattern preserves real-time response for the control path even when communication ISRs run at high duty cycle.
Inter-task communication
- Counting semaphore — resource count or event-counter signalling; produced by ISRs, consumed by tasks.
- Binary semaphore — synchronisation flag; semantically a 0/1 counting semaphore.
- Mutex — mutual exclusion with priority inheritance (FreeRTOS, Zephyr, QNX, VxWorks all support it; some make it default, others optional). Always use mutex instead of binary semaphore for resource locking — the priority-inheritance is the whole point.
- Queue / mailbox — fixed-size message passing. Bounded depth; FIFO ordering; produces back-pressure when full. Common ISR-to-task interface.
- Event flags / event groups — bitmask; wait on AND / OR combinations. Cheap for one-to-many signalling.
- Stream buffer (FreeRTOS) / pipe (Zephyr) / message queue (POSIX 1003.1b) — variable-size byte stream; single-producer single-consumer optimised.
- Mailbox with pointer payload — pass a pointer to a buffer instead of copying; zero-copy but requires lifetime discipline.
Timers
Software timers run callbacks from a dedicated daemon task (Timer Service in FreeRTOS, k_timer in Zephyr). They share the daemon’s priority — too low and timers run late, too high and timer callbacks block real work. Use hardware timers (TIM peripherals) for any deadline tighter than ~1 ms or any callback that must run from ISR context. Tick-driven RTOSs (1 kHz default) can resolve timer events no finer than the tick; tickless RTOSs program the next wakeup directly into a hardware timer.
Memory management
Heap (malloc/free or pvPortMalloc/vPortFree) is avoided in safety-critical code: fragmentation creates unbounded allocation latency. MISRA C:2012 rule 21.3 bans dynamic memory allocation. Three industrial patterns:
- Static allocation — every buffer, TCB, and stack declared at compile time. Provable bounded memory; certifiable.
- Fixed-size pool / slab allocator — pre-allocated array of identical blocks; O(1) alloc/free, no fragmentation. FreeRTOS supports via
pvPortMallocStack-class APIs; Zephyr hask_mem_slab. - Region-based allocation — separate heap regions per subsystem, each statically sized. Used in QNX, Integrity, AUTOSAR.
Tick rate
The system tick interrupt drives time-slicing, delay timers, and timeout management. Typical rates: 100 Hz (slow PLC), 1000 Hz (default for most Cortex-M RTOSs), 10 kHz (motor-control RTOSs). Trade-off: higher tick rate = finer time resolution + more ISR overhead (every tick costs ~200 cycles on Cortex-M4). Tickless idle (FreeRTOS configUSE_TICKLESS_IDLE, Zephyr CONFIG_TICKLESS_KERNEL) suppresses the tick when no work is pending, programming the next wakeup into a low-power timer — essential for battery-operated products (1.4 µA vs 100 µA average current).
6. Memory and execution model
- Stack-per-task — 256 B (idle task) to 16 kB (FPU-using task). Stack-overflow detection via canary word (FreeRTOS method 1: write a magic pattern at stack bottom, check on context-switch), or MPU guard region (method 2: configure an MPU region at the stack top with no-access permissions, MemManage fault on overflow). Always enable method 2 in any product that ships.
- MPU (Memory Protection Unit) — Cortex-M3/M4/M7/M33 with optional MPU defines up to 8 (M3/M4) or 16 (M7/M33) memory regions with read/write/execute permissions per privilege level. ARMv8-M adds attribute-based access for TrustZone. The MPU separates Flash (RX), RAM (RW), peripherals (RW, no-cache, no-buffer), and stacks (RW with overflow detection). Real-time-aware RTOSs (Zephyr
CONFIG_USERSPACE, FreeRTOS-MPU) reconfigure MPU on every context switch. - MMU (Memory Management Unit) — full virtual memory and paging; only on Cortex-A class and richer chips. Linux PREEMPT_RT, QNX, Integrity, VxWorks (SMP edition) use it for inter-process isolation. The TLB-miss penalty is the determinism cost.
- TrustZone-M — Armv8-M Secure / Non-Secure worlds on Cortex-M23/M33/M55/M85. Secure side hosts cryptography, key storage, and root-of-trust; Non-Secure side hosts application + RTOS. TF-M (Trusted Firmware-M) is the reference open-source implementation, PSA Certified, integrated into Zephyr, NuttX, and vendor SDKs.
Memory placement for determinism
The linker script is the WCET engineer’s primary tool. Standard layout for a Cortex-M7 product:
| Region | Memory | Speed | Use |
|---|---|---|---|
.isr_vector | Flash, sector 0 | 5 cycles @ 480 MHz with ART | reset + IRQ vector table |
.text (cold) | Flash | 5 cycles + ART prefetch | bulk application code |
.text.fastcode | ITCM (64 kB) | 1 cycle | hot ISRs, control inner loops |
.data | DTCM (128 kB) | 1 cycle | task stacks, hot variables |
.bss.dma | AXI SRAM (512 kB), non-cacheable | 2 cycles | DMA buffers (avoids cache coherence) |
.heap | external SDRAM | 20–50 cycles | RTOS heap, telemetry buffers |
.text.crypto_secure | secure Flash | 5 cycles | TF-M Secure World |
__attribute__((section(".text.fastcode"))) on a function moves it into ITCM at link time. The .map file reveals where every symbol landed — auditing it monthly catches the silent migration of a hot ISR back into slow Flash after a refactor.
Lock-free patterns
When two contexts (ISR ↔ task, or core ↔ core) must communicate but cannot share a mutex (ISR cannot block on a mutex), use lock-free patterns:
- Single-producer single-consumer (SPSC) ring buffer. Producer increments a head index, consumer increments a tail index. With
headandtailin separate cache lines andvolatile(or C11atomic_load/atomic_storewith appropriate memory order on Cortex-M), no lock needed. FreeRTOSStreamBufferand Zephyrring_bufare SPSC. - Sequence-lock (seqlock). Writer increments a counter before and after; readers retry if the counter changed during their read. Bounded retries on Cortex-M (writer is the higher-priority ISR), but unbounded under hypothetical priority inversion.
- Read-Copy-Update (RCU). Standard in Linux; rare on Cortex-M because the read-side overhead and grace-period machinery cost more than a simple disabling of interrupts for the same critical region.
__LDREX/__STREXatomics. Cortex-M3 and up have load-exclusive / store-exclusive instructions; compare-and-swap composed from these implements atomic increment, atomic linked-list push, etc. C11_Atomicand Rustcore::sync::atomiccompile down to LDREX/STREX on the M-class.
7. Real-time languages and practices
- C — dominant. MISRA C:2012 (with Amendments 1–4) and CERT C are the safety-critical rule sets. Bans on dynamic allocation, recursion (rule 17.2), undefined behaviour, type punning, mixed signed/unsigned arithmetic.
- C++ — increasing in autotive and medical. MISRA C++:2023 replaces the older MISRA C++:2008. AUTOSAR C++14 (now folded into MISRA C++:2023) is the automotive subset. Exceptions disabled (
-fno-exceptions), RTTI disabled (-fno-rtti), nonew/deletein real-time paths, no STL containers that allocate. - Ada / Ada 2022 —
Ravenscar ProfileandJorvik Profileare real-time subsets with provable scheduling properties. SPARK Ada is a formally verifiable Ada subset with contract-based proof. Aerospace deployments: Boeing 777/787 fly-by-wire, Lockheed Martin F-35 fuel-management, Eurofighter Typhoon mission system, Ariane 5 onboard computer (post-1996 rewrite). - Rust — embedded growth:
no_stdmode,embedded-halperipheral traits, RTIC (Real-Time Interrupt-driven Concurrency) framework provides static-priority scheduling with the Stack Resource Policy at compile time. Ferrocene (Ferrous Systems / AdaCore) is a qualified Rust toolchain — ISO 26262 ASIL-D and IEC 61508 SIL-4 since 2024. AUTOSAR Rust integration guidelines published 2024. - Forth, eRTL, Hume, Esterel, Lustre / SCADE — niche or academic; SCADE Suite (ANSYS) is widely used for DO-178C airborne software code generation (Airbus, Embraer, Bombardier).
- Model-based design (MBD) — MATLAB/Simulink Embedded Coder, ANSYS SCADE Suite, dSPACE TargetLink, ETAS ASCET generate MISRA-compliant C from block diagrams. The generated code is tool-qualified (Embedded Coder is ISO 26262 / DO-178C / IEC 62304 / IEC 61508 tool-qualified).
8. Worked examples
Example A — Rate-monotonic schedulability
Three periodic tasks on a single Cortex-M4 core, no preemption overhead, no blocking:
| Task | T (ms) | C (ms) | D (ms) | U |
|---|---|---|---|---|
| τ_1 (current loop) | 50 | 10 | 50 | 0.20 |
| τ_2 (speed loop) | 100 | 20 | 100 | 0.20 |
| τ_3 (telemetry) | 200 | 50 | 200 | 0.25 |
ΣU = 0.65. Liu-Layland bound for n = 3: 3·(2^(1/3) − 1) = 3 · 0.2599 ≈ 0.779. Since 0.65 < 0.779, the task set is RM-schedulable.
Verify with response-time analysis for the lowest-priority task τ_3 (hp = {τ_1, τ_2}):
R_3^(0) = C_3 = 50 R_3^(1) = 50 + ⌈50/50⌉·10 + ⌈50/100⌉·20 = 50 + 10 + 20 = 80 R_3^(2) = 50 + ⌈80/50⌉·10 + ⌈80/100⌉·20 = 50 + 20 + 20 = 90 R_3^(3) = 50 + ⌈90/50⌉·10 + ⌈90/100⌉·20 = 50 + 20 + 20 = 90 ✓ (converged)
R_3 = 90 ms ≤ D_3 = 200 ms — feasible with 110 ms of slack.
Example B — Priority inversion and the fix
Three tasks: H (high), M (medium), L (low). H and L share a mutex on a sensor-data structure; M is independent.
Timeline without priority inheritance:
- t = 0: L runs, locks mutex.
- t = 5: H released. H pre-empts L. H attempts to lock mutex → blocks. L resumes.
- t = 6: M released. M pre-empts L. L is suspended while holding the mutex. H still blocked.
- t = 6 to t = 6 + C_M: M runs to completion. H blocked the whole interval.
- t = 6 + C_M: L resumes briefly, finishes critical section, releases mutex. H unblocks.
Inversion duration = C_M, which can be arbitrarily long. Mars Pathfinder’s case had C_M ≈ 1.5 s, exceeding the watchdog’s 0.8 s → spacecraft reset.
Timeline with priority inheritance:
- t = 0: L runs, locks mutex.
- t = 5: H released, attempts lock, blocks. L’s priority is raised to H’s.
- t = 6: M released. M’s priority < L’s elevated priority → M waits.
- L runs to end of critical section, releases mutex, drops back to original priority.
- H unblocks, pre-empts L, runs.
- Eventually M runs.
Inversion duration ≤ longest critical section of L, which is bounded by code review. FreeRTOS xSemaphoreCreateMutex() enables PI by default; binary semaphores do not. Zephyr k_mutex is always PI. POSIX 1003.1c mutexes require PTHREAD_PRIO_INHERIT to be set explicitly.
Example C — 1 kHz motor-control loop on STM32G474 with FreeRTOS
Hardware setup: TIM1 advanced timer at 170 MHz, configured for center-aligned PWM at 1 kHz (ARR = 169999), with TRGO triggering ADC1 on each PWM update. ADC1 in DMA mode samples 4 channels (Ia, Ib, V_DC, encoder), DMA TC fires interrupt that posts a binary semaphore to the control task. Control task at priority 4 (configMAX_PRIORITIES = 5, idle = 0).
Profiling with DWT cycle counter (CYCCNT register, 170 MHz tick):
ISR (DMA TC) entry → semaphore give: 18 cycles (106 ns)
Context switch to control task: 192 cycles (1.13 µs)
Clarke + Park transforms (CORDIC accelerated): 1320 cycles (7.76 µs)
Two PI current loops + anti-windup: 980 cycles (5.76 µs)
Inverse Park + SVPWM: 1840 cycles (10.82 µs)
Write TIM1->CCR1/2/3: 42 cycles (0.25 µs)
Total compute (typical): 12500 cycles (~73.5 µs)
WCET (worst path, observed over 24h): 16200 cycles (95.3 µs)
WCET / T = 95.3 µs / 1000 µs ⇒ U = 0.095. Stack usage uxTaskGetStackHighWaterMark() = 320 bytes peak from a 1024-byte allocation. Jitter (timestamp at task entry, sample-to-sample variation) measured on a logic analyser: ±1.8 µs, dominated by FreeRTOS context-switch variance. Sufficient for 1 kHz field-oriented control of a 4-pole-pair PMSM at up to 12 000 rpm electrical, well within the 200 Hz current-loop bandwidth target.
The same code authored without RTOS (bare-metal, ISR-only) achieves ±50 ns jitter — better, but the RTOS buys clean separation of telemetry, CAN, and fault-handling tasks at the cost of ~1.5 µs jitter. Acceptable trade for any commercial product.
Example D — Sporadic + periodic mix, schedulability test
A drone autopilot has four tasks plus a sporadic IMU-interrupt task:
| Task | Type | T or T_min (ms) | C (ms) | D (ms) | Priority |
|---|---|---|---|---|---|
| τ_IMU | sporadic | 1 (T_min) | 0.15 | 1 | 1 (highest) |
| τ_rate | periodic | 2 | 0.40 | 2 | 2 |
| τ_attitude | periodic | 5 | 0.80 | 5 | 3 |
| τ_navigation | periodic | 20 | 4.00 | 20 | 4 |
| τ_telemetry | periodic | 100 | 12.0 | 100 | 5 (lowest) |
ΣU = 0.15 + 0.20 + 0.16 + 0.20 + 0.12 = 0.83. Liu-Layland bound for n = 5: 5·(2^(1/5) − 1) = 0.743 — task set fails the sufficient condition but may still be schedulable.
Response-time analysis on τ_telemetry:
R^(0) = 12.0 R^(1) = 12 + ⌈12/1⌉·0.15 + ⌈12/2⌉·0.40 + ⌈12/5⌉·0.80 + ⌈12/20⌉·4.0 = 12 + 1.80 + 2.40 + 2.40 + 4.0 = 22.6 R^(2) = 12 + 23·0.15 + 12·0.40 + 5·0.80 + 2·4.0 = 12 + 3.45 + 4.8 + 4.0 + 8.0 = 32.25 R^(3) = 12 + 33·0.15 + 17·0.40 + 7·0.80 + 2·4.0 = 12 + 4.95 + 6.8 + 5.6 + 8.0 = 37.35 R^(4) = 12 + 38·0.15 + 19·0.40 + 8·0.80 + 2·4.0 = 12 + 5.70 + 7.6 + 6.4 + 8.0 = 39.7 R^(5) = 12 + 40·0.15 + 20·0.40 + 8·0.80 + 2·4.0 = 12 + 6.0 + 8.0 + 6.4 + 8.0 = 40.4 R^(6) ≈ 40.4 (converged)
R_telemetry = 40.4 ms ≤ D_telemetry = 100 ms ✓ schedulable, with 59.6 ms slack. The sufficient Liu-Layland test rejected the set; the necessary-and-sufficient response-time analysis accepted it. Always run the latter — the former is a conservative shortcut, not a verdict.
9. Edge cases and gotchas
Stack overflow silently corrupts adjacent memory. A 256-byte stack with an unintended 280-byte allocation does not crash — it writes 24 bytes into the next task’s TCB or heap. The bug surfaces later as a mysterious mutex corruption or context-switch fault. Always enable stack overflow checking method 2 (MPU guard) on Cortex-M; FreeRTOS configCHECK_FOR_STACK_OVERFLOW = 2 + vApplicationStackOverflowHook() callback.
Priority inversion — covered above. Always use mutex (not binary semaphore) for resource locking, and verify that the RTOS implements priority inheritance for the chosen mutex flavour.
Race conditions between ISR and task. A shared variable read by a task and written by an ISR must be accessed atomically — single-word writes are atomic on 32-bit Cortex-M (32-bit word at aligned address), but read-modify-write sequences are not. Use __disable_irq() / __enable_irq() to bracket the task-side access, or use LDREX/STREX atomics, or use a queue instead of a shared variable.
Watchdog refresh discipline. Refresh from the main loop only, never from an ISR. An ISR can keep refreshing the watchdog while the main loop is stuck — exactly the failure mode the watchdog should catch. ISO 26262 ASIL-D requires a windowed watchdog: refresh too early triggers a reset, refresh too late triggers a reset.
Memory fragmentation from malloc. Heap allocators (even FreeRTOS heap_4 with coalescing) fragment over time. A long-running system that allocates and frees variable-sized buffers will eventually fail to allocate a buffer despite having sufficient total free memory. Solution: fixed-size pools, or static allocation, or a region/slab allocator.
Deadlock from disordered lock acquisition. Task A locks mutex M1 then attempts M2; Task B locks M2 then attempts M1 → both blocked forever. Mitigation: define a global lock-ordering rule (sort by address, by ceiling priority, or by hand-assigned ID), or use priority ceiling protocol which provably prevents deadlock.
Timer wrap-around. A 32-bit millisecond counter wraps every 2^32 / 1000 / 86400 ≈ 49.7 days. A naïve check if (now > deadline) fails after wrap. Always compare via signed subtraction: if ((int32_t)(now - deadline) >= 0). Use uint64_t time bases in any system that runs > 49 days without reset (i.e. almost any industrial product).
Tickless-idle wakeup jitter. Coming out of low-power sleep modes adds 5–50 µs of CPU resume latency (PLL relock, Flash wake, regulator stabilisation). RTOS schedulers compensate by waking N ticks early — but compensation accuracy varies per platform. For tight real-time on a battery device, profile wake jitter and budget against it.
Cache effects on WCET (Cortex-M7 / Cortex-A). With instruction or data cache enabled, the first access to a code path is a cache miss (40–100 cycles); subsequent accesses are 1-cycle. WCET assumes the cold case. Mitigations: lock critical code into cache (Cortex-M7 supports cache way-locking via SCB→CCR), execute critical code from TCM (single-cycle deterministic SRAM), or disable cache for the affected paths.
DMA + cache coherence on Cortex-M7. When DMA writes to memory and CPU reads it (or vice versa), with D-cache enabled, the CPU may read stale cached data. Required pattern: invalidate cache before DMA-write-to-memory then CPU-read; clean cache after CPU-write then DMA-read-from-memory. Use SCB_InvalidateDCache_by_Addr() and SCB_CleanDCache_by_Addr(). Forgetting this is a common bug; symptom is data that “should be there” reading as zeros or stale.
Interrupt latency floor. Cortex-M0: ~16 cycles. Cortex-M0+: ~15 cycles. Cortex-M3/M4: 12 cycles (zero wait-state). Cortex-M7 with cache: 12 cycles + cache miss. These are the best case; ISR jitter from higher-priority ISRs adds on top. NVIC tail-chaining avoids one stack-pop/push pair when ISRs are back-to-back (saves ~6 cycles).
MISRA C false positives and deviations. No real-world embedded C is 100 % MISRA-compliant without deviations. Standard practice: document each deviation with rationale and review, accept the static-analysis warning, and route it to a controlled deviation log (Polyspace, LDRA, and PC-Lint all support per-line suppression with rationale). Auditors expect deviations; they do not expect zero warnings.
RTOS-certification scope creep. SafeRTOS kernel is ISO 26262 ASIL-D certified. Your application using SafeRTOS is not automatically ASIL-D — you must produce the application’s own evidence (requirements traceability, unit tests, MC/DC coverage, integration tests, static analysis, code review). The kernel certification is a foundation, not a finish line.
Linux PREEMPT_RT is soft real-time. With the rt-preempt patch (mainlined as of Linux 6.12), most kernel paths are preemptible and a process with SCHED_FIFO priority can achieve ~50 µs worst-case latency on a tuned x86_64 or Cortex-A platform. Adequate for ms-class industrial control. Not adequate for hard-real-time at sub-ms scale — use a dedicated RTOS core on a heterogeneous SoC (i.MX 8M Plus M7, AM62x M4, STM32MP1 M4) for that.
Cybersecurity attacks on safety functions. A safety-critical loop with a network interface is also a potential attack surface. IEC 62443 (industrial automation security), ISO 21434 (automotive cybersecurity), and the EU Cyber Resilience Act require isolation of safety bus from corporate IT and signed/encrypted firmware updates. TF-M PSA Certified is the Cortex-M secure-boot baseline.
Field updates / OTA. A/B partitioning (two Flash slots, switchable bootloader), version rollback after failed boot, signed images. SUIT (Software Updates for IoT, RFC 9019/9124) is the IETF standard; vendor toolkits (Mender, MCUBoot, Nordic nRF Secure Bootloader, SiLabs Gecko Bootloader) implement it.
ARINC 653 partitioning vs RTOS scheduling. ARINC 653 (avionics) is a partition-level cyclic schedule on top of a task-level RTOS. Each partition gets a fixed time window each major frame; tasks inside the partition are scheduled by the RTOS in the conventional sense. Both layers must close timing.
Critical-section length bounds blocking. Under priority inheritance, the worst-case blocking term B_i added to response-time analysis is the longest critical section among all lower-priority tasks that lock any mutex shared with τ_i. R_i = C_i + B_i + Σ ⌈R_i/T_j⌉·C_j. Long critical sections (memcpy of a kB-sized struct, slow Flash erase) silently destroy the timing budget. Keep critical sections sub-microsecond; copy pointers under lock, not buffers.
False sharing on dual-core M7+M4 (STM32H7, i.MX RT). When both cores access independent variables that happen to live in the same cache line, the cache coherence protocol bounces the line between cores. Symptom: a fast inner loop slows down 5–20× when a low-priority task on the other core touches “unrelated” data. Fix: align hot data to cache-line boundaries (__attribute__((aligned(32))) on M7) and group per-core data into separate regions.
FreeRTOS-MPU vs Zephyr userspace. Both provide MPU-isolated tasks (“processes”) with restricted access to memory and peripherals. FreeRTOS-MPU is opt-in per task and requires hand-declaring memory regions. Zephyr CONFIG_USERSPACE gives full kernel/user separation with system-call gates. Userspace adds 20–60 cycles per system call (MPU reprogramming) — verify the timing budget.
Single-event upsets (SEU) in space and high-altitude. Cosmic-ray-induced bit flips in SRAM occur at rates of ~10^{-7} upsets per bit per day at sea level, ~10^{-5} per bit per day at 12 km altitude (commercial aviation), ~10^{-3} per bit per day in LEO. Toyota’s unintended-acceleration case alleged SEU in non-ECC RAM; whether causal or not, automotive ECUs since 2015 widely ship with ECC SRAM (S32K3, STM32G491, AURIX TC3xx). Aerospace and space-grade parts add radiation-hardened cells and triple-modular-redundancy (TMR) registers.
Worst-case-stack vs deepest-call-chain. GCC’s -fstack-usage flag emits a .su file per source listing per-function stack frames; combined with a call graph (gcc -fcallgraph-info or static analysis), you can compute provable per-task stack usage. Required evidence for ISO 26262 / DO-178C. Recursion and function pointers defeat the analysis — MISRA C 17.2 bans recursion for exactly this reason; indirect calls must be enumerated by hand.
10. Standards and certification by industry
| Industry | Primary standard | Software clause | Highest classification |
|---|---|---|---|
| Avionics | DO-178C / RTCA SC-205 | software considerations | DAL-A (catastrophic) |
| Avionics RTOS partitioning | ARINC 653 P1-5 | time + space partitioning | required for DAL-A |
| Automotive | ISO 26262 (2018) | Part 6: software | ASIL-D (life-threatening) |
| Automotive cybersecurity | ISO 21434 (2021) | software security | n/a (process) |
| Medical software | IEC 62304 (2015) | software life cycle | Class C (life-threatening) |
| Industrial safety | IEC 61508 (2010) | Part 3: software | SIL-4 |
| Industrial machinery | ISO 13849 / IEC 62061 | based on IEC 61508 | PL e / SIL CL 3 |
| Industrial automation security | IEC 62443 | software development life cycle | SL 4 |
| Railway | EN 50128 / EN 50657 | software for railway control | SIL-4 |
| Nuclear | IEC 60880 | software for safety I&C | Category A |
| Process industries | IEC 61511 | functional safety in process | SIL-3 |
POSIX 1003.1b (1993) defines real-time extensions to POSIX: priority-preemptive scheduling, message queues, named semaphores, timers, shared memory, asynchronous I/O. Implemented (in part) by QNX, VxWorks, RTEMS, NuttX, Linux. POSIX 1003.1c (1995) adds threads and mutex attributes.
11. MISRA C rule categories (selected)
| Category | Example rules | Topic |
|---|---|---|
| Mandatory | 9.1, 13.2, 13.6 | Variables initialised before read; side-effect ordering; volatile in sequence points |
| Required | 8.4, 8.5, 11.x, 17.2 | External linkage; pointer-type conversions; banning recursion |
| Advisory | 4.1, 8.13, 20.x | Octal usage discouraged; const-correctness; preprocessor restrictions |
| Directives | Dir 4.6, Dir 4.12 | Use of typedefs for fixed-width types; no dynamic memory |
MISRA C:2012 has ~143 rules + 16 directives. Amendment 4 (2023) adds C18 / C2x coverage. A MISRA-compliant codebase has all mandatory rules satisfied, required rules satisfied or deviation-documented, and advisory rules considered. Tools (Polyspace Bug Finder, LDRA Testbed, PC-Lint Plus, Klocwork) automate ~90 % of rule checking; the rest requires human review.
12. Scheduling-algorithm comparison
| Algorithm | Priority assignment | Schedulable bound | Pre-emption | Optimality | Typical use |
|---|---|---|---|---|---|
| Rate-Monotonic (RM) | static, 1/T | n·(2^(1/n) − 1), → ln 2 | yes | optimal fixed-priority for D = T | most fixed-priority RTOSs |
| Deadline-Monotonic (DM) | static, 1/D | same shape, replace T with D | yes | optimal fixed-priority for D ≤ T | aerospace fixed-priority |
| Earliest-Deadline-First (EDF) | dynamic, smallest absolute deadline | ΣU ≤ 1 | yes | optimal uniprocessor | research, Linux SCHED_DEADLINE |
| Least-Laxity-First (LLF) | dynamic, smallest (D − C_remaining) | ΣU ≤ 1 | yes | optimal uniprocessor; thrashes | rarely deployed |
| Cyclic executive | offline schedule | by construction | no | hand-tuned | ARINC 653 partitions, legacy avionics |
| Round-robin | equal priority | n/a (no guarantee) | timeslice | soft real-time only | UI / non-real-time threads |
| Fair-share (CFS) | dynamic, weighted vruntime | n/a | yes | average-fairness | Linux default; not real-time |
| Time-Triggered (TTA / TTEthernet) | offline global schedule | by construction | yes | safety-critical | avionics networks, AUTOSAR FlexRay |
13. Worker / code-pattern examples
A canonical FreeRTOS deferred-interrupt skeleton on Cortex-M4 (illustrates priority discipline, semaphore-from-ISR, and stack-aware FPU use):
/* High-priority control task — period 1 ms, triggered from TIM1 ISR */
static StackType_t control_stack[1024] __attribute__((aligned(8)));
static StaticTask_t control_tcb;
static SemaphoreHandle_t control_sem;
void TIM1_UP_IRQHandler(void) {
TIM1->SR &= ~TIM_SR_UIF; /* clear update flag */
BaseType_t hpw = pdFALSE;
xSemaphoreGiveFromISR(control_sem, &hpw);
portYIELD_FROM_ISR(hpw); /* request context switch if needed */
}
static void control_task(void *arg) {
(void)arg;
for (;;) {
xSemaphoreTake(control_sem, portMAX_DELAY);
uint32_t t0 = DWT->CYCCNT;
run_foc_pipeline(); /* Clarke/Park/PI/SVPWM */
uint32_t cycles = DWT->CYCCNT - t0;
if (cycles > wcet_observed) wcet_observed = cycles;
}
}
int main(void) {
SystemClock_Config_170MHz();
NVIC_SetPriorityGrouping(0); /* all preemption, no sub */
NVIC_SetPriority(TIM1_UP_IRQn, 2); /* high, above RTOS */
NVIC_SetPriority(PendSV_IRQn, 15); /* lowest */
control_sem = xSemaphoreCreateBinary();
xTaskCreateStatic(control_task, "ctl", 1024, NULL, 4,
control_stack, &control_tcb);
vTaskStartScheduler();
for (;;);
}Notes: ISR priority (NVIC = 2) is numerically higher than the RTOS-managed range (configMAX_SYSCALL_INTERRUPT_PRIORITY typically 5–11), so this ISR is not allowed to call FreeRTOS API, except for the *FromISR() variants which are designed to be lock-free against the scheduler. Calling a non-*FromISR API from above this priority is the single most common FreeRTOS bug.
14. Interrupt-latency and ISR-overhead reference
For zero-wait-state memory access and no higher-priority ISRs pending (best case):
| Core | Pipeline | IRQ entry | Tail-chain | Lazy FP push | IRQ exit | DMIPS/MHz |
|---|---|---|---|---|---|---|
| Cortex-M0 | 3-stage | 16 cyc | n/a | n/a | 12 cyc | 0.84 |
| Cortex-M0+ | 2-stage | 15 cyc | n/a | n/a | 11 cyc | 0.95 |
| Cortex-M3 | 3-stage | 12 cyc | 6 cyc | n/a | 10 cyc | 1.25 |
| Cortex-M4 (no FPU) | 3-stage | 12 cyc | 6 cyc | n/a | 10 cyc | 1.25 |
| Cortex-M4F (FPU) | 3-stage | 12 cyc | 6 cyc | +17 words on first FP op | 10 cyc | 1.25 |
| Cortex-M7 | 6-stage dual-issue | 12 cyc + cache | 6 cyc | +17 words | 10 cyc + cache | 2.14 |
| Cortex-M33 | 3-stage | 12 cyc | 6 cyc | +17 words | 10 cyc | 1.50 |
| Cortex-M55 / M85 | 4–7 stage + Helium | ~14 cyc | 6 cyc | +17 words + Helium state | ~12 cyc | 1.6 / 3.0 |
Tail-chaining: when an ISR returns and another pending IRQ of equal or higher priority is ready, the NVIC reuses the stacked context — saves the stack-pop and re-push (~6 cycles). Significant under high IRQ load.
Lazy FP stacking: floating-point registers (S0–S15, FPSCR) are pushed only on the first FP instruction inside an ISR. ISRs that never touch the FPU pay zero FP overhead even with FPCCR.ASPEN = 1.
15. Tools and ecosystem
IDEs and toolchains. STM32CubeIDE (free, GCC, ST RTOS-aware debug), MCUXpresso (NXP, free, GCC), e² studio (Renesas, free, GCC + IAR), MPLAB X (Microchip, free, XC GCC), ESP-IDF + VS Code (Espressif, free), nRF Connect SDK + VS Code (Nordic, Zephyr-based, free), Keil MDK (Arm, commercial USD ~5 k/seat, deep CMSIS integration), IAR Embedded Workbench (commercial USD 3–7 k/seat, smallest code), SEGGER Embedded Studio (free for non-commercial), Green Hills MULTI (commercial, safety-critical), PlatformIO (open-source cross-vendor orchestration).
Debug probes. SEGGER J-Link family (EDU USD 70 → ULTRA+ USD 1500), ST-Link/V3 (USD 30–90), CMSIS-DAP clones, Raspberry Pi Debug Probe (USD 12), Black Magic Probe, NXP MCU-Link, Arm ULINKplus/pro (energy + ETM trace), Lauterbach TRACE32 (USD 5 k–50 k, full ETM trace, gold-standard for safety-critical bring-up).
RTOS-aware debug. All major IDE / probe pairings display task lists, mutex/semaphore states, queue contents, and stack high-water-marks at breakpoints. SEGGER Ozone debugger is best-in-class for FreeRTOS/embOS/Zephyr.
Trace and profiling. SEGGER SystemView + RTT (sub-microsecond event capture via SWD). Percepio Tracealyzer (RTOS-aware timeline visualiser, supports FreeRTOS, Zephyr, ThreadX, µC/OS-III, SafeRTOS). Lauterbach TRACE32 + ETM (full instruction trace). Cortex-M DWT cycle counter (free, in-target profiling).
Static analysis and MISRA / CERT compliance. PC-Lint Plus (Gimpel, commercial), Polyspace Bug Finder + Code Prover (MathWorks, abstract interpretation, run-time error proofs), Coverity (Synopsys), LDRA Testbed (DO-178C tool-qualified), PRQA / QA-C (Perforce), Klocwork (Perforce, security focus), clang-tidy + cppcheck (open-source first pass).
WCET analysis. AbsInt aiT (industry standard, IPET-based static WCET, Airbus / SpaceX use cases), Rapita RapiTime (measurement-based hybrid WCET, EASA / FAA accepted for DO-178C), Tidorum Bound-T (open-source, dated but conceptually clean).
Unit and integration test. VectorCAST (MC/DC coverage, DO-178C / ISO 26262 tool-qualified), LDRA TBrun, Parasoft C/C++test, Cantata (QA Systems), open-source Unity + CMock for FreeRTOS / Zephyr projects.
Model-based / code-gen. MATLAB Simulink + Embedded Coder, ANSYS SCADE Suite + KCG (DO-178C-qualified code generator), dSPACE TargetLink, ETAS ASCET. All produce MISRA-compliant C with traceability artefacts for certification.
HIL platforms. dSPACE SCALEXIO / MicroAutoBox III, NI VeriStand + PXI, Speedgoat baseline / performance / mobile, OPAL-RT OP4510 / OP5707, Vector CANoe + VT System. Drives the target ECU’s I/O from a real-time PC simulating sensors, actuators, and network traffic. Required for ASIL-D / DAL-A test campaigns.
Simulation. Renode (Antmicro, open-source full-system multi-MCU emulation; CI-friendly), QEMU (Cortex-M models), Wokwi (browser-hosted AVR / ESP32 / RP2040 / STM32), Proteus VSM (commercial mixed-signal).
16. Cross-references
[[Engineering/microcontrollers]]— the silicon substrate on which all of this runs; NVIC, MPU, FPU, peripheral primitives.[[Engineering/digital-logic]]— clock, ISR-and-IRQ basics, synchronous design that the MCU itself implements.[[Engineering/digital-control]]— sampled-data control loop math; this note is its implementation layer.[[Engineering/classical-control]]— the continuous-time controller designs that become discrete loops on the MCU.[[Engineering/mpc-control]](planned) — embedded MPC; QP solved every sample.[[Engineering/power-electronics]]— motor drives and switching converters controlled from MCU PWM peripherals.[[Engineering/electric-motors]]— FOC and direct-torque firmware running on the controllers described here.[[Engineering/fpga-design]](planned, same Engineering batch) — hardware companion to the software-real-time approach for sub-µs loops.[[Robotics/motors-electric]](planned) — FOC, sensorless estimation, SVPWM at 10–32 kHz on STM32G4 / TI C2000.[[Robotics/bayesian-estimation]](planned) — Kalman / complementary filters running inside the real-time task structure.[[Languages/Tier3/embedded-firmware]](planned) — C and Rust idioms forno_std, ISRs, volatile, linker scripts.[[Languages/Tier3/automotive-onvehicle]](planned) — AUTOSAR ARXML configuration description format.[[Languages/ada]](planned) — Ravenscar, Jorvik, SPARK proof annotations.
17. Citations
- Liu, C. L. & Layland, J. W. (1973). “Scheduling Algorithms for Multiprogramming in a Hard-Real-Time Environment.” J. ACM, 20(1), 46–61. Canonical paper introducing rate-monotonic and earliest-deadline-first scheduling, the Liu-Layland utilisation bound, and the optimality proofs.
- Buttazzo, G. C. (2011). Hard Real-Time Computing Systems: Predictable Scheduling Algorithms and Applications (3rd ed.). Springer. Comprehensive treatment of scheduling theory, aperiodic servers, resource access protocols. The reference.
- Liu, J. W. S. (2000). Real-Time Systems. Prentice Hall. Classroom standard, broader scope than Buttazzo, strong on aperiodic and multiprocessor.
- Burns, A. & Wellings, A. (2009). Real-Time Systems and Programming Languages: Ada, Real-Time Java and C/Real-Time POSIX (4th ed.). Addison-Wesley. Language-level real-time programming; Ravenscar Profile origin.
- Cottet, F., Delacroix, J., Kaiser, C. & Mammeri, Z. (2002). Scheduling in Real-Time Systems. Wiley. Practitioner-oriented schedulability proofs and analysis.
- Joseph, M. & Pandya, P. (1986). “Finding response times in a real-time system.” The Computer Journal, 29(5), 390–395. The response-time analysis fixed-point iteration.
- Sha, L., Rajkumar, R. & Lehoczky, J. P. (1990). “Priority inheritance protocols: an approach to real-time synchronization.” IEEE Transactions on Computers, 39(9), 1175–1185. The PIP / PCP paper; Mars Pathfinder’s eventual fix referenced this work.
- Audsley, N. C., Burns, A., Richardson, M., Tindell, K. & Wellings, A. J. (1993). “Applying new scheduling theory to static priority pre-emptive scheduling.” Software Engineering Journal, 8(5), 284–292. Extension of response-time analysis to arbitrary deadlines and release jitter.
- Labrosse, J. J. (2009). µC/OS-III: The Real-Time Kernel. Micrium Press. Kernel-from-first-principles companion to one of the cleanest RTOS codebases.
- Koopman, P. (2010). Better Embedded System Software. Drumnadrochit Education. Watchdog discipline, code-review checklists, certification mindset.
- Ganssle, J. G. (2008). The Art of Designing Embedded Systems (2nd ed.). Newnes. Bench-engineering wisdom on real-time, interrupts, and the hardware-software boundary.
- Stewart, D. B. (2006). “Twenty-Five Most Common Mistakes with Real-Time Software Development.” Embedded Systems Conference paper. Practitioner errors catalogue.
- Reeves, G. E. (1997). “What really happened on Mars Rover Pathfinder.” The Risks Digest, 19(49). First-person account of the priority-inversion incident and JPL’s fix.
- Leveson, N. G. & Turner, C. S. (1993). “An Investigation of the Therac-25 Accidents.” IEEE Computer, 26(7), 18–41. Definitive analysis of the most-cited safety-critical software failure.
- MISRA Consortium. MISRA C:2012 Guidelines for the Use of the C Language in Critical Systems, with Amendments 1 (2016), 2 (2020), 3 (2022), 4 (2023). HORIBA MIRA Ltd.
- MISRA Consortium. MISRA C++:2023 Guidelines for the Use of C++17 in Critical and Safety-Related Systems. Supersedes MISRA C++:2008 and AUTOSAR C++14.
- CERT / SEI (Carnegie Mellon). CERT C Secure Coding Standard, 2nd ed. (2014, updated 2024). Complementary to MISRA C for security-relevant code.
- AUTOSAR Consortium. Classic Platform R22-11 Specifications: Operating System (SWS_Os), RTE, COM Stack, Memory Stack, Crypto Stack. autosar.org.
- ARINC 653 Parts 1–5 (2015–2019). Avionics Application Software Standard Interface. ARINC Industry Activities.
- ISO 26262:2018, Parts 1–12. Road Vehicles — Functional Safety.
- RTCA DO-178C (2011). Software Considerations in Airborne Systems and Equipment Certification. With supplements DO-330 (tool qualification), DO-331 (MBD), DO-332 (OOT), DO-333 (formal methods).
- IEC 62304:2006 + AMD1:2015. Medical Device Software — Software Life Cycle Processes.
- IEC 61508:2010, Parts 1–7. Functional Safety of Electrical / Electronic / Programmable Electronic Safety-related Systems.
- IEEE Std 1003.1b-1993 + 1003.1c-1995. POSIX Real-Time and Threads Extensions. Merged into POSIX.1-2017 and POSIX.1-2024.
- Wittenstein High-Integrity Systems. SafeRTOS User Manual and Safety Case Documents. highintegritysystems.com.
- Wind River. VxWorks 7 Architecture and Programmer’s Guide; DO-178C Certification Evidence Pack.
- BlackBerry. QNX Neutrino RTOS System Architecture Guide; QNX OS for Safety Pre-Certified Pack.
- Green Hills Software. INTEGRITY-178 tuMP and DO-178C / EAL 6+ Common Criteria Certifications. ghs.com.
- Zephyr Project. Zephyr Documentation: Kernel, Scheduling, Memory Protection, Userspace. zephyrproject.org.
- FreeRTOS / AWS. FreeRTOS Kernel Developer’s Guide and FreeRTOS-MPU Documentation. freertos.org.
- Nordic Semiconductor. nRF Connect SDK + Zephyr Real-Time Documentation.
- AbsInt. aiT WCET Analyzer User’s Manual. absint.com.
- Rapita Systems. RapiTime Verification Tool Documentation. rapitasystems.com.
- TF-M / Trusted Firmware-M. Reference Implementation Documentation; PSA Certified Level 2 and 3 Guidance. trustedfirmware.org.