K1Guitar/tools/erase_extflash_chip.py

166 lines
4.9 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""Erase entire W25Q128 external Flash via RTT command: flash erase chip
Requires firmware that implements the RTT command (APP/app_log.c).
Chip erase typically takes 30120s; do not power-cycle mid-erase.
"""
from __future__ import annotations
import argparse
import time
DEFAULT_DEVICES = ("Cortex-M4", "AT32F403AC", "AT32F403A")
def connect_jlink(device: str):
import pylink
jlink = pylink.JLink()
jlink.open()
try:
jlink.exec_command("HideDeviceSelection = 1")
except Exception:
pass
jlink.set_tif(pylink.enums.JLinkInterfaces.SWD)
last_error = None
for candidate in (device, *DEFAULT_DEVICES):
try:
try:
jlink.exec_command(f"Device = {candidate}")
except Exception:
pass
jlink.connect(candidate)
print(f"Connected as {candidate}")
return jlink
except Exception as exc:
last_error = exc
jlink.close()
raise SystemExit(f"Failed to connect J-Link: {last_error}")
def find_rtt_control_block(jlink, ram_base: int = 0x20000000, ram_size: int = 0x18000):
needle = b"SEGGER RTT"
chunk = 0x1000
was_halted = jlink.halted()
if not was_halted:
jlink.halt()
try:
for off in range(0, ram_size, chunk):
data = bytes(jlink.memory_read8(ram_base + off, min(chunk, ram_size - off)))
idx = data.find(needle)
if idx >= 0:
return ram_base + off + idx
finally:
if not was_halted:
if hasattr(jlink, "restart"):
jlink.restart()
else:
jlink.go()
time.sleep(0.1)
return None
def rtt_read_text(jlink, timeout_s: float = 0.2) -> str:
deadline = time.time() + timeout_s
chunks: list[bytes] = []
while time.time() < deadline:
try:
data = bytes(jlink.rtt_read(0, 512) or [])
except Exception:
data = b""
if data:
chunks.append(data)
deadline = time.time() + timeout_s
else:
time.sleep(0.02)
return b"".join(chunks).decode("ascii", errors="replace")
def wait_for(jlink, marker: str, timeout_s: float) -> str:
deadline = time.time() + timeout_s
buf = ""
last_print = 0.0
while time.time() < deadline:
chunk = rtt_read_text(jlink, 0.2)
if chunk:
buf += chunk
if marker in buf:
return buf
now = time.time()
if now - last_print >= 5.0:
elapsed = now - (deadline - timeout_s)
print(f" ... waiting erase ({elapsed:.0f}s)")
last_print = now
time.sleep(0.05)
raise SystemExit(f"Timeout waiting for {marker!r}\n--- RTT ---\n{buf[-1200:]}")
def send_bytes(jlink, payload: bytes) -> None:
off = 0
while off < len(payload):
try:
n = jlink.rtt_write(0, list(payload[off : off + 64]))
except Exception:
time.sleep(0.05)
continue
if n is None or n <= 0:
time.sleep(0.02)
continue
off += n
def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument("--device", default="Cortex-M4")
parser.add_argument("--no-reset", action="store_true")
parser.add_argument("--timeout", type=float, default=180.0,
help="seconds to wait for FLASH_ERASE_OK (default 180)")
args = parser.parse_args()
jlink = connect_jlink(args.device)
try:
cb = find_rtt_control_block(jlink)
if cb is None:
raise SystemExit("RTT control block not found")
print(f"RTT CB @ 0x{cb:08X}")
jlink.rtt_start(cb)
time.sleep(0.3)
_ = rtt_read_text(jlink, 0.3)
if not args.no_reset:
print("hardware reset...")
jlink.reset(halt=False)
time.sleep(3.5)
try:
jlink.rtt_stop()
except Exception:
pass
cb = find_rtt_control_block(jlink)
if cb is None:
raise SystemExit("RTT CB missing after reset")
print(f"RTT CB @ 0x{cb:08X}")
jlink.rtt_start(cb)
time.sleep(0.5)
_ = rtt_read_text(jlink, 0.5)
print("Sending: flash erase chip")
send_bytes(jlink, b"flash erase chip\n")
log = wait_for(jlink, "FLASH_ERASE_BEGIN", 20.0)
print(log.strip().splitlines()[-1])
print(f"Chip erase running (timeout {args.timeout:.0f}s)...")
log = wait_for(jlink, "FLASH_ERASE_OK", args.timeout)
print(log.strip().splitlines()[-1])
print("ExtFlash chip erase done. Flash MCU + extflash_ALL*.res manually.")
finally:
try:
jlink.rtt_stop()
except Exception:
pass
jlink.close()
if __name__ == "__main__":
main()