Safety-Critical Firmware
A safety surface for the firmware, not a certification: voted and verified I/O, radiation-tolerant storage, bounds-checked arithmetic, and ASIL-graded rule checks — all compiled into the program you'd have written anyway.
safe.read / safe.write
I/O with a verdict. read samples the pin three times and votes (2-of-3), then checks the pin's mode — reading an output-configured pin is a configuration fault, not a signal fault. write verifies with a readback.
Example first
import { safe, SafetyFaultCategory } from '@typecad/cuttlefish/safety';
import { GPIO, BUTTON_PIN } from '@typecad/hal';
const pin = new GPIO(BUTTON_PIN, GPIO.INPUT);
safe.read(pin).ok((r) => render(r.value)).fail((r) => {
// r.value is the 2-of-3 vote anyway — use it with care
if (r.category === SafetyFaultCategory.Configuration) {
// the pin is configured as an output — a code bug, not noise
}
});safe.write(enable_pin, 1).fail((r) => {
// write-then-readback mismatch — the actuator isn't following
});Results carry value, category (Signal vs Configuration), a code (stuck-high, stuck-low, vote disagreement, mode mismatch, write mismatch),
and ok() / fail() continuation methods.
SafeVariable / SafeInt
Ambient global types — declared in the generated env file, no import. SafeVariable is triple-redundant storage with inverted-redundancy validation: a bit flip in one replica is detected and repaired. SafeInt is chainable arithmetic that clamps at the type's limits and raises a sticky fault.
Example first
let position: SafeVariable<number> = SafeVariable(0);
position.set(42);
if (position.valid()) {
display(position.get());
}
// bit-flip a replica and the XOR invariant repairs it — hasFault() reports itlet total: SafeInt<number> = SafeInt(2147483640);
total.add(5); // clamps at INT32_MAX — sticky fault
if (total.hasFault()) {
log('overflow');
}
total.reset(0); // clears the faultASIL annotations
Comment decorators grade functions into the ISO 26262 rule set. Unannotated functions are quality-managed and unchecked; annotated ones must satisfy the rules for their level or the build fails.
Example first
// @asilD — deepest rule set for this function
function deploy(flux: number): number { ... }
// @asilC — recursion + loop checks
// @asilB — recursion check
// no annotation = quality-managed, no rules| Rule | From | Catches |
|---|---|---|
| Recursion | ASIL B and up | Any recursive call in a safety function |
| Dynamic allocation | ASIL D | new / array growth outside setup — nothing allocates at runtime |
| Unbounded loops | ASIL C and up | while(true) without a bound |
| Initialization | ASIL D | Variables used in a safety function but never initialized in setup |
| Goto | ASIL C and up | Including the labeled-break shapes |