---
title: Flash Programming
description: Read, write, erase, verify, and protect on-chip flash memory banks through OpenOCD.
---
import { Tabs, TabItem, Aside } from '@astrojs/starlight/components';
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:
```python
# Async
session.flash
# Sync
sync_session.flash
```
## 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.
```python
import asyncio
from 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())
```
```python
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
`info(bank=0)` returns a `FlashBank` with its `sectors` list populated. Each sector is a `FlashSector` with index, offset, size, and protection status.
```python
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}")
```
```python
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
Two methods for reading flash content:
- **`read(bank, offset, size)`** -- returns raw `bytes` by 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.
```python
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"))
```
```python
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
### 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.
```python
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)
```
```python
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
`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.
```python
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")
```
```python
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
### 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.
```python
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)
```
```python
with Session.connect_sync() as session:
session.flash.erase_sector(bank=0, first=0, last=3)
```
### Erase an entire bank
`erase_all(bank=0)` queries the bank info to find the last sector, then erases the full range.
```python
await session.flash.erase_all(bank=0)
```
## Write protection
`protect(bank, first, last, on)` sets or clears hardware write protection on a range of sectors.
```python
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)
```
```python
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
`verify(bank, path)` compares flash contents against a reference binary file and returns `True` if they match, `False` otherwise.
```python
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")
```
```python
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
### 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
| 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
All flash operations raise `FlashError` on failure. The error message includes the OpenOCD response for diagnostics.
```python
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
| 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 |