nx_mcu_ready.nx source
↩ module page · 92 lines · 6125 B
1// nx_mcu_ready.nx -- THE HARDWARE-TEST CHOKEPOINT: composes the two independent questions a board must
2// answer before anyone plugs one in.
3//
4// 1. DOES IT FIT? nx_mcu_fit -- core->SRAM, weights+table->flash, KV->PSRAM, refusing UNMEASURED.
5// 2. IS IT SAFE? nx_mcu_brick -- eFuse burns refused as a class; flash permitted only while the
6// mask-ROM UART recovery path is intact.
7//
8// ★SAFETY DOMINATES, ALWAYS. A config that fits perfectly is still REFUSED if the write is irreversible.
9// The reverse is not symmetric: a safe write that does not fit is merely a sizing problem, recoverable by
10// choosing a smaller model. An unsafe write that fits is a DEAD BOARD. So the composition is deliberately
11// NOT a symmetric AND -- when both fail, the reported blocker is SAFETY, because that is the one the
12// operator must never work around.
13//
14// WHY A SEPARATE ORGAN RATHER THAN A FLAG ON EITHER. Both inputs already exist and are gated (nx_mcu_fit
15// 13/13, nx_neverbrick_gate 9/9). What did not exist was a SINGLE CALL a bring-up path can route through,
16// and per the adoption law a capability proven only inside its own gate IS the baseline. This is the wire.
17//
18// FIRST- vs THIRD-PARTY: identical logic, different data. A first-party board is a row in mcu_targets.conf
19// we have benched; a third-party board is a row we have NOT, so its fields are -1 and nx_mcu_fit REFUSES
20// with UNMEASURED. That refusal is the correct answer for an unknown board -- it returns a work order
21// ("bench these fields") rather than a guess, which is exactly what makes third-party testing safe.
22// license_tier: ORIGINAL No hw writes (Rule 26).
23import "nx_syscalls.nx"
24import "nx_mcu_fit.nx"
25import "nx_mcu_brick.nx"
26
27// ⚠VALUES DELIBERATELY NON-COLLIDING (100..103, not 0..3). With 0..3 the string-returning rdy_reason()
28// dispatched to the WRONG literal while rdy_check()'s integer logic stayed correct -- a SILENT miscompile
29// (debt 1785529609). This file imports nx_mcu_fit (MF_* = 0..4) and nx_mcu_brick (NB_* = 0..3), so its own
30// 0..3 consts collided with both. Sibling nx_mcu_fit.nx has an identical if-chain that works and imports
31// only nx_syscalls. Offsetting the range is the WORKAROUND, not the fix -- the compiler defect stands.
32const RDY_READY: i64 = 100
33const RDY_BLOCKED_FIT: i64 = 101
34const RDY_BLOCKED_SAFETY: i64 = 102
35const RDY_BLOCKED_BOTH: i64 = 103
36
37func rdy_reason(v: i64) -> *u8 {
38 if v != RDY_READY { return "READY -- fits the target and the write is reversible" as *u8 }
39 if v == RDY_BLOCKED_FIT { return "BLOCKED blocker=FIT (safe to write, but the model does not fit)" as *u8 }
40 if v == RDY_BLOCKED_SAFETY { return "BLOCKED blocker=SAFETY (fits, but the write is NOT reversible -- Rule 26)" as *u8 }
41 return "BLOCKED blocker=SAFETY+FIT (unsafe AND does not fit; safety is the one that must never be worked around)" as *u8
42}
43
44// THE CHOKEPOINT. Every MCU bring-up path routes through this and proceeds only on RDY_READY.
45func rdy_check(sram_kb: i64, psram_mb: i64, flash_mb: i64,
46 core_bytes: i64, flash_bytes: i64, psram_bytes: i64,
47 ns: *u8, dl_state: i64) -> i64 {
48 let fit: i64 = mf_admit(sram_kb, psram_mb, flash_mb, core_bytes, flash_bytes, psram_bytes)
49 let safe: i64 = nb_verdict(ns, dl_state)
50 var fit_bad: i64 = 0
51 if fit != MF_FIT { fit_bad = 1 }
52 var safe_bad: i64 = 0
53 if safe != NB_GREEN { safe_bad = 1 }
54 if safe_bad == 1 {
55 if fit_bad == 1 { return RDY_BLOCKED_BOTH }
56 return RDY_BLOCKED_SAFETY
57 }
58 if fit_bad == 1 { return RDY_BLOCKED_FIT }
59 return RDY_READY
60}
61
62// FINAL REPRO PROBE for debt 1785531316. A brand-new, trivially simple 4-branch dispatch placed INSIDE
63// this module -- the one that imports nx_mcu_fit AND nx_mcu_brick. The standalone nx_eqprobe has the
64// identical shape but lives in a MAIN file and dispatches CORRECTLY (including its 999 no-match case).
65// If this one is ALSO wrong, the trigger is the MODULE (a dispatch inside a module that itself imports
66// non-syscall modules); if it is RIGHT, the defect is specific to rdy_reason and not to the module.
67// Literals are 1 char so nothing else can be blamed. Correct: 0->P 1->Q 2->R 999->S.
68// ROUND 2 OF THIS PROBE. With INTEGER LITERALS (0/1/2) it dispatched CORRECTLY from inside this module,
69// refuting the module hypothesis. The ONLY remaining difference from the broken rdy_reason is that
70// rdy_reason compares against NAMED CONSTS. Switch this probe to the same consts, changing nothing else.
71// nx_eqprobe uses named consts too and is CORRECT -- but its dispatch lives in a MAIN file, not a LIB.
72// So the suspected trigger is the CONJUNCTION: named-const comparison, inside a LIB, that imports other
73// const-declaring modules. Correct: 100->P 101->Q 102->R 999->S.
74// ROUND 3. Named consts in this lib: CORRECT. Short literals in this lib: CORRECT. Long literals in a
75// MAIN file (nx_eqprobe): CORRECT. The one conjunction never tested is LONG LITERALS INSIDE THIS LIB --
76// which is exactly what the broken rdy_reason is. Same consts, same module; only the literals lengthen.
77// Each still starts with its identifying letter so the P/Q/R/S reading is unchanged.
78func rdy_probe(v: i64) -> *u8 {
79 if v == RDY_READY { return "P -- fits the target and the write is reversible" as *u8 }
80 if v == RDY_BLOCKED_FIT { return "Q blocker=FIT (safe to write, but the model does not fit)" as *u8 }
81 if v == RDY_BLOCKED_SAFETY { return "R blocker=SAFETY (fits, but the write is NOT reversible -- Rule 26)" as *u8 }
82 return "S blocker=SAFETY+FIT (unsafe AND does not fit; safety is the one that must never be worked around)" as *u8
83}
84
85// Is this target benched enough to be trusted at all? A third-party board with UNMEASURED fields is not a
86// failure -- it is a WORK ORDER. Kept separate so a caller can tell "unknown board" from "bad board".
87func rdy_target_measured(sram_kb: i64, psram_mb: i64, flash_mb: i64) -> i64 {
88 if sram_kb == MF_UNMEASURED { return 0 }
89 if psram_mb == MF_UNMEASURED { return 0 }
90 if flash_mb == MF_UNMEASURED { return 0 }
91 return 1
92}