Skip to content

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:

# Async
session.swd
# Sync
sync_session.swd

SWD and JTAG are the two debug transport protocols supported by OpenOCD. The key architectural difference:

JTAGSWD
ModelScan chain: TAPs, IR/DR shiftsDAP-centric: DP + AP registers
TopologyDaisy-chained TAPsPoint-to-point (one DAP)
PinsTDI, TDO, TCK, TMS (+ TRST)SWDIO, SWCLK (2 pins)
Subsystemsession.jtagsession.swd
OpenOCD modelGlobal 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.

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

info() queries the DAP for identification and topology. Returns a DAPInfo with the DPIDR value and discovered AP count.

import asyncio
from 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())

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}")

Two commonly-needed DP registers have dedicated methods:

# DP IDR (address 0x0) — identifies the debug port
dpidr = await session.swd.dpidr()
print(f"DPIDR: 0x{dpidr:08X}")
# TARGETID (address 0x24, DPv2+) — identifies the target device
target_id = await session.swd.target_id()
print(f"TARGETID: 0x{target_id:08X}")

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)

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}")

A typical STM32F1 output:

AP #0: MEM-AP
IDR: 0x04770031
BASE: 0xE00FF003

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.

Returned by swd.info().

FieldTypeDescription
namestrDAP instance name (e.g. stm32f1x.dap)
dpidrintDP ID Register value
ap_countintNumber of APs found in the dap info output
raw_infostrFull dap info output for detailed parsing

Returned by swd.list_aps().

FieldTypeDescription
indexintAP number (0, 1, 2…)
idrintAP ID Register (from offset 0xFC)
baseintROM table base address (from offset 0xF8)
ap_typestr"MEM-AP", "JTAG-AP", or "unknown"

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
MethodReturn TypeDescription
info(dap=None)DAPInfoQuery DAP identification and topology
list_aps(dap=None)list[APInfo]Enumerate Access Ports
dpreg(address, value=None, *, dap=None)intRead or write a DP register
apreg(ap, address, value=None, *, dap=None)intRead or write an AP register
dpidr(dap=None)intRead the DP IDR (address 0x0)
target_id(dap=None)intRead TARGETID (DP address 0x24)