fix(rp2040): keep the SPICE input path off the CYW43 pins

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.
This commit is contained in:
David Montero Crespo 2026-08-17 22:55:45 +02:00
parent a27c7d61b6
commit bb46d51a3c
1 changed files with 18 additions and 0 deletions

View File

@ -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