firmware/, simulation/, and tests/. It borrows direction from MISRA C:2012 and the SEI CERT C coding standard. It is not a claim of MISRA compliance. Formal compliance would require a qualified checker, a guideline enforcement plan, and approved deviation records, which becomes worthwhile once the STM32 project and production toolchain exist.
Automatically enforced gates
Warning set
The strict warning set is kept identical inCMakeLists.txt and scripts/run-safety.ps1:
Types and arithmetic
- Use fixed-width types (
uint32_t,int16_t) for anything with a hardware or protocol width. Useboolfor truth values. - Bitmasks are unsigned:
#define EV_OUT_5V ((uint32_t)1u << 0). Do not use enumerators for masks: enumeration constants areint, so~maskwould be a signed operand. - No implicit narrowing, sign changes, or float-to-double promotion. Write the cast where a conversion is intended.
- Never compare floating-point values with
==/!=in firmware. Use range or tolerance checks, for examplefabsf(gain) < FLT_MINto reject a zero gain. - Check every
floatinput withisfinitebefore using it. - Time uses
uint32_tmilliseconds. Compare ages as(uint32_t)(now - then)so the check still works when the counter rolls over.
Control flow
- A
switchon an enum lists every enumerator. Adefaultis still required and must fail safe (OFF, UNKNOWN, or a fault), because stored or received values can be out of range. - No recursion, no
goto, no variable-length arrays, nosetjmp/longjmp. - Loops have a fixed upper bound tied to an array size or a configured limit.
Memory and interfaces
- No dynamic allocation (
malloc/free) in firmware. State lives in caller-owned structs, and queues are fixed-size ring buffers. - Public functions reject null pointers and out-of-range arguments and report failure through their return value. They must not assume a valid caller.
- Internal functions and data are
static. Every external function has a prototype in the module header. - Buffer writes check the remaining capacity before writing. Formatted output goes through a function carrying a
printfformat attribute so every call is type-checked. - Hardware access goes only through board callbacks. Core logic never names a pin, peripheral, or register.
Safety behavior
- The default and error state is outputs OFF. Any unexpected state, invalid measurement, or missing configuration de-energizes and reports why.
- Safety-relevant decisions are testable on the host, and tests check that the OFF command was actually issued, not just what state was reported.
Naming and layout
- Public identifiers use the
EV_prefix with module-based CamelCase (EV_SafetyArm). Macros and enumerators are upper case (EV_FAULT_STOP). - Units go in names or comments:
_ms,_mv,_ma,_ohms,_hz. - Files are UTF-8 with LF line endings (enforced by
.gitattributes). C sources use 4-space indentation (.editorconfig).
Recorded deviations
Add a row to the deviation table, with its rationale, before introducing any new exception.