Skip to content

Instantly share code, notes, and snippets.

@a-gavin
Created August 30, 2022 05:14
Show Gist options
  • Save a-gavin/3dcffe98c625f711a93c53a527ec374b to your computer and use it in GitHub Desktop.
Save a-gavin/3dcffe98c625f711a93c53a527ec374b to your computer and use it in GitHub Desktop.
Raspberry Pi Picoprobe Debug Setup/Tips & Tricks

Raspberry Pi Picoprobe Debug Setup/Tips & Tricks

This document details useful information on how to use a Raspberry Pi Pico as a SWD probe for another Pico using the Picoprobe software. This information assumes you have followed all of the steps in Appendix A of the Getting Started with Raspberry Pi Pico Guide and that you are doing development on a Linux system.

Essential Setup

  1. Add the below files to your system to enable non-root access to the devices

    /etc/udev/rules.d/99-openocd.rules:

    # Raspberry Pi Picoprobe
    ATTRS{idVendor}=="2e8a", ATTRS{idProduct}=="0004", MODE="0666"
    

    /etc/udev/rules.d/95-pi-pico-udev.rules:

    # Raspberry Pi Pico
    ATTRS{idVendor}=="2e8a", ATTRS{idProduct}=="0003", MODE="0666", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1"
    
  2. Then run the following command:

    sudo udevadm control --reload-rules && sudo udevadm trigger

Typical Debug Workflow

  1. Use terminal multiplexer of some sort (e.g. screen or tmux) or just two windows to spawn two shells, one to build your code and another with GDB to step through your code.

    NOTE: This setup unfortunately has not worked with semihosting output for me.

  2. In the GDB shell, run gdb-multiarch with your desired ELF executable. See section GDB Init File for example invocation and usage of init files.

  3. Still in GDB, run load followed by whatever breakpoints and other things you'd like to set before running the program.

  4. Finally, still in GDB, run continue or c to run the program on the chip.

Notes on Output via Direct USB and Semihosting

The following assumes that you have followed Essential Setup to enable user-land usage of the Raspberry Pi Pico both as Picoprobe and other programs and have followed the debug probe setup in Appendix A of the Getting Started with Raspberry Pi Pico Guide (or have setup similar otherwise).

USB Output

When simply flashing and running a program on the Pico when attached directly to the development machine via USB (i.e. no middleman debug probe), output from the Pico can be viewed in terminal by installing minicom and running it via:

minicom -b 115200 -D /dev/ttyACMn, where n is some number like 0, 1, 2, etc.

If you have more than one Pico or similar device plugged in to your development machine via USB, there will be more than one ttyACM file.

Semihosting Output

To get program output while using a debug probe, enable Semihosting, your very slow partner in debugging crime, do the following (assuming you're using Pico-SDK's stdio):

NOTE: You must run OpenOCD separately to get output from the device. See GDB Init File for details.

  1. In your project's CMake file add the following:

    pico_enable_stdio_semihosting(<target_name> 1)

  2. Re-run both cmake and your build system (probably Make, possibly Ninja).

  3. Assuming OpenOCD is running, start GDB with the commands in GDB Init File in addition to:

    The second command disables logging to ensure semihosting output is not hidden by log output.

    monitor arm semihosting enable
    monitor debug_level -2
    
  4. Finally, flash the device and run your program via GDB at which point you should get output from the device in the OpenOCD shell, albeit very slowly.

GDB Init File

To simplify debugging setup, run GDB with the --init-command flag before you enter interactive GDB session to run desired commands. Example usage is as follows:

Not Using Semihosting (will SIGTRAP when program using semihosting)

gdb-multiarch --init-command=/path/to/gdbinit/file /path/to/exec.elf

GDB Init File: (first line spawns OpenOCD from GDB and pipes output to GDB)

tar ext | openocd -c "gdb_port pipe" -f interface/picoprobe.cfg -f target/rp2040.cfg -s tcl
monitor reset halt

Using Semihosting (make sure you built target w/ semihosting enabled)

Leave running in one shell: openocd -f interface/picoprobe.cfg -f target/rp2040.cfg -s tcl

Next, in another shell: gdb-multiarch --init-command=/path/to/gdbinit/file /path/to/exec.elf

GDB Init File: (first line connects to OpenOCD TCP server)

tar ext :3333
monitor arm semihosting enable
monitor debug_level -2
monitor reset halt

Shell Functions to Flash via UF2

Functions to check for existence of /media/RPI-RP2 and copy UF2 file to Raspberry Pi Pico for flashing via UF2 bootloader. Remove sudo in commands if the user you're logged in as has proper permissions.

Example usage:

rpi-check && rpi-copy /path/to/exec.uf2

rpi-copy --mount-from /path/to/device/dir /path/to/exec.uf2

Functions (tested in zsh):

RPI_STANDARD_DIR=/media/alex/RPI-RP2

rpi-copy() {
    # Copy executable to RaspberryPi Pico
    # Verify args
    if [ "$#" -ne 1 ]  && [ "$#" -ne 3 ]; then
        echo "usage: rpi-copy [--mount-from MOUNT] EXECUTABLE" >&2
        return 1
    fi

    if [ "$#" -eq 1 ]; then
        # Standard copy executable
        #
        # $1: Executable
        sudo cp $1 $RPI_STANDARD_DIR
    else
        # Mount and then copy
        # Sometimes RPI shows up in weird places
        #
        # $1: --mount
        # $2: Mount source
        # $3: Executable
        RPI_MOUNT=/mnt/rpi

        sudo mkdir $RPI_MOUNT
        sudo mount $2 $RPI_MOUNT

        sudo cp $3 $RPI_MOUNT

        sudo umount $RPI_MOUNT
    fi

    return 0
}

rpi-check-dir() {
    if [ -d $RPI_STANDARD_DIR ]; then
        echo "device mounted to standard directory \"$RPI_STANDARD_DIR\""
        return 0;
    else
        echo "rpi-check-dir: device not mounted to standard directory "\
             "\"$RPI_STANDARD_DIR\"" >&2
        return 1;
    fi
}

Useful References:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment