SearchA-ZR › Raspberry Pi Pico W

Raspberry Pi Pico W

2022 Open source · MIT Online

The real Raspberry Pi Pico W, in the browser. The RP2040's two ARM Cortex-M0+ cores run on the vendored rp2040js SoC — a complete Raspberry Pi Pico with XIP flash, 264 KB SRAM, GPIO, timers, UART and a USB device controller. The Pico W's signature detail is that its on-board LED is not an RP2040 pin: it hangs off the added Infineon CYW43439 WiFi chip's WL_GPIO0 and is driven over the chip's gSPI bus. A high-level model of the CYW43439 speaks that gSPI protocol back to the real driver, so setting the LED over the WiFi chip actually lights the on-board LED. It boots the official MicroPython firmware to an interactive REPL over USB-CDC, shows the CYW43 LED and serial console on screen, and wires the whole ARM Cortex-M0+ into this site's shared Thumb debugger. Being a bare Pico W, it is naturally silent — there is no on-board audio.

Visit the official Raspberry Pi Pico W page ↗

Runs on: Web browser

Raspberry Pi Pico W Online Emulator

Play Raspberry Pi Pico W using JavaScript directly in your browser.

Configurations

ConfigurationEmulatorMachineOSLegal
MicroPython REPLRaspberry Pi Pico WRaspberry Pi Pico WopenOpen ⛶
CYW43 LED (via WiFi chip)Raspberry Pi Pico WRaspberry Pi Pico WopenOpen ⛶
Hello UART (serial)Raspberry Pi Pico WRaspberry Pi Pico WopenOpen ⛶

Chips

Notes

Embedding

The RP2040 SoC is Uri Shaked's rp2040js (MIT), a complete in-browser Raspberry Pi Pico: two Cortex-M0+ cores, XIP flash, 264 KB SRAM, the SIO/GPIO block, timers, UART, a USB device controller with a USB-CDC class, and the PIO. It is used unmodified, bundled once to a single file-safe browser global (window.rp2040js) with esbuild — no core was reinvented.

The Raspberry Pi Pico W adds an Infineon CYW43439 WiFi chip. Crucially, the board's on-board LED is wired to that chip's WL_GPIO0, not to an RP2040 pin, and the RP2040 talks to the CYW43439 over a bit-banged gSPI bus (a PIO program on GPIO24 DATA, GPIO25 CS, GPIO29 CLK, power on GPIO23). rp2040js runs that PIO, so the real driver's gSPI words flow through PIO1's TX/RX FIFOs. Our cyw43.js intercepts those FIFO words and answers as a high-level model of the CYW43439's gSPI slave:

// esbuild entry.ts --bundle --format=iife  ->  window.rp2040js
var R   = window.rp2040js;
var sim = new R.Simulator();          // owns the RP2040 + a simulation clock
var mcu = sim.rp2040;
mcu.loadBootrom(R.bootromB1);
loadUF2(firmwareBytes, mcu);            // real Pico W MicroPython UF2 -> mcu.flash
CYW43.attach(mcu, R, { onLED: setLed }); // HLE CYW43439 on PIO1's gSPI FIFOs
var cdc = new R.USBCDC(mcu.usbCtrl);    // USB-CDC = the MicroPython REPL

The gSPI slave. The real cyw43-driver frames every bus access as a 32-bit command word (write<<31 | inc<<30 | fn<<28 | addr<<11 | len). The chip boots little-endian with 16-bit halves swapped until SPI_BUS_CONTROL selects 32-bit big-endian; cyw43.js honours both. It answers the 0xFEEDBEAD detect, the ALP/HT clock handshake (SDIO_CHIP_CLOCK_CSR), the backplane address windowing, and the ARM-core reset registers, and it decodes the SDPCM/CDC control channel. The on-board LED is set by the WLC_SET_VAR "gpioout" ioctl for WL_GPIO0 — cyw43.js recognises that exact frame and toggles the panel LED. WiFi RF itself is not modelled; the chip-GPIO/LED path is.

MemberKindWhat it does
mcu.core.executeInstruction()methodExecute one Thumb instruction on core 0, advancing core.PC; returns a cycle estimate. The single-step primitive.
mcu.core.registers (Uint32Array 16)fieldr0-r12, SP(13), LR(14), PC(15), plus core.xPSR and the flag fields — all readable and writable.
mcu.flash / mcu.sram / mcu.bootromfieldThe real memories as typed arrays — read side-effect-free for the hex + disassembly views.
mcu.pio[1] TXF0 / RXF0hookPIO1's gSPI FIFOs; cyw43.js intercepts these words to model the CYW43439 slave and drive WL_GPIO0.
cdc.sendSerialByte(b) / mcu.uart[0].feedByte(b)methodSerial RX in — the keyboard drives the MicroPython REPL through the USB-CDC port.

Debugger integration

The plug-in (pico-w-debug.js) describes the RP2040 to the shared debugger and nothing more — rp2040js is not patched:

  • Reused decoder. The Cortex-M0+ runs ARMv6-M Thumb, a subset of the existing /debugger/src/cpus/cortex-m7.js disassembler (written for the Playdate-ARM Cortex-M7). It is reused as-is under the display name "ARM Cortex-M0+"; no new decoder was needed.
  • Registers are read live each refresh: r0-r12, SP, LR, PC, xPSR, the APSR flags N Z C V, and PRIMASK / nPRIV. Each has a set() that writes straight back into core.registers / the core's flag fields.
  • Memory is exposed as three chips, all read side-effect-free directly off the typed arrays, each with its real base address so the disassembly lines up with PC: the XIP flash at 0x10000000 (disassembled), the SRAM at 0x20000000, and the 16 KB boot ROM at 0x00000000 (also disassembled).
  • Single step is one executeInstruction(). Breakpoints are host-side checks of core.PC. Write-watchpoints wrap mcu.writeUint8/16/32 and pause the loop the moment a watched bus address is written.

Architecture

The Raspberry Pi Pico W (2022) is the wireless member of the Pico family: the same RP2040 as the Pico, with an added Infineon CYW43439 2.4 GHz WiFi + Bluetooth chip.

  • Dual ARM Cortex-M0+ — two 32-bit ARMv6-M cores at up to 133 MHz (modelled here at 125 MHz), Thumb instruction set, sharing the bus through the SIO single-cycle I/O block.
  • Memory — 264 KB on-chip SRAM at 0x20000000; code executes in place (XIP) from external QSPI flash at 0x10000000; a 16 KB mask boot ROM at 0x00000000.
  • On-board LED via the WiFi chip — this is the Pico W's signature quirk. Unlike the Pico (LED on GPIO25), the Pico W's on-board LED is wired to the CYW43439's WL_GPIO0. GPIO25 is instead the CYW43 chip-select. Software toggles the LED with machine.Pin('LED') in MicroPython or cyw43_arch_gpio_put() in C, both of which send a chip-GPIO ioctl to the CYW43439 over the gSPI bus.
  • CYW43439 gSPI — the RP2040 drives the WiFi chip over a PIO-bit-banged gSPI bus (GPIO24 DATA, GPIO25 CS, GPIO29 CLK, GPIO23 power). Here it is modelled by cyw43.js, a high-level emulation of the chip's gSPI slave, so the LED-over-WiFi-chip path works end-to-end.
  • Serial — firmware talks to a host over UART0 or, as MicroPython does, over a USB-CDC virtual serial port — which is where the MicroPython REPL appears.

The default firmware is the official MicroPython for Raspberry Pi Pico W (.uf2) — real, unmodified firmware. It boots through the same path as on hardware to an interactive >>> prompt on the USB-CDC console, and its cyw43-driver really does bring the CYW43439 up over our gSPI model. The CYW43 LED firmware then demonstrates the quirk directly: a small bare-metal program that issues the authentic CYW43439 gSPI command stream (the SPI_BUS_CONTROL endian select and the gpioout chip-GPIO ioctl) and visibly blinks the on-board LED through the WiFi chip.

Honest limits. rp2040js is cycle-approximate. The CYW43439 is a high-level model of its gSPI/GPIO surface — the chip's own firmware, WiFi RF, scanning and networking are not emulated. In the stock MicroPython image, machine.Pin('LED') makes the real driver bit-bang the CYW43 bus through our model (chip detect, endian, clock handshake, backplane windowing and ARM-core reset all real); the driver then streams its ~225 KB WLAN firmware to the chip over a PIO-paced DMA transfer, which the vendored rp2040js does not clock through, so MicroPython's own LED bring-up stops there. The dedicated bare-metal CYW43 LED firmware drives the same authentic chip-GPIO ioctl over the CPU-fed FIFO path and blinks the LED end-to-end. The bare Pico W has no on-board sound hardware, so this emulator is naturally silent.