Firmware Architecture
This document provides a high-level overview of the Debug Probe firmware's architecture, components, and design principles.
Core Components
The firmware is built upon several key technologies:
- Raspberry Pi RP2040: The microcontroller at the heart of the probe. Its most critical feature for this application is the Programmable I/O (PIO).
- FreeRTOS: A real-time operating system that manages the concurrent tasks required for handling USB, DAP commands, and UART communication.
- TinyUSB: A lightweight, open-source USB stack that enables the probe to function as a composite USB device.
- CMSIS-DAP: An implementation of the ARM CoreSight Debug Access Port protocol, providing a standard interface for debug tools.
PIO-based SWD Implementation
The most distinctive feature of this firmware is its use of the RP2040's Programmable I/O (PIO) to implement the low-level Serial Wire Debug (SWD) protocol.
PIO is a set of flexible state machines that can be programmed to handle I/O protocols independently of the main CPU cores. In this project:
- PIO state machines are responsible for the precise, high-speed bit-banging of the SWCLK and SWDIO lines.
- The CPU only needs to send high-level commands (e.g., "write 32 bits", "read 8 bits") to the PIO via FIFOs.
- This offloads the timing-critical aspects of the protocol from the CPU, allowing the main firmware to focus on processing DAP commands and managing USB traffic without being burdened by bit-level signal generation.
The PIO programs are defined in .pio source files:
src/probe.pio: The main PIO program for standard two-wire SWD.src/probe_oen.pio: A variant for hardware configurations that use a separate output-enable pin for the SWDIO line buffer.
This architecture ensures deterministic timing and enables high-speed debug communication.
Task Structure (FreeRTOS)
The firmware operates as a multi-threaded system using FreeRTOS. The main tasks, defined in src/main.c and related files, are:
usb_thread: Manages the TinyUSB device stack. It handles USB events, enumeration, and the low-level transport of data packets.dap_thread: The core of the debug functionality. It waits for DAP command packets to arrive from the USB host. When a packet is received, it parses the command and calls the appropriate functions from the CMSIS-DAP library, which in turn drive the PIO state machines to communicate with the target.cdc_thread: Handles the USB-to-UART bridge. It polls for data from both the USB CDC interface and the RP2040's hardware UART, forwarding data between them.dev_mon: A simple watchdog task that monitors USB Start-of-Frame (SOF) packets. If SOF packets stop arriving, it indicates a potential USB bus hang-up, and the task resets the USB peripheral to recover.
This multi-tasking approach allows the probe to handle simultaneous debug commands and UART traffic smoothly.
USB Composite Device
The Debug Probe presents itself to the host computer as a composite USB device. This means it has multiple functions (interfaces) available over a single USB connection. The interfaces are defined in src/usb_descriptors.c:
-
DAP Interface (Interface 0): This is the primary debugging interface.
- DAP v1 Mode: Implemented as a USB HID (Human Interface Device). This is the older, widely compatible standard.
- DAP v2 Mode: Implemented as a USB Vendor-Specific device with Bulk endpoints. This allows for much higher data throughput compared to HID. The firmware advertises itself as
WinUSBcompatible to work seamlessly on Windows. The protocol version is determined at compile time.
-
CDC-ACM Interface (Interfaces 1 & 2): This pair of interfaces creates a standard virtual serial port, used for the UART bridge.
This composite nature is what allows you to debug a target and view its serial output simultaneously over one USB cable.