name: rp2350-flash
description: >
SWD-only flashing/debugging of RP2350 targets via a CMSIS-DAP debug
probe (PiProbe, Pico Debug Probe, J-Link in CMSIS-DAP mode).
REQUIRES a probe physically wired to the target's SWD pins. Does
NOT cover USB BOOTSEL drag-drop, UF2 install, OTA, or any non-probe
flashing path. Covers the install (Pico SDK 2.x, picotool 2.x,
raspberrypi/openocd fork), the canonical openocd flash command, the
rp2350-bootsel SRAM-stub wrapper, and probe-side troubleshooting.
Scope: host down to the probe's SWD pins — not target-board pinouts.
Trigger words: flash rp2350 via swd, swd flash rp2350, swd program
rp2350, piprobe flash, pico debug probe rp2350, cmsis-dap probe
rp2350, openocd rp2350, rp2350 swd, debug probe rp2350, rp2350 gdb,
reset_usb_boot via swd, force bootsel via swd, rp2350-bootsel,
pico sdk rp2350 swd.

rp2350-flash — flash RP2350 over SWD via a CMSIS-DAP debug probe

Scope — SWD debug probe ONLY. Use this skill only when the user has a CMSIS-DAP debug probe (PiProbe, Pico Debug Probe, J-Link in CMSIS-DAP mode) physically wired to the RP2350 target's SWD pins. Do NOT invoke this skill for:

  • USB BOOTSEL drag-drop / UF2 file copy
  • OTA / firmware-over-the-air updates
  • Self-bootloader flashing from running firmware
  • Any flash path that doesn't go through a CMSIS-DAP probe over SWD

Full setup + troubleshooting reference: flashing-rp2350.md (also published as the body of skills/rp2350-flash on the Adom Wiki).

When to use this skill

  • User has a CMSIS-DAP probe wired to a target's SWD pins and wants to flash an .elf onto the RP2350.
  • User wants to build firmware with the Pico SDK for PICO_PLATFORM=rp2350, then flash via the probe.
  • User wants to debug RP2350 via gdb-over-openocd, with the probe as the SWD bridge.
  • User wants to force a target into USB BOOTSEL via SWD (the rp2350-bootsel wrapper) — only relevant when the target's USB lines are also wired to a host.
  • User mentions "openocd rp2350", "piprobe flash", "pico debug probe", "swd flash", "swd program rp2350", etc.

When NOT to use this skill

  • User wants to drag-and-drop a .uf2 onto the target's BOOTSEL drive over USB (no probe in the loop).
  • User wants to update firmware over WiFi / BLE / OTA.
  • User is flashing a running RP2350 from its own firmware (self-bootloader / dual-bank).
  • There is no debug probe in the topology.

Scope: host → USB → probe → SWD pins. Target-board specifics (which GPIO is the LED, where SWCLK lands on castellations, USB-D± wiring) are out of scope — refer to the target board's own docs.

Required downloads (the install is non-optional)

# What Source How
1 apt deps distro apt-get install
2 arm-none-eabi-gcc distro gcc-arm-none-eabi apt-get install
3 cmake distro cmake apt-get install
4 Pico SDK 2.x https://github.com/raspberrypi/pico-sdk git clone + submodule init
5 picotool 2.x https://github.com/raspberrypi/picotool git clone + cmake build
6 OpenOCD (raspberrypi fork) https://github.com/raspberrypi/openocd, branch sdk-2.0.0 git clone + autotools build

Stock distro OpenOCD 0.12.0 does not work — it predates RP2350 and ships no target config. This is the #1 cause of no driver found for target rp2350. The full install takes ~10 min on a fresh container.

Step-by-step install commands: flashing-rp2350.md § Install steps.

Pre-flight check — always run this first

Before doing anything, confirm probe + target are alive:

openocd -f interface/cmsis-dap.cfg -f target/rp2350.cfg -c "init; targets; exit"

Expected: CMSIS-DAP: FW Version = 2.0.0, SWD DPIDR 0x4c013477, and both rp2350.dap.core0 + core1 as Cortex-M33 r1p0. If you see those, the toolchain is installed and the wiring is good — skip to "Flash via SWD" below.

