Safety-Critical Firmware
Cuttlefish can verify the firmware it generates against ISO 26262 safety requirements, detect radiation-induced memory corruption at runtime, and produce traceability artifacts for safety assessment — all from TypeScript.
In Short
- Import the safety authoring surface from
@typecad/cuttlefish/safety. - Use
safe.read()instead of a raw GPIO read — it votes 3 times and validates. - Use
SafeVariable<T>for critical state — it stores 3 replicas and catches single-event upsets. - Annotate functions with
// @asilDto enforce ISO 26262 rules at build time. - The build produces a
.safety-sidecar.jsonartifact mapping each function to its safety level and rule results.
Verified GPIO Reads
A standard digital read can return a corrupted value if a cosmic ray flips a bit in the GPIO register. The safety API reads the pin three times with temporal separation and votes on the result:
import { GPIO } from '@typecad/hal';
import { safe, SafetyFaultCategory } from '@typecad/cuttlefish/safety';
import { BUTTON, LED } from '@typecad/board';
const button = new GPIO(BUTTON, GPIO.INPUT | GPIO.PULL_UP);
const led = new GPIO(LED, GPIO.OUTPUT);
function loop() {
safe.read(button)
.ok(r => {
// r.value is the voted result — 2 of 3 reads agreed
led.set(r.value !== 0);
})
.fail(r => {
// A fault was detected
if (r.category === SafetyFaultCategory.Signal) {
UART0.writeLine('vote disagreed — transient noise');
} else if (r.category === SafetyFaultCategory.Configuration) {
UART0.writeLine('pin mode wrong or unknown');
}
});
}The voter also verifies the pin’s recorded mode. If the pin was configured as an output but you tried to read it, the result carries a configuration fault — not a corrupted signal.
Write Verification
safe.write() writes a value to a pin and immediately reads it back to confirm the write succeeded:
import { GPIO } from '@typecad/hal';
import { safe } from '@typecad/cuttlefish/safety';
import { LED } from '@typecad/board';
const brake = new GPIO(LED, GPIO.OUTPUT);
// @asilD
function engageBrake(): void {
safe.write(brake, 1)
.fail(r => {
// The readback didn't match — the write may not have taken effect.
UART0.writeLine('write verify failed, code:', r.code);
});
}SEU-Resistant Storage (SafeVariable)
Single-event upsets can flip bits in RAM. SafeVariable<T> stores three replicas of the value using triple modular redundancy with inverted-redundancy validation. Annotate a variable with the type (it’s declared globally by the project’s cuttlefish-env.d.ts) and the transpiler lowers it to the redundancy template:
let pressCount: SafeVariable<int32_t> = 0;
let temperature: SafeVariable<float> = 25.0;
function loop() {
if (pressCount.valid()) {
const current: int32_t = pressCount.get();
pressCount.set(current + 1);
} else {
// RAM corruption detected — at least one replica disagrees.
// The majority vote still returned a value via get(),
// but valid() told you the replicas are inconsistent.
pressCount.set(0); // repair
}
}For integer types, each replica stores the value alongside its bitwise inverse. A single-bit flip in any replica breaks the XOR invariant — the corrupted replica is excluded from the vote and silently repaired on the next set(). For float types, three identical copies are stored and majority-voted.
Bounds-Checked Arithmetic (SafeInt)
Integer overflow silently wraps in C++. SafeInt<T> detects overflow and clamps to the type’s limits (also declared globally by cuttlefish-env.d.ts):
let counter: SafeInt<int32_t> = SafeInt(2147483640);
counter.add(5); // would overflow — clamps to INT32_MAX, sets fault
counter.add(5); // sticky fault — stays at INT32_MAX
counter.reset(0); // clears fault, ready for next cycleISO 26262 Build-Time Rule Checking
Annotate functions with their ASIL level using comment annotations. The build enforces ISO 26262 Part 6 software rules on annotated functions:
import { safe } from '@typecad/cuttlefish/safety';
// @asilD — highest level: recursion, heap, loops, init all enforced
function deployParachute(): void {
safe.write(pin, 1);
}
// @asilC — recursion + loops enforced
function readLimit(): number {
return votedLimit;
}
// No annotation — quality-managed, no rules enforced
function logStatus(): void {
UART0.writeLine('OK');
}What gets checked
| Rule | ASIL | What it catches |
|---|---|---|
| Recursion | B+ | Direct or mutual recursion — violates bounded call depth |
| Dynamic allocation | D | new, malloc, calloc in non-setup functions |
| Unbounded loops | C+ | while(true), for(;;) — no guaranteed termination |
| Init completeness | D | Variables used in safety functions without initialization in setup() |
| Goto | C+ | goto statements generated from labeled break |
Diagnostics with error severity abort the build. warning severity logs but continues.
Safety Sidecar Artifact
When your project uses the safety surface, every build produces a <name>.safety-sidecar.json file next to the emitted C++ code. It maps each safety-critical function to its ASIL level, the mechanisms that protect it, and the results of each rule check:
{
"safetyFunctions": [
{
"name": "engageBrake",
"asilLevel": "D",
"source": { "tsFile": "src/main.ts", "tsLine": 42 },
"mechanisms": ["safe.write"],
"rules": {
"B1_RECURSION": "pass",
"B2_DYNAMIC_ALLOC": "pass",
"B3_UNBOUNDED_LOOP": "pass"
}
}
]
}A safety assessor uses this artifact to trace each safety requirement to the code that implements it and the checks that verify it.
What it doesn’t do
- No certification. The package produces evidence, not a certificate. A human or external tool reviews the sidecar.
- No safety case. ISO 26262 requires a full safety case (hazard analysis, ASIL decomposition, etc.). The package provides software-level evidence, not system-level analysis.
- No electrical verification. Reading an output pin returns the last value written to the port register. The write-verify check catches software bugs (wrong pin, wrong mode) but not electrical faults (shorted pin, disconnected wire).
Verification
Build your project as usual (cuttlefish build). When the safety surface is in use, the ISO 26262 rule checks run as part of the build and the .safety-sidecar.json artifact is written next to the emitted C++ for your assessors to review.
On This Page