fix(cyw43): WiFi only worked on the first Run after a page load
The CYW43439 emulator is stateful — bus handshake, backplane window, SDPCM sequence and credit, event mask, link state — and attachPioPeripheral is idempotent, called once at board-add. So the chip's state SURVIVED every later Run while the guest driver started over from scratch. The second Run had a fresh driver talking to a chip mid-conversation: the handshake "passed" against stale registers, IOCTL replies came back zeroed (WiFi.macAddress() read all 00s), and the first call that actually waited on the chip — WiFi.begin(), or the second IOCTL of any kind — blocked forever. A rebuilt MCU means a rebooted board, so the chip is power-cycled with it: the peripheral is replaced wholesale (it owns its network bridge and its whole register model, so there is no partial-reset path to get wrong) right before the FIFO hooks are re-installed on the new PIO. Also: overlay board simulators now get onSimulationStart. The Pimoroni Pico Plus 2 W and Badger 2350 carry the same CYW43439 die on an RP2350 simulator, which is not an RP2040Simulator, so they never had their network bridge opened — WiFi that reached an IP and could then reach nothing. Verified on the RP2350 side with a harness that boots a real arduino-pico binary twice in one simulator (pro overlay); before the fix run 2 hung with a zeroed MAC, after it both runs associate.
This commit is contained in:
parent
664c3581a3
commit
24ea1a73f8
|
|
@ -183,6 +183,9 @@ export class RP2040Simulator {
|
|||
// ── Generic PIO/gSPI bus peripheral (e.g. the pro WiFi co-processor). Null
|
||||
// in OSS (no factory installed); attached for boards a factory supports.
|
||||
private pioPeripheral: PioPeripheral | null = null;
|
||||
/** Board id the PIO peripheral was created for, so a rebooting guest can be
|
||||
* handed a freshly powered chip (see powerCyclePioPeripheral). */
|
||||
private pioBoardId: string | null = null;
|
||||
private pioHookedFifos: Array<{ restore: () => void }> = [];
|
||||
// The board kind this simulator runs (set by attachPioPeripheral). A
|
||||
// 'pi-pico-w' boots the RPI_PICO_W firmware (with the `network` module)
|
||||
|
|
@ -386,6 +389,7 @@ export class RP2040Simulator {
|
|||
// RP2040, so those hooks now point at the discarded instance. Re-install
|
||||
// them on the new PIO FIFOs or the peripheral never sees the bus traffic.
|
||||
if (this.pioPeripheral) {
|
||||
this.powerCyclePioPeripheral();
|
||||
this.pioHookedFifos = [];
|
||||
this.installPioPeripheralHooks();
|
||||
}
|
||||
|
|
@ -418,6 +422,7 @@ export class RP2040Simulator {
|
|||
// factory-not-installed-yet) so loadMicroPython still picks the W firmware
|
||||
// for a pi-pico-w board.
|
||||
this.boardKind = boardKind;
|
||||
this.pioBoardId = boardId;
|
||||
if (this.pioPeripheral) return this.pioPeripheral;
|
||||
const peripheral = createPioPeripheral(boardKind, boardId);
|
||||
if (!peripheral) return null;
|
||||
|
|
@ -435,6 +440,48 @@ export class RP2040Simulator {
|
|||
return peripheral;
|
||||
}
|
||||
|
||||
/**
|
||||
* Give a rebooting guest a chip that has also rebooted.
|
||||
*
|
||||
* The CYW43439 emulator is stateful: the bus handshake result, the backplane
|
||||
* window, the SDPCM sequence counters and credit, the event mask, the link
|
||||
* state. attachPioPeripheral is idempotent and the store only calls it at
|
||||
* board-add, so that state SURVIVED every later Run while the guest driver
|
||||
* started over from scratch. The second Run then had a fresh driver talking
|
||||
* to a chip mid-conversation: the handshake "passed" against stale
|
||||
* registers, IOCTL replies came back zeroed, and the first call that waited
|
||||
* on the chip blocked forever. Only the first Run after a page load worked.
|
||||
*
|
||||
* Called from the paths that rebuild the MCU, before the FIFO hooks are
|
||||
* re-installed, so the new chip is what gets wired to the new PIO.
|
||||
*/
|
||||
private powerCyclePioPeripheral(): void {
|
||||
if (!this.pioPeripheral || !this.pioBoardId) return;
|
||||
const kind = this.boardKind;
|
||||
const boardId = this.pioBoardId;
|
||||
try {
|
||||
this.pioPeripheral.detach?.();
|
||||
} catch {
|
||||
/* the old chip is being thrown away either way */
|
||||
}
|
||||
for (const h of this.pioHookedFifos) {
|
||||
try {
|
||||
h.restore();
|
||||
} catch {
|
||||
/* noop */
|
||||
}
|
||||
}
|
||||
this.pioHookedFifos = [];
|
||||
this.pioPeripheral = null;
|
||||
const fresh = createPioPeripheral(kind, boardId);
|
||||
if (!fresh) return;
|
||||
this.pioPeripheral = fresh;
|
||||
fresh.onHostWake((active: boolean) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
try { (this.rp2040 as any)?.gpio?.[24]?.setInputValue(active); } catch { /* noop */ }
|
||||
});
|
||||
}
|
||||
|
||||
/** Detach the PIO peripheral (called from teardown). */
|
||||
detachPioPeripheral(): void {
|
||||
for (const h of this.pioHookedFifos) h.restore();
|
||||
|
|
|
|||
|
|
@ -2571,12 +2571,22 @@ export const useSimulatorStore = create<SimulatorState>((set, get) => {
|
|||
// Notify an attached PIO peripheral (the pro CYW43 WiFi co-processor)
|
||||
// that the simulation started, with the board's source files so it can
|
||||
// detect WiFi usage and open its network bridge. No-op in OSS.
|
||||
if (rpSim instanceof RP2040Simulator) {
|
||||
//
|
||||
// Overlay boards carry the same peripheral: the Pimoroni Pico Plus 2 W
|
||||
// and Badger 2350 have the RM2 (the same CYW43439 die) on an RP2350
|
||||
// simulator, which is NOT an RP2040Simulator. Without this branch they
|
||||
// associated to the local virtual AP but never got the network bridge
|
||||
// opened, so real internet and the Pro custom-AP config never applied
|
||||
// — WiFi that reached an IP and could reach nothing.
|
||||
const pioSim = rpSim as
|
||||
| (typeof rpSim & { getPioPeripheral?: () => { onSimulationStart?: (f: unknown[]) => void } | null })
|
||||
| null;
|
||||
if (typeof pioSim?.getPioPeripheral === 'function') {
|
||||
const editorState = useEditorStore.getState();
|
||||
const rawFiles = editorState.fileGroups[board.activeFileGroupId];
|
||||
const boardFiles =
|
||||
rawFiles && rawFiles.length > 0 ? rawFiles : editorState.files;
|
||||
rpSim.getPioPeripheral()?.onSimulationStart?.(boardFiles);
|
||||
pioSim.getPioPeripheral()?.onSimulationStart?.(boardFiles);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue