dal-c library

Safety-critical C · DAL-C style

dal-c

Fifteen reusable C99 components, each held to a Design Assurance Level C process: written requirements, requirements-based tests, 100% MC/DC, saturating arithmetic, zero dynamic memory.

/* bounded PID, fixed-point, anti-windup */
sc_pid_config_t cfg = { .kp=3*ONE, .ki=ONE/8,
                    .kd=ONE/2, .d_filter=ONE/3,
                    .out_min=0, .out_max=100*ONE };
if (sc_pid_config_valid(&cfg) != SC_OK) return;
sc_pid_init(&pid, /*out*/0, /*meas*/ y);

sc_q16_t u = sc_pid_update(&cfg, &pid, setpoint, y);
100%
Line cov
100%
MC/DC
VERIFY  PASS
15 modules   73 HLR / 182 LLR
2479 test checks   0 fail
no UB · ASan + UBSan clean
fuzz: 200k iters · 0 fail
builds: gcc · clang · -m32

§01Components

The parts list

Each is one header and one source file, dependent only on <stdint.h> / <stdbool.h>, holding no static state.

RefModuleFunctionLLRCov
SC‑SATsc_satSaturating 32-bit add / subtract — pins at the integer limits, never wraps.6
SC‑FIXsc_fixedQ16.16 fixed-point: saturating add / sub / mul (rounded) / div, clamp, abs.21
SC‑CRCsc_crcBitwise CRC-8/SMBUS, CRC-16/CCITT-FALSE, CRC-32/ISO-HDLC. No tables.8
SC‑HYSsc_hysteresisSchmitt-trigger comparator with independent assert / clear thresholds.12
SC‑DBNsc_debounceIntegrator debounce for a noisy digital input — rejects bursts shorter than a sample-count threshold.9
SC‑MEDsc_medianSliding-window median filter — rejects isolated spikes an averaging filter would smear. Odd window, in-state storage, bounded insertion sort.11
SC‑COBSsc_cobsConsistent Overhead Byte Stuffing — frame a byte stream so 0x00 can delimit it. Encode / decode, exact round-trip.13
SC‑RBsc_ringbufFixed-capacity byte FIFO over caller storage. No malloc, unambiguous full / empty.12
SC‑RLsc_ratelimitSlew-rate limiter with output clamp, on saturating arithmetic.18
SC‑LUTsc_lutPiecewise-linear lookup table with clamped extrapolation — sensor linearisation, command shaping.10
SC‑SMsc_smTable-driven finite state machine engine — transition table of (from, event, to, action), bounded scan, first match wins.11
SC‑SCHsc_schedCooperative cyclic scheduler — task table with per-task period and phase, deterministic tick-driven dispatch, no preemption.9
SC‑PIDsc_pidPositional PID: derivative-on-measurement + low-pass, output clamp, conditional-integration anti-windup.16
SC‑VOTEsc_voteM-of-N redundancy voter — clusters replicated channel readings by tolerance, returns the majority value, agreeing count, dissenting-channel mask and a consensus verdict.14
SC‑WDGsc_watchdogLatching deadline supervisor — a supervised activity must kick once per timeout ticks; a missed deadline trips and stays tripped until re-armed. Bounded up-counter, fail-safe on NULL.12

§02Architecture

How the parts fit

Ten components are leaf primitives; sc_sat is the saturating-arithmetic base three others build on, and sc_fixed in turn underpins the PID. Nothing reaches outside the standard headers.

sc_sat base sc_fixedsc_ratelimit sc_lutsc_pid sc_crcsc_hysteresissc_debounce sc_ringbufsc_sm no dal-c dependency

The worked example wires six of them into one loop: a buffered, averaged sensor reading drives a demand lamp and a PID whose command is slew-limited before it reaches the plant.

sensorsc_ringbufmean sc_hysteresissc_pidsc_ratelimit heatersc_crc32 plant feedback

Full architecture — the sc_sm state diagram, per-component size ↗

§03Verification

Evidence, not adjectives

73 / 182
HLR / LLR
2479
test checks
100%
statement cov
100%
MC/DC
clean
ASan + UBSan
0
fuzz failures
3
toolchains
make report
$ make report
  sc_sat           8 tests    20 checks  PASS
  sc_fixed         7 tests    37 checks  PASS
  sc_crc           5 tests    16 checks  PASS
  sc_hysteresis   11 tests    35 checks  PASS
  sc_ringbuf       9 tests    74 checks  PASS
  sc_ratelimit    10 tests    55 checks  PASS
  sc_lut           6 tests    20 checks  PASS
  sc_debounce      6 tests    29 checks  PASS
  sc_sm            7 tests    38 checks  PASS
  sc_sched         4 tests    24 checks  PASS
  sc_cobs          6 tests  1081 checks  PASS
  sc_pid           8 tests   835 checks  PASS
  sc_median        9 tests    93 checks  PASS
  sc_vote          9 tests    69 checks  PASS
  sc_watchdog      9 tests    53 checks  PASS

2479 checks, 0 failure(s)

Lines executed:100.00% of 543
Condition outcomes covered:100.00% of 374
Traceability: complete (every HLR has LLRs and tests;
              every LLR is implemented and traced).

§04Traceability

Requirement to test, both directions

Every low-level requirement names its parent and its implementing function; every source block carries an /* LLR-... */ tag; the matrix generator fails the build on any break.

Customer‑Req System‑Req HLR LLR Source Test verified‑by

Architecture — dependency graph, the state-machine framework, the example's signal flow ↗

§05How it's built

  1. Requirementsrequirements/<module>.md: HLRs, then LLRs that each trace up and name their function.
  2. Code — every implementation block carries an /* LLR-... */ tag.
  3. Teststests/test_<module>.c: each test names its requirement; compound decisions get MC/DC cases.
  4. Coveragemake coverage instruments with GCC condition coverage; CI fails under 100% line and 100% branch.
  5. Traceabilitytools/gen_rtm.py rebuilds the matrix and fails on any orphan requirement.
  6. Analysis — cppcheck + MISRA C:2012 addon (deviations recorded); the suite, example and a 200k-iteration invariant fuzz run under UBSan / ASan; built on gcc, clang and -m32.

§06Use it

$ make lib          # -> build/libdal_c.a
$ make example      # builds + runs a composed control loop
$ make install PREFIX=/opt/dal-c

// then
#include <dal_c/dal_c.h>   // or one component header

examples/control_loop.c composes sc_pid + sc_ratelimit + sc_hysteresis + sc_ringbuf + sc_fixed + sc_crc into a simulated room-temperature loop.