Raspberry Pico: Simple Debugging with just one Device

Tool Overview

Compiler & GDB

$> sudo apt update
$> sudo apt install cmake gcc-arm-none-eabi libnewlib-arm-none-eabi build-essential
$> arm-none-eabi-gcc -vUsing built-in specs.
Target: arm-none-eabi
apt install gdb-multiarch
$> db-multiarch -vGNU gdb (Ubuntu 9.2-0ubuntu1~20.04) 9.2
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>


$> sudo apt install automake autoconf texinfo libtool libhidapi-dev libusb-1.0-0-dev
$> git clone https://github.com/majbthrd/openocd.git --recursive --branch rp2040_cmsisdap_demo --depth=1
$ cd openocd
$ ./bootstrap
$ ./configure --enable-cmsis-dap
$ make -j4
$ sudo make install
$> openocd -hOpen On-Chip Debugger 0.10.0+dev-gb4af1af-dirty (2021-03-21-10:40)
Licensed under GNU GPL v2

Preparing the Pico

  1. The Pico needs to be booted with a special UF2 image so that it can be used as a debugging device
  2. The target program needs to be compiled with a special debug compiler flag

Install the Debugging UF2 Image

[50852.541543] usb 1-2: new full-speed USB device number 70 using xhci_hcd
[50852.690691] usb 1-2: New USB device found, idVendor=1209, idProduct=2488, bcdDevice=10.02
[50852.690696] usb 1-2: New USB device strings: Mfr=0, Product=1, SerialNumber=0
[50852.690698] usb 1-2: Product: CMSIS-DAP

Compile the target program

├── debug
│ ├── build
│ ├── CMakeLists.txt
│ ├── pico-debug.uf2
│ ├── pico_sdk_import.cmake
│ └── src -> /home/work/development/pico2/src/
├── include
│ ├── pico
│ │ └── config_autogen.h
│ └── README
├── lib
│ └── README
├── LICENSE.txt
├── platformio.ini
├── README.md
├── src
│ ├── CMakeLists.txt
│ └── main.c
└── test
  1. Create the directories build and debug
  2. In debug, copy the special uf2 files, and add a symbolic link to the src directory
  3. In debug, put this CMakeLists.txt file into it.
cmake_minimum_required(VERSION 3.12)# Pull in SDK (must be before project)
project(pico_examples C CXX ASM)
set(PICO_EXAMPLES_PATH ${pwd})# Initialize the SDK
  1. In src/ folder, add this CMakeLists.txt file.
# Pull in our pico_stdlib which pulls in commonly used features
target_link_libraries(main pico_stdlib)
# create map/bin/hex file etc.
  1. Set the environment variable PICO_SDK_PATH to point to the special SDK
export PICO_SDK_PATH=/home/work/development/pico-debug-sdk/
  1. Go to the debug/build folder, and run the following commands:
cmake -DCMAKE_BUILD_TYPE=Debug -g0 ..
Using PICO_SDK_PATH from environment ('/home/work/development/pico-debug-sdk/')
PICO_SDK_PATH is /home/work/development/pico-debug-sdk
Defaulting PICO_PLATFORM to rp2040 since not specified.
Defaulting PICO platform compiler to pico_arm_gcc since not specified.
PICO compiler is pico_arm_gcc
PICO_GCC_TRIPLE defaulted to arm-none-eabi
-- The C compiler identification is GNU 9.2.1
-- The CXX compiler identification is GNU 9.2.1
-- The ASM compiler identification is GNU
-- Found assembler: /usr/bin/arm-none-eabi-gcc
Using regular optimized debug build (set PICO_DEOPTIMIZED_DEBUG=1 to de-optimize)
Defaulting PICO target board to pico since not specified.
Using board configuration from /home/work/development/pico-debug-sdk/src/boards/include/boards/pico.h
-- Found Python3: /usr/bin/python3.8 (found version "3.8.5") found components: Interpreter
TinyUSB available at /home/work/development/pico-debug-sdk/lib/tinyusb/src/portable/raspberrypi/rp2040; adding USB support.
Compiling TinyUSB with CFG_TUSB_DEBUG=1
-- Found Doxygen: /usr/bin/doxygen (found version "1.8.17") found components: doxygen dot
ELF2UF2 will need to be built
-- Configuring done
-- Generating done
-- Build files have been written to: /home/work/development/pico2/debug/build
  1. In the build folder, you will now see src - change into this directory, and execute make
cd src/
Scanning dependencies of target ELF2UF2Build
[ 1%] Creating directories for 'ELF2UF2Build'
[ 3%] No download step for 'ELF2UF2Build'
[ 5%] No patch step for 'ELF2UF2Build'
[ 6%] No update step for 'ELF2UF2Build'
[ 8%] Performing configure step for 'ELF2UF2Build'
-- The C compiler identification is GNU 9.3.0
-- The CXX compiler identification is GNU 9.3.0
-- Build files have been written to: /home/work/development/pico2/debug/build/elf2uf2
[ 10%] Performing build step for 'ELF2UF2Build'
Scanning dependencies of target elf2uf2
[ 50%] Building CXX object CMakeFiles/elf2uf2.dir/main.cpp.o
[100%] Linking CXX executable elf2uf2
[100%] Built target elf2uf2
[ 11%] No install step for 'ELF2UF2Build'
[ 13%] Completed 'ELF2UF2Build'
[ 13%] Built target ELF2UF2Build
Scanning dependencies of target bs2_default
[ 15%] Building ASM object pico-sdk/src/rp2_common/boot_stage2/CMakeFiles/bs2_default.dir/boot2_w25q080.S.obj
[ 16%] Linking ASM executable bs2_default.elf
[100%] Linking CXX executable main.elf
[100%] Built target main

