Firmware — shared-resource ownership tracker¶
Single source of truth for every scarce RP2354B resource the firmware carves up. Whenever a module takes a slice (a PIO state machine, a USB endpoint, a DMA channel, a GPIO pin, a USB Configuration slot), update this file in the same pull request. A reviewer who reads only this page must be able to tell what is claimed, what is free, and what is earmarked for a named milestone.
The numbers here are the authoritative budget — code comments like "PIO0 is reserved for can2040" point at this page, not the other way round.
1. USB Configurations¶
USB 2.0 lets a composite device advertise several configurations;
the host picks exactly one at a time with SET_CONFIGURATION. We
use that to offer a driverless-compatible descriptor set today and
reserve room for a future driver-native set. See
ADR-0004 for the full
rationale.
bConfigurationValue |
Name | Descriptor set | Status |
|---|---|---|---|
1 |
Compat | 4× CDC-ACM + 2× Vendor (RPBP, gs_usb) † | Active / shipping |
2 |
Native | 2× Vendor (RPBP, gs_usb) only | Reserved — M9 milestone |
3..255 |
— | — | Available |
† — A 5th CDC-ACM ("RPBridge Debug Log") can be enabled as a
build-time option (-DRPBRIDGE_DEBUG_CDC=ON, default OFF). When
ON, it adds CDC #4 at bInterfaceNumber {8,9} and shifts the two
vendor interfaces from {8,9} to {10,11}. Configuration value,
EP allocation for #0..#3, and the vendor EPs are unchanged. See
the #Config 1 EP table for the alternate row, and
docs/usb-vid-pid.md for the descriptor view.
Rules:
- Config 1 is the always-on default. Never renumber it.
- A new configuration must be added here before its descriptor
gets wired into
usb_descriptors.c. bNumConfigurationsindesc_devicemust match the count of active rows above.
2. USB Endpoints¶
RP2350's USB controller offers 15 non-zero endpoints per direction (IN 1..15 + OUT 1..15) plus EP0 (shared bidirectional control). Running inventory:
Config 1 (Compat) — current¶
| Endpoint | Direction | Size | Type | Owner |
|---|---|---|---|---|
| EP0 | IN + OUT | 64 | Control | TinyUSB core + MS OS 2.0 vendor requests |
| EP1 IN | IN | 8 | Interrupt | CDC-ACM #0 notification |
| EP2 OUT | OUT | 64 | Bulk | CDC-ACM #0 data |
| EP2 IN | IN | 64 | Bulk | CDC-ACM #0 data |
| EP3 IN | IN | 8 | Interrupt | CDC-ACM #1 notification |
| EP4 OUT | OUT | 64 | Bulk | CDC-ACM #1 data |
| EP4 IN | IN | 64 | Bulk | CDC-ACM #1 data |
| EP5 IN | IN | 8 | Interrupt | CDC-ACM #2 notification |
| EP6 OUT | OUT | 64 | Bulk | CDC-ACM #2 data |
| EP6 IN | IN | 64 | Bulk | CDC-ACM #2 data |
| EP7 IN | IN | 8 | Interrupt | CDC-ACM #3 notification |
| EP8 OUT | OUT | 64 | Bulk | CDC-ACM #3 data |
| EP8 IN | IN | 64 | Bulk | CDC-ACM #3 data |
| EP9 OUT | OUT | 64 | Bulk | Vendor RPBP control |
| EP9 IN | IN | 64 | Bulk | Vendor RPBP control |
| EP10 OUT | OUT | 64 | Bulk | Vendor gs_usb CAN |
| EP10 IN | IN | 64 | Bulk | Vendor gs_usb CAN |
| EP11 IN ‡ | IN | 8 | Interrupt | CDC-ACM #4 notification (debug log) |
| EP12 OUT ‡ | OUT | 64 | Bulk | CDC-ACM #4 data (debug log) |
| EP12 IN ‡ | IN | 64 | Bulk | CDC-ACM #4 data (debug log) |
‡ — present only when -DRPBRIDGE_DEBUG_CDC=ON. The vendor EPs
(EP9, EP10) are not renumbered when the flag flips — only the
interface numbers shift. This keeps host-side code that walks bulk
endpoints stable across build variants.
Totals: 10 IN + 6 OUT used (default), 12 IN + 7 OUT used with the debug CDC; 5 IN + 9 OUT (resp. 3 IN + 8 OUT) free for future streaming pipes.
Config 2 (Native) — reserved for M9¶
| Endpoint | Direction | Size | Type | Owner |
|---|---|---|---|---|
| EP0 | IN + OUT | 64 | Ctrl | unchanged |
| EP1 OUT | OUT | 64 | Bulk | Vendor RPBP (UART streams + control) |
| EP1 IN | IN | 64 | Bulk | Vendor RPBP |
| EP2 OUT | OUT | 64 | Bulk | Vendor gs_usb CAN |
| EP2 IN | IN | 64 | Bulk | Vendor gs_usb CAN |
Totals: 2 IN + 2 OUT used, 13 IN + 13 OUT free for future streaming pipes (logic capture, IIO trigger, audio UAC2, …).
3. PIO blocks, state machines, instructions¶
RP2350 = 3 PIO blocks × 4 SMs × 32 instruction slots = 12 SMs / 96 instructions total. Instruction memory is per-block; programs cannot cross block boundaries.
Ring allocation policy:
- PIO0 — hard-reserved for
can2040. Do not place other applets here, even if SMs look free;can2040expects exclusive control of the block's IRQs and instruction memory. - PIO1 — user-UART tier. PIO-UART #2 currently lives here; RS485 precision-DE would move here when we stop relying on the software timer.
- PIO2 — optional applets (1-Wire, WS2812, I²S, logic capture). First-come-first-served among "future nice-to-haves".
Today¶
| Block | SMs used | Instr used | Owner(s) |
|---|---|---|---|
| PIO0 | 3 / 4 | 32 / 32 | can2040 (M5) — block-exclusive |
| PIO1 | 3 / 4 | ~25 / 32 | uart_pio_tx (≈4), uart_pio_rx (≈8), onewire (≈14) |
| PIO2 | 3 / 4 | ~17 / 32 | ws2812 (≈4), dmx_tx (≈5), i2s_tx (≈8) |
After all planned applets land¶
| Block | SMs used | Instr used | Owner(s) |
|---|---|---|---|
| PIO0 | 3 / 4 | 32 / 32 | can2040 (sync, rx, tx) — M5 |
| PIO1 | 3 / 4 | ~17 / 32 | uart_pio_tx, uart_pio_rx, rs485_de.pio (M4d-PIO) |
| PIO2 | ~4 / 4 | ~30 / 32 | 1-Wire, WS2812, I²S-TX, Logic-Capture (mixed) |
Reserve at worst case: ~2 SMs + ~50 instructions across blocks. A PR that pushes PIO2 above 28 instr MUST coordinate with other planned PIO2 consumers or push to a second-instance PIO2 applet that's no longer in v1 scope.
Pending applets and their cost estimates¶
| Applet | SMs | Instr | Target block | Milestone | Status |
|---|---|---|---|---|---|
can2040 |
3 | ~32 | PIO0 | M5 | landed |
rs485_de.pio |
1 | ~6 | PIO1 | M4d-PIO | optional |
onewire |
1 | ~14 | PIO1 | M12 | landed |
ws2812 |
1 | ~4 | PIO2 | M12 | landed |
dmx_tx |
1 | ~5 | PIO2 | M12 | landed |
i2s_tx |
1 | ~8 | PIO2 | M12 | landed |
logic_capture |
1-4 | ~20 | PIO2 / PIO1 | future | planned |
| Extra PIO-UART #2..n | 2/ea | ~11 | PIO1-rest | future | optional |
Any row labelled "pending" blocks the associated milestone; any row labelled "optional" can be skipped without affecting downstream work.
4. DMA channels¶
RP2350 has 16 DMA channels shared across all peripherals.
Allocation is dynamic through dma_claim_unused_channel() — no
fixed-channel reservations today. As subsystems land we track
expected steady-state usage here:
| Peripheral / use case | Typical channels | Pattern |
|---|---|---|
| USB bulk (TinyUSB) | 0 (polled) | Polled service |
| I²C DMA burst (M2) | 2 | TX + RX paired |
| SPI DMA burst (M3) | 2 | TX + RX paired |
| ADC round-robin (M3) | 1 | Ring-buffered stream |
| can2040 (M5) | 0 | Core-bound / PIO IRQ |
| Reserved for logic capture | 1-2 | Future |
Expected worst-case total: ~6 channels. 10 channels free at M5 completion. No capacity concern.
5. GPIO ownership¶
The pin broker
(firmware/src/peripherals/pin_broker.{c,h})
owns the runtime ledger. Every module that takes a pin MUST call
pin_broker_claim() during init; overlapping claims return
-RPBP_EBUSY and fail the init. The board header
firmware/boards/rpbridge_rp2354b.h
remains the static reference and the authoritative pinout.
Live claims as of M3:
| Pin(s) | Owner | Function |
|---|---|---|
| GP0, GP1 | uart-hw |
UART0 TX / RX (phy-bank backend) |
| GP2, GP3 | i2c |
I²C #0 SDA / SCL (Qwiic 1+2) |
| GP4, GP5 | — | UART1 TX / RX (owned by pico-sdk stdio_uart) |
| GP6 | uart-phy |
RS485 DE (handed to uart_hw while in RS485 mode) |
| GP7 | uart-phy |
RS485 EN |
| GP8 | uart-phy |
RS232 /SHDN |
| GP9 | free | SPI0 user IRQ (lazy claim — see cmd_spi + docs) |
| GP10..GP12 | spi |
SPI1 SCK / MOSI / MISO |
| GP13..GP15 | spi (lazy) |
SPI1 CS0 / CS1 / CS2 (claimed on first XFER) |
| GP16 | spi |
SPI0 MISO |
| GP17 | spi (lazy) |
SPI0 CS (claimed on first XFER) |
| GP18, GP19 | spi |
SPI0 SCK / MOSI |
| GP20, GP21 | can |
CAN 2.0B TX / RX (can2040 on PIO0) |
| GP22..GP24 | system+pwm |
RGB status LED (PWM-driven, 1 kHz) |
| GP26..GP29 | adc |
ADC0..ADC3 (user header A0..A3) |
| GP30, GP31 | gpio/pwm |
User header — lazy claim on CONFIGURE |
| GP32 | 1-wire |
1-Wire data line (PIO1 SM 2; external 4.7 kΩ pull-up) |
| GP33, GP36 | uart-pio |
PIO-UART TX / RX |
| GP34, GP35 | i2c |
I²C #1 SDA / SCL (Qwiic 3) |
| GP37..GP40 | gpio/pwm |
User header — lazy claim on CONFIGURE |
| GP41 | system |
I²S BCLK (PIO2 SM 2) |
| GP42 | system |
I²S LRCLK (PIO2 SM 2; must be BCLK + 1) |
| GP43 | system |
WS2812 / NeoPixel data line (PIO2 SM 0) |
| GP44 | system |
DMX512 TX (to external RS-485 transceiver, PIO2 SM 1) |
| GP45 | system |
I²S DOUT (PIO2 SM 2) |
The pico2 variant overlays the PIO2 / PIO1 applet pins onto its narrower GP8..GP15 user range:
RPBRIDGE_LED_STRIP_PIN= GP14RPBRIDGE_DMX_TX_PIN= GP15RPBRIDGE_ONEWIRE_PIN= GP12RPBRIDGE_I2S_BCLK_PIN= GP8 (LRCLK on GP9)RPBRIDGE_I2S_DOUT_PIN= GP13
See firmware/boards/rpbridge_pico2.h.
Pending subsystems (logic capture, RS485 precision-DE, RX extensions for 1-Wire / DMX) will land in future milestones. Update this table in the same PR that wires each one in.
The broker is introspectable at runtime via
pin_broker_snapshot(); future SYS SELFTEST payloads and
rpbridge-ctl pin list consume that snapshot so developers can
diagnose conflicts without flashing a debug image.
6. Procedure for allocating new resources¶
- Open this file and find the relevant table.
- Mark the row you intend to consume.
- Implement. The code comment that introduces the consumer SHOULD reference this doc by stable anchor, not by duplicating the rationale.
- In the same commit, add a log entry in the changelog below so history stays legible.
If you discover a conflict (two PRs claiming the same SM, for example) resolve it here first, in the tracker, before touching code — the earliest-PR-wins rule applies.
Changelog¶
- 2026-04-23 — doc introduced. Captures current USB config, endpoint, PIO, DMA and GPIO state as of the M4e commit
- 2026-04-23 — M3 landed. SPI (GP10..19), ADC (GP26..29), PWM-driven
RGB LED (GP22..24), plus the user-header lazy-claim zone
(GP30/31/37..45). No new PIO or DMA usage — all HW-backed.
(
49416a6). Reserves Config 2 for M9 driver-priority mode. - 2026-04-25 — M12 PIO2 applets landed. WS2812 (
led_strip, PIO2 SM 0, ~4 instr) on GP43 (rev-B) / GP14 (pico2); DMX512-A TX (dmx, PIO2 SM 1, ~5 instr) on GP44 (rev-B) / GP15 (pico2). Subsystem IDs0x07LED +0x08DMX. - 2026-04-25 — M12 PIO1 + PIO2 expansion. 1-Wire master (
onewire, PIO1 SM 2, ~14 instr) on GP32 (rev-B) / GP12 (pico2); I²S TX (i2s, PIO2 SM 2, ~8 instr) on GP41/42/45 (rev-B) / GP8/9/13 (pico2). Final PIO budget: PIO0 sealed, PIO1 ¾ SMs ~25/32 instr, PIO2 ¾ SMs ~17/32 instr. Subsystem IDs0x09ONEWIRE +0x0AI²S.