SWD Operations
The SWDController provides access to SWD (Serial Wire Debug) operations through the DAP (Debug Access Port) interface: DAP discovery, DP register reads/writes, AP register reads/writes, and AP enumeration. Access it through the session:
# Asyncsession.swd
# Syncsync_session.swdSWD vs JTAG
Section titled “SWD vs JTAG”SWD and JTAG are the two debug transport protocols supported by OpenOCD. The key architectural difference:
| JTAG | SWD | |
|---|---|---|
| Model | Scan chain: TAPs, IR/DR shifts | DAP-centric: DP + AP registers |
| Topology | Daisy-chained TAPs | Point-to-point (one DAP) |
| Pins | TDI, TDO, TCK, TMS (+ TRST) | SWDIO, SWCLK (2 pins) |
| Subsystem | session.jtag | session.swd |
| OpenOCD model | Global commands (scan_chain, irscan) | Named DAP objects (<dap> dpreg, <dap> apreg) |
Both transports share the higher-level subsystems (target, memory, registers, flash, etc.) — those work identically regardless of transport. The transport-specific subsystems expose the wire-level protocol details.
DAP architecture
Section titled “DAP architecture”An ARM CoreSight debug system has a Debug Port (DP) connected to one or more Access Ports (APs):
graph TD DP["<b>Debug Port (DP)</b><br/>DPIDR · TARGETID · CTRL/STAT"] AP0["<b>AP #0</b> — MEM-AP<br/>AHB / APB / AXI bus access"] AP1["<b>AP #1</b> — JTAG-AP<br/>Additional MEM-AP, etc."] APn["..."] DP --> AP0 DP --> AP1 DP --> APn
- DP registers (address 0x0 - 0x24): Debug port identification and control
- AP registers (per-AP, address 0x00 - 0xFC): Access port configuration and data transfer
DAP discovery
Section titled “DAP discovery”info() queries the DAP for identification and topology. Returns a DAPInfo with the DPIDR value and discovered AP count.
import asynciofrom openocd import Session
async def main(): async with await Session.connect() as session: info = await session.swd.info() print(f"DAP: {info.name}") print(f" DPIDR: 0x{info.dpidr:08X}") print(f" Access Ports: {info.ap_count}")
asyncio.run(main())from openocd import Session
with Session.connect_sync() as session: info = session.swd.info() print(f"DAP: {info.name}") print(f" DPIDR: 0x{info.dpidr:08X}") print(f" Access Ports: {info.ap_count}")DP register access
Section titled “DP register access”dpreg(address, value=None) reads or writes Debug Port registers. When value is None, it reads. When provided, it writes.
async with await Session.connect() as session: # Read DPIDR (DP address 0x0) dpidr = await session.swd.dpreg(0x0) print(f"DPIDR: 0x{dpidr:08X}")
# Read CTRL/STAT (DP address 0x4) ctrl_stat = await session.swd.dpreg(0x4) print(f"CTRL/STAT: 0x{ctrl_stat:08X}")with Session.connect_sync() as session: dpidr = session.swd.dpreg(0x0) print(f"DPIDR: 0x{dpidr:08X}")Convenience methods
Section titled “Convenience methods”Two commonly-needed DP registers have dedicated methods:
# DP IDR (address 0x0) — identifies the debug portdpidr = await session.swd.dpidr()print(f"DPIDR: 0x{dpidr:08X}")
# TARGETID (address 0x24, DPv2+) — identifies the target devicetarget_id = await session.swd.target_id()print(f"TARGETID: 0x{target_id:08X}")AP register access
Section titled “AP register access”apreg(ap, address, value=None) reads or writes Access Port registers. The ap parameter is the AP index (0, 1, 2…).
async with await Session.connect() as session: # Read AP #0 IDR (identifies the AP type) ap_idr = await session.swd.apreg(0, 0xFC) print(f"AP #0 IDR: 0x{ap_idr:08X}")
# Read AP #0 BASE (ROM table address) ap_base = await session.swd.apreg(0, 0xF8) print(f"AP #0 BASE: 0x{ap_base:08X}")
# Write AP CSW register (configure transfer parameters) await session.swd.apreg(0, 0x00, value=0x23000052)with Session.connect_sync() as session: ap_idr = session.swd.apreg(0, 0xFC) print(f"AP #0 IDR: 0x{ap_idr:08X}")AP enumeration
Section titled “AP enumeration”list_aps() probes the DAP to discover all Access Ports by reading their IDR registers. Returns a list of APInfo dataclasses.
async with await Session.connect() as session: aps = await session.swd.list_aps() for ap in aps: print(f"AP #{ap.index}: {ap.ap_type}") print(f" IDR: 0x{ap.idr:08X}") print(f" BASE: 0x{ap.base:08X}")with Session.connect_sync() as session: aps = session.swd.list_aps() for ap in aps: print(f"AP #{ap.index}: {ap.ap_type}") print(f" IDR: 0x{ap.idr:08X}") print(f" BASE: 0x{ap.base:08X}")A typical STM32F1 output:
AP #0: MEM-AP IDR: 0x04770031 BASE: 0xE00FF003Multi-DAP boards
Section titled “Multi-DAP boards”Most boards have a single DAP, and the SWDController auto-discovers it via dap names. For multi-DAP boards (e.g. STM32H7 dual-core with separate DAPs for M7 and M4), pass the DAP name explicitly:
# Auto-discover (single-DAP boards)dpidr = await session.swd.dpidr()
# Explicit DAP name (multi-DAP boards)m7_dpidr = await session.swd.dpidr(dap="stm32h7x.m7.dap")m4_info = await session.swd.info(dap="stm32h7x.m4.dap")Every SWDController method accepts an optional dap keyword argument. When omitted, the controller caches the result of dap names and reuses the first discovered DAP.
Data types
Section titled “Data types”DAPInfo
Section titled “DAPInfo”Returned by swd.info().
| Field | Type | Description |
|---|---|---|
name | str | DAP instance name (e.g. stm32f1x.dap) |
dpidr | int | DP ID Register value |
ap_count | int | Number of APs found in the dap info output |
raw_info | str | Full dap info output for detailed parsing |
APInfo
Section titled “APInfo”Returned by swd.list_aps().
| Field | Type | Description |
|---|---|---|
index | int | AP number (0, 1, 2…) |
idr | int | AP ID Register (from offset 0xFC) |
base | int | ROM table base address (from offset 0xF8) |
ap_type | str | "MEM-AP", "JTAG-AP", or "unknown" |
Error handling
Section titled “Error handling”All SWD/DAP operations raise SWDError on failure.
from openocd import SWDError
try: dpidr = await session.swd.dpidr()except SWDError as e: print(f"SWD error: {e}")Common failure causes:
- No DAP found (transport not set to SWD)
- Target powered off or not connected
- Invalid AP index
- DP/AP register read returns no data
Method reference
Section titled “Method reference”| Method | Return Type | Description |
|---|---|---|
info(dap=None) | DAPInfo | Query DAP identification and topology |
list_aps(dap=None) | list[APInfo] | Enumerate Access Ports |
dpreg(address, value=None, *, dap=None) | int | Read or write a DP register |
apreg(ap, address, value=None, *, dap=None) | int | Read or write an AP register |
dpidr(dap=None) | int | Read the DP IDR (address 0x0) |
target_id(dap=None) | int | Read TARGETID (DP address 0x24) |