cyw43 (main): frequent firmware verify failures with a single-bit corruption on Pico 2 W, and re-running init() after a failure hangs
Environment
- crate:
cyw43@ main 73b44d7e (2026-09-03, the xarxa-era rework with the fallibleBus/Chipsplit; unreleased),cyw43-pio0.10.0 (DEFAULT_CLOCK_DIVIDER),embassy-rp0.10, rustc 1.98 - hardware: Raspberry Pi Pico 2 W (RP2350) on a custom carrier board, USB powered; firmware blobs from this repo unchanged (43439A0.bin sha256 a5007948…, clm e2422bde…, nvram 6b81a482…)
- observed over ~15 cold boots while porting our firmware to the new API
Symptom 1: verify_download fails with a single-bit error, frequently
After runner.init() downloads the firmware, verify_download's first sample read (fw offset 0, 16 bytes) frequently mismatches by exactly one bit of one byte:
FW expected: [00, 00, 00, 00, 71, 14, 00, 00, 9d, 13, 00, 00, 9d, 13, 00, 00]
FW acutal: [00, 00, 00, 00, 71, 14, 00, 00, 9d, 13, 00, 00, 9c, 13, 00, 00](byte 12: 0x9d -> 0x9c, i.e. LSB cleared; note bytes 8..12 and 12..16 are the same word 9d 13 00 00 — the first copy reads back fine, the second one doesn't.)
Per boot the behavior is bimodal:
- on ~half the boots, re-reading the sample 1-2 times returns the correct bytes (a read-side transient), verify passes after the re-reads;
- on the other boots, three consecutive re-reads agree on the wrong byte (we could not yet determine whether that is a worse transient or the byte is actually wrong in chip RAM — a "rewrite the region from the source image, then re-verify" experiment is still pending on our side).
The released cyw43 0.7 with the same blobs, same cyw43-pio, same clock divider and the same board uploads and verifies cleanly (weeks of daily use), so this tracks with the main-branch bus rework. The obvious pacing difference: 0.7 allocated a fresh zeroed ~4 KB stack buffer per bp_read/bp_write call; main reuses one shared aligned buffer and writes the cmd word into its first 4 bytes, which removes the per-chunk delay the old code accidentally provided.
Possibly related: #4791 (Pico 2 W SPI marginality at DEFAULT_CLOCK_DIVIDER -> init loop/assert, fixed by slowing the divider) and #6313 (an independent report of an LSB-stuck bit arriving via cmd_read — the same single-bit signature we see).
Symptom 2: re-running runner.init() after a failure never returns
With a retry loop around init (first failure observed, chip still alive):
loop {
match runner.init(firmware, nvram, None).await {
Ok(()) => break,
Err(_) => Timer::after(Duration::from_millis(500)).await, // then retry
}
}the second init() call hangs synchronously — it never completes and never errors; on our board the whole executor freezes behind it (proven by an armed 15 s watchdog resetting the board). Stock cyw43::new() unwrap()s instead, so today this only bites anyone adding a retry — but it also means init is not currently retry-safe if fallibility is meant to be handled rather than unwrapped.
Reproduction
Any main-branch Pico W/W2 WiFi example (e.g. wifi_blinky), flashed repeatedly (power-cycle between boots), with defmt logs on the boot path; the FW expected/acutal error (or the re-read heal) shows within ~4 s of boot. The hang needs the retry snippet above plus one failing boot.
Workaround
Pinning cyw43 = 0.7 (with embassy-net-driver-channel 0.4) restores clean uploads on the same hardware.
Source: embassy-rs/embassy