JTAG Operations
The JTAGController provides direct access to the JTAG interface: chain discovery, register scanning, TAP state machine control, and boundary scan file execution. It acts as a facade that delegates to specialized submodules (chain, scan, state, boundary).
Access it through the session:
# Asyncsession.jtag
# Syncsync_session.jtagScan chain discovery
Section titled “Scan chain discovery”scan_chain() queries OpenOCD for every TAP (Test Access Port) on the JTAG chain and returns them as TAPInfo dataclasses.
import asynciofrom openocd import Session
async def main(): async with await Session.connect() as session: taps = await session.jtag.scan_chain() for tap in taps: print(f"TAP: {tap.name}") print(f" Chip: {tap.chip}, TAP name: {tap.tap_name}") print(f" IDCODE: 0x{tap.idcode:08X}") print(f" IR length: {tap.ir_length} bits") print(f" Enabled: {tap.enabled}")
asyncio.run(main())from openocd import Session
with Session.connect_sync() as session: taps = session.jtag.scan_chain() for tap in taps: print(f"TAP: {tap.name}") print(f" Chip: {tap.chip}, TAP name: {tap.tap_name}") print(f" IDCODE: 0x{tap.idcode:08X}") print(f" IR length: {tap.ir_length} bits") print(f" Enabled: {tap.enabled}")A typical STM32 output:
TAP: stm32f1x.cpu Chip: stm32f1x, TAP name: cpu IDCODE: 0x3BA00477 IR length: 4 bits Enabled: TrueAdding a new TAP
Section titled “Adding a new TAP”new_tap(chip, tap, ir_len, expected_id=None) declares a new TAP on the chain. This is typically done before scan chain initialization, but can be useful when dynamically configuring multi-device chains.
async with await Session.connect() as session: # Declare a TAP with known IDCODE await session.jtag.new_tap( chip="fpga", tap="bs", ir_len=6, expected_id=0x0362D093 )
# Declare a TAP without IDCODE verification await session.jtag.new_tap(chip="cpld", tap="cpu", ir_len=8)with Session.connect_sync() as session: session.jtag.new_tap( chip="fpga", tap="bs", ir_len=6, expected_id=0x0362D093 )Scan operations
Section titled “Scan operations”Instruction register scan
Section titled “Instruction register scan”irscan(tap, instruction) shifts an instruction into a TAP’s instruction register (IR) and returns the value shifted out.
async with await Session.connect() as session: # Select the IDCODE instruction (commonly 0x0E on ARM DAPs) shifted_out = await session.jtag.irscan("stm32f1x.cpu", 0x0E) print(f"IR shifted out: 0x{shifted_out:X}")with Session.connect_sync() as session: shifted_out = session.jtag.irscan("stm32f1x.cpu", 0x0E) print(f"IR shifted out: 0x{shifted_out:X}")Data register scan
Section titled “Data register scan”drscan(tap, bits, value) shifts a value of the specified bit width through the data register (DR) and returns the captured output.
async with await Session.connect() as session: # Read IDCODE: shift 32 bits through DR after selecting IDCODE via IR await session.jtag.irscan("stm32f1x.cpu", 0x0E) idcode = await session.jtag.drscan("stm32f1x.cpu", 32, 0x0) print(f"IDCODE: 0x{idcode:08X}")with Session.connect_sync() as session: session.jtag.irscan("stm32f1x.cpu", 0x0E) idcode = session.jtag.drscan("stm32f1x.cpu", 32, 0x0) print(f"IDCODE: 0x{idcode:08X}")Run-Test/Idle clocking
Section titled “Run-Test/Idle clocking”runtest(cycles) clocks the specified number of TCK pulses while the TAP controller is in the Run-Test/Idle state. Some devices require idle clocking between operations.
# Clock 100 TCK cycles in Run-Test/Idleawait session.jtag.runtest(100)The cycle count must be non-negative; passing a negative value raises JTAGError.
TAP state machine control
Section titled “TAP state machine control”pathmove(states) walks the TAP controller through an explicit sequence of IEEE 1149.1 states. Each state must be a legal single-step transition from the previous one. OpenOCD validates the path and reports an error for illegal transitions.
from openocd import Session, JTAGState
async with await Session.connect() as session: await session.jtag.pathmove([ JTAGState.DRSELECT, JTAGState.DRCAPTURE, JTAGState.DRSHIFT, ])from openocd import Session, JTAGState
with Session.connect_sync() as session: session.jtag.pathmove([ JTAGState.DRSELECT, JTAGState.DRCAPTURE, JTAGState.DRSHIFT, ])The list must contain at least one state. An empty list raises JTAGError.
JTAGState enum
Section titled “JTAGState enum”The JTAGState enum defines all 16 IEEE 1149.1 TAP controller states:
| State | Description |
|---|---|
RESET | Test-Logic-Reset |
IDLE | Run-Test/Idle |
DRSELECT | Select-DR-Scan |
DRCAPTURE | Capture-DR |
DRSHIFT | Shift-DR |
DREXIT1 | Exit1-DR |
DRPAUSE | Pause-DR |
DREXIT2 | Exit2-DR |
DRUPDATE | Update-DR |
IRSELECT | Select-IR-Scan |
IRCAPTURE | Capture-IR |
IRSHIFT | Shift-IR |
IREXIT1 | Exit1-IR |
IRPAUSE | Pause-IR |
IREXIT2 | Exit2-IR |
IRUPDATE | Update-IR |
JTAGState is a str enum, so JTAGState.IDLE.value returns the string "IDLE".
Boundary scan files
Section titled “Boundary scan files”SVF execution
Section titled “SVF execution”svf(path, tap=None, quiet=False, progress=True) executes a Serial Vector Format file. SVF files describe JTAG test vectors and are commonly used for FPGA configuration, board-level test, and CPLD programming.
from pathlib import Path
async with await Session.connect() as session: await session.jtag.svf( path=Path("board_test.svf"), tap="fpga.bs", quiet=False, progress=True, )from pathlib import Path
with Session.connect_sync() as session: session.jtag.svf( path=Path("board_test.svf"), tap="fpga.bs", quiet=False, progress=True, )Parameters:
path— path to the.svffiletap— restrict operations to a specific TAP (optional; whenNone, OpenOCD applies vectors to the appropriate TAP)quiet— suppress per-statement logging inside OpenOCDprogress— show a progress indicator (defaultTrue)
XSVF execution
Section titled “XSVF execution”xsvf(tap, path) executes a Xilinx-extended SVF file against a specific TAP.
from pathlib import Path
await session.jtag.xsvf("cpld.bs", Path("config.xsvf"))Data types
Section titled “Data types”TAPInfo
Section titled “TAPInfo”| Field | Type | Description |
|---|---|---|
name | str | Full TAP name (e.g. stm32f1x.cpu) |
chip | str | Chip portion of the name |
tap_name | str | TAP portion of the name |
idcode | int | Detected IDCODE |
ir_length | int | Instruction register length in bits |
enabled | bool | Whether the TAP is enabled |
Error handling
Section titled “Error handling”All JTAG operations raise JTAGError on failure.
from openocd import JTAGError
try: await session.jtag.irscan("nonexistent.tap", 0x0E)except JTAGError as e: print(f"JTAG error: {e}")Method reference
Section titled “Method reference”| Method | Return Type | Description |
|---|---|---|
scan_chain() | list[TAPInfo] | Enumerate TAPs on the chain |
new_tap(chip, tap, ir_len, expected_id=None) | None | Declare a new TAP |
irscan(tap, instruction) | int | Shift instruction into IR |
drscan(tap, bits, value) | int | Shift data through DR |
runtest(cycles) | None | Clock TCK in Run-Test/Idle |
pathmove(states) | None | Walk TAP through state sequence |
svf(path, tap=None, quiet=False, progress=True) | None | Execute SVF file |
xsvf(tap, path) | None | Execute XSVF file |