E-Stop Integrity Chain Decisions
Companion to e_stop_integrity_chain_safety_design.md. This is the editable artifact: read
each decision, edit the Decision: line (tick a box [x], or write your own
under "Other"), and add Notes: as you go. I'll implement from your marked-up
copy. Nothing here changes pstop_c β all of it lives in the application shell.
Legend: β = my recommended default Β· π° = effort Β· π‘οΈ = safety/DC impact
Group A β Loopback sensingβ
A1. Challenge generator (per core)β
Context: today the drive level is counter & 1 β fully predictable, so a
fault that happens to track it isn't forced to reveal itself. We want each
core's drive value to be unpredictable-but-locally-known (the core generates it,
so it always knows what to compare the echo against).
Options:
- A) Keep
counter & 1. π° none. π‘οΈ poor β predictable, low DC. (Listed only for completeness; not advised.) - β B) Per-core 16-bit Galois LFSR, seeded at boot from
esp_random(), then advanced deterministically each tick. π° tiny. π‘οΈ good β pattern is unpredictable across boots and to a stuck fault, yet fully reproducible by the owning core for echo comparison and signature recompute. Different seed/polynomial per core feeds the diversity story (D2). - C) Per-tick
esp_random()directly. π° tiny. π‘οΈ good unpredictability but no clean reproducible sequence for the round-trip signature; you'd have to store every challenge. LFSR gives the same unpredictability with reproducibility.
Decision: β A β B β β C β Other: __________ Notes: _______________________________________________
A2. Challenge width per tick (1 bit vs serial burst)β
Context: more bits exercised per tick = more chances to catch a partial
fault = higher diagnostic coverage. Each bit is drive β settle (~10 Β΅s) β read.
Options:
- A) 1 bit/tick. π° none. π‘οΈ low DC (a single high/low compare/tick).
- β B) 4-bit serial burst/tick (~4 Γ (10 Β΅s settle + read) β <100 Β΅s of the 100 ms tick). π‘οΈ 16 patterns/tick, catches stuck/partial faults fast; negligible CPU. This is the "make it a real serial loopback" answer.
- C) 8-bit (or wider) burst/tick. π° still tiny. π‘οΈ highest DC; more margin for the FMEDA. Slightly longer busy-wait per tick.
Decision: β A β β B β C(width=__) β Other: __________ Notes: _______________________________________________
A3. Settle time per bitβ
Context: the loop is a wire + switch contacts; propagation is sub-Β΅s, but
long harnesses + the pulldown RC need margin. Currently ESTOP_SETTLE_US = 10.
Options:
- β A) 10 Β΅s (current). Fine for short bench wiring.
- B) 25β50 Β΅s if the deployed harness is long / capacitive.
- C) Measure it (drive an edge, time the echo) and set with margin.
Decision: β A β β B(__ Β΅s) β C β Other: __________ Notes: _______________________________________________
Group B β Verdict integrity (no-bypass)β
B1. Liveness accumulatorβ
Context: replace the bypassable closed ? OK : STOP branch with a value
that is the safety state. Proposed (per core):
each bit b in the tick's burst: acc ^= (challenge_b ^ echo_b) // 0 iff equal
fault if acc != seed at end of tick // any miscompare ever β non-zero, sticky
OK is emitted only when acc == seed AND the freshness counter (B2) advanced.
Options:
- β A) 32-bit sticky XOR accumulator + sticky fault flag as above.
π° tiny. π‘οΈ strong:
OKbecomes a pure function of correct live reads; no branch to shortcut; data dependency defeats compiler hoisting. - B) Per-tick compare only (no accumulator). π° tiny. π‘οΈ weaker β loses the "history of correct reads" property; a one-tick glitch wouldn't persist.
Decision: β A β β B β Other: __________ Notes: _______________________________________________
B2. Freshness mechanismβ
Context: prove the read is this tick's, not stale/frozen.
Options:
- β A) Monotonic per-core read sequence, checked by the comparator to have advanced every tick (a frozen core β no advance β STOP). Composs with the existing 80 ms publish-timeout.
- B) Rely only on the existing publish-timeout. π‘οΈ weaker (catches a fully-hung task, not a task that runs but reuses a stale sample).
Decision: β A β β B β Other: __________ Notes: _______________________________________________
B3. Fault latch / re-arm policyβ
Context: when a sensing fault is detected, does it auto-clear when the fault goes away, or latch until a deliberate operator action?
Options:
- β A) Latch STOP until operator re-arm (the same pressβrelease that arms after bond). Consistent with the NEED_STOP philosophy: a sensing fault must not silently self-clear. π‘οΈ strongest.
- B) Latch until reboot. π‘οΈ strong but heavy-handed operationally.
- C) Auto-clear when reads go good again. π‘οΈ weakest β a flapping wire could intermittently re-enable; not advised for SIL2.
Decision: β A β β B β C β Other: __________ Notes: _______________________________________________
Group C β Splicing GPIO into the black channelβ
C1. Counter-advance gating (machine-enforced stallβSTOP)β
Context: gate the pstop counter increment on a correct echo, so a GPIO
fault stops counter progress and the machine's native checks react. What
should the remote do on a bad echo?
Options:
- A) Stop sending entirely β machine heartbeat-times-out (β€ heartbeat timeout, e.g. 1 s). Simple; slower reaction.
- β B) Send an explicit
STOP(verdict STOP) immediately, and hold the counter discipline. π‘οΈ fastest machine reaction (STOP on the next packet, not a timeout); still degrades to timeout if packets are lost. - C) Freeze the counter (resend last) β machine sees no progress β
MSG_LOST path. π‘οΈ machine-enforced but reaction depends on
max_lost_messages.
Decision: β A β β B β C β Other: __________ Notes: _______________________________________________
C2. Stamp echo-signature (anti-replay / freshness proof)β
Context: carry a rolling signature of the measured echoes in the low bits
of stamp; the machine echoes it verbatim in received_stamp; the remote
verifies the returned signature against the challenge sequence it drove. Proves
the machine acted on the exact, current GPIO-derived message (not a stale or
injected packet). The carrier is the REMOTE's stamp (esp_timer uptime); it
must stay strictly increasing. The machine merely echoes it (received_stamp)
and runs its own monotonic clock, so there is no machine-side clock mode to
worry about.
Options:
- β A) Yes β embed an N-bit rolling signature in
stamp[low N](proposed N = 8), high bits remain the remote's monotonic uptime. Verify onreceived_stamp. π° small (encode + monotonic guard + verify). π‘οΈ adds explicit end-to-end freshness/anti-replay on top of C1. - B) Counter-gating only (skip the stamp signature). π° least. π‘οΈ relies on the counter chain alone for end-to-end coverage; no explicit anti-replay.
Decision: β A β (N=__) β B β Other: __________ Notes: _______________________________________________
C3. Signature contentβ
Context: what the rolling signature is computed over.
Options:
- β A) Running CRC/hash of the echo bursts (per core, folded). Detects any divergence over the window.
- B) Just the latest tick's echo. π‘οΈ weaker window.
Decision: β A β β B β Other: __________ Notes: _______________________________________________
Group D β Redundancy & common-cause (the SIL2 crux)β
D1. Comparator contractβ
Context: diversity (D2) requires comparing meaning, not bytes; today's
byte-memcmp can't accommodate diverse cores.
Options:
- A) Keep byte-identical
memcmp. π° none. π‘οΈ great for random faults, ~0 for systematic. Forces identical cores β leaves G1 open. Only viable if you accept covering systematic faults purely by process (MISRA/V&V). - β B) Semantic 1oo2D. Each core emits {verdict, liveness-proof, fresh seq}; comparator checks they agree on meaning AND each proof is valid+fresh, then builds the single canonical pstop message. π° moderate (rework comparator). π‘οΈ real systematic-fault detection; keeps one-message-out + de-energize-to-STOP.
Decision: β A β B β β Other: __________ Notes: _______________________________________________
D2. Diversity scheme (how different the two cores are)β
Context: to break common-cause, the two channels should fail differently under the same systematic bug. How far to push it?
Options (cumulative β tick all you want):
- β a) Inverted sense logic (core 0 active-high, core 1 active-low).
- β b) Different read path (core 0 reads the GPIO data register; core 1 via the driver API).
- β c) Different LFSR seed AND polynomial per core.
- d) Different accumulator/signature algorithm per core (e.g. XOR-CRC vs additive checksum). π‘οΈ higher Ξ²-reduction, π° more code/test.
- e) Different drive polarity / idle level per channel.
Decision (set): _________________________ β Other: __________ Notes: _______________________________________________
D3. Cross-channel readβ
Context: each core also reads the other core's IN pin (read-only) and flags inconsistency β diagnoses a core-local sensing fault independently.
Options:
- β A) Include it. π° small. π‘οΈ +DC on the sensing hardware.
- B) Skip it. Rely on the comparator's verdict cross-check only.
Decision: β A β β B β Other: __________ Notes: _______________________________________________
Group E β Hardware diagnosticsβ
E1. GPIO configuration integrity checkβ
Context: a corrupted IO register (direction/mux/pulldown) could turn an open loop into a false "closed" read. Periodically re-assert + verify the config.
Options:
- β A) Re-verify every ~1 s (re-read IO mux/dir/pull, compare to expected; mismatch β STOP). π° small. π‘οΈ closes a classic dangerous-undetected fault.
- B) Re-assert (blindly rewrite) every ~1 s without read-back. π‘οΈ weaker (no detection, just correction).
- C) Skip. Not advised at SIL2.
Decision: β A β (interval=__) β B β C β Other: __________ Notes: _______________________________________________
E2. Output drive read-backβ
Context: confirm the OUT pin actually drives the level we set (catch a driver stuck off / shorted).
Options:
- β A) Read back the OUT pin's actual level each burst (where the SoC exposes it) and include it in the accumulator. π° small.
- B) Skip (covered indirectly: if OUT is dead, the echo won't match).
Decision: β A β β B β Other: __________ Notes: _______________________________________________
E3. Watchdog couplingβ
Context: kick the hardware watchdog (TWDT) only on a completed, fresh, correct loop+round-trip cycle, so a hung/bypassed path resets the chip β STOP.
Options:
- β A) Dedicated safety WDT, kicked only on a valid cycle, timeout ~500 msβ1 s. π° small. π‘οΈ strong liveness backstop.
- B) Reuse the existing app heartbeat/TWDT. π° least. π‘οΈ less targeted.
- C) None. Rely on the comparator publish-timeout only.
Decision: β A β (timeout=__) β B β C β Other: __________ Notes: _______________________________________________
Group F β Timing & inputs (need your numbers)β
F1. Reaction-time budgetβ
Context: sets the heartbeat timeout, tick rate, and proof-test interval, and is the number the assessor will check. Please fill in from the robot's requirements.
- Process Safety Time (max from hazard to safe state): ______ ms
- Required E-stopβmachine-STOP latency: ______ ms
- pstop tick rate (today 10 Hz / 100 ms): ______ Hz
- machine silenceβSTOP timeout β now the library's NATIVE check_heartbeats on a
monotonic clock, =
heartbeat_ms Γ (max_missed_heartbeats+1)(today 1000 Γ 2 β 1β2 s; tune via[limits].max_missed_heartbeats+ the operatorheartbeat_ms, NOT a wrapper watchdog): ______ ms - Diagnostic test interval (= tick): ______ ms
Notes: _______________________________________________
F2. SIL2 systematic-capability scope (process)β
Context: SIL2 is also a lifecycle obligation on the new sensing/comparator
code (not on pstop_c, which is leveraged unchanged). Confirm which apply:
- MISRA C compliance for the new safety code
- Static analysis (e.g. cppcheck/clang-tidy/Coverity) in CI
- Unit + fault-injection tests (the Β§10 plan in the design doc)
- Requirements traceability for the safety functions
- FMEDA to quantify DC / SFF / Ξ²
Decision (set): _________________________ Notes: ____________
Group G β Electrical / wiring (for the hardware build)β
G1. Channel independenceβ
Context: software diversity is moot if the two channels share a failure.
- β a) Separate GPIO banks for the two channels where possible.
- β b) Route so the two channels cannot short to each other (a single short must not fake both loops closed).
- c) Separate pull / reference domains per channel.
- d) Consider series resistors / protection on the loop pins (ESD, fault current) per the harness environment.
Decision (set): _________________________ Notes: ____________
G2. Sense idle level / pull directionβ
Context: today IN pins are pulled down, so an open loop reads 0 (safe). With diverse inverted logic (D2a), core 1 may need a pull-up so its open state is also the safe state. Confirm per the final logic choice.
Decision: _________________________ Notes: ____________
Summary of my defaults (if you just want to accept the β set)β
A1-B Β· A2-B(4-bit) Β· A3-A(10Β΅s) Β· B1-A Β· B2-A Β· B3-A(latchβre-arm) Β· C1-B(explicit STOP) Β· C2-A(N=8) Β· C3-A Β· D1-B(semantic 1oo2D) Β· D2-{a,b,c} Β· D3-A Β· E1-A(1s) Β· E2-A Β· E3-A(~750ms) Β· F1=you fill in Β· G1-{a,b} Β· G2 per final logic.
Tell me to "build from the β defaults" or hand back this file edited, and I'll turn Β§4 of the design doc into a concrete implementation plan (data structures, the per-core diverse sense, the new comparator contract, and the fault-injection tests) for review before writing code.