perf(cyw43): fast-path firmware download in boot harness
Add PioBusSniffer.inDiscardableWriteData(): true while framing a large non-F2 write (firmware/backplane bulk write the chip discards). The boot harness drops those data words (keeping ~4 so the PIO raises TXSTALL, which is all the driver's write path waits for) instead of bit-banging the full ~224 KB through the PIO. F2/SDPCM IOCTL writes and every count/command word are retained in full, so the bring-up still completes the 23-IOCTL wifi_on sequence (F1 framing 3613 -> 97, F2 unchanged). Also adds IPSR + PC-histogram sampling: confirmed the post-mcast_list stall is thread-mode (no GPIO IRQ storm) inside MicroPython's host-side cyw43_cb_tcpip_init (lwIP), above the chip emulation.
This commit is contained in:
parent
c4cbb17591
commit
6bdb590b0a
|
|
@ -233,6 +233,16 @@ describe.skipIf(!process.env.CYW43_HARNESS)('Pico W cyw43 boot harness (investig
|
|||
pushCount++;
|
||||
rawWords.push(v >>> 0);
|
||||
if (rawWords.length > 600) rawWords.splice(0, rawWords.length - 400); // ring
|
||||
// Firmware/backplane bulk-write data: discard most of it (keep up to
|
||||
// 4 words so the PIO drains a little then raises TXSTALL, which is all
|
||||
// the driver's write path waits for). This fast-paths the ~224 KB
|
||||
// download instead of bit-banging every word through the PIO. F2
|
||||
// (IOCTL) writes and all count/command words go through feedWord and
|
||||
// are retained in full.
|
||||
if (sniffer.inDiscardableWriteData()) {
|
||||
if (q.length - head < 4) q.push(v >>> 0);
|
||||
return;
|
||||
}
|
||||
feedWord(v, sm);
|
||||
q.push(v >>> 0);
|
||||
};
|
||||
|
|
@ -352,9 +362,13 @@ describe.skipIf(!process.env.CYW43_HARNESS)('Pico W cyw43 boot harness (investig
|
|||
// blocks in active(True) with no bus activity, so a PC histogram localizes
|
||||
// the stuck loop — DMA wait, TXSTALL wait, or a delay).
|
||||
const pcHist = new Map<number, number>();
|
||||
const ipsrHist = new Map<number, number>();
|
||||
const pcSampler = setInterval(() => {
|
||||
const pc = ((sim.rp2040 as any).core?.PC ?? 0) >>> 0;
|
||||
const core = (sim.rp2040 as any).core;
|
||||
const pc = (core?.PC ?? 0) >>> 0;
|
||||
pcHist.set(pc, (pcHist.get(pc) ?? 0) + 1);
|
||||
const ipsr = (core?.IPSR ?? 0) & 0x3f; // 0=thread, else exception number
|
||||
ipsrHist.set(ipsr, (ipsrHist.get(ipsr) ?? 0) + 1);
|
||||
}, 20);
|
||||
const deadline = setTimeout(finish, 70_000);
|
||||
function finish() {
|
||||
|
|
@ -396,7 +410,10 @@ describe.skipIf(!process.env.CYW43_HARNESS)('Pico W cyw43 boot harness (investig
|
|||
`===== CPU FAULTS faultCount=${faultCount} =====\n` + faultLog.join('\n') + '\n' +
|
||||
'===== HOT PCs (busy-wait localization) =====\n' +
|
||||
[...pcHist.entries()].sort((a, b) => b[1] - a[1]).slice(0, 12)
|
||||
.map(([pc, n]) => ` ${String(n).padStart(6)} PC=0x${pc.toString(16)}`).join('\n') + '\n\n' +
|
||||
.map(([pc, n]) => ` ${String(n).padStart(6)} PC=0x${pc.toString(16)}`).join('\n') + '\n' +
|
||||
'===== IPSR (0=thread, else exception#) =====\n' +
|
||||
[...ipsrHist.entries()].sort((a, b) => b[1] - a[1])
|
||||
.map(([ipsr, n]) => ` ${String(n).padStart(6)} IPSR=${ipsr}`).join('\n') + '\n\n' +
|
||||
'===== PIO/SM STATE AT DEADLINE (deadlock) =====\n' + pioState + '\n' +
|
||||
'===== F2/IOCTL TRANSFERS (total seen, non-ring) =====\n' +
|
||||
`count=${f2Log.length}\n` + f2Log.join('\n') + '\n\n' +
|
||||
|
|
|
|||
|
|
@ -146,6 +146,20 @@ export class PioBusSniffer {
|
|||
/** Debug hook (tests): fired with each count word's raw value + decode. */
|
||||
onCount: ((kind: 'out' | 'in', raw: number, bytes: number) => void) | null = null;
|
||||
|
||||
/**
|
||||
* True while the in-flight transfer is a large non-F2 write — i.e. a firmware
|
||||
* download block (or any backplane bulk write the chip discards). The host
|
||||
* pushes ~224 KB of these; their data words carry no information the emulator
|
||||
* needs (firmware lands in chip SRAM we don't model), so a non-dropping FIFO
|
||||
* forces the PIO to bit-bang every one and the emulation crawls. The harness
|
||||
* uses this to drop those data words while keeping F2/SDPCM writes (IOCTLs)
|
||||
* and every command/count word intact. 36 bytes = count(0) + cmd(4) + a few
|
||||
* config words; firmware blocks are 64-byte payloads, well above it.
|
||||
*/
|
||||
inDiscardableWriteData(): boolean {
|
||||
return this.state === 'writeData' && this.cmd?.function !== 2 && this.outBytes > 36;
|
||||
}
|
||||
|
||||
*feedWord(rawWord: number): Generator<SnifferEvent> {
|
||||
const raw = rawWord >>> 0;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue