Atira debug probe
DebugProbe drives a Binho Atira: target power, SWD
debug on Arm Cortex-M, an SPI controller with SPI NOR flash programming, and a
USB-to-UART bridge. It is its own class rather than a
Device, and a probe and a Supernova or Pulsar can be
open in the same process. The async twin is
AsyncDebugProbe.
The probe needs firmware fw-atira v1.0 or newer. RTT, SWO, UPDI and JTAG are not available in this release.
Setup
On Linux, the probe needs the same udev rule as the host adapters. The file
install_udev_rules.sh writes (see Getting started)
covers the Atira, USB ID 1fc9:8347. Without it, open() fails with
DeviceNotFoundError, naming the permission problem.
Opening a probe
from pycosmicsdk import DebugProbe, list_debug_probes
for found in list_debug_probes():
print(found.serial_number, found.path)
with DebugProbe.open(serial="3BDA1AEC98B43363374253A2CA79B756") as probe:
info = probe.info
print(f"fw {info.fw_major}.{info.fw_minor}.{info.fw_patch}")
open() without arguments takes the first probe found. It raises
UnsupportedFirmwareError when the probe answered but
runs firmware older than v1.0. It raises
DeviceNotFoundError when nothing matched, when another
process holds the probe, or when this user cannot open it.
Probe-wide calls live on the probe: info (cached at open; fetch_device_info() reads it again), settings(), mode() /
set_mode(), pin_status(), swd_coexist_status(), enter_boot_mode().
Each function of the probe is a sub-object, returned by a call as on a
Device:
Call |
Returns |
What it does |
|---|---|---|
|
Switch the target rail, read voltage, current and power |
|
|
SWD: attach, halt, step, run, registers, memory, breakpoints |
|
|
SPI controller, with |
|
|
SPI NOR: detect, read, erase, program, verify |
|
|
The bridge’s serial port name and saved line settings |
Target power
J4’s I/O runs at the probe’s signal voltage, and the probe can also supply the target on pin 1. Switch the signal voltage on before an SWD or SPI session: it powers the level shifters on J4.
On fw-atira 1.1 and newer (the firmware 1.9.0 pairs with) the two are separate
switches. Decide with signal_voltage_check(), which reads pin 1 and changes
nothing:
from pycosmicsdk.debugprobe import SignalVoltageState
power = probe.power()
check = power.signal_voltage_check()
if check.state == SignalVoltageState.NO_SUPPLY: # pin 1 dead: the probe powers the target
power.set_signal_voltage_level(3300)
power.set_signal_voltage_enabled(True)
power.set_target_power_enabled(True)
else: # self-powered: match its supply, no target power
power.signal_voltage_use_external()
reading = power.reading()
print(reading.voltage_mv, "mV", reading.current_ua, "uA", reading.power_mw, "mW")
atira_power_on() in examples/helpers.py is this logic plus the fw 1.0.x path;
the examples use it. Current is in µA because a sleeping target draws well under
1 mA. Decide “self-powered” with signal_voltage_check(), not with one
reading(), which lags 300-600 ms after a switch-off.
The signal goes on before target power, so for a moment the I/O drives a target
that has no supply yet. That matters on boards without input protection.
set_target_power_enabled(True) normally answers at once, but while pin 1 still
holds charge from an earlier switch-off the firmware re-reads it until it stops
falling, and the reply can take up to 0.5 s with the signal already on. Switch on
a while after the last off.
fw-atira 1.0.x
One switch, set_output, turns the signal voltage and 3.3 V on pin 1 on
together. Each generation’s calls raise DEBUGPROBE_POWER_USE_* on the other.
import time
power.set_output(True)
time.sleep(0.5) # the reading lags the switch by about half a second
Warning
On fw-atira 1.0.x, do not switch it on when the target has its own supply wired to J4 pin 1: two supplies end up in parallel. Leave pin 1 unconnected on a self-powered board, or update the probe to fw-atira 1.1.
Flashing the target
probe.target_flash() programs the target’s own flash through the vendor’s
CMSIS flash algorithm (.FLM), from a .pack or a bare .FLM with its RAM
window. It needs an attached session, and leaves the target halted.
from pycosmicsdk import TargetFlashVerifyError
from pycosmicsdk.debugprobe import TargetFlash, TargetFlashPackSupport
pack = "Keil.STM32H5xx_DFP.2.3.1.pack"
dev = next(d for d in TargetFlash.list_pack_devices(pack) if d.name == "STM32H503RBTx")
if dev.support == TargetFlashPackSupport.NOT_SUPPORTED:
raise SystemExit(dev.reason)
if dev.support == TargetFlashPackSupport.NEEDS_CONNECT_STEPS:
# The SDK skips these vendor steps. Pass them only after the user accepts that.
raise SystemExit(f"{dev.name} needs vendor connect steps {dev.connect_steps}: confirm first")
probe.debug().attach()
flash = probe.target_flash()
info = flash.load_pack_device(pack, dev.name, confirmed_connect_steps=[]) # FLASHABLE
try:
flash.write(info.flash_base, open("app.bin", "rb").read(), verify=True)
except TargetFlashVerifyError as e:
print(f"mismatch at 0x{e.first_mismatch:08X}")
probe.debug().run()
confirmed_connect_steps has no default. A FLASHABLE part takes an empty list.
A NEEDS_CONNECT_STEPS part lists vendor connect steps the SDK does not run:
pass dev.connect_steps only once the user has accepted that, as MC3 asks each
time. erase, erase_chip,
program and verify run one step each; last_report() says what the last
operation did. Only the STM32H503 is verified on hardware.
SPI and SPI NOR flash
On the Atira, SPI uses J4 with its own pin assignment. The SWD names on a debug cable do not apply:
Flash pin |
J4 pin |
|---|---|
/CS |
2 (fw-atira 1.1; 9 on 1.0.x) |
CLK |
4 |
DI (MOSI) |
6 |
DO (MISO) |
8 |
VCC |
1 (the target rail) |
GND |
3 |
Tie /WP and /HOLD to VCC if the flash breakout does not. The chip select is
CS_0 on fw-atira 1.1 (CS_2 on 1.0.x), and the clock tops out at 8 MHz. Power
J4 first (see Target power above).
from pycosmicsdk import FlashVerifyMethod, SpiTransport
from pycosmicsdk.enums import (
SpiBitOrder, SpiChipSelect, SpiChipSelectPolarity, SpiDataWidth, SpiMode,
)
spi = probe.spi().initialize(
bit_order=SpiBitOrder.MSB,
mode=SpiMode.MODE_0,
data_width=SpiDataWidth.BITS_8,
chip_select=SpiChipSelect.CS_0, # J4 pin 2 on fw-atira 1.1
chip_select_polarity=SpiChipSelectPolarity.ACTIVE_LOW,
frequency_hz=8_000_000,
)
print(spi.transfer(b"\x9f" + bytes(3))[1:].hex()) # JEDEC id
image = open("firmware.bin", "rb").read()
flash = probe.flash()
part = flash.detect()
flash.erase(0x10_0000, len(image))
flash.program(0x10_0000, image)
check = flash.verify(0x10_0000, image, method=FlashVerifyMethod.HASH)
if not check.passed:
print(f"first mismatch at 0x{check.first_mismatch:X}")
spi.release()
program() uses the faster bulk interface when the host can open it and falls
back to HID otherwise; flash.last_transport says which one ran. Pass
transport=SpiTransport.HID or SpiTransport.BULK to choose. verify() and
blank_check() both return FlashCheck. A HASH verify
compares a CRC-32 computed on the probe and reads the flash back only to locate
a mismatch.
UART bridge
The bridge is an ordinary USB serial port. Open it with any serial library; the probe does not proxy the bytes.
port = probe.uart().bridge_port # e.g. /dev/ttyACM0 or COM7; empty if not found
uart().config() and uart().configure() read and save the bridge settings
the probe boots with. The line coding in use is the one the host sets when it
opens the port.
Asyncio
AsyncDebugProbe mirrors every call with await,
including the sub-objects:
async with AsyncDebugProbe.open() as probe:
power = probe.power()
await power.set_signal_voltage_level(3300) # fw 1.1, unpowered target
await power.set_signal_voltage_enabled(True)
await power.set_target_power_enabled(True)
state = await probe.debug().attach()
The probe answers one command at a time, so calls on one probe never overlap.
Examples
Runnable scripts under examples/blocking_api/debugprobe/ and
examples/async_api/debugprobe/:
get_probe_info.py: identity, supply state and pin ownership. Read-only.debug_halt_and_read.py: attach, halt, registers, the vector table, a step and a breakpoint. Powers J4 withatira_power_on()(fw-atira 1.0.x or 1.1, unpowered or self-powered target).flash_program_and_verify.py: detect, erase, program and verify 64 KiB at 1 MiB. Needs a W25Q on J4 andPYCOSMICSDK_ATIRA_SPI_FLASH=W25Q.
The API reference is Debug probe.