.. _debug_probe: 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 :ref:`debug_probe_target_flash`). 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 :ref:`debug_probe_connect_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 :ref:`debug_probe_flash_target`). 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 :ref:`download_and_install`. Next to the host adapter files, each one carries: - ``lib/``: the debug probe library, ``libdebugprobe.so`` / ``libdebugprobe.dylib`` / ``debugprobe.dll`` (with ``debugprobe.lib``), and the static ``debugprobe_static``. - ``include/debugprobe/`` and ``include/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 examples ``target_flash_example.cpp`` (C++), ``target_flash_c_example.c`` (C) and ``target_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: .. code-block:: cpp #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, :c:func:`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: .. code-block:: bash 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. .. _debug_probe_connect_target: 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. .. code-block:: bash # 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: .. code-block:: bash ./debugprobe_demo --serial= ./debugprobe_c_demo .. _debug_probe_target_flash: 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``: ``targetFlashLoadAlgorithm`` copies it and holds it until ``targetFlashUnloadAlgorithm`` or 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``/``RAMsize`` in 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). .. code-block:: cpp #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 ```` 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 ``DebugPortStart`` on 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 returns ``DEBUGPROBE_TFLASH_PACK_NEEDS_CONFIRM``. After an operation that touched the target, the report's ``connectStepsSkipped`` names them (C: ``DebugProbe_TargetFlashGetLastSkippedSteps``). Operations can then fail before the target answers, because the steps did not run. - **Not supported:** refused, with ``reason`` saying 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 ``.FLM`` over the same range, the copy of the core on AP0, the one the Atira attaches, is loaded with its ``RAMstart``. When no single core is on AP0, the part is not listed. ================================ ========================================================== Call What it does ================================ ========================================================== ``targetFlashLoadAlgorithm`` Parse the FLM and place it in the RAM window, on the host. Touches nothing on the target. ``targetFlashWrite`` Erase the sectors the image touches, program, and verify if asked, in one algorithm session. ``targetFlashEraseRange`` Erase every sector that overlaps the range. Bytes of those sectors outside the range are lost. ``targetFlashEraseChip`` Erase the whole flash region: the FLM's ``EraseChip`` when it has one, else every sector. ``targetFlashProgram`` Program pages padded with the erased value, skipping pages that are all erased. Does not erase. ``targetFlashVerify`` Read back with the core halted and compare; returns the first differing address. ``targetFlashGetLastReport`` What the last call did: whether it touched the target, sectors erased and the erased range, bytes programmed, the failing routine and its ``r0``, and whether the final reset held. ``targetFlashGetInfo`` The loaded algorithm's flash description. ``targetFlashUnloadAlgorithm`` 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``, ``ProgramPage`` and ``UnInit``. Verify reads flash back over the debug port, never through the algorithm's ``Verify`` entry. - **Reset and halt last**, on success, failure and cancel. The target is left halted at the reset vector and the session stays attached; ``debugRun`` starts it. - **After a failure**, the SDK calls ``UnInit`` if ``Init`` ran, then resets. If ``finalResetOk`` in the report is false, the target's state is unknown: detach and power-cycle it. When that reset was the only failure, the call returns ``DEBUGPROBE_TFLASH_RESET_FAILED``. - **A readback that differs** returns ``DEBUGPROBE_TFLASH_VERIFY_MISMATCH``, and the report sets ``verifyMismatch`` and ``firstMismatch`` (also after ``targetFlashWrite`` with verify, which has no out-parameter for it). - **Each routine has a time budget** taken from the FLM's own timeouts (``Init`` and ``UnInit``: 2 s). A routine that runs past it returns ``DEBUGPROBE_DEBUG_TIMEOUT``. A routine that overruns its stack returns ``DEBUGPROBE_TFLASH_ROUTINE_FAILED``. - **Progress** counts bytes over the whole operation (erase, program, verify) on the calling thread. Returning ``false`` cancels between pages and sectors, never inside a routine; the cleanup is a failure's and the code is ``DEBUGPROBE_TFLASH_CANCELLED``. - **Blocking**, one operation per handle at a time. Result codes ~~~~~~~~~~~~ ``DebugProbeTargetFlashCodes.h``, group ``0x1D``: ========================================= ====== ================================================== Code Value Meaning ========================================= ====== ================================================== ``DEBUGPROBE_TFLASH_NO_ALGORITHM`` 0x1D01 No algorithm loaded. ``DEBUGPROBE_TFLASH_BAD_ALGORITHM`` 0x1D02 Not an ELF32 FLM, an entry missing, a bad sector table, or over a cap (8 MiB file, 512 sector-table entries). ``DEBUGPROBE_TFLASH_RAM_WINDOW`` 0x1D03 Algorithm, stack and page buffer do not fit the window, or the window overlaps flash. ``DEBUGPROBE_TFLASH_OUT_OF_RANGE`` 0x1D04 The range leaves the flash region. Target untouched. ``DEBUGPROBE_TFLASH_LOAD_MISMATCH`` 0x1D05 The algorithm read back different: the window is not writable RAM, or the target does not answer coherently. ``DEBUGPROBE_TFLASH_ROUTINE_FAILED`` 0x1D06 A routine returned non-zero, or overran its stack. Routine and ``r0`` are in the report. ``DEBUGPROBE_TFLASH_VERIFY_MISMATCH`` 0x1D07 Read-back differs from the image. ``DEBUGPROBE_TFLASH_CANCELLED`` 0x1D08 The progress callback returned ``false``. ``DEBUGPROBE_TFLASH_BREAKPOINTS_ACTIVE`` 0x1D09 FPB breakpoints are set. Target untouched. ``DEBUGPROBE_TFLASH_BAD_PACK`` 0x1D0A Not a readable ``.pack``. ``DEBUGPROBE_TFLASH_PACK_NO_DEVICE`` 0x1D0B The pack has no device by that name. ``DEBUGPROBE_TFLASH_PACK_UNSUPPORTED`` 0x1D0C The device is listed as not supported. ``DEBUGPROBE_TFLASH_PACK_NEEDS_CONFIRM`` 0x1D0D The device's connect steps were not confirmed. ``DEBUGPROBE_TFLASH_RESET_FAILED`` 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.FLM`` from ST's ``STM32H5xx_DFP`` and RAM window ``0x20000000``/``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. .. _debug_probe_flash_target: 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. .. code-block:: bash 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 -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 -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.