K1Guitar/tools/rtt_reset.py

205 lines
5.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
"""Send sys reset via J-Link RTT to reboot K1 MCU (software NVIC reset)."""
from __future__ import annotations
import argparse
import time
DEFAULT_DEVICES = ("Cortex-M4", "AT32F403AC", "AT32F403A")
RESET_CMD = b"sys reset\n"
ACK = b"SYS_RESET_OK"
def import_deps():
try:
import pylink # noqa: F401
except ImportError as exc:
raise SystemExit("Missing dependency: pip install pylink-square") from exc
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 pylink.errors.JLinkException 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) -> int | None:
needle = b"SEGGER RTT"
chunk = 0x1000
was_halted = jlink.halted()
did_halt = False
if not was_halted:
jlink.halt()
did_halt = True
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 did_halt:
resume_cpu(jlink)
return None
def hw_reset_and_run(jlink, wait_s: float = 4.0) -> None:
"""J-Link 硬件复位并释放 CPURTT 不可用时的唯一可靠恢复手段)。"""
print("Hardware reset + run...")
resume_cpu(jlink)
if wait_s > 0:
print(f"Waiting {wait_s:.0f}s for boot...")
time.sleep(wait_s)
resume_cpu(jlink)
def resume_cpu(jlink) -> None:
"""J-Link often halts the core on reset; must run() or UI stays black."""
try:
jlink.rtt_stop()
except Exception:
pass
try:
jlink.reset(halt=False)
except Exception:
pass
try:
if hasattr(jlink, "restart"):
jlink.restart()
else:
jlink.go()
except Exception:
jlink.go()
time.sleep(0.05)
def wait_rtt_ready(jlink, timeout_s: float = 15.0) -> None:
deadline = time.time() + timeout_s
while time.time() < deadline:
try:
if jlink.rtt_get_num_up_buffers() > 0:
return
except Exception:
pass
time.sleep(0.2)
raise SystemExit("RTT control block found but buffers not ready")
def send_reset(device: str, timeout_s: float, wait_boot_s: float, hw_fallback: bool) -> None:
jlink = connect_jlink(device)
ack_seen = False
try:
if jlink.halted():
print("Target was halted, resuming before RTT...")
resume_cpu(jlink)
time.sleep(0.2)
cb = find_rtt_control_block(jlink)
if cb is None:
print("SEGGER RTT control block not found in SRAM.")
if hw_fallback:
hw_reset_and_run(jlink, wait_boot_s)
print("Done (hardware recovery). If still black, run: flash_app.bat")
else:
raise SystemExit(
"RTT not available. Re-run without --no-hw-fallback, or flash_app.bat"
)
return
print(f"RTT CB @ 0x{cb:08X}")
resume_cpu(jlink)
time.sleep(0.15)
jlink.rtt_start(cb)
wait_rtt_ready(jlink)
wrote = 0
for _ in range(30):
wrote = jlink.rtt_write(0, list(RESET_CMD))
if wrote > 0:
break
time.sleep(0.2)
if wrote <= 0:
raise SystemExit("Failed to send sys reset on RTT down channel 0")
print(f"Sent sys reset ({wrote} bytes)")
deadline = time.time() + timeout_s
while time.time() < deadline:
chunk = jlink.rtt_read(0, 1024)
if chunk:
text = bytes(chunk)
if ACK in text:
ack_seen = True
print("SYS_RESET_OK (firmware handled reset)")
break
time.sleep(0.05)
if not ack_seen:
print("No SYS_RESET_OK from firmware.")
if hw_fallback:
hw_reset_and_run(jlink, 0.0)
else:
print("Hint: re-run with --hw-fallback or flash latest firmware.")
print("Resuming CPU after reset...")
resume_cpu(jlink)
if wait_boot_s > 0:
print(f"Waiting {wait_boot_s:.0f}s for auto boot...")
time.sleep(wait_boot_s)
resume_cpu(jlink)
print("Done. Device should be back on mode-select screen.")
finally:
try:
resume_cpu(jlink)
except Exception:
pass
jlink.close()
def main() -> None:
parser = argparse.ArgumentParser(description="Reboot K1 via RTT sys reset command")
parser.add_argument("--device", default="AT32F403AC", help="J-Link device name")
parser.add_argument("--timeout", type=float, default=3.0, help="Seconds to wait for ACK")
parser.add_argument(
"--wait-boot",
type=float,
default=4.0,
help="Seconds to wait after reset for auto power-on (0 to skip)",
)
parser.add_argument(
"--no-hw-fallback",
action="store_true",
help="Do not use J-Link hardware reset when firmware ACK is missing",
)
args = parser.parse_args()
import_deps()
send_reset(args.device, args.timeout, args.wait_boot, not args.no_hw_fallback)
if __name__ == "__main__":
main()