From bb46d51a3c43d9a6c7d33dc401a97008fbeb2668 Mon Sep 17 00:00:00 2001 From: David Montero Crespo Date: Mon, 17 Aug 2026 22:55:45 +0200 Subject: [PATCH] fix(rp2040): keep the SPICE input path off the CYW43 pins MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Every Pico W WiFi example in the gallery hangs at `while not wlan.isconnected()`, with `wlan.status()` stuck at 1 (CONNECTING) and `wlan.active()` reading True. The CYW43439 sits on GP23/24/25/29, inside the module — there is no canvas net for those pins. attachPioPeripheral() drives WL_HOST_WAKE (GPIO24) with setInputValue() straight from the chip's hostWakeLevel(). When RP2040 gained spice-driven digital inputs, the GPIO listener began seeding a pull level onto every input pin, GPIO24 included: the CYW43 driver configures that pad as an input, the seed landed on top of host-wake, the host stopped polling the chip, and no association event was ever delivered. The fault is invisible from the CYW43 side — its own harness drives a raw rp2040js with its own FIFO/PIO plumbing and passes — which is why this looked like a WiFi bug rather than a GPIO one. Diagnosed by patching the sketch in the editor to print wlan.status() each iteration: "WAIT 1 status 1 active True" repeating forever, i.e. the join was accepted and the link never came up. --- frontend/src/simulation/RP2040Simulator.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/frontend/src/simulation/RP2040Simulator.ts b/frontend/src/simulation/RP2040Simulator.ts index 996f77c5..ca6d9609 100644 --- a/frontend/src/simulation/RP2040Simulator.ts +++ b/frontend/src/simulation/RP2040Simulator.ts @@ -29,6 +29,16 @@ import { requestElectricalResolve } from './spice/electricalResolveHook'; * Default SPI → SPI0 (GPIO16=MISO, GPIO19=MOSI, GPIO18=SCK, GPIO17=CS) */ +/** + * GPIOs wired to an on-module PIO peripheral rather than to the canvas. + * + * On the Pico W the CYW43439 hangs off GP23 (WL_ON), GP24 (WL_HOST_WAKE / gSPI + * data), GP25 (WL_CS) and GP29 (WL_CLK). They are internal to the module, so no + * SPICE net exists for them and the spice-driven input path must not touch + * them — see the guard in setupGpioListeners(). + */ +const PIO_PERIPHERAL_PINS = new Set([23, 24, 25, 29]); + const F_CPU = 125_000_000; // 125 MHz const CYCLE_NANOS = 1e9 / F_CPU; // nanoseconds per cycle (~8 ns) const FPS = 60; @@ -784,6 +794,14 @@ export class RP2040Simulator { // on a mode/pull change (an external value change via setInputValue does // not alter `value` for an input pin), so we can split cleanly. if (state >= GPIOPinState.Input) { + // Pins that belong to an attached PIO peripheral are NOT canvas pins. + // WL_HOST_WAKE (GPIO24) is driven by attachPioPeripheral() with + // setInputValue() from hostWakeLevel(); seeding a pull level here + // overwrites it the moment the CYW43 driver configures the pad as an + // input. The host then stops polling the chip, no association event is + // ever delivered, and the sketch sits forever at status=1 (CONNECTING) + // while `active()` still reads True. Leave them to the peripheral. + if (this.pioPeripheral && PIO_PERIPHERAL_PINS.has(pin)) return; // INPUT pin. Surface the internal pull so NetlistBuilder stamps the // weak resistor; the actual logic level is injected from the SPICE // solve by connectDigitalInputsToMcu. We do NOT mark the pin as an MCU