A dependency graph of one contributor's work on the Raspberry Pi Pico 2 and Pico 2 W.
I'm thoroughly confused. I guess the tests were added to the chip crate in one PR, used in a different PR for testing, but reverted from the second PR's main.rs changes before the second PR was merged. [...] It doesn't seem like there is a clear testing/bring up strategy for the rp2x PIO.
— bradjc, reviewing #5126
That was fair, and this page is the answer to it. The work is a stack: one long chain of commits where each depends on the one before, and the early links are also open as small reviewable pull requests of their own. GitHub cannot express a stack when the branches live in a fork, so the same commit appears in two places and reads as the same work submitted twice.
Below is that stack, drawn. Click a pull request to light up exactly the commits it carries. Every node also shows how it is checked, which is the other half of the question: a pure code move is proved by the binary coming out with identical sections, logic is covered by a host test that fails without the change, and anything touching hardware is run on a board.
The list of blocks is not kept here. Every chip crate has a
resets.rs holding one bit per resettable hardware block, so the
chip's own source says what it has — 23 blocks on the RP2350,
21 on the RP2040. A cell is filled when a driver module for that
block exists in the crate. That is presence, not completeness: it says a
driver is there, not that every feature of the block is reachable.
6driven upstream
of 23 RP2350 blocks
10driven on this fork
the bench kernel, everything merged
14driven on the RP2040
the older chip, better covered
a driver module covers it
the block is there, nothing drives it
this chip does not have the block
Hardware blockRP2040RP2350forkcovered by
Pins
Buses
Analogue
Time
Moving data
Clocks
Chip services
Click a block to trace it down to its registers. The
! marks a block with a defect
found here.
Read at upstream/master (a7b14ffaf) and
bench/stepper-pico2w. Not shown because they are not blocks the reset
controller names: chip, clocks, deferred_calls, interrupts, lib, mod, resets, ticks, watchdog, xosc.
From a system call to a register
Every rung comes from the tree. The driver number from the one
enum that assigns them, the capsule from the board's own with_driver,
the kernel interface from what that capsule imports, the chip driver from the
crate implementing it, and the registers from its register_structs!.
A rung with no answer is a fact about the tree rather than a gap here.
Pin function select IO_BANK0
Which peripheral each pin is wired to. Set it wrong and a write to the pin goes nowhere, silently.
Known defect. Pins work; pin interrupts panic the kernel. IO_IRQ_BANK0 is defined on the RP2350 and routed nowhere, so a legal syscall brings the board down. Four lines on rp2350-gpio-irq. rp2350-gpio-irq
System call0x00004capsules_core::gpio · reachable on raspberry_pi_pico, raspberry_pi_pico_2, raspberry_pi_pico_2_w, raspberry_pi_pico_w
Capsulecapsules/core/src/gpio.rs
System call0x00002capsules_core::led · reachable on raspberry_pi_pico, raspberry_pi_pico_2, raspberry_pi_pico_2_w, raspberry_pi_pico_w
Capsulecapsules/core/src/led.rs
System call0x00011capsules_extra::stepper · reachable on raspberry_pi_pico_2_w
The analogue half of a pin: drive strength, pull-up, pull-down, input enable, schmitt trigger.
System callno capsule sits above this block
Kernel interfacethe driver implements no HIL trait, so nothing in the kernel's device interface describes this block
Chip driverchips/rp2xxx/src/pads.rs57 lines
Registersno register block in these files
Flash pad controls PADS_QSPI
The same pad controls, for the flash pins.
System callno capsule sits above this block
Kernel interfacethe driver implements no HIL trait, so nothing in the kernel's device interface describes this block
Chip drivernothing in the tree drives this block on an RP2350
Flash pin select IO_QSPI
The same function select, for the six pins that talk to the flash chip.
System callno capsule sits above this block
Kernel interfacethe driver implements no HIL trait, so nothing in the kernel's device interface describes this block
Chip drivernothing in the tree drives this block on an RP2350
Programmable IO PIO0 PIO1 PIO2
Small state machines running an instruction set of their own, so the chip can speak a bus it has no hardware for. On a Pico 2 W they clock the radio.
Known defect. Five defects, all with fixes proposed in #5157. Four were found by the driver's first host tests; the fifth came out of a security pass and is the one demonstrated on silicon — a block interrupt flag delivered to the wrong state machine's client, which can hang the kernel. #5157
System callno capsule sits above this block
Kernel interfacethe driver implements no HIL trait, so nothing in the kernel's device interface describes this block
The console. The kernel's own output and an application's both arrive over one of these.
Known defect. An aborted receive tears down every other receive on the same multiplexer. Three defects, fix written on rp2-uart-abort-fix. rp2-uart-abort-fix
System call0x00001capsules_core::console · reachable on raspberry_pi_pico, raspberry_pi_pico_2, raspberry_pi_pico_2_w, raspberry_pi_pico_w
Chip id, revision and manufacturer, readable at runtime.
System callno capsule sits above this block
Kernel interfacethe driver implements no HIL trait, so nothing in the kernel's device interface describes this block
Chip drivernothing in the tree drives this block on an RP2350
Inside a register
31:28REVISION27:12PART11:0MANUFACTURER
31:21ASIC0FPGA
31:0SOURCE_GIT_HASH
On the RP2040chips/rp2040/src/sysinfo.rs98 lines
Debug port JTAG
Where the debug probe attaches.
System callno capsule sits above this block
Kernel interfacethe driver implements no HIL trait, so nothing in the kernel's device interface describes this block
Chip drivernothing in the tree drives this block on an RP2350
Hash accelerator SHA256
SHA-256 in hardware, for verifying a boot image.
System callno capsule sits above this block
Kernel interfacethe driver implements no HIL trait, so nothing in the kernel's device interface describes this block
Chip drivernothing in the tree drives this block on an RP2350
Random numbers TRNG
A hardware entropy source.
System callno capsule sits above this block
Kernel interfacethe driver implements no HIL trait, so nothing in the kernel's device interface describes this block
Chip drivernothing in the tree drives this block on an RP2350
System config SYSCFG
Chip-level odds and ends: processor configuration, which pins the debug interface uses.
System callno capsule sits above this block
Kernel interfacethe driver implements no HIL trait, so nothing in the kernel's device interface describes this block
Chip drivernothing in the tree drives this block on an RP2350
Testbench manager TBMAN
Reports whether the code is running on real silicon or in simulation.
System callno capsule sits above this block
Kernel interfacethe driver implements no HIL trait, so nothing in the kernel's device interface describes this block
Chip drivernothing in the tree drives this block on an RP2350
The board in your hand
The forty pins, and what each one is doing on a kernel you
can actually flash. The header's shape is the board's form factor; every
role on it is read from that board's own source, so a pin the kernel has
taken shows as taken and a pin an application can drive shows the number it
drives it by. The Pico 2 W's map is the bench kernel: the display, the
stepper and the radio are all on it.
an application can drive it
the kernel has taken it
the radio has it
an analogue input
named by the board, withheld from applications
the board does not name it
1GP0UART0 TX, the console out
2GP1UART0 RX, the console in
3GNDground
4GP2an app can drive it as gpio 2
5GP3an app can drive it as gpio 3
6GP4an app can drive it as gpio 4
7GP5an app can drive it as gpio 5
8GNDground
9GP6an app can drive it as gpio 6
10GP7an app can drive it as gpio 7
11GP8an app can drive it as gpio 8
12GP9an app can drive it as gpio 9
13GNDground
14GP10an app can drive it as gpio 10
15GP11an app can drive it as gpio 11
16GP12an app can drive it as gpio 12
17GP13an app can drive it as gpio 13
18GNDground
19GP14an app can drive it as gpio 14
20GP15an app can drive it as gpio 15
40VBUSpower
39VSYSpower
38GNDground
373V3_ENboard control
363V3(OUT)power
35ADC_VREFboard control
34GP28an app can drive it as gpio 28
33AGNDground
32GP27an app can drive it as gpio 27
31GP26an app can drive it as gpio 26
30RUNboard control
29GP22an app can drive it as gpio 22
28GNDground
27GP21an app can drive it as gpio 21
26GP20an app can drive it as gpio 20
25GP19an app can drive it as gpio 19
24GP18an app can drive it as gpio 18
23GNDground
22GP17an app can drive it as gpio 17
21GP16an app can drive it as gpio 16
Not brought out to the header
Four of the chip's GPIOs are not on the forty pins. On a Pico 2 W they are the radio, which is why an application cannot have them and why the board has no fourth ADC channel.
GP23an app can drive it as gpio 23
GP24an app can drive it as gpio 24
GP25on-board LED
GP29an app can drive it as gpio 29
1GP0UART0 TX, the console out
2GP1UART0 RX, the console in
3GNDground
4GP2SPI0 SCK, the display's clock
5GP3SPI0 TX, the display's data
6GP4an app can drive it as gpio 4
7GP5an app can drive it as gpio 5
8GNDground
9GP6an app can drive it as gpio 6
10GP7an app can drive it as gpio 7
11GP8an app can drive it as gpio 8
12GP9an app can drive it as gpio 9
13GNDground
14GP10an app can drive it as gpio 10
15GP11an app can drive it as gpio 11
16GP12an app can drive it as gpio 12
17GP13an app can drive it as gpio 13
18GNDground
19GP14an app can drive it as gpio 14
20GP15an app can drive it as gpio 15
40VBUSpower
39VSYSpower
38GNDground
373V3_ENboard control
363V3(OUT)power
35ADC_VREFboard control
34GP28analogue pad, prepared for the ADC
33AGNDground
32GP27analogue pad, prepared for the ADC
31GP26analogue pad, prepared for the ADC
30RUNboard control
29GP22an app can drive it as gpio 22
28GNDground
27GP21stepper phase
26GP20stepper phase
25GP19stepper phase
24GP18stepper phase
23GNDground
22GP17SPI chip select
21GP16an app can drive it as gpio 16
Not brought out to the header
Four of the chip's GPIOs are not on the forty pins. On a Pico 2 W they are the radio, which is why an application cannot have them and why the board has no fourth ADC channel.
GP23radio power
GP24radio gSPI, clocked by PIO
GP25radio chip select
GP29radio gSPI, clocked by PIO
What a process can actually call
Not what the kernel supports — what an application on
each board can reach, read from every with_driver arm on it.
A board can answer some driver numbers in its own file and pass the rest to a
base platform in another crate, so both hops are followed — reading only
the first reports one driver on a board that exposes ten.
11Pico WRP2040, upstream
5Pico 2RP2350, upstream
9Pico 2 WRP2350, this fork
DriverPico WPico 2Pico 2 Wnumber
alarm0x00000
console0x00001
led0x00002
gpio0x00004
adc0x00005
stepper0x00011
ipc0x10000
spi_controller0x20001
i2c_master0x20003
wifi0x30008
temperature0x60000
date_time0x90007
The stack
One column per pull request, 14 of them, holding all 29 commits.
Depth down a column is stack order: an arrow runs from each commit to the one that needs it
first. A dashed box is a commit that belongs to two pull requests, and a
dashed orange arrow is a dependency that crosses between columns — together
those are the whole reason the same work appears twice on GitHub. The word at the bottom left
of a box is how that change is verified.
Click a pull request to light up exactly the commits it carries.
Click a node for detail.
How each change is verified
host test6A test in the tree that fails if the change is reverted.
on silicon10Run on a real board and observed, not inferred.
identical sections5A pure move: the linked binary comes out with the same text, data and bss, so the change provably alters no behaviour.
build only4Checked by the thing building or linking, and nothing further.
unchecked4No automated check reaches this.
Nothing selected
Click any node in the graph.
The queue
The fork moves at whatever speed the work goes; upstream gets a trickle. Nothing is proposed until it is finished, demonstrated and small enough to review in one sitting, and only a few are in flight at a time — the constraint upstream is review throughput, not how fast patches can be written. Everything below exists as working code on the fork. The column it sits in is a decision about when to ask someone to read it, not about whether it is done.
Read from the working clones, so it is what exists rather than what
was last written down. A branch is matched to its pull request by which commits they
share, not by name.
What the columns mean
A unit of work moves left to right. The first column is not a backlog
being worked through — everything in it is finished, demonstrated and
pushed. It is waiting to be asked for, a few at a time, because
the constraint upstream is review throughput. A dashed arrow is one piece
of work that cannot go until another lands.
Five PIO fixes and the driver's first host tests. Four defects demonstrated by tests that fail on unmodified upstream; one of them panics the kernel.**Open as #5157**, in review, the same five commits. #5150 was the first attempt and was closed 2026-09-07 for a body written by AI under a checkbox promising it would not be, which is the one part of that close not worth contesting. **#5150 cannot be reopened, and that was tested rather than reasoned about** -- it refuses in the browser and through the API with `Could not open the pull request`, as #5154 does, with `viewerCanUpdate` and `viewerDidAuthor` true on both. #5150 was never force-pushed and refuses identically, which is what ruled out the force-push blamed for #5154; the only property the two share is `isDraft: true`. It is moot in any case, because this work is already open as its successor. Two earlier notes here argued that #5150 was reopenable where #5154 was not, and a third called the question untested; all three were written without reading the test that had already been run. #5157 carries a rewritten body of its own, 2,233 characters against a house median of 1,062 measured 2026-09-10 across the last 200 merged pull requests, so roughly twice the house length rather than the 5,714 this note used to claim for a body that is in fact 2,203.
Fixes #4770: `make program` cannot flash an application on any RP2 board, because splicing one in gives the (NOLOAD) `.stack` segment file content for SRAM and both UF2 converters refuse the result. One `objcopy -R .stack` per rule, five rules, four boards.**Open as #5156**, head `223d878c0`. **Both approvals were dismissed on 2026-09-10** by a force-push that corrected the Makefile comments and the commit message: they said the `--set-section-flags` step caused the bad `p_filesz`, and it does not -- a bare `objcopy` does the same thing, and the cause is the empty `.relocate` section. bradjc and ppannuto need re-asking; lschuermann re-approved #5158 after exactly this. The first attempt, #5154, was closed the same day it was opened -- for a placeholder description, not for the change -- and could not be reopened afterwards because the branch had been force-pushed. Its evidence is the reporter's own RP2040 run plus a `program-openocd` A/B on RP2350 silicon. Four Makefiles, thirteen added lines, no Rust. Touches `boards/raspberry_pi_pico_2/Makefile`, which #5141 also edits -- different regions of the file, so it merges cleanly either order.
Finished, not proposed yet — 7 branches, 38 commits
tock · rp2-adc3 commits
Moves the SAR ADC driver into the shared rp2xxx crate, adds the RP2350's, and wires it up on the Pico 2 so driver 0x00005 answers. Three commits, the same shape as #5112, which merged.Ready. Unblocks the whole analogue tier of the breadboard kit — joystick, potentiometer, light sensor, thermistor — none of which needs new wiring.Evidence for a pull request — 3 commits
Cut from upstream/master at 0cc36ca6c, 2 commits behind · 8 files changed, 431 insertions(+), 238 deletions(-)
What it adds
The RP2350 has no ADC driver at all — nor I2C nor PWM; the port is thinner than the RP2040's across the board. This moves the SAR ADC into the crate the two chips share and adds the RP2350's half: base address, channel set, and the interrupt routing.
Why the shape is safe
It is the shape that already merged. #5112 moved the PL022 SPI into `rp2xxx` and added the RP2350's the same way, in the same crate, with the same argument. No new driver number, no new API surface, no question about whether the feature is wanted.
What actually differs between the chips
Register offsets are identical. The base moves to 0x400a0000, CS.AINSEL is four bits rather than three and CS.RROBIN nine rather than five, and the channel count depends on the package — QFN-60 bonds four inputs with the temperature sensor on channel 4, QFN-80 bonds eight with it on channel 8. The channel set is a trait the chip crate implements, so the package question is answered where it belongs.
Two things changed rather than moved
The driver used to decide the temperature sensor's channel with `if *channel as u32 == 4`, a literal that is right for the RP2040 and a QFN-60 RP2350 and wrong for a QFN-80 one. And a field written on every sample and never read anywhere has gone.
Sizes
Not identical, and could not be: moving code across a crate boundary changes the partition the optimiser starts from. .text on the three RP2040 boards that build it goes 95392 to 95244, 98616 to 98696, and 91664 to 91588. Behaviour unchanged — same registers, same write order, same interrupt handling.
The board half, and why it is in the same branch
A chip driver with no consumer invites the question of what it is for, so the third commit wires it on the upstream `raspberry_pi_pico_2`: mux, four channels, syscall driver. GPIO26-28 reach the header as ADC0-ADC2 and GPIO29 measures VSYS through a divider. Those four leave the GPIO syscall list, commented out exactly as `nano_rp2040_connect` already does it, because a pin cannot usefully be both — an application driving GPIO26 as an output while another samples channel 0 is a short through the pad driver, and neither driver can see the other to refuse.
A pad trap the chip driver cannot fix
Out of reset an RP2350 pad has its isolation latch set and its pull-down enabled. The pull-down is the one that bites: across an analogue source it forms the lower leg of a divider, and the reading that produces is the one diagnosed as a bad sensor. **That degraded case was not reproduced here** -- what was measured is the configured case below, which reaches both rails. The pad belongs to GPIO, not to the converter, so the board configures it: `set_function(NULL)` clears the isolation latch, `PullNone` removes the pull, `deactivate_pads` switches the digital input buffer off. The same three steps `adc_gpio_init` takes in the C SDK. `nano_rp2040_connect` does none of them today.
Read on silicon, and the pad sequence is measured rather than argued
A joystick on a Pico 2 W reads 0 to 65408 on one axis and 0 to 65520 on the other, out of 65535 — both rails, both axes. That is the check that matters for the pad configuration: 65408 and 65520 against a full scale of 65535 are both rails. A pad left with its reset pull-down would sit inside a divider and could not reach them, though that degraded case was not reproduced here. “The pads are configured” and “the range reaches the rails” are different claims and only the second is checkable. Left-justification is confirmed in the same run: every raw reading is a multiple of sixteen.
What that covers, and what it does not
The chip driver, the ADC FIFO interrupt routing and the three-step pad sequence are exercised on silicon. The `raspberry_pi_pico_2` board file in this branch is not — the measurement is on a Pico 2 W, whose board file is #5141 and not upstream, and there is no plain Pico 2 here to run it on. The two boards share the chip half and differ only in which channels they expose. Builds, fmt and clippy clean, and .text on raspberry_pi_pico_2 goes 65092 to 66708 by `size -A`, so the driver is demonstrably in the binary rather than optimised out.
Commits
boards: raspberry_pi_pico_2: expose the ADC to userspace
chips: rp2350: add the SAR ADC
chips: rp2040: move the SAR ADC driver into the shared rp2xxx crate
libtock-rs · async/alarm15 commits
Futures over the alarm and console drivers, a single-task executor, select, and fakes that model cancellation. Meant to become two pull requests, but it is not two branches yet: the two unittest commits sit inside this one and have to be lifted out first.Hard order, not a preference. The async tests call fake::Alarm::new_deferred and fake::Console::new_deferred in three places, and those constructors are exactly what the unittest commits add — so either the unittest fixes land first and the async work builds on them, or the async pull request carries both commits itself. It cannot go first and cannot go alone. The branch still carries both commits inside it, and which way that resolves depends on how unittest-fakes is received: rebase to drop the duplicates if it merges, ship as-is if it does not. Deliberately not decided in advance, because guessing means doing the work twice.Evidence for a pull request — 15 commits
Cut from upstream/master at 469d9a037 · 22 files changed, 2080 insertions(+), 22 deletions(-)
What it adds
A Future and executor layer over Tock's syscalls: futures for the alarm and console drivers, a single-task executor with `block_on`, `join` and `select`, and unittest fakes that model alarm expiration and an outstanding console receive.
How it was verified
`make test` green. On a Pico 2 W running the Pico 2 W kernel, every 500 ms await lands within a few hundred microseconds of its deadline, including the two immediately after a cancelled five-second sleep — 500250 and 500249 ticks. That discriminates working cancellation from both a leaked upcall, which would return instantly, and a timer left armed, which would take five million.
It is not one pull request
Intended as two, and it is not two branches yet: the two unittest commits sit inside this one and have to be lifted out first. The order is forced rather than preferred — the async tests call `fake::Alarm::new_deferred` and `fake::Console::new_deferred` in three places and those constructors are what the unittest commits add.
Known gap
The console half of concurrency is untested, and cannot be tested until the UART defect above is fixed. The failure is in the kernel, not in the futures.
Commits
console: record why the abort's return value carries no information
examples: exercise select on hardware in async_probe
async: add join and select
unittest: model an outstanding receive in fake::Console
unittest: model alarm expiration so cancellation can be asserted
examples: add async_probe, an on-device self-test for the async stack
make: build and lint the async examples in the test gate
examples: add async_sleep, the first app built on the async stack
syscalls_tests: clean up the async_call test fixtures
syscalls_tests: test async_call directly
platform: extract the async soundness argument into one audited place
console: add a Future-returning read
alarm: document the invariant the waker swap leans on
alarm: stop the alarm when a Sleep is cancelled
alarm: add a Future-returning sleep and a block_on executor
libtock-rs · pico2-platform3 commits
The original three commits, now superseded: what is ready went to platform-pico2, and what is left is the Pico 2 W row.Waits for its board. The W platform row names raspberry_pi_pico_2_w, which is #5141 and not upstream, so this cannot go until that lands.Evidence for a pull request — 3 commits
Cut from upstream/master at 469d9a037 · 3 files changed, 11 insertions(+), 3 deletions(-)
What it adds
Build platform entries for the Raspberry Pi Pico 2 and Pico 2 W, and a build error that named neither the platform nor the file to edit.
Where the addresses came from
Read off a linked kernel with nm rather than off the linker script, because the application region is what the kernel leaves rather than what the board reserves. For the Pico 2 W: _sapps 0x10090000, _eapps 0x100d0000, _sappmem 0x20005c04, _eappmem 0x20082000. The RAM row starts at 0x20020000 and +392K lands exactly on _eappmem.
Verified
Applications built through these rows load, run and print on a Pico 2 W. Both boards' numbers were reproduced independently in two sessions against separately built kernels that came out to identical text and bss.
It should be split before it goes
Two of its three commits are ready: the Pico 2 row and the error-message fix. The third adds a `raspberry_pi_pico_2_w` row, and that board is not upstream yet — it is an open pull request in the kernel repository. A reviewer meeting a platform entry for a board they cannot find would be right to ask, and the answer is another repository's unmerged work. Send the Pico 2 row now; the W row follows the board.
Commits
runner: say which platform has no architecture mapping
Add the Raspberry Pi Pico 2 W as a build platform
Add the Raspberry Pi Pico 2 as a build platform
libtock-rs · stepper3 commits
The userspace half of the stepper: `libtock_stepper` and an example that turns a revolution each way.**The kernel half landed in `main` on 2026-09-13**, so this no longer waits on it. It remains the only half still on its own branch.Evidence for a pull request — 3 commits
Cut from upstream/master at 469d9a037 · 6 files changed, 364 insertions(+)
What it is
`libtock_stepper` — exists, step_forward, step_reverse, stop — wired into the umbrella crate, plus an example turning one revolution each way. The interval is a newtype in microseconds so a caller cannot silently pass milliseconds.
Gate
Full gate green, seven tests under both cargo test and miri. The fake lives in the crate's own tests rather than in libtock_unittest, since there is no upstream capsule to model yet.
A limitation worth knowing
From the blocking API the owner cannot reach `stop`: `step_forward` blocks in a yield loop for the whole run, so the only process allowed to stop the motor is inside a call that will not return until it finishes. Stop is reachable from an upcall handler or from the async version. That is an argument for the async stepper rather than a defect in either half.
Verified on silicon
Drove the capsule on a Pico 2 W: 4096 steps forward, 4096 reverse, first flash, turning a real 28BYJ-48.
Commits
stepper: document Reserve for a non-owner stop
stepper: document half-steps, BUSY, and what a stop reports
stepper: add a userspace driver for the stepper capsule
libtock-rs · platform-pico22 commits
The Raspberry Pi Pico 2 build platform row, plus a build error that named neither the platform nor the file to edit.Ready. Split out from pico2-platform so that it carries nothing depending on an unmerged board.Evidence for a pull request — 2 commits
Cut from upstream/master at 469d9a037 · 3 files changed, 9 insertions(+), 3 deletions(-)
What it adds
A build platform row for the Raspberry Pi Pico 2, and a build error that previously named neither the platform nor the file to edit — registering a platform takes three files that do not know about each other, and missing the third failed with "Failed to determine ELF's architecture".
Where the addresses came from
Read off a linked kernel with nm rather than off the linker script, because the application region is what the kernel leaves rather than what the board reserves. Re-checked against a kernel built from current master: _sapps 0x10040000, _eapps 0x10080000, _eappmem 0x20082000, so the row's flash 0x10040000 + 256K and RAM 0x20020000 + 392K both land exactly. Applications built through this row load, run and print on real hardware.
Gate
Full gate green at ce33db6, the same suite: cross-target example builds, workspace tests, fmt, clippy including riscv32imac, and miri under strict provenance.
Why the Pico 2 W row is not here
It names a board that is not upstream. Only boards/raspberry_pi_pico_2 exists at master; the W board is #5141 and still open. That row waits for its board rather than asking a reviewer to accept a platform entry they cannot resolve.
Commits
runner: say which platform has no architecture mapping
Add the Raspberry Pi Pico 2 as a build platform
libtock-rs · unittest-fakes2 commits
Two fixes to the test fakes: model alarm expiration so cancellation can be asserted, and model an outstanding receive in the console fake.The first thing proposed anywhere in libtock-rs, and deliberately the smallest — it is independently useful, hard to argue with, and the async series cannot go until it does. **Agreed as the first probe on 2026-08-31 and still unsent as of 09-09.** Zero pull requests have ever been opened in that repository from here, so this one is about establishing a relationship as much as landing a fix.Evidence for a pull request — 2 commits
Cut from upstream/master at 469d9a037 · 4 files changed, 254 insertions(+), 20 deletions(-)
What it fixes
Two gaps in libtock-rs's test fakes. `unittest/src/fake/alarm/mod.rs` did not model expiration, so a test could not assert that cancelling an armed alarm actually stopped it; the console fake in `unittest/src/fake/console/` did not model an outstanding receive. Command 3 was genuinely unimplemented in both, which is a fidelity bug in the fakes independent of anything built on them.
Why it is first
The smallest and least arguable thing in the queue, and it blocks the async series: those tests call `fake::Alarm::new_deferred` and `fake::Console::new_deferred` in three places, and these commits are what add those constructors.
Gate
Full gate green at f85f019: examples built for thumbv7em and riscv32imc, the workspace test run, fmt, three clippy passes including riscv32imac, and workspace-wide miri under strict provenance. 101 cargo tests and 90 under miri in libtock_unittest alone.
Provenance
Extracted from the async branch rather than cherry-picked: the originals also touched the async crate's own tests, and those stayed behind. Four files, all under `unittest/`, nothing outside it. Builds and tests standalone on master — 95 unit tests and 2 doc tests pass.
How the fakes were validated, in three parts
The alarm fake models command 3 clearing the deadline, which is what was measured on a Pico 2 W: an armed five-second sleep dropped, then a 500 ms sleep timing 500261 ticks — discriminating a working stop from both a leaked upcall, which would return instantly, and a timer left armed, which would take five million. The console fake answering success to an abort either way matches the capsule source and an instrumented kernel trace. The console fake's model of a receive that stays outstanding is unvalidated, and stays that way until the UART defect is fixed, because no environment currently holds a console read open.
Commits
unittest: model an outstanding receive in fake::Console and implement abort
unittest: model alarm expiration and implement stop
book · pico2-getting-started10 commits
A getting-started page for the Pico 2. The book has no Pico coverage at all.Written and green; waiting on its pull request description.Evidence for a pull request — 10 commits
Cut from origin/master at 760025b0d · 3 files changed, 240 insertions(+)
What it adds
A getting-started page for the Raspberry Pi Pico 2. The book currently has no Pico coverage at all.
Verified
The documented route was walked end to end on real hardware, and five things that were wrong got fixed in the process.
Caveat to disclose
The BOOTSEL flashing route is unverified on macOS, because the board is never connected to the development machine here. The page tells the reader to check where the volume mounted before naming it, so the literal path is illustrative rather than load-bearing, and the bootrom behaviour underneath is documented and host-independent.
Commits
setup: the by-hand UF2 command ran in the wrong directory
setup: the headless mount commands failed if you actually ran them
setup: a headless Linux box mounts nothing, and the page assumed it did
setup: the console echoes, so say what actually proves the input wire
setup: cite the page that actually makes the no-reflash promise
setup: tighten the Pico 2 page for density and reading order
setup: cut the Pico 2 page's applications section
setup: refresh the Pico 2 page's size line
setup: link the Pico 2 page's first mention of TBF
setup: add a getting started page for the Raspberry Pi Pico 2
Never going upstream — 12
Bench harnesses and teaching material. Listed so that nobody browsing
the fork has to guess which branches are waiting to be proposed.
tock · learning/series188 commits
The merged tree the course cites. The nine chapters moved to the site repository and are published at /read/; what is left here is the kernel work they were written against, kept because every chapter pins one commit on it and no other tree resolves those citations — upstream resolves 7 of the 14, #5140 six, #5141 seven, and the bench kernel, which contains all of it, also seven, because each later branch shifts the same file further along.Not a queue item. Retires itself when the chapters stop citing line numbers, or when #5140 and #5141 land and the citations are re-pinned to upstream.
tock · main92 commits
**The trunk.** Renamed from `rp2350-distro` and made the fork's default branch on 2026-09-13, so landing on the repository shows our work rather than upstream's. `master` stays pinned to upstream as the clean reference that rebases and A/B baselines are cut from. Our own flavour of Tock for the RP2350, and the branch all work lands on. **Head `fab074b7c`, 76 commits ahead of upstream after all ten of 2026-09-13's branches merged in.** Kept current with a merge rather than a rebase so the hand-resolved conflicts survive.Not a staging area and not intended for upstream as a branch; pull requests are cut from the feature branches instead. Merged current with upstream on 2026-09-12, after which all six RP2 boards build and the 19 PIO tests pass. One of its eleven merges needed manual three-way resolution -- git merges the moved PIO driver clean and silently reverts five fixes and deletes the test module -- which is why this is merged rather than rebased.
tock · bench/stepper-pico2w23 commits
The Pico 2 W branch, the stepper capsule and a board wiring for GPIO 18-21, merged so the motor can actually be driven. A vehicle, not a contribution: the capsule is cut from master and the board only exists on #5141, so there is nowhere upstream the two currently meet.
tock · pico2w-radio-bench16 commits
Every commit titled NOT FOR UPSTREAM: it starts the radio from the board so a scan can be driven without an app.
tock · bench/uart-abort-pico2w15 commits
The vehicle for the 2026-09-13 silicon A/B: `pico2w-typed` with `rp2-uart-abort-fix` merged and nothing else, so the only difference on the RP2350 path is where `rx_status` returns to Idle. Both kernels build to text=323116.Not a contribution -- the board only exists on our branches, so there is nowhere upstream the two meet. Kept so the measurement can be repeated rather than only cited, the same reason `bench/uart-fix-at-pinned-rev` is kept. Transcripts at `learning/bench/uart-abort-silicon/`.
tock · rp2-pad-controls2 commits
Shared pad enums and the RP2350 pad controls. **SUPERSEDED: `main` already shares them** -- both gpio.rs files reference `rp2xxx::pads` nine times each, with the casing corrected to `Drive12mA` where this branch still spells it `Drive12ma`.Merging it on 2026-09-13 broke all six RP2 boards with 19 errors: the conflicted files were resolved to `main`'s superset, but `chips/rp2350/src/gpio.rs` merged **cleanly** carrying the old casing. The merge was dropped. **A clean merge is not a safe merge** -- the conflict was visible and the incompatibility was not.
tock · bench/reclaim-leak-demo1 commit
Reproduces the GPIO reclaim leak on a board.
tock · bench/uart-fix-at-pinned-rev1 commit
The UART abort fix applied at the revision libtock-rs pins, which is where the A/B behind rp2-uart-abort-fix was actually run — the reproduction application does not load on a master kernel. Kept so the measurement can be repeated rather than only cited.
tock · rp2350-spi-bench1 commit
A bench harness that drives the SPI loopback.
libtock-rs · hw/pico2w-async59 commits
The two branches above merged together, as a vehicle for running on hardware — and, for now, the only home of `examples/console_read_busy.rs`, the QEMU reproduction of the UART defect.The branch itself is not for upstream, but the reproduction on it is meant to be run by other people, so it needs a home that is. Where it goes depends on whether it travels with a defect report or stands alone as a libtock-rs example.
libtock-rs · bench/reclaim-leak5 commits
The two apps that demonstrate the reclaim leak.
libtock-rs · kit-examples2 commits
Loopback and pin-walk apps written to exercise the bench.
Where the open branches collide
Derived by comparing the file list of every open pull request against
every other, so it cannot drift from what the branches do.
#5126 and #5141 both change chips/rp2040/src/pio.rs, chips/rp2040/src/pio_gspi.rs, chips/rp2040/src/pio_pwm.rs and 1 more — whichever lands first, the other rebases.
#5126 and #5157 both change chips/rp2040/src/pio.rs, chips/rp2040/src/pio_gspi.rs — whichever lands first, the other rebases.
#5140 and #5141 both change chips/rp2040/src/gpio.rs, chips/rp2350/src/gpio.rs, chips/rp2xxx/src/lib.rs and 1 more — whichever lands first, the other rebases.
#5141 and #5156 both change boards/raspberry_pi_pico_2/Makefile — whichever lands first, the other rebases.
#5141 and #5157 both change boards/raspberry_pi_pico_w/src/pio_gspi_component.rs, chips/rp2040/src/chip.rs, chips/rp2040/src/pio.rs and 1 more — whichever lands first, the other rebases.
Each demonstrated before it was written down — by a test that fails
without the fix, or by an instrumented kernel on a board. The UART pair needs no board
at all: make qemu-example EXAMPLE=console_read_busy against libtock-rs's
own pinned kernel prints read -> 0 bytes, Err(BUSY) on an affected build,
where a correct one would leave the read outstanding.
fix proposedPIO: the RX FIFO join never happened#5150Joining the FIFOs silently did nothing, so a program relying on the depth dropped words.
fix proposedPIO: add_program panicked on half its own range#5150A valid load address panicked the kernel instead of returning an error.
fix proposedPIO: three of four interrupt lines unserviced#5150Only one state machine's interrupt was ever handled.
fix proposedPIO: an interrupt flag scoped to the wrong thing#5150The flag belongs to the block; treating it as per-state-machine mis-attributes interrupts.
fix written, not sentUART: an aborted receive tears down every other receiverp2-uart-abort-fixThe abort completion calls the client back before marking the receiver idle, so the multiplexer's restart is refused and it ends every device's receive instead. The same code is in three chip drivers, and eleven boards pair a process console with the userspace console on one multiplexer. Reproducible under QEMU with no hardware, on hifive1, at the eighteen-month-old revision libtock-rs already pins — and no application can avoid it. Removing the delay before the read, and then issuing the read before any other system call, both still fail: the process console's prompt prints before the application's first line, so the receive is already armed before the application's first instruction. “The app read too early” is not an available explanation. What the reproduction still lacks is the opposite control, a kernel with no process console on that multiplexer, which is a board change rather than an application one.
fix written, not sentUART: a failed receive hands back the wrong static bufferrp2-uart-abort-fixA virtual device propagates the multiplexer's error with `?`, and that error carries the multiplexer's buffer rather than the caller's. Two static buffers change owners and the multiplexer's slot is left empty for the life of the board.
fix written, not sentUART: the teardown drops a buffer it cannot deliverrp2-uart-abort-fixWhen a restart fails the mux takes every device's buffer, but only returns it to devices still in the Receiving state — so a device that had aborted a read loses its buffer permanently. Found while fixing the two above.
fix written, not sentAny process can panic a Pico 2 kernel through GPIOrp2350-gpio-irq`IO_IRQ_BANK0` is declared in `interrupts.rs` and referenced nowhere else, so `service_interrupt` returns false for it and the chip panics with `unhandled interrupt 21`. Enabling a GPIO interrupt is command 7 of the GPIO syscall driver, so **any application on the upstream raspberry_pi_pico_2 can take the kernel down**, on any of the twenty-four pins that board exposes. Reproduced on that board with buttons on GP14 and GP15: it panics before the change and reports every press and release after, four lines.
fix written, not sentA Pico 2 panics at boot on an interrupt nothing claimsrp2350-stale-nvic-pendingIRQ 14 is USBCTRL_IRQ and this chip crate has no USB driver, so nothing services it and the first pass through `service_pending_interrupts` panics. The pending bit is a leftover, not a live interrupt: read over SWD in the panic handler, NVIC ISPR bit 14 is set while the USB block's MAIN_CTRL, INTR, INTE and INTS are all zero. The bootrom runs that controller with its interrupt enables set, so a source still asserting when `Chip::init()` clears pending re-latches at once, and the peripheral reset afterwards silences it without taking the latched bit back.
not filedA stopped process never gets its GPIO reclaimedThe pin stays driven forever. A sibling capsule already has the fix, which makes this a consistency bug. Five of forty-five capsules are affected.
not filed`make program` cannot flash an app on the Pico 2The objcopy step gives the stack segment a file size it should not have; picotool then refuses the image and leaves a zero-byte UF2 behind.
Findings
Pico problems worked through far enough to be useful to whoever
fixes them — some reported by other people, some our own changes written up
so a reviewer can check them. Each page cites the tree at a commit, quotes tool
output rather than summarising it, and ends with what it does not
establish — which for the USB one is most of it.
All of them, with how to read one.
open#4770 — make program builds an ELF that no UF2 tool will takeSplicing an app into the kernel makes objcopy lay the segments out again, and the (NOLOAD) stack segment comes back claiming 5,376 bytes of file content for RAM. The reported cause is not what is happening, and the workaround in the issue strips a section that is only safe to strip by accident. Six candidate fixes measured against a kernel built from upstream; the one that works is a single objcopy line, and it is A/B verified on all four RP2 boards by running their real make targets.Reported by potto216. The issue on GitHub.
closed#5160 — a flashing route with no ELF to get wrongThe reviewer who approved the four-line fix for #4770 asked for something else in the same sentence: that these boards stop splicing an application into the kernel ELF and use tockloader local-board instead. That route was run end to end -- assembled by tockloader, loaded three ways (SWD, the bootloader, probe-rs), with two applications co-resident and live in the kernel's own process list. It cannot hit the defect because it never opens an ELF. He then asked for it in the shape esp32-c3-devkitM-1 already uses, which retired the cost this page had led with: make no longer takes an APP at all. Four costs remain, and the tool swap on the RP2040 boards is the one a reviewer would stop on. Written up under #5156 until that change had a number of its own; /findings/5156/ redirects.Opened by bigmark222. The pull request on GitHub.
closed#5165 — a pending interrupt survives the reset that silenced itIRQ 14 is USBCTRL_IRQ, and this chip crate has no USB driver at all, so nothing can claim it. The pending bit is a latch rather than a live interrupt: read over SWD in the panic handler, NVIC ISPR bit 14 is set while the USB block reads byte-for-byte identical to a boot that did not panic. The bootrom hands over a controller that is enabled, addressed and permitted to interrupt, so `Chip::init()`'s clear cannot stick and the peripheral reset afterwards silences the source without taking the latched bit back. Erasing the first flash sector reproduces it on demand, and one statement removes it -- measured A/B/A on silicon.Opened by bigmark222. The pull request on GitHub.
open#5157 — a PIO interrupt flag delivered to the wrong clientA PIO block raises eight IRQ flags that belong to the block, and the driver delivered flag n to state machine n's client -- an association the hardware does not have and the datasheet invites by naming those bits SM0-SM3. A flag whose state machine has no client is never cleared, so the peripheral keeps asserting and the kernel spins. Invisible in the tree because the only user sits on the diagonal. Four more defects alongside it, and the misrouting plus its fix were both run on silicon.Opened by bigmark222. The pull request on GitHub.
open#5153 — EP0 IN is armed at bus reset and never taken backThe RP2040 USB driver hands EP0's IN buffer to the controller during bus reset, with a length of 64 and a PID of DATA0 and nothing queued to send. A SETUP packet does not take it back, so the first control read can be answered out of a buffer nobody filled. Two of the three claims in the issue's own AI analysis hold up; the third points at a delay already in the code. Not tested -- there is no RP2040 board here, and rp2350 has no USB driver to stand in for one.Reported by potto216. The issue on GitHub.
Written with AI assistance, disclosed on each page, which is also why
the evidence is arranged to be checked rather than believed.
Run on hardware
A Raspberry Pi flashes the board and holds its serial line, so the
machine that builds never touches the hardware.
A boot panic reproduced on demand, and removedErasing the first flash sector drops the bootrom into its own USB device, and resetting out of that live session panicked thirteen times in thirteen where the trigger had been unknown. The one-statement fix was then measured A/B/A on the same board: six panics, none, six again.
Pico 2 W scans WiFiFirmware up over PIO and DMA, MAC read from the radio's OTP, scan completed.
SPI loopbackA 32-byte pattern written and read back through the RP2350 SPI driver.
Boot from RAMThe layout from #5109, booting.
Two co-resident appsApplications load, run and print, which is what makes any userspace claim checkable.
Async sleep and cancellationA 500 ms await lands within a few hundred microseconds of its deadline, including immediately after a cancelled five-second sleep. That distinguishes working cancellation from both a leaked callback and a timer left armed.
PIO and DMA register readbackThe interrupt block's offset, the state machine count and the instruction memory size, read off the chip.
Nine chapters on how the kernel works, written while learning
it on this board and checked the same way this page is — every figure is
something to drive rather than a picture to read, and every claim about the
tree is verified against a commit the chapter names. It starts at what a
register is and ends at grants.
Its own gate runs 35 assertions on the cover and a suite per
chapter. Sources are under learning/; read/ is what
is served, and a check here fails if the two disagree.
Downstream
blockedAsync userspaceA Future and executor layer over Tock's syscalls in libtock-rs, validated on a Pico 2 W running the Pico 2 W kernel. The alarm half is done and measured. The console half is blocked on the UART defect being fixed, and that question is now closed rather than open: QEMU was the last candidate for an environment where a userspace console read stays outstanding, and it reproduces the defect too.
readyA Pico page for the Tock bookThe book has no Pico coverage at all. Written and pushed; the pull request is not open yet.
draftedNine chapters on how the kernel worksFrom what a register is through grants and the memory protection unit, written while learning the codebase.
Not done
Give the QEMU reproduction a home someone can reach`examples/console_read_busy.rs` currently exists only on a branch marked never-for-upstream, which is the wrong address for the one artefact here that a maintainer is meant to run themselves. It travels with the defect report, or goes up on its own as a libtock-rs example, but it cannot stay where it is.
Open the first libtock-rs pull request`unittest-fakes` was extracted, is gate-green and has been pushed since 2026-08-31. It is still unsent. **Zero pull requests have ever been opened in tock/libtock-rs from here** -- not zero merged, zero opened -- and the async series cannot go until this one does, because its tests call the constructors these two commits add. Two commits, the smallest ask available anywhere in the queue, and the only item whose value is opening a relationship rather than closing a defect. It is also the natural task for a second session, since nothing about it touches the RP2 work.
File the four unfiled defectsEach is demonstrated and none is filed. The constraint is review throughput, not the work.
Fix the reclaim leakThe sibling capsule already shows what the fix looks like.
Two RP2 Makefile bugs found while replacing the flash route`make flash-openocd` on nano_rp2040_connect exits 0 having flashed nothing. Its recipe references OPENOCD and OPENOCD_OPTIONS, which neither that Makefile nor boards/Makefile.common defines, so the line begins with `-c`; make reads the leading dash as ignore-errors, strips it, runs a command called `c` and swallows the result -- `c: command not found`, `Error 127 (ignored)`, exit 0. Established by expanding the recipe rather than by grepping for the variable, which had wrongly implicated two nRF boards that never use it. It is the only one of the eight flash-openocd recipes in the tree that breaks this way, and the only one of the eight not declared .PHONY. Separately, raspberry_pi_pico/Makefile has said it builds the Pico Explorer Base since 049db0bd4. Both are upstream, both are one line, and neither belongs in the local-board change.
The RP2350 has no USB driver at all`chips/rp2350/src/` has no `usb.rs`, so `USBCTRL_IRQ` has no owner. That is why a stale pending bit on it took the kernel down rather than being handled harmlessly the way it is on the RP2040, whose crate does have one. The boot panic that exposed this is fixed on `rp2350-stale-nvic-pending`, in the queue above, and **that fix does not close this**: any peripheral with no driver can still strand a pending bit, and a Pico 2 still cannot be a USB device at all.
A userspace driver for PIOThe RP2's most distinctive peripheral, and no process can reach it.
Hardware CI for the RP2 boardsThe project's testbed runs one board and never on pull requests. Named as a dependency in #5152 rather than promised.
Keys
/
Search everything on the page
?
This list
jk
Next and previous hardware block
Esc
Close, or clear the pull request filter
Type to search everything on this page. Enter jumps to it.