Skip to content

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.rtt

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.

The typical RTT flow is: setup (configure search parameters), then start (find the control block and activate channels), then read/write, then stop.

setup(address, size, id_string="SEGGER RTT") tells OpenOCD where to look for the RTT control block in target RAM.

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

Parameters:

  • address — start address of the RAM region to search
  • size — size of the search region in bytes
  • id_string — RTT control block identifier (default "SEGGER 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().

stop() deactivates RTT communication.

await session.rtt.stop()

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)

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()

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 respond
await asyncio.sleep(0.1)
response = await session.rtt.read(0)
print(response)
import asyncio
from 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())
FieldTypeDescription
indexintChannel number
namestrChannel name (e.g. Terminal)
sizeintBuffer size in bytes
directionLiteral["up", "down"]up = target-to-host, down = host-to-target

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
MethodReturn TypeDescription
setup(address, size, id_string="SEGGER RTT")NoneConfigure control block search
start()NoneFind control block, activate channels
stop()NoneDeactivate RTT
channels()list[RTTChannel]List discovered channels
read(channel)strRead from an up-channel
write(channel, data)NoneWrite to a down-channel