Getting Started with the Debug Probe
CosmicSDK drives the Binho Atira debug probe (since 1.7) next to the Supernova and
Pulsar host adapters. Nothing changes for host adapter users: CosmicSDK and
libcosmicsdk stay as they are, and the debug probe has its own class and its
own library.
What 1.9 covers
Target flash: program, erase and verify the target’s own flash through the vendor’s CMSIS flash algorithm (
.FLM), straight from a.pack(see Flashing a target with the SDK). New in 1.9.Target power on fw-atira 1.1: the signal voltage and target power as separate switches, and self-powered targets (see Connecting a target). New in 1.9.
SWD debug: attach, halt, run, step, hardware breakpoints, registers, memory.
External SPI flash: detect, read, erase, program, verify.
The probe’s USB serial ports.
Rebooting the probe into its bootloader for a firmware update.
pyOCD still works for flashing (see Flashing a target with pyOCD). Python is
pycosmicsdk 1.9.0 (DebugProbe, target_flash()). JTAG, SWO and RTT are not
part of 1.9.
What is in the download
The same archives and installers as Download and Installation. Next to the host adapter files, each one carries:
lib/: the debug probe library,libdebugprobe.so/libdebugprobe.dylib/debugprobe.dll(withdebugprobe.lib), and the staticdebugprobe_static.include/debugprobe/andinclude/DebugProbe_API_c.h: the C++ and C headers.examples/debugprobe/:debugprobe_demo.cpp(C++ tour),debugprobe_c_demo.c(C tour),spi_flash_example.cpp(program and verify an SPI NOR flash), and the target flash examplestarget_flash_example.cpp(C++),target_flash_c_example.c(C) andtarget_flash_pack_example.cpp(load a part from a.pack).
Minimum firmware
The probe needs firmware fw-atira v1.0 or later. From v1.0 the firmware
answers with 16-bit status codes, and connect() refuses an older image: it
returns false (DebugProbe_Connect returns false in C) and prints
the firmware answers with an 8-bit status (before fw-atira v1.0) ... update the
probe firmware on stderr. Update the probe, then connect again.
To read the version of a probe that connects:
#include "debugprobe/DebugProbe.hpp"
using namespace debugprobe;
DebugProbe probe;
auto boards = DebugProbe::listDevices(); // serial, path, vid, pid
if (boards.empty() || !probe.connect("", boards[0].serial)) {
return 1;
}
DeviceInfo info;
if (probe.getDeviceInfo(info) == SUCCESS) {
std::printf("firmware %u.%u.%u\n", info.fw_major, info.fw_minor, info.fw_patch);
}
In C, DebugProbe_GetDeviceInfo() fills DebugProbe_DeviceInfo with the same
fw_major, fw_minor and fw_patch fields.
Every call returns a CosmicSDK result code and is checked against SUCCESS,
like any CosmicSDK call.
Setting up the host
Linux. Install the udev rule shipped in the archive, then unplug and replug the probe:
sudo ./scripts/install_udev_rules.sh
The rule grants the logged-in user access (uaccess), whether or not the
distribution has a plugdev group, and covers the probe’s bootloader too.
Without the rule, opening the probe fails with a permission error.
Windows. No driver installation is expected. Until the probe firmware
declares its bulk interface to Windows, SPI flash programming uses the HID path,
which is slower (spiBulkAvailable() reports it as unavailable).
macOS. Nothing to install.
Connecting a target
These are the Atira Rev A/B connectors. Switch the signal voltage on before any
SWD or SPI traffic: it powers the level shifters on the debug header. On fw-atira
1.1 that is setSignalVoltageEnabled(true), plus setTargetPowerEnabled(true)
only when pin 1 has no supply; on 1.0.x it is powerSetOutput(true), which
also drives pin 1 (powerSetOutput answers DEBUGPROBE_POWER_USE_SPLIT_CONTROLS
on 1.1).
Debug header J4 (10-pin, 1.27 mm):
Pin |
Signal |
Pin |
Signal |
|---|---|---|---|
1 |
VDD (target) |
2 |
SWDIO |
3 |
GND |
4 |
SWCLK |
5 |
GND |
6 |
SWO |
7 |
key |
8 |
TDI |
9 |
TRST |
10 |
nRESET |
Warning
Pin 9 is TRST, not the GNDDetect of the standard Cortex debug pinout. A
cable that grounds pin 9 is fine for SWD. On fw-atira 1.0.x it is also the SPI
chip select, so such a cable breaks SPI there; fw-atira 1.1 moved the chip
select to pin 2.
For a target with its own supply: on fw-atira 1.1, keep pin 1 connected. The
probe reads the target’s supply there and sets the signal voltage to it
(signalVoltageUseExternal) without powering pin 1. On 1.0.x, leave pin 1
unconnected, since powerSetOutput(true) would drive it over the target’s supply.
SPI flash also uses J4. Wire a 25-series NOR flash as /CS → 2 (fw-atira 1.1;
pin 9 on 1.0.x), CLK → 4, DI (MOSI) → 6, DO (MISO) → 8, VCC → 1 and GND → 3,
with /WP and /HOLD tied to VCC. SCK goes up to 8 MHz. The chip select to pass to
spiControllerInit() is SPI_CHIP_SELECT_0 on 1.1 and SPI_CHIP_SELECT_2
on 1.0.x. examples/debugprobe/spi_flash_example.cpp finds it on its own.
UART bridge is on J3 (3-pin): 1 = TX (out of the probe, to the target’s
RX), 2 = GND, 3 = RX. The same signals are on J2: 4 = TX, 5 = RX, 1 = GND.
⚠ J2 pin 3 is the target rail. The bridge appears as the probe’s first serial
port (uartBridgePort()).
Building against the library
Link the debug probe library. A host adapter program keeps linking
cosmicsdk; a program that uses both boards links both.
# Linux / macOS, from the extracted archive
c++ -std=c++17 -Iinclude my_probe_app.cpp -Llib -ldebugprobe -o my_probe_app
cc -std=c11 -Iinclude my_probe_app.c -Llib -ldebugprobe -o my_probe_app
The examples in examples/debugprobe/ build with these commands. Run one
with the probe’s serial number to pin it on a bench with several boards:
./debugprobe_demo --serial=<iSerial>
./debugprobe_c_demo <iSerial>
Flashing a target with the SDK
From 1.9, DebugProbe programs a target’s internal flash through a CMSIS flash
algorithm, the .FLM file in the vendor’s device pack. pyOCD and Ground Control
are not involved. The SDK runs the algorithm on the target with
debugCallFunction; the probe needs no new firmware.
The inputs are bytes in memory. The SDK takes no file paths:
the
.FLM:targetFlashLoadAlgorithmcopies it and holds it untiltargetFlashUnloadAlgorithmor disconnect. One algorithm per handle.a RAM window that holds the algorithm, a 512-byte stack and one page buffer: from the datasheet, or
RAMstart/RAMsizein the pack’s.pdsc.the image, as raw bytes at an address. HEX, ELF and S-record files are not read.
flashSize comes from the FLM, not from the part. A family FLM can declare more
than one part has: Nordic’s nrf52xxx.flm declares 2 MiB, the nRF52840 has 1 MiB.
Keep images inside the part’s real flash (datasheet, or IROM in the .pdsc).
A write past it fails with DEBUGPROBE_TFLASH_ROUTINE_FAILED. On an nRF52840 that
write left the real flash unchanged (checked byte for byte).
#include "debugprobe/DebugProbe.hpp"
#include "debugprobe/TargetFlash.hpp"
DebugProbe probe;
probe.connect("", serial);
// fw-atira 1.1, unpowered target: Atira supplies it. A self-powered board and
// fw 1.0.x need other calls: see powerTarget() in target_flash_example.cpp.
probe.setSignalVoltageLevel(3300);
probe.setSignalVoltageEnabled(true);
probe.setTargetPowerEnabled(true);
DebugState st;
probe.debugAttach(false, st);
TargetFlashInfo info; // flash base, size, page size, sectors
probe.targetFlashLoadAlgorithm(flm, {0x20000000, 0x8000}, info); // STM32H503 SRAM1
uint16_t rc = probe.targetFlashWrite(info.flashBase, image, /*verify=*/true,
[](uint32_t done, uint32_t total) { return true; }); // false cancels
probe.debugRun(st); // the target is left halted at reset
Check every result against SUCCESS. The full programs are
examples/debugprobe/target_flash_example.cpp and target_flash_c_example.c.
From a CMSIS-Pack
targetFlashListPackDevices lists a .pack’s devices, each with its
<variant> part numbers; targetFlashLoadPackDevice loads one by device name
in place of targetFlashLoadAlgorithm, with the .pdsc’s RAM window. Both
take the pack as bytes; listing needs no handle.
Each device comes back with a support value:
Flashable: loads as is.
Needs connect steps: the pack declares vendor connect steps (for example
DebugPortStarton NXP MCX parts) that the SDK does not run. The device loads only when the caller passes its steps back as confirmed, same names in the same order; otherwise the load returnsDEBUGPROBE_TFLASH_PACK_NEEDS_CONFIRM. After an operation that touched the target, the report’sconnectStepsSkippednames them (C:DebugProbe_TargetFlashGetLastSkippedSteps). Operations can then fail before the target answers, because the steps did not run.Not supported: refused, with
reasonsaying why.
In C, DebugProbe_TargetFlashListPackDevices and _ListPackVariants take the
caller’s sizeof of the entry (entrySize); a device’s connect steps are read
one at a time with DebugProbe_TargetFlashGetPackDeviceConnectStep.
Multi-core parts (multiCore in the listing) load by one rule, with no core
choice:
The memory map is every core’s, merged. The part loads only when its flash is one contiguous block, as on the STM32H745 (CM7’s bank 1 + CM4’s bank 2). Flash split across cores is refused as several flash regions.
When each core declares its own copy of the
.FLMover the same range, the copy of the core on AP0, the one the Atira attaches, is loaded with itsRAMstart. When no single core is on AP0, the part is not listed.
Call |
What it does |
|---|---|
|
Parse the FLM and place it in the RAM window, on the host. Touches nothing on the target. |
|
Erase the sectors the image touches, program, and verify if asked, in one algorithm session. |
|
Erase every sector that overlaps the range. Bytes of those sectors outside the range are lost. |
|
Erase the whole flash region: the FLM’s |
|
Program pages padded with the erased value, skipping pages that are all erased. Does not erase. |
|
Read back with the core halted and compare; returns the first differing address. |
|
What the last call did: whether it touched the target,
sectors erased and the erased range, bytes programmed, the
failing routine and its |
|
The loaded algorithm’s flash description. |
|
Drop the algorithm. |
The C API has the same calls as DebugProbe_TargetFlash*, with a
bool (*)(uint32_t done, uint32_t total, void* user) progress callback.
DebugProbe_TargetFlashReport stays 28 bytes; the erased range comes from
DebugProbe_TargetFlashGetLastErasedRange.
The erased range is whole sectors, so it can reach past the requested range. It
counts only erases that finished: after a failed EraseChip the whole region is
in an unknown state, and after a failed EraseSector so is the next sector.
What every operation does
Refuses before touching the target when no algorithm is loaded, no debug session is attached, the range leaves the algorithm’s flash region, or FPB breakpoints are set. Clear breakpoints first.
Reset and halt first, never a plain halt, so running firmware stops at the reset vector. Then it loads the algorithm and calls
Init,EraseSector,ProgramPageandUnInit. Verify reads flash back over the debug port, never through the algorithm’sVerifyentry.Reset and halt last, on success, failure and cancel. The target is left halted at the reset vector and the session stays attached;
debugRunstarts it.After a failure, the SDK calls
UnInitifInitran, then resets. IffinalResetOkin the report is false, the target’s state is unknown: detach and power-cycle it. When that reset was the only failure, the call returnsDEBUGPROBE_TFLASH_RESET_FAILED.A readback that differs returns
DEBUGPROBE_TFLASH_VERIFY_MISMATCH, and the report setsverifyMismatchandfirstMismatch(also aftertargetFlashWritewith verify, which has no out-parameter for it).Each routine has a time budget taken from the FLM’s own timeouts (
InitandUnInit: 2 s). A routine that runs past it returnsDEBUGPROBE_DEBUG_TIMEOUT. A routine that overruns its stack returnsDEBUGPROBE_TFLASH_ROUTINE_FAILED.Progress counts bytes over the whole operation (erase, program, verify) on the calling thread. Returning
falsecancels between pages and sectors, never inside a routine; the cleanup is a failure’s and the code isDEBUGPROBE_TFLASH_CANCELLED.Blocking, one operation per handle at a time.
Result codes
DebugProbeTargetFlashCodes.h, group 0x1D:
Code |
Value |
Meaning |
|---|---|---|
|
0x1D01 |
No algorithm loaded. |
|
0x1D02 |
Not an ELF32 FLM, an entry missing, a bad sector table, or over a cap (8 MiB file, 512 sector-table entries). |
|
0x1D03 |
Algorithm, stack and page buffer do not fit the window, or the window overlaps flash. |
|
0x1D04 |
The range leaves the flash region. Target untouched. |
|
0x1D05 |
The algorithm read back different: the window is not writable RAM, or the target does not answer coherently. |
|
0x1D06 |
A routine returned non-zero, or overran its stack.
Routine and |
|
0x1D07 |
Read-back differs from the image. |
|
0x1D08 |
The progress callback returned |
|
0x1D09 |
FPB breakpoints are set. Target untouched. |
|
0x1D0A |
Not a readable |
|
0x1D0B |
The pack has no device by that name. |
|
0x1D0C |
The device is listed as not supported. |
|
0x1D0D |
The device’s connect steps were not confirmed. |
|
0x1D0E |
Written (and verified, if asked); the final reset failed. |
The debug plane’s codes keep their meaning here: DEBUGPROBE_DEBUG_NOT_ATTACHED,
DEBUGPROBE_DEBUG_NO_TARGET (no target answers, for example with the rail off),
DEBUGPROBE_DEBUG_FAULT (for example a RAM window with no memory behind it),
DEBUGPROBE_DEBUG_TIMEOUT and DEBUGPROBE_DEBUG_CALL_FAULTED.
Supported in 1.9
Verified part: STM32H503RB on a NUCLEO-H503RB, with
STM32H503_128k_0800.FLMfrom ST’sSTM32H5xx_DFPand RAM window0x20000000/0x8000. Other parts are not verified.Probe: Atira Rev B over SWD, target either powered from the probe’s rail or self-powered from its own USB (the release-commit run was self-powered).
Host: Linux. Windows and macOS are not run on hardware.
Not supported: running vendor connect steps or debug sequences; HEX, ELF or S-record images; several flash regions, TrustZone pairs; choosing the core of a multi-core part (one with a single contiguous flash loads, with the AP0 core’s
.FLM); option bytes; readout protection; the browser (WASM). Python target flash is in pycosmicsdk 1.9.0 (DebugProbe.target_flash()).
Measured on Linux, Atira Rev B with fw-atira 1.1.0, NUCLEO-H503RB, 2026-10-20:
Image |
Runs |
Median, write + verify |
|---|---|---|
2 KiB |
10/10 |
1.0 s |
16 KiB |
10/10 |
4.1 s |
128 KiB |
10/10 |
29.0 s |
At the release commit (2026-10-28, target self-powered from its own USB), a 128 KiB
targetFlashWrite took 11.44–11.47 s (faster than the 10-20 table because of
#444, which batches the flash routine calls’ register traffic and waits in the probe,
and #450, which skips their register save/restore), 10/10, read back identical; erase range
(5/5, 3 sectors reported), erase chip (5/5), load by pack name (3/3) and cancel
(3/3) passed.
A fixture firmware written through the API booted and ran 10/10. On the same bench,
each refusal returned its code: breakpoint set → BREAKPOINTS_ACTIVE; truncated or
garbage FLM → BAD_ALGORITHM; small window, or a window over flash →
RAM_WINDOW; window with no memory → DEBUG_FAULT; detached → NOT_ATTACHED;
rail off → NO_TARGET with 0 bytes written.
Flashing a target with pyOCD
The probe is a standard CMSIS-DAP probe, so pyOCD drives it with no extra setup. Verified on Linux, programming an STM32H503RB over SWD: pyOCD 0.44.1, 11/11 program, verify and erase cycles, about 6.5 s per 128 KiB; pyOCD 0.45.1 on a Rev A/B probe, program with a byte-identical read-back.
python -m pip install pyocd
pyocd list # shows the probe and its serial
pyocd pack install stm32h503rb # the device pack, from the vendor
pyocd flash -u <iSerial> -t stm32h503rbtx --base-address 0x08000000 firmware.bin
Close any program that holds the probe (your own SDK program, Mission Control) before running pyOCD: only one program can own the probe’s debug interface.
The target must be powered. The probe can power it from its own rail, or it can run from its own supply.
Read flash back only while the core is halted (
pyocd cmd -u <iSerial> -t stm32h503rbtx -c halt -c "savemem 0x08000000 131072 out.bin"). A read taken while the target runs can return wrong data.
Verified targets
Target |
Tool |
Result |
Host |
|---|---|---|---|
STM32H503RB |
pyOCD |
11/11 program + verify |
Linux |
STM32H503RB |
pyOCD |
program + read-back |
Linux (Rev A/B) |
Other Cortex-M devices with a CMSIS pack should work the same way; they are not verified yet.