Skip to content
binho.io (opens in a new tab)Contact Us

Binho Atira: Protocols

Examples of using the Binho Atira for SWD debug, SPI flash programming and the UART bridge, and of flashing a target with pyOCD.

Install the SDK:

pip install "pycosmicsdk>=1.7.0"

On Linux, install the udev rule first; see Binho Atira hardware setup.

from pycosmicsdk import DebugProbe, list_debug_probes
for found in list_debug_probes():
print(found.serial_number, found.path)
with DebugProbe.open() as probe: # first probe found; or open(serial="...")
info = probe.info
print(f"Serial: {probe.serial_number}")
print(f"Firmware: {info.fw_major}.{info.fw_minor}.{info.fw_patch}")

open() raises UnsupportedFirmwareError when the probe runs firmware older than 1.0, and DeviceNotFoundError when no probe matched, another program holds it, or the current user has no access to it.

import time
from pycosmicsdk import DebugProbe
with DebugProbe.open() as probe:
power = probe.power()
power.set_output(True)
time.sleep(0.5) # the reading lags the switch by ~0.5 s
r = power.reading()
print(f"{r.voltage_mv} mV, {r.current_ua / 1000:.2f} mA, {r.power_mw} mW")
print("rail on:", power.output())

SWD: attach, halt, registers, memory, breakpoint

Section titled “SWD: attach, halt, registers, memory, breakpoint”

Connect the target to J4 with a straight 10-pin cable.

import struct
import time
from pycosmicsdk import DebugProbe
REG_SP, REG_PC = 13, 15 # 0-12 = r0-r12, 13 = sp, 14 = lr, 15 = pc
SCB_VTOR = 0xE000_ED08 # vector table base, on every Cortex-M
with DebugProbe.open() as probe:
probe.power().set_output(True) # the SWD level shifters run from the rail
time.sleep(0.5)
debug = probe.debug()
debug.attach()
state = debug.halt()
print(f"halted at PC 0x{state.pc:08X}")
print(f"SP 0x{debug.read_register(REG_SP):08X}")
(vtor,) = struct.unpack("<I", debug.read_memory(SCB_VTOR, 4))
print("vector table:", debug.read_memory(vtor, 64).hex())
stepped = debug.step() # one instruction
bp = debug.set_breakpoint(stepped.pc) # hardware (FPB) breakpoint
debug.run()
deadline = time.monotonic() + 2.0
while not (hit := debug.state()).halted and time.monotonic() < deadline:
time.sleep(0.01)
if hit.halted:
print(f"breakpoint hit at 0x{hit.pc:08X} ({hit.stop_reason.name})")
else:
debug.halt()
debug.clear_breakpoint(bp)
debug.run()
debug.detach() # target keeps running

write_memory() writes RAM only; program flash with pyOCD (below). A read that runs past the end of a memory region raises DebugError.

Wire the flash to J4 as in J4 as an SPI flash header, with no SWD target on J4.

import time
from pycosmicsdk import DebugProbe, FlashVerifyMethod
from pycosmicsdk.enums import (
SpiBitOrder, SpiChipSelect, SpiChipSelectPolarity, SpiDataWidth, SpiMode,
)
ADDR = 0x10_0000
image = open("firmware.bin", "rb").read()
with DebugProbe.open() as probe:
probe.power().set_output(True) # powers the flash on J4 pin 1
time.sleep(0.5)
spi = probe.spi()
spi.initialize(
bit_order=SpiBitOrder.MSB,
mode=SpiMode.MODE_0,
data_width=SpiDataWidth.BITS_8,
chip_select=SpiChipSelect.CS_2, # J4 pin 9
chip_select_polarity=SpiChipSelectPolarity.ACTIVE_LOW,
frequency_hz=8_000_000, # maximum
)
try:
flash = probe.flash()
part = flash.detect()
print(f"{part.name or '(unnamed)'}: {part.capacity // 1024} KiB, page {part.page_size} B")
flash.erase(ADDR, len(image))
flash.program(ADDR, image)
print("transport:", flash.last_transport)
check = flash.verify(ADDR, image, method=FlashVerifyMethod.HASH)
if check.passed:
print("verify OK")
else:
print(f"first mismatch at 0x{check.first_mismatch:X}")
finally:
spi.release()
  • program() uses the USB bulk interface when available and falls back to HID otherwise. flash.last_transport shows which ran.
  • FlashVerifyMethod.HASH compares a CRC-32 computed on the probe and reads the flash back only to locate a mismatch. FlashVerifyMethod.READBACK reads the whole span back.
  • erase() erases whole sectors, so it can clear data on either side of the span passed.
from pycosmicsdk import DebugProbe
with DebugProbe.open() as probe:
print(probe.uart().bridge_port) # e.g. /dev/ttyACM0 or COM7; empty if not found

Open that port with any serial library, for example pySerial: serial.Serial(port, 115200). Switch the target rail on first.

AsyncDebugProbe has the same calls with await:

import asyncio
from pycosmicsdk import AsyncDebugProbe
async def main():
async with AsyncDebugProbe.open() as probe:
power = probe.power()
await power.set_output(True)
await asyncio.sleep(0.5)
r = await power.reading()
print(f"{r.voltage_mv} mV, {r.current_ua} uA")
debug = probe.debug()
await debug.attach()
state = await debug.halt()
print(f"halted at 0x{state.pc:08X}")
await debug.run()
await debug.detach()
asyncio.run(main())

The probe answers one command at a time, so calls on one probe never overlap.

#include <cstdio>
#include "debugprobe/DebugProbe.hpp"
using namespace debugprobe;
int main() {
DebugProbe probe;
auto boards = DebugProbe::listDevices();
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);
}
probe.powerSetOutput(true);
return 0;
}

Build: c++ -std=c++17 -Iinclude app.cpp -Llib -ldebugprobe -o app. More in the CosmicSDK debug probe guide.

The Atira is a standard CMSIS-DAP probe. pyOCD programs the target’s internal flash with no plugin.

  1. Close any program that holds the probe (for example an SDK script). Only one program can own the probe’s debug interface.

  2. Switch the target rail on. pyOCD does not switch it, and the rail stays on after the script exits:

    python -c "from pycosmicsdk import DebugProbe; p = DebugProbe.open(); p.power().set_output(True); p.close()"
  3. Install pyOCD and the target’s device pack, then flash. Example for an STM32H503RB:

    python -m pip install pyocd
    pyocd list # shows the probe and its serial
    pyocd pack install stm32h503rb
    pyocd flash -u <serial> -t stm32h503rbtx --base-address 0x08000000 firmware.bin
  4. Read flash back only while the core is halted:

    pyocd cmd -u <serial> -t stm32h503rbtx -c halt -c "savemem 0x08000000 131072 out.bin"

Verified: STM32H503RB on Linux with pyOCD 0.44.1, 11/11 program + verify + chip-erase cycles, about 6.5 s per 128 KiB. Other Cortex-M devices with a CMSIS pack should work the same way; they are not verified yet.