Skip to content

Memory Operations

The Memory subsystem provides typed reads and writes at 8, 16, 32, and 64-bit widths, plus raw byte access, hexdump formatting, memory search, and file dump utilities. Access it through session.memory.

All memory operations use OpenOCD’s read_memory and write_memory TCL commands, which provide reliable structured I/O.

Read one or more values at a specific width. All read methods return a list[int].

async def read_u8(addr: int, count: int = 1) -> list[int]
async def read_u16(addr: int, count: int = 1) -> list[int]
async def read_u32(addr: int, count: int = 1) -> list[int]
async def read_u64(addr: int, count: int = 1) -> list[int]
async with Session.connect() as ocd:
# Read a single 32-bit word
values = await ocd.memory.read_u32(0x08000000)
stack_pointer = values[0]
print(f"Initial SP: 0x{stack_pointer:08X}")
# Read 8 consecutive 32-bit words (vector table)
vectors = await ocd.memory.read_u32(0x08000000, count=8)
for i, v in enumerate(vectors):
print(f" Vector[{i}] = 0x{v:08X}")
# Read 16-bit values (useful for Thumb disassembly)
instructions = await ocd.memory.read_u16(0x08001000, count=4)
# Read individual bytes
header = await ocd.memory.read_u8(0x20000000, count=16)

For bulk data, read_bytes() returns raw bytes:

async def read_bytes(addr: int, size: int) -> bytes
async with Session.connect() as ocd:
data = await ocd.memory.read_bytes(0x08000000, 1024)
print(f"Read {len(data)} bytes")
print(f"First 4 bytes: {data[:4].hex()}")

Internally, read_bytes reads using 8-bit width and converts the result to a bytes object.

Write one or more values at a specific width. Accepts either a single integer or a list of integers.

async def write_u8(addr: int, values: int | list[int]) -> None
async def write_u16(addr: int, values: int | list[int]) -> None
async def write_u32(addr: int, values: int | list[int]) -> None
async with Session.connect() as ocd:
# Write a single 32-bit word
await ocd.memory.write_u32(0x20000000, 0xDEADBEEF)
# Write multiple 32-bit words
await ocd.memory.write_u32(0x20000000, [0x11111111, 0x22222222, 0x33333333])
# Write 16-bit values
await ocd.memory.write_u16(0x20001000, [0x1234, 0x5678])
# Write individual bytes
await ocd.memory.write_u8(0x20002000, [0x48, 0x65, 0x6C, 0x6C, 0x6F])

For bulk data, write_bytes() accepts a bytes object:

async def write_bytes(addr: int, data: bytes) -> None
async with Session.connect() as ocd:
payload = b"Hello, target!"
await ocd.memory.write_bytes(0x20000000, payload)

hexdump() reads memory and returns a formatted string with hex and ASCII columns, 16 bytes per line:

async def hexdump(addr: int, size: int) -> str
async with Session.connect() as ocd:
dump = await ocd.memory.hexdump(0x08000000, 64)
print(dump)

Output format:

08000000: 00 50 00 20 A1 01 00 08 AB 01 00 08 AD 01 00 08 |.P. ............|
08000010: AF 01 00 08 B1 01 00 08 B3 01 00 08 00 00 00 00 |................|
08000020: 00 00 00 00 00 00 00 00 00 00 00 00 B5 01 00 08 |................|
08000030: B7 01 00 08 00 00 00 00 B9 01 00 08 BB 01 00 08 |................|

Each line shows:

  • Address (8 hex digits)
  • Hex bytes in two groups of 8, separated by a gap
  • ASCII representation (non-printable bytes displayed as .)

search() scans a memory range for a byte pattern and returns all matching addresses:

async def search(pattern: bytes, start: int, end: int) -> list[int]
async with Session.connect() as ocd:
# Search for a magic number in flash
matches = await ocd.memory.search(
b"\xDE\xAD\xBE\xEF",
start=0x08000000,
end=0x08020000,
)
for addr in matches:
print(f" Found at 0x{addr:08X}")

The search reads memory in 4096-byte chunks with an overlap of len(pattern) - 1 bytes to handle patterns that span chunk boundaries. This is a client-side search since OpenOCD has no native memory search command.

dump() reads memory and writes the raw bytes to a file:

async def dump(addr: int, size: int, path: Path) -> None
from pathlib import Path
async with Session.connect() as ocd:
# Dump the first 128KB of flash to a file
await ocd.memory.dump(0x08000000, 128 * 1024, Path("flash_dump.bin"))

Under the hood, all memory operations use the OpenOCD TCL RPC read_memory and write_memory commands:

read_memory 0x08000000 32 4 -> "00500020 080001A1 080001AB 080001AD"
write_memory 0x20000000 32 {0xDEADBEEF}

The response from read_memory is a space-separated list of hex values. The library parses these into Python integers. If the response contains “error”, a TargetError is raised.

MethodReturnsDescription
read_u8(addr, count=1)list[int]Read 8-bit values
read_u16(addr, count=1)list[int]Read 16-bit values
read_u32(addr, count=1)list[int]Read 32-bit values
read_u64(addr, count=1)list[int]Read 64-bit values
read_bytes(addr, size)bytesRead raw bytes
write_u8(addr, values)NoneWrite 8-bit values
write_u16(addr, values)NoneWrite 16-bit values
write_u32(addr, values)NoneWrite 32-bit values
write_bytes(addr, data)NoneWrite raw bytes
search(pattern, start, end)list[int]Search for byte pattern
dump(addr, size, path)NoneDump memory to file
hexdump(addr, size)strFormatted hex+ASCII dump

All memory operations raise TargetError if the OpenOCD command fails (e.g., target not halted, invalid address, bus fault).