Debugging on the CLI

OpenOCD: Executing as Normal User

ACTION!="add|change", GOTO="openocd_rules_end"
SUBSYSTEM!="usb|tty|hidraw", GOTO="openocd_rules_end"
ATTRS{product}=="*CMSIS-DAP*", MODE="664" GROUP="plugdev"
$> sudo groupadd plugedev
$> sudo gpasswd -a devcon plugdev
$> sudo udevadm control --reload
$> groups
devcon adm dialout cdrom sudo dip plugdev lpadmin lxd sambashare

Start OpenOCD

$> cd openocd/tcl
$> openocd -f interface/cmsis-dap.cfg -f target/rp2040-core0.cfg -c "transport select swd" -c "adapter speed 4000"
  • The two -f flags indicate loading of configuration files, here we load the Debug Access Port (DAP) config and a config for the Pico board (RP2040)
  • The two -c flags indicate additional commands that are run, we will connect via serial wire debug and set the connection speed in kHz between the host and the Pico.
Open On-Chip Debugger 0.10.0+dev-gb4af1af-dirty (2021-03-21-10:40)
Licensed under GNU GPL v2
For bug reports, read
adapter speed: 4000 kHz

Info : Hardware thread awareness created
Info : RP2040 Flash Bank Command
Info : Listening on port 6666 for tcl connections
Info : Listening on port 4444 for telnet connections
Info : CMSIS-DAP: SWD Supported
Info : CMSIS-DAP: FW Version = 2.0.0
Info : CMSIS-DAP: Interface Initialized (SWD)
Info : SWCLK/TCK = 0 SWDIO/TMS = 0 TDI = 0 TDO = 0 nTRST = 0 nRESET = 0
Info : CMSIS-DAP: Interface ready
Info : clock speed 4000 kHz
Info : SWD DPIDR 0x0bc12477
Info : SWD DLPIDR 0x00000001
Info : rp2040.core0: hardware has 4 breakpoints, 2 watchpoints
Info : starting gdb server for rp2040.core0 on 3333
Info : Listening on port 3333 for gdb connections
$> cd debug/build/src
$> gdb-multiarch main.elf
gdb-multiarch main.elf
GNU gdb (Ubuntu 9.2-0ubuntu1~20.04) 9.2
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
Find the GDB manual and other documentation resources online at:

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from src/main.elf...
(gdb) target remote localhost:3333
Remote debugging using localhost:3333
main () at /home/work/development/pico2/debug/src/main.c:37
37 int main() {
(gdb) load
Loading section .boot2, size 0x100 lma 0x10000000
Loading section .text, size 0x4b10 lma 0x10000100
Loading section .rodata, size 0xd84 lma 0x10004c10
Loading section .binary_info, size 0x20 lma 0x10005994
Loading section .data, size 0xa04 lma 0x100059b4
Start address 0x100001e8, load size 25528
Transfer rate: 8 KB/sec, 4254 bytes/write.
(gdb) monitor reset init
target halted due to debug-request, current mode: Thread
xPSR: 0xf1000000 pc: 0x000000ee msp: 0x20041f00
(gdb) b main
Breakpoint 1 at 0x100003d8: file /home/work/development/pico2/debug/src/main.c, line 37.
(gdb) c
Note: automatically using hardware breakpoints for read-only addresses.

Breakpoint 1, main () at /home/work/development/pico2/debug/src/main.c:37
37 int main() {
(gdb) n
38 setup();
(gdb) n
40 printf("Hello World\n");
(gdb) n
43 printf(".");
(gdb) n
44 blink();
(gdb) n
42 while (true) {
(gdb) p LED_GREEN
$1 = 15
C/C++ Extension Pack (by Microsoft)
Cortex-Debug (by marus25)
CMake Tools (by Microsoft)
"version": "0.2.0",
"configurations": [
"name": "Pico Debug",
"device": "RP2040",
"gdbPath": "gdb-multiarch",
"cwd": "${workspaceRoot}",
"executable": "${workspaceRoot}/debug/build/src/main.elf",
"request": "launch",
"type": "cortex-debug",
"servertype": "openocd",
"configFiles": ["interface/cmsis-dap.cfg", "target/rp2040-core0.cfg"],
"openOCDLaunchCommands": ["transport select swd", "adapter speed 4000"],
"svdFile": "${env:PICO_SDK_PATH}/src/rp2040/hardware_regs/rp2040.svd",
"searchDir": ["/home/work/development/openocd/tcl"],
"runToMain": true,
"postRestartCommands": ["break main", "continue"]
  • executable: Needs to point to the debug-ready compiled file
  • svdFile: SVD is a file format containing the specifics of a microcontroller, this file is used by openocd to provide hardware related debugging information like registers etc.
  • serachDir: Configure this to your OpenOCD installations tcl path




Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store