wip(picow): deterministic gSPI framing via per-transfer restart hook
cyw43_spi_transfer calls pio_sm_restart before each transfer's count words, so hooking restart() to reset the sniffer makes framing deterministic across the firmware-stream fast-path (no phantom-transfer carryover). Verified: restarts fire 3625x (once per transfer), F1 phantom count drops, and the CLM IOCTL write now frames correctly (cmd decodes to F2, 'clmload' payload). Wired into RP2040Simulator + the harness. Remaining (next session): the CLM/IOCTL write doesn't complete its payload and wifi_on still fails (active()=False) — bus_init stalls at/around clm_load with only 2 STATUS reads and goes idle. Next: trace the CLM write's DMA/PIO drain and the SDPCM IOCTL response path. See findings.md F-13.
This commit is contained in:
parent
f183f8add2
commit
b172cadbc5
|
|
@ -48,19 +48,26 @@ const INJECT_CODE = [
|
|||
'import network, time',
|
||||
'print("STEP_IMPORT_OK")',
|
||||
'w=network.WLAN(network.STA_IF)',
|
||||
'print("STEP_WLAN_OBJ_OK pre_active=" + str(w.active()))',
|
||||
'print("STEP_WLAN_OBJ_OK")',
|
||||
'try:',
|
||||
' w.active(True)',
|
||||
' print("STEP_ACTIVE active=" + str(w.active()) + " status=" + str(w.status()))',
|
||||
'except Exception as e:',
|
||||
' print("ACTIVE_EXC " + repr(e))',
|
||||
// Give the background driver task time to finish wifi_on (CLM load + IOCTLs).
|
||||
'for i in range(40):',
|
||||
' print("ACT", i, "active=" + str(w.active()) + " status=" + str(w.status()))',
|
||||
' if w.active():',
|
||||
' break',
|
||||
' time.sleep_ms(150)',
|
||||
'try:',
|
||||
' w.connect("Velxio-GUEST", "")',
|
||||
' print("STEP_CONNECT_CALLED status=" + str(w.status()))',
|
||||
'except Exception as e:',
|
||||
' print("CONNECT_EXC " + repr(e))',
|
||||
'for i in range(4):',
|
||||
'for i in range(20):',
|
||||
' print("POLL", i, "status", w.status(), "conn", w.isconnected())',
|
||||
' if w.isconnected():',
|
||||
' break',
|
||||
' time.sleep_ms(200)',
|
||||
'print("HARNESS_DONE")',
|
||||
].join('\n');
|
||||
|
|
@ -163,12 +170,26 @@ describe.skipIf(!process.env.CYW43_HARNESS)('Pico W cyw43 boot harness (investig
|
|||
}
|
||||
|
||||
let rxPulled = 0;
|
||||
let restartCount = 0;
|
||||
for (const pio of (sim.rp2040 as any).pio) {
|
||||
for (const sm of pio.machines) {
|
||||
const tx = sm.txFIFO;
|
||||
if (!tx) continue;
|
||||
const orig = tx.push.bind(tx);
|
||||
tx.push = (v: number) => { pushCount++; rawWords.push(v >>> 0); feedWord(v, sm); return orig(v); };
|
||||
tx.push = (v: number) => {
|
||||
pushCount++;
|
||||
rawWords.push(v >>> 0);
|
||||
if (rawWords.length > 600) rawWords.splice(0, rawWords.length - 400); // ring
|
||||
feedWord(v, sm);
|
||||
return orig(v);
|
||||
};
|
||||
// Reset the sniffer at each transfer boundary: cyw43_spi_transfer
|
||||
// calls pio_sm_restart before pushing the count words, so this keeps
|
||||
// framing deterministic even after the firmware-stream fast-path.
|
||||
if (typeof sm.restart === 'function') {
|
||||
const origRestart = sm.restart.bind(sm);
|
||||
sm.restart = () => { restartCount++; sniffer.reset(); return origRestart(); };
|
||||
}
|
||||
const rx = sm.rxFIFO;
|
||||
if (rx) {
|
||||
const origPull = rx.pull.bind(rx);
|
||||
|
|
@ -247,11 +268,12 @@ describe.skipIf(!process.env.CYW43_HARNESS)('Pico W cyw43 boot harness (investig
|
|||
'===== TOP COMMAND COUNTS (poll loops) =====\n' +
|
||||
polls.map(([k, n]) => ` ${String(n).padStart(6)} ${k}`).join('\n') + '\n\n' +
|
||||
`===== FUNCTION HISTOGRAM F0..F3 = ${funcHist.join(',')} =====\n` +
|
||||
`===== initInbound=${initInbound} statusReads=${statusReads} statusReadsWithPkt=${statusReadsWithPkt} finalInbound=${chip.debugInboundCount()} =====\n\n` +
|
||||
`===== initInbound=${initInbound} statusReads=${statusReads} statusReadsWithPkt=${statusReadsWithPkt} finalInbound=${chip.debugInboundCount()} restarts=${restartCount} =====\n\n` +
|
||||
'===== F2/IOCTL TRANSFERS (total seen, non-ring) =====\n' +
|
||||
`count=${f2Log.length}\n` + f2Log.join('\n') + '\n\n' +
|
||||
'===== TRACE TAIL (post-firmware) =====\n' + trace.slice(-120).join('\n') + '\n';
|
||||
void rawWords;
|
||||
'===== TRACE TAIL (post-firmware) =====\n' + trace.slice(-120).join('\n') + '\n\n' +
|
||||
'===== LAST RAW TX WORDS (hex) — the end of the run =====\n' +
|
||||
rawWords.slice(-70).map((w) => w.toString(16).padStart(8, '0')).join(' ') + '\n';
|
||||
try { writeFileSync('/tmp/cyw43-trace.txt', report); } catch { /* noop */ }
|
||||
resolve({
|
||||
serial,
|
||||
|
|
|
|||
|
|
@ -460,6 +460,20 @@ export class RP2040Simulator {
|
|||
this.feedCyw43Word(value);
|
||||
return origPush(value);
|
||||
};
|
||||
// Reset the gSPI framing at each transfer boundary. cyw43_spi_transfer
|
||||
// does pio_sm_restart before pushing the count words, so this keeps the
|
||||
// sniffer deterministic even across the firmware-stream fast-path.
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
if (typeof (sm as any).restart === 'function') {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const origRestart: () => void = (sm as any).restart.bind(sm);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(sm as any).restart = () => { this.cyw43Sniffer?.reset(); return origRestart(); };
|
||||
this.cyw43HookedFifos.push({
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
restore: () => { (sm as any).restart = origRestart; },
|
||||
});
|
||||
}
|
||||
// Serve the chip's response when the driver's DMA actually reads the
|
||||
// RX FIFO. Pushing into the FIFO eagerly raced the async DMA/PIO and
|
||||
// the data arrived late or was lost; serving on pull keeps it in lock
|
||||
|
|
|
|||
Loading…
Reference in New Issue