Failure modes:

  • no driver found for target rp2350 → stock OpenOCD, install the raspberrypi fork (download #6).
  • Could not find a USB device → probe isn't on USB. lsusb | grep 2e8a:000c. Fix the cable.
  • SWD DPIDR 0x00000000 → target unpowered, SWD wires bad/swapped, or no common ground between probe and target.

Build firmware for RP2350

CMakeLists.txt must set:

cmake_minimum_required(VERSION 3.13)
set(PICO_BOARD pico2 CACHE STRING "")
set(PICO_PLATFORM rp2350 CACHE STRING "")
include($ENV{PICO_SDK_PATH}/external/pico_sdk_import.cmake)
project(myapp C CXX ASM)
pico_sdk_init()
add_executable(myapp myapp.c)
target_link_libraries(myapp pico_stdlib)
pico_add_extra_outputs(myapp)

Build:

mkdir -p build && cd build && cmake .. && make -j$(nproc)

Produces myapp.elf (for SWD flash) and myapp.uf2 (for USB BOOTSEL drag-drop, if applicable).

Flash via SWD — the recommended path

openocd -f interface/cmsis-dap.cfg -f target/rp2350.cfg \
  -c "adapter speed 5000; program firmware.elf verify reset exit"

Successful run ends with ** Verified OK ** and ** Resetting Target **. The new firmware starts running immediately.

Don't bother with BOOTSEL when you have a probe. SWD flashes work even if the target's USB pins aren't wired anywhere.

Force USB BOOTSEL via SWD (rare path)

picotool reboot -u -f only works when the target is already on USB — it cannot drive a CMSIS-DAP probe in the upstream build. If you need to force BOOTSEL via SWD only:

rp2350-bootsel

This wraps OpenOCD + a tiny SRAM stub (~/tools/force-bootsel/) that calls the bootrom's reset_usb_boot(0, 0).

Caveat: BOOTSEL is only useful if the target's USB D± lines are physically wired to a USB host. In a probe-only setup (SWD between probe and target, no USB cable on the target), BOOTSEL puts the chip into MSC mode but there's no host to see the drive. Use the SWD flash path instead — that works regardless.

Debug via gdb over the probe

Terminal 1 (leave running):

openocd -f interface/cmsis-dap.cfg -f target/rp2350.cfg

Terminal 2:

arm-none-eabi-gdb firmware.elf -ex 'target extended-remote :3333'

Stream printf via ARM semihosting through the probe

When the target has no UART/USB wired, route printf through SWD via semihosting:

Firmware CMake:

pico_enable_stdio_semihosting(myapp 1)
pico_enable_stdio_uart(myapp 0)
pico_enable_stdio_usb(myapp 0)

Host command (leave it running):

openocd -f interface/cmsis-dap.cfg -f target/rp2350.cfg \
  -c "adapter speed 5000" -c "init" -c "reset halt" \
  -c "arm semihosting enable" -c "resume"

Order matters: init → reset halt → arm semihosting enable → resume. Plain reset (without halt) leaves the target running, then arm semihosting enable and resume will fail with not halted / context restore failed.

Common failure modes (one-liner triage)

Error Likely cause
no driver found for target rp2350 Stock OpenOCD; install raspberrypi fork (download #6)
Could not find a USB device PiProbe missing from lsusb; fix cabling
error submitting USB read: Input/Output Error (flood) Another openocd is holding the probe — pgrep -af openocd then kill <PID> that specific PID (never pkill openocd)
SWD DPIDR 0x00000000 Target unpowered, SWD wires bad, or no common GND
timed out while waiting for target halted SWD clock too fast — drop adapter speed to 1000
not halted / context restore failed, aborting resume (after arm semihosting enable) Used reset instead of reset halt — reorder to init; reset halt; arm semihosting enable; resume
resume of a SMP target failed RP2350 fork's SMP resume is fragile — use reset halt; resume or set USE_CORE 0
picotool: No accessible RP-series devices in BOOTSEL Target USB not on host bus — use OpenOCD/SWD instead
Verify fails after flash Wrong PICO_BOARD / PICO_PLATFORM; nuke build/ and rebuild
VECTRESET is not supported... using SYSRESETREQ Cosmetic warning, ignore

Full troubleshooting table: flashing-rp2350.md § Troubleshooting.

Quick command reference

# Pre-flight
openocd -f interface/cmsis-dap.cfg -f target/rp2350.cfg -c "init; targets; exit"

# Flash an .elf via SWD (canonical)
openocd -f interface/cmsis-dap.cfg -f target/rp2350.cfg \
  -c "adapter speed 5000; program firmware.elf verify reset exit"

# Start a gdb-server (terminal 1)
openocd -f interface/cmsis-dap.cfg -f target/rp2350.cfg

# Attach gdb (terminal 2)
arm-none-eabi-gdb firmware.elf -ex 'target extended-remote :3333'

# Stream printf via ARM semihosting through the probe (no UART/USB needed)
openocd -f interface/cmsis-dap.cfg -f target/rp2350.cfg \
  -c "adapter speed 5000" -c "init" -c "reset halt" \
  -c "arm semihosting enable" -c "resume"

# Probe stuck after a wedged session? Kill the SPECIFIC stale openocd PID
pgrep -af openocd            # find the PID
kill <PID>                   # only that PID — never `pkill openocd`

# Force BOOTSEL via SWD (only if target USB is wired to host)
rp2350-bootsel

# Build a Pico SDK project for RP2350
cd myapp && mkdir -p build && cd build && cmake .. && make -j$(nproc)