NOTE: Work in Progress
This note is a work in progress. It is incomplete and may contain serious errors. If you have feedback, please send it to me.
As an aspiring core CircuitPython developer, you will soon come to the realization that inserting printf
or blinking an LED can get you just so far on your debugging journey. This note is a collection tips and tricks that I've found useful on my personal journey into the depths of CircuitPython debugging using PicoProbe, OpenOCD, and the RP2040.
Background
The RP2040 SoC includes a special hardware blob that can control its on-chip processors and peripherals at a low level. Among its useful capabilities are the ability to control processors, set breakpoints and watch points, examine and manipulate processor and peripheral registers, and examine and manipulate flash and RAM. When used in concert with a hardware debug probe and a software debug stack, almost any debugging scenario you can imagine is within reach. The RP2040 exposes this blob on its SWD (Serial Wire Debugger) pins.
Hardware debug probes can be prohibitively expensive items. The Raspberry Pi Foundation has developed a capable hardware probe that is extremely cost effective. PicoProbe is an application program that runs on a dedicated RP2040 attaching an RP2040 system under test by means of its SWD pins.
Glossary of Terms
- Build Environment
- Core CircuitPython
- Debug Build
- Debug Probe
- Debug Target
- GDB
- Host OS
- IDE
- On-Chip Debugger
- SWD
- Target Toolchain
Hardware Requirements
- An RP2040-based board to use as your debug probe. Almost any RP2040 board will work as long as it exposes enough GPIO pins to attach a serial port and an SWD port.
- An RP2040-based board that will be your debug target. You will need a board that exposes the RP2040 SWD port. Examples of boards that work are: Raspberry Pi Pico (W), Adafruit Feather RP2040, and Adfruit RP2040 Metro.
Software Requirements
- A suitable host OS to run the debugging software stack. This note uses Ubuntu Jammy Jellyfish (22.04) for its host OS, but success can be had with other host OSs including Raspberry Pi OS, Windows, and MacOS.
Building CircuitPython for Debugging
Dan Halbert has written an excellent guide for obtaining and building CircuitPython from its source code. There are a few tweaks you can make to his recipe that will improve your results:
- Use a Python virtual environment. CircuitPython utilizes a fairly large collection Python packages to check and build itself, so it can be useful to isolate this collection from your usual Python environment.
- Use the current ARM GNU toolchain to build CircuitPython. This will ensure that your tooling matches the tooling used by the CircuitPython team. As of this writing, the current version is
13.2.rel1
. - Modify CircuitPython to activate its serial REPL console.
- Modify CircuitPython to build a lightly optimized version of itself with debugging symbols.
Installing the ARM GNU Toolchain
You can download the ARM GNU Toolchain from here. The available toolchains are grouped by target within host. The target is named by a triplet, in our case the target triplet we want is arm-none-eabi
. Since our host is x86_64 Linux
, we'll download arm-gnu-toolchain-13.2.rel1-x86_64-arm-none-eabi.tar.xz
from the x86_64 Linux hosted cross toolchains
section of the page.
It's up to you where you want to install the toolchain. My personal preference is to put it inside my Development
folder like this:
/home/eightycc/Development/arm_tools
└── arm-gnu-toolchain-13.2.Rel1-x86_64-arm-none-eabi
├── arm-none-eabi
├── bin
├── include
├── lib
├── libexec
└── share
mkdir -p ~/Development/arm_tools tar -xJf arm-gnu-toolchain-13.2.rel1-x86_64-arm-none-eabi.tar.xz -C ~/Development/arm_tools/