code wiki / (root) / nx_mcu_ready.nx

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}