Flash Programming
The Flash subsystem wraps OpenOCD’s flash command family for programming on-chip flash memory. It handles bank enumeration, sector-level erase, raw byte read/write through temporary files, high-level firmware image programming, verification, and write protection.
Access it through the session:
# Asyncsession.flash
# Syncsync_session.flashListing flash banks
Section titled “Listing flash banks”banks() returns a list of FlashBank descriptors for every flash bank OpenOCD has configured. These come without detailed sector information — use info() for that.
import asynciofrom openocd import Session
async def main(): async with await Session.connect() as session: banks = await session.flash.banks() for bank in banks: print(f"Bank #{bank.index}: {bank.name}") print(f" Base: 0x{bank.base:08X}, Size: 0x{bank.size:X}") print(f" Bus width: {bank.bus_width}, Chip width: {bank.chip_width}") print(f" Target: {bank.target}")
asyncio.run(main())from openocd import Session
with Session.connect_sync() as session: banks = session.flash.banks() for bank in banks: print(f"Bank #{bank.index}: {bank.name}") print(f" Base: 0x{bank.base:08X}, Size: 0x{bank.size:X}") print(f" Bus width: {bank.bus_width}, Chip width: {bank.chip_width}") print(f" Target: {bank.target}")Getting bank details with sectors
Section titled “Getting bank details with sectors”info(bank=0) returns a FlashBank with its sectors list populated. Each sector is a FlashSector with index, offset, size, and protection status.
async with await Session.connect() as session: bank = await session.flash.info(0) print(f"{bank.name}: {len(bank.sectors)} sectors") for sector in bank.sectors: prot = "protected" if sector.protected else "unprotected" print(f" Sector {sector.index}: offset=0x{sector.offset:X}, " f"size=0x{sector.size:X}, {prot}")with Session.connect_sync() as session: bank = session.flash.info(0) print(f"{bank.name}: {len(bank.sectors)} sectors") for sector in bank.sectors: prot = "protected" if sector.protected else "unprotected" print(f" Sector {sector.index}: offset=0x{sector.offset:X}, " f"size=0x{sector.size:X}, {prot}")Reading flash
Section titled “Reading flash”Two methods for reading flash content:
read(bank, offset, size)— returns rawbytesby writing to a temp file, then reading the data back through TCL or from the local filesystem.read_to_file(bank, path)— dumps the entire bank directly to a file on disk.
from pathlib import Path
async with await Session.connect() as session: # Read 256 bytes from the start of bank 0 data = await session.flash.read(bank=0, offset=0, size=256) print(f"Read {len(data)} bytes: {data[:16].hex()}")
# Dump the entire bank to a file await session.flash.read_to_file(bank=0, path=Path("flash_dump.bin"))from pathlib import Path
with Session.connect_sync() as session: data = session.flash.read(bank=0, offset=0, size=256) print(f"Read {len(data)} bytes: {data[:16].hex()}")
session.flash.read_to_file(bank=0, path=Path("flash_dump.bin"))Writing flash
Section titled “Writing flash”Raw byte write
Section titled “Raw byte write”write(bank, offset, data) writes raw bytes to a flash bank at a given offset. Like read(), it uses a temporary file since OpenOCD’s flash write_bank reads from a file.
async with await Session.connect() as session: config_data = b"\x01\x02\x03\x04" await session.flash.write(bank=0, offset=0x1000, data=config_data)with Session.connect_sync() as session: config_data = b"\x01\x02\x03\x04" session.flash.write(bank=0, offset=0x1000, data=config_data)Firmware image programming
Section titled “Firmware image programming”write_image(path, erase=True, verify=True) is the high-level “flash and go” command. It handles erase, write, and verification in one operation. It accepts .bin, .hex, and .elf files.
from pathlib import Path
async with await Session.connect() as session: firmware = Path("build/firmware.hex") await session.flash.write_image(firmware, erase=True, verify=True) print("Firmware programmed and verified")from pathlib import Path
with Session.connect_sync() as session: firmware = Path("build/firmware.hex") session.flash.write_image(firmware, erase=True, verify=True) print("Firmware programmed and verified")When verify=True, the method runs verify_image after writing. If the verification finds a mismatch, it raises FlashError.
Erasing flash
Section titled “Erasing flash”Erase a sector range
Section titled “Erase a sector range”erase_sector(bank, first, last) erases sectors from first to last (both inclusive). Validates that first <= last and raises FlashError if the range is invalid.
async with await Session.connect() as session: # Erase sectors 0 through 3 in bank 0 await session.flash.erase_sector(bank=0, first=0, last=3)with Session.connect_sync() as session: session.flash.erase_sector(bank=0, first=0, last=3)Erase an entire bank
Section titled “Erase an entire bank”erase_all(bank=0) queries the bank info to find the last sector, then erases the full range.
await session.flash.erase_all(bank=0)Write protection
Section titled “Write protection”protect(bank, first, last, on) sets or clears hardware write protection on a range of sectors.
async with await Session.connect() as session: # Protect the bootloader (sectors 0-1) await session.flash.protect(bank=0, first=0, last=1, on=True)
# Unprotect application sectors (2-7) await session.flash.protect(bank=0, first=2, last=7, on=False)with Session.connect_sync() as session: session.flash.protect(bank=0, first=0, last=1, on=True) session.flash.protect(bank=0, first=2, last=7, on=False)Verifying flash
Section titled “Verifying flash”verify(bank, path) compares flash contents against a reference binary file and returns True if they match, False otherwise.
from pathlib import Path
async with await Session.connect() as session: matches = await session.flash.verify(bank=0, path=Path("golden.bin")) if matches: print("Flash contents match reference file") else: print("MISMATCH detected")from pathlib import Path
with Session.connect_sync() as session: matches = session.flash.verify(bank=0, path=Path("golden.bin")) if matches: print("Flash contents match reference file") else: print("MISMATCH detected")Data types
Section titled “Data types”FlashBank
Section titled “FlashBank”| Field | Type | Description |
|---|---|---|
index | int | Bank number |
name | str | Bank name (e.g. stm32f1x.flash) |
base | int | Base address |
size | int | Total size in bytes |
bus_width | int | Bus width |
chip_width | int | Chip width |
target | str | Associated target or driver name |
sectors | list[FlashSector] | Sector list (empty from banks(), populated from info()) |
FlashSector
Section titled “FlashSector”| Field | Type | Description |
|---|---|---|
index | int | Sector number within the bank |
offset | int | Byte offset from the bank base |
size | int | Sector size in bytes |
protected | bool | Whether write protection is enabled |
Error handling
Section titled “Error handling”All flash operations raise FlashError on failure. The error message includes the OpenOCD response for diagnostics.
from openocd import Session, FlashError
try: with Session.connect_sync() as session: session.flash.write_image(Path("firmware.bin"))except FlashError as e: print(f"Flash operation failed: {e}")Method reference
Section titled “Method reference”| Method | Return Type | Description |
|---|---|---|
banks() | list[FlashBank] | List all configured flash banks |
info(bank=0) | FlashBank | Detailed bank info with sectors |
read(bank, offset, size) | bytes | Read raw flash via temp file |
read_to_file(bank, path) | None | Dump entire bank to file |
write(bank, offset, data) | None | Write raw bytes via temp file |
write_image(path, erase=True, verify=True) | None | High-level flash programming |
erase_sector(bank, first, last) | None | Erase a sector range |
erase_all(bank=0) | None | Erase entire bank |
protect(bank, first, last, on) | None | Set/clear write protection |
verify(bank, path) | bool | Verify flash against a file |