RTT Communication
The RTTManager subsystem provides access to SEGGER Real-Time Transfer (RTT), a protocol for high-speed bidirectional communication between a debug host and an embedded target. RTT uses a shared control block in target RAM rather than dedicated hardware, making it significantly faster than semihosting while requiring no additional pins.
Access it through the session:
session.rttHow RTT works
Section titled “How RTT works”RTT places a small control block in the target’s RAM that contains ring buffers for communication. The control block starts with a known identifier string (typically "SEGGER RTT") so the debugger can locate it by scanning a RAM region.
Communication flows through numbered channels:
- Up-channels (target to host): the firmware writes data that the host reads
- Down-channels (host to target): the host writes data that the firmware reads
Channel 0 is conventionally used as a terminal for printf-style logging.
Setup and lifecycle
Section titled “Setup and lifecycle”The typical RTT flow is: setup (configure search parameters), then start (find the control block and activate channels), then read/write, then stop.
Configuring the search region
Section titled “Configuring the search region”setup(address, size, id_string="SEGGER RTT") tells OpenOCD where to look for the RTT control block in target RAM.
import asynciofrom openocd import Session
async def main(): async with await Session.connect() as session: # Search for the control block in the first 8KB of SRAM await session.rtt.setup( address=0x2000_0000, size=0x2000, id_string="SEGGER RTT" )
asyncio.run(main())from openocd import Session
with Session.connect_sync() as session: # Use the raw command interface for sync RTT access session.command('rtt setup 0x20000000 0x2000 "SEGGER RTT"')Parameters:
address— start address of the RAM region to searchsize— size of the search region in bytesid_string— RTT control block identifier (default"SEGGER RTT")
Starting RTT
Section titled “Starting RTT”start() scans the configured region for the control block and activates all discovered channels.
await session.rtt.start()Raises OpenOCDError if the control block is not found. Make sure the target firmware is running and has initialized RTT before calling start().
Stopping RTT
Section titled “Stopping RTT”stop() deactivates RTT communication.
await session.rtt.stop()Discovering channels
Section titled “Discovering channels”channels() returns a list of RTTChannel descriptors after start() has been called.
async with await Session.connect() as session: await session.rtt.setup(address=0x2000_0000, size=0x2000) await session.rtt.start()
channels = await session.rtt.channels() for ch in channels: direction = "target->host" if ch.direction == "up" else "host->target" print(f"Channel {ch.index}: {ch.name} " f"(size={ch.size}, {direction})")Typical output:
Channel 0: Terminal (size=1024, target->host)Channel 0: Terminal (size=16, host->target)Reading data
Section titled “Reading data”read(channel) reads pending data from an up-channel (target to host). Returns an empty string if nothing is available.
data = await session.rtt.read(0)if data: print(f"Received: {data}")For continuous monitoring, poll in a loop:
import asyncio
async def rtt_monitor(session): await session.rtt.setup(address=0x2000_0000, size=0x2000) await session.rtt.start()
try: while True: data = await session.rtt.read(0) if data: print(data, end="", flush=True) await asyncio.sleep(0.05) # 50ms poll interval finally: await session.rtt.stop()Writing data
Section titled “Writing data”write(channel, data) sends a string to a down-channel (host to target).
await session.rtt.write(0, "help\n")
# Wait for the target to process and respondawait asyncio.sleep(0.1)response = await session.rtt.read(0)print(response)Complete example
Section titled “Complete example”import asynciofrom openocd import Session
async def main(): async with await Session.connect() as session: # Make sure the target is running (RTT needs active firmware) await session.target.reset(mode="run") await asyncio.sleep(0.5) # Let firmware initialize
# Configure and start RTT await session.rtt.setup( address=0x2000_0000, size=0x4000, id_string="SEGGER RTT" ) await session.rtt.start()
# List available channels channels = await session.rtt.channels() print(f"Found {len(channels)} RTT channels")
# Read for a few seconds for _ in range(20): data = await session.rtt.read(0) if data: print(f"[RTT] {data}", end="") await asyncio.sleep(0.1)
await session.rtt.stop()
asyncio.run(main())Data types
Section titled “Data types”RTTChannel
Section titled “RTTChannel”| Field | Type | Description |
|---|---|---|
index | int | Channel number |
name | str | Channel name (e.g. Terminal) |
size | int | Buffer size in bytes |
direction | Literal["up", "down"] | up = target-to-host, down = host-to-target |
Error handling
Section titled “Error handling”RTT operations raise OpenOCDError on failure (there is no dedicated RTT exception type).
from openocd import OpenOCDError
try: await session.rtt.start()except OpenOCDError as e: print(f"RTT failed: {e}")Common failure causes:
- Control block not found (firmware not running, wrong search address or size)
- RTT not set up before calling
start() - Channel index out of range
Method reference
Section titled “Method reference”| Method | Return Type | Description |
|---|---|---|
setup(address, size, id_string="SEGGER RTT") | None | Configure control block search |
start() | None | Find control block, activate channels |
stop() | None | Deactivate RTT |
channels() | list[RTTChannel] | List discovered channels |
read(channel) | str | Read from an up-channel |
write(channel, data) | None | Write to a down-channel |