Skip to content

Target Control

The Target subsystem controls the execution state of the debug target — halting, resuming, single-stepping, resetting, and querying the current state. Access it through session.target.

Most target operations return a TargetState, a frozen dataclass with three fields:

@dataclass(frozen=True)
class TargetState:
name: str
state: Literal["running", "halted", "reset", "debug-running", "unknown"]
current_pc: int | None = None
FieldDescription
nameTarget name as reported by OpenOCD (e.g. "stm32f1x.cpu")
stateCurrent execution state
current_pcProgram counter value when halted, None otherwise

The current_pc is only populated when state is "halted". If the target is halted but the PC cannot be read for any reason, current_pc will be None and a debug log message is emitted.

Call state() to get the current execution state without changing it:

async with Session.connect() as ocd:
state = await ocd.target.state()
print(f"Target: {state.name}")
print(f"State: {state.state}")
if state.state == "halted" and state.current_pc is not None:
print(f"PC: 0x{state.current_pc:08X}")

Internally, state() sends the targets command to OpenOCD and parses the tabular output using a regex. The output looks like:

TargetName Type Endian TapName State
-- ------------------ ---------- ------ ------------------ -----
0* stm32f1x.cpu cortex_m little stm32f1x.cpu halted

halt() stops the target and returns the resulting state:

state = await ocd.target.halt()
print(f"Halted at PC=0x{state.current_pc:08X}")

If the target is already halted, halt() succeeds silently (OpenOCD reports “already halted”, which the library does not treat as an error). A TargetError is raised only if the halt command actually fails.

resume() starts the target running from the current PC, or from a specific address:

# Resume from current PC
await ocd.target.resume()
# Resume from a specific address
await ocd.target.resume(address=0x08000000)

resume() returns None. If you need to verify the target started running, call state() afterward.

step() executes one instruction and returns the resulting state:

state = await ocd.target.step()
print(f"Stepped to PC=0x{state.current_pc:08X}")
# Step from a specific address
state = await ocd.target.step(address=0x08001000)

Like halt(), step() returns a TargetState with the updated program counter.

reset() issues a target reset with one of three modes:

ModeBehavior
"halt"Reset and halt at the reset vector (default)
"run"Reset and immediately resume execution
"init"Reset and run OpenOCD init scripts
# Reset and halt (most common for debugging)
await ocd.target.reset(mode="halt")
# Reset and run (for production flash-and-go)
await ocd.target.reset(mode="run")
# Reset and run init scripts
await ocd.target.reset(mode="init")

reset() returns None. After a reset("halt"), the target should be halted at the reset vector. Query state() to confirm and read the PC.

wait_halt() blocks until the target halts or the timeout expires. This is useful after setting a breakpoint and resuming:

await ocd.target.resume()
try:
state = await ocd.target.wait_halt(timeout_ms=5000)
print(f"Target halted at PC=0x{state.current_pc:08X}")
except TimeoutError:
print("Target did not halt within 5 seconds")
ParameterDefaultDescription
timeout_ms5000Maximum wait time in milliseconds

The timeout is enforced by OpenOCD (the wait_halt command), not by the Python library. A TimeoutError is raised if the response contains “timed out” or “time out”. Any other error response raises a TargetError.

A typical debug workflow that halts, inspects, modifies, and resumes:

import asyncio
from openocd import Session, TargetNotHaltedError, TimeoutError
async def debug_session():
async with Session.connect() as ocd:
# Reset and halt at the reset vector
await ocd.target.reset(mode="halt")
state = await ocd.target.state()
print(f"Reset vector: 0x{state.current_pc:08X}")
# Step through the first 5 instructions
for i in range(5):
state = await ocd.target.step()
print(f" Step {i+1}: PC=0x{state.current_pc:08X}")
# Set a breakpoint and run to it
await ocd.breakpoints.add(0x08001000)
await ocd.target.resume()
try:
state = await ocd.target.wait_halt(timeout_ms=3000)
print(f"Hit breakpoint at PC=0x{state.current_pc:08X}")
except TimeoutError:
print("Breakpoint not hit, halting manually")
await ocd.target.halt()
# Clean up breakpoint and resume
await ocd.breakpoints.remove(0x08001000)
await ocd.target.resume()
asyncio.run(debug_session())
MethodReturnsDescription
state()TargetStateQuery current state without changing it
halt()TargetStateHalt the target
resume(address=None)NoneResume execution
step(address=None)TargetStateSingle-step one instruction
reset(mode="halt")NoneReset the target
wait_halt(timeout_ms=5000)TargetStateBlock until target halts
ExceptionWhen
TargetErrorAny target command fails (halt, resume, step, reset)
TimeoutErrorwait_halt exceeds its deadline