đĄïž Deterministic C â Safety-Critical Avionics
C âprofilĂ©â pour systĂšmes critiques : dĂ©terminisme (temps/mĂ©moire/flux), MISRA-C, certification DO-178C, WCET, MC/DC, tooling qualifiĂ©.
Le âC avioniqueâ nâest pas un langage diffĂ©rentâŠ
âŠcâest une discipline : un sous-ensemble strict du C, un style de conception, des rĂšgles, des outils et un dossier de preuves qui rendent le logiciel audit-friendly et certifiable.

Définition : Deterministic C
DĂ©terminisme fonctionnel + temporel + mĂ©moire. Pourquoi lâavionique cherche lâauditabilitĂ© et la prĂ©visibilitĂ© avant la âflexibilitĂ©â.
DeterminismBoundedAuditRĂšgles de codage : MISRA / CERT
Interdits usuels (malloc, rĂ©cursion, UB), typage strict, conversions explicites, conventions dâAPI, âno surprisesâ.
MISRA-CCERT CNo UBRTOS / Partitioning
ModĂšle temps rĂ©el : tĂąches, pĂ©riodes, prioritĂ©, jitter. Partitionnement type ARINC 653 (concept). Ăviter lâimprĂ©visible.
RTOSSchedulingPartitioningWCET & Worst-Case
Le vrai nerf : analyser/expliquer le pire cas. Boucles bornées, chemins bornés, latences IO maßtrisées.
WCETBounded loopsTimingV&V : tests, MC/DC, coverage
Unit, integration, system tests. Justifications. Couverture structurelle (dont MC/DC selon criticité). Rapports traçables.
MC/DCCoverageTraceabilityOutils qualifiés & pipeline
Analyse statique, rÚgles, build reproductible, tool qualification (selon usage), CI de conformité (style, warnings, reports).
Static analysisTool qualCIDomaines avionique
FCS, autopilot, navigation, engine control, braking. OĂč âdeterministic Câ est typiquement utilisĂ© (calculateurs embarquĂ©s).
FCSNavEngineA320 vs 737 MAX (MCAS)
Lecture âengineeringâ : dĂ©pendance capteurs, gestion de modes, dĂ©gradation, alarmes, tolĂ©rance de panne. Sans sensationalisme.
A320 family737 MAXMCASPatterns sûrs
State machine explicite, watchdogs, sanitization, âdefensive designâ, double validation, âsafe defaultsâ, instrumentation.
FSMWatchdogDefensiveChecklist âC dĂ©terministeâ
Une checklist de revue : UB, overflow, conversions, bornes, reentrancy, ISR-safe, concurrency, logs, degradations.
ReviewBoundedSafetyRéférences & ressources
Standards (DO-178C, DO-330, DO-331), MISRA, CERT C, concepts RTOS. Liens officiels / pages dâintro.
DO-178CDO-330MISRAMini-projet : module âsafe clampâ
Exemple concret : normalisation capteur, filtrage simple, clamp, diagnostics, tests unitaires, rapport couverture (structurel).
ExampleUnit testsDocsLe déterminisme en 3 axes
| Axe | Objectif | Ce quâon maĂźtrise | Exemples |
|---|---|---|---|
| Fonctionnel | MĂȘmes entrĂ©es â mĂȘmes sorties | Ătats, modes, transitions | FSM explicite, invariants, âsafe defaultsâ |
| Temporel | Temps bornĂ© / prĂ©visible | Chemins dâexĂ©cution, boucles | Loops bornĂ©es, WCET, budget CPU |
| Mémoire | Ressources bornées / pas de surprises | Stack, statique, buffers | Pas de malloc, buffers dimensionnés, pas de fragmentation |
Diagramme (concept) : boucle de contrÎle temps réel
flowchart LR
A[Read sensors] --> B[Validate / sanitize]
B --> C[Compute control law]
C --> D[Clamp & rate limit]
D --> E[Output actuators]
E --> F[Diagnostics / logging]
F --> A
note1([All steps are bounded in time & memory]):::note
classDef note fill=#0b1220,stroke=#334155,color=#cbd5e1;
Pourquoi le C reste un standard de fait en avionique
- Mapping machine : relation directe avec lâexĂ©cution (stack, registres, mĂ©moire).
- Toolchains robustes : compilateurs, linkers, debuggers, trace tools, coverage tools.
- AuditabilitĂ© : le code est âexplainableâ au niveau machine (essentiel pour WCET / V&V).
- MaĂźtrise des features : en imposant un profil strict (MISRA/CERT + rĂšgles internes), on neutralise les zones dangereuses.
Exemple minimaliste (style safety-critical)
#include#include #define ALT_MAX_FT (60000U) static uint16_t g_altitude_ft = 0U; bool set_altitude_ft(uint16_t new_alt_ft) { if (new_alt_ft <= ALT_MAX_FT) { g_altitude_ft = new_alt_ft; return true; } return false; /* input out of range */ }
Anti-patterns ânon dĂ©terministesâ (Ă Ă©viter)
- Allocation dynamique dans la boucle temps réel : fragmentation, latence imprévisible.
- Boucles non bornĂ©es : `while(x) {...}` sans borne claire â WCET indĂ©montrable.
- UB (Undefined Behavior) : overflow signé, shifts hors bornes, aliasing agressif, non init.
- Concurrence implicite : accÚs partagés sans modÚle clair (ISR/tasks).
Restrictions typiques (profil âDeterministic Câ)
| Zone | Interdit / limité | Pourquoi | Alternative sûre |
|---|---|---|---|
| Mémoire | malloc/free, realloc | latence + fragmentation | buffers statiques, pools bornés (si autorisés) |
| Flow | récursion, goto (souvent), boucles non bornées | WCET incertain | FSM, boucles bornées |
| Types | casts implicites, conversions silencieuses | bugs âinvisiblesâ | casts explicites, types stdint.h |
| UB | overflow signĂ©, shift hors borne, non init | comportement non dĂ©fini | fonctions âsafeâ, checks, saturation |
| Concurrence | accÚs partagés non protégés | race conditions | sections critiques, double-buffer, message passing |
Exemple : boucle bornée + clamp + saturation
#include#include static int32_t clamp_i32(int32_t x, int32_t lo, int32_t hi) { if (x < lo) { return lo; } if (x > hi) { return hi; } return x; } /* Bounded loop: always <= N iterations */ bool moving_average_i16(const int16_t *samples, uint16_t n, int16_t *out_avg) { uint16_t i = 0U; int32_t acc = 0; if ((samples == (void*)0) || (out_avg == (void*)0) || (n == 0U) || (n > 64U)) { return false; } for (i = 0U; i < n; i++) { acc += (int32_t)samples[i]; } acc = acc / (int32_t)n; acc = clamp_i32(acc, -32768, 32767); *out_avg = (int16_t)acc; return true; }
Points âreviewâ sur cet exemple
- Bornes :
n†64 â timing bornĂ© (WCET plus simple). - Accumulateur : type Ă©largi
int32_tpour éviter overflow. - Checks : pointeurs & paramÚtres validés (defensive design).
- Clamp : saturation explicite â Ă©vite UB, rĂ©sultats stables.
- Style : simple, lisible, testable, audit-friendly.
Le âC dĂ©terministeâ vit presque toujours dans un environnement temps rĂ©el : tĂąches pĂ©riodiques, contraintes de latence, prioritĂ©s, interruptions, et parfois partitionnement (ex: concepts ARINC 653).
Diagramme : tùches périodiques (concept)
sequenceDiagram
participant Timer as RTOS Timer
participant Task1 as Task@10ms
participant Task2 as Task@50ms
participant ISR as Sensor ISR
Timer->>Task1: Wake up (10ms)
Task1->>Task1: Read/Validate/Compute/Output
ISR-->>Task1: New sample (interrupt)
Timer->>Task2: Wake up (50ms)
Task2->>Task2: Health monitoring / logging / built-in tests
Table : risques ânon dĂ©terministesâ typiques
| Risque | Effet | Mitigation |
|---|---|---|
| Priority inversion | latence imprévisible | protocoles (mutex prio-inherit), design sans lock sur boucle critique |
| ISR trop lourde | jitter + WCET explosif | ISR minimale + buffer + traitement en task |
| Partage de données | races / incohérences | double-buffer, messages, sections critiques bornées |
Ce qui rend le WCET prouvable
- Chemins bornés : éviter la combinatoire (états/modes explicites).
- Boucles bornées : pas de dépendance à des entrées non bornées.
- IO maßtrisées : appels systÚme/driver bornés, pas de retries infinis.
- Structure stable : code simple, branches limitées, tables pré-calculées.
Exemple : boucle strictement bornée
#define N_MAX (32U)
/* Always executes at most N_MAX iterations */
int16_t dot_i16(const int16_t *a, const int16_t *b, uint16_t n)
{
uint16_t i = 0U;
int32_t acc = 0;
if ((a == 0) || (b == 0) || (n == 0U) || (n > N_MAX)) { return 0; }
for (i = 0U; i < n; i++)
{
acc += (int32_t)a[i] * (int32_t)b[i];
}
/* clamp to int16 range */
if (acc > 32767) { acc = 32767; }
if (acc < -32768) { acc = -32768; }
return (int16_t)acc;
}
Ce qui casse le WCET (Ă bannir en boucle critique)
| Pattern | Pourquoi |
|---|---|
while(!done) ... | Nombre dâitĂ©rations dĂ©pend dâentrĂ©es/Ă©tats, pire-cas indĂ©montrable |
| allocations / frees | latence et fragmentation imprévisibles |
| algos non bornĂ©s | ex: tri âvariableâ, recherche non bornĂ©e, parsing non bornĂ© |
| logs âlourdsâ | IO variable, buffers, contention |
Table : V&V âsafety-criticalâ (lecture pratique)
| Niveau | Objectif | Artefacts | Exemples |
|---|---|---|---|
| Unit | prouver la logique locale | tests + rapports | clamp, filtrage, conversions, checks |
| Integration | interfaces + timings | stubs/mocks | driver capteur â normalisation â loi de commande |
| System | comportement global | scénarios | modes, dégradations, alarmes, transitions |
| Robustness | entrĂ©es âhostilesâ | campagnes | valeurs extrĂȘmes, capteur incohĂ©rent, intermittence |
Exemple âtestableâ (pseudo-framework)
/* Given: clamp_i32(x, lo, hi) */
TEST(clamp_i32, clamps_low)
{
ASSERT_EQ(-10, clamp_i32(-999, -10, 10));
}
TEST(clamp_i32, clamps_high)
{
ASSERT_EQ(10, clamp_i32(999, -10, 10));
}
TEST(clamp_i32, passes_through)
{
ASSERT_EQ(3, clamp_i32(3, -10, 10));
}
MC/DC : lâidĂ©e (sans entrer dans la jungle)
/* Decision: if (A && (B || C)) */
Case A B C Result
1 0 0 0 0
2 1 0 0 0
3 1 1 0 1
4 1 0 1 1
Goal: show each of A, B, C can independently change outcome.
Traceability : le âvrai livrableâ
flowchart TB
R[System Requirements] --> SR[Software Requirements]
SR --> AD[Architecture / Design]
AD --> SC[Source Code]
SR --> UT[Unit Tests]
SR --> IT[Integration Tests]
SR --> ST[System Tests]
SC --> COV[Coverage Reports]
UT --> REP[Test Reports]
IT --> REP
ST --> REP
COV --> PKG[Certification Data Package]
REP --> PKG
Pipeline âdeterministic Câ (concept CI)
1) Build (warnings treated as errors)
2) Static analysis (MISRA/CERT rules + deviations justified)
3) Unit tests (host) + reports
4) Integration tests (target or HIL) + reports
5) Structural coverage (statement/branch/MC/DC as required)
6) Packaging: traceability matrices + evidence bundle
Table : catĂ©gories dâoutils et rĂŽle
| Catégorie | Objectif | Sortie attendue |
|---|---|---|
| Static analysis | détecter UB, violations de rÚgles, code mort | rapports + dérogations justifiées |
| Compiler / linker | build maßtrisé, flags stables | binaires reproductibles |
| Coverage | preuve structurelle | rapports coverage par unité / module |
| Trace | timing/latence/jitter | mesures, logs bornés, profiling temps réel |
Modules typiques (lecture âsafety-criticalâ)
| Domaine | Pourquoi déterminisme ? | Exemples de contraintes |
|---|---|---|
| Flight Control | commande dâactionneurs, stabilitĂ© | latence max, modes, dĂ©gradations |
| Autopilot / Navigation | guidage, lois, modes | gestion dâĂ©tats, transitions sĂ»res |
| Engine control (FADEC) | moteur = safety-critical | calculs bornés, robustesse capteurs |
| Braking / steering | contrÎle au sol / sécurité | détection défauts, fallback |
| Monitoring | surveillance & BIT | séparer hard RT vs maintenance |
Pattern âcapteur â normalisation â loi â clamp â diagnosticâ
flowchart LR
S[Sensor raw] --> V[Validate & plausibility]
V --> N[Normalize / filter]
N --> L[Control law]
L --> C[Clamp / rate limit]
C --> A[Actuator command]
V --> D[Fault flags]
N --> D
L --> D
A320 (famille) â ce quâon illustre cĂŽtĂ© software
- Gestion de modes : âstate machinesâ explicites, transitions contrĂŽlĂ©es.
- Lois de commande : séparation des boucles, saturation, rate limiting.
- Diagnostics : plausibility checks, flags défaut, dégradation contrÎlée.
737 MAX / MCAS â ce que ça illustre (au niveau conception)
- DĂ©pendance capteurs : comment valider la plausibilitĂ©, gĂ©rer les incohĂ©rences et Ă©viter lâamplification dâune mesure erronĂ©e.
- Modes & autoritĂ©s : âqui commande quoiâ, quelles limites, quelles conditions dâactivation/dĂ©sactivation.
- Degraded modes : que fait le logiciel quand une entrée critique devient douteuse ? (fallback / safe mode / inhibit).
Lessons âDeterministicâ (transposables)
| ProblĂšme Ă maĂźtriser | Pattern deterministic | Preuve attendue |
|---|---|---|
| EntrĂ©es capteurs âfollesâ | sanitization + plausibility + clamp | tests robustesse + coverage |
| Transition de modes | FSM explicite + invariants | tests de transitions + MC/DC (si requis) |
| Autorité de commande | rate limit + bounds + inhibit | analyses + campagnes de tests |
Pattern : machine Ă Ă©tats (FSM) âmode-drivenâ
typedef enum
{
MODE_OFF = 0,
MODE_STANDBY,
MODE_ACTIVE,
MODE_DEGRADED,
MODE_FAULT
} mode_t;
typedef struct
{
mode_t mode;
uint16_t fault_flags;
} ctx_t;
/* explicit transitions only */
static mode_t next_mode(mode_t cur, bool input_ok, bool health_ok)
{
switch (cur)
{
case MODE_OFF: return MODE_STANDBY;
case MODE_STANDBY: return input_ok ? MODE_ACTIVE : MODE_DEGRADED;
case MODE_ACTIVE: return health_ok ? MODE_ACTIVE : MODE_DEGRADED;
case MODE_DEGRADED: return health_ok ? MODE_ACTIVE : MODE_FAULT;
default: return MODE_FAULT;
}
}
Checklist design (vraiment avionique)
- Chaque mode a des invariants (ce qui doit toujours ĂȘtre vrai).
- Transitions explicites : pas de âmagieâ cachĂ©e dans 3 fonctions.
- Safe defaults : en cas de doute â sortie bornĂ©e et neutre.
- Diagnostics : flags, compteurs, dĂ©tection dâintermittence.
- Watchdog : timeouts, supervision de boucle.
Checklist (copiable)
[Deterministic C Checklist]
A) Determinism
- [ ] All loops are bounded (explicit N_MAX, time budget)
- [ ] No dynamic allocation in real-time path
- [ ] No recursion, no hidden unbounded retries
- [ ] Worst-case state/mode transitions documented
B) Undefined Behavior / Types
- [ ] No signed overflow assumptions
- [ ] Shifts are guarded (0 <= shift < width)
- [ ] All variables initialized
- [ ] Explicit casts; no implicit narrowing conversions
- [ ] Use stdint.h fixed-width types everywhere
C) Interfaces / Defensive Design
- [ ] Input ranges validated (plausibility + sanity)
- [ ] Outputs clamped/rate-limited (safe defaults)
- [ ] Error handling returns explicit status codes
- [ ] Side effects are minimized and documented
D) Concurrency / Interrupts
- [ ] Shared data has a clear ownership model
- [ ] ISR is minimal; no heavy work; bounded time
- [ ] Critical sections are short and bounded
- [ ] No data races (double-buffer/message passing where possible)
E) Verification
- [ ] Unit tests cover normal + edge + invalid inputs
- [ ] Structural coverage measured (statement/branch/MC/DC as required)
- [ ] Static analysis clean; deviations justified
- [ ] Traceability exists Req -> Code -> Test -> Report
Liens âentry pointsâ (Ă adapter selon tes prĂ©fĂ©rences / ton rĂ©fĂ©rentiel IDEO-Lab).
Spécification (style requirement)
| ID | Requirement | Notes âdeterministicâ |
|---|---|---|
| SSR-001 | Le module valide lâentrĂ©e capteur dans une plage plausible. | EntrĂ©es invalides â status explicite |
| SSR-002 | Le module normalise en Q15 (fixed-point) et applique un clamp. | pas de float obligatoire |
| SSR-003 | Le traitement est borné : O(1), pas de boucles non bornées. | WCET simple |
| SSR-004 | Le module expose des diagnostics (flags + compteur). | observabilité |
Implémentation (exemple)
#include#include typedef struct { uint16_t invalid_count; uint16_t flags; } diag_t; #define DIAG_INVALID_RANGE (1U << 0) /* Normalize raw (e.g., 0..4095) into Q15 [-1..+1] */ bool sensor_normalize_q15(uint16_t raw, uint16_t raw_min, uint16_t raw_max, int16_t *out_q15, diag_t *diag) { int32_t num, den, q15; if ((out_q15 == 0) || (diag == 0) || (raw_min >= raw_max)) { return false; } /* plausibility check */ if ((raw < raw_min) || (raw > raw_max)) { diag->invalid_count++; diag->flags |= DIAG_INVALID_RANGE; *out_q15 = 0; /* safe default */ return false; } /* map [raw_min..raw_max] -> [-32768..32767] */ num = ((int32_t)raw - (int32_t)raw_min); den = ((int32_t)raw_max - (int32_t)raw_min); /* scale to [0..65535], then shift to [-32768..32767] */ q15 = (num * 65535) / den; q15 = q15 - 32768; if (q15 > 32767) { q15 = 32767; } if (q15 < -32768) { q15 = -32768; } *out_q15 = (int16_t)q15; return true; }
Tests recommandés (edge cases)
- Range invalid : raw_min >= raw_max.
- Below min / above max : status false + safe default + flags + compteur.
- Nominal : raw = min, mid, max (valeurs attendues).
- Overflow : vĂ©rifie quâaucun calcul intermĂ©diaire ne dĂ©passe int32 (selon tes bornes).
TEST(sensor_normalize_q15, below_min_sets_safe_default)
{
int16_t out = 123;
diag_t d = {0};
ASSERT_FALSE(sensor_normalize_q15(10, 100, 200, &out, &d));
ASSERT_EQ(0, out);
ASSERT_TRUE((d.flags & DIAG_INVALID_RANGE) != 0);
ASSERT_EQ(1, d.invalid_count);
}
1) Always fixed-width types: uint16_t, int32_t, ...
2) No dynamic allocation in real-time path
3) No recursion; no unbounded loops
4) Validate all inputs + plausibility checks
5) Bound outputs (clamp + rate limit) + safe defaults
6) Avoid UB: signed overflow, invalid shifts, uninit vars
7) Keep functions small, single-responsibility, testable
8) Make state machines explicit (modes, transitions)
9) Separate control loop from monitoring/logging tasks
10) Evidence-driven: tests + coverage + static analysis + traceability
La certification nâest pas âun label du langageâ. Câest un processus + un dossier de preuves. Le C dĂ©terministe facilite la production de preuves (timing, absence dâUB, tests structurĂ©s, traçabilitĂ©).
Ce que cherche un auditeur
- Exigences claires & traçables
- Design explicite (modes / états / interfaces)
- V&V robuste : tests + couverture structurelle
- Analyse statique + gestion des dérogations
- Build reproductible + configuration maßtrisée
Diagramme : âevidence packageâ
flowchart LR
Req[Requirements] --> Code[Deterministic C Implementation]
Code --> SA[Static Analysis Reports]
Code --> UT[Unit Tests + Reports]
Code --> COV[Structural Coverage]
Req --> TM[Traceability Matrix]
SA --> PKG[Certification Evidence Package]
UT --> PKG
COV --> PKG
TM --> PKG
