Files
bootloader/test/pbsim.py
BlackMark 0fa53e1cad pureboot: the classic megas and the word-addressed 1284P groundwork
Device: boot-section detection probes SPMCR beside SPMCSR, the link
picks any hardware USART through the instance-aware lookups (URSEL
chips included), WDRF reads MCUSR-or-MCUCSR, and the >64 KiB shape
lands — word-addressed wire flash (info flag bit 1, page byte 0 means
256, base as a word address), far reads through flash_load_far, a
single 32-bit byte-cursor page walk (the 256-byte page wraps its low
byte exactly), and slot arithmetic in words (the return address
already is one). Host: addresses stay bytes internally and scale at
the wire, the boot-fuse decode becomes a per-signature table (byte
index + BOOTSZ ladder — the m168A's lives in EXTENDED), and the
planner tests pin every chip's ladder plus the word-addressed info
decode. Tests: the device runner serves every mega over the USART pty,
pbapp banners over the right link, the update rehearsal synthesizes
its assumed fuses from the tool's own table, and the PI lint tracks
the renamed info symbol. All six classic-mega/168A targets pass the
full suite (size, PI, planner, protocol, reloc, self-update) at
466–504 B; the 1284P builds await a libavr far-path slimming to make
its 512.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 12:00:19 +02:00

62 lines
2.1 KiB
Python

"""Shared simavr harness for the pureboot tests: spawn the device runner,
hand out its pty, restart it from a flash dump (the power-fail path), and
keep its chatter out of undrained pipes."""
import os
import signal
import subprocess
class Device:
def __init__(self, binary, elf, mcu, hz, base_hex, page, baud, dump, reset_hex=None, resume=None):
cmd = [binary, elf, mcu, hz, base_hex, str(page), str(baud), dump]
if reset_hex is not None or resume is not None:
cmd.append(reset_hex if reset_hex is not None else ("0" if not mcu.startswith("atmega") else base_hex))
if resume is not None:
cmd.append(resume)
self.log = open(dump + ".log", "a")
self.proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=self.log, text=True)
self.dump = dump
self.pty = None
for _ in range(50):
line = self.proc.stdout.readline()
if not line:
break
if line.startswith("PB_PTY"):
self.pty = line.split()[1]
break
if not self.pty:
self.stop()
raise RuntimeError("device did not report a pty")
def reset(self):
"""The external reset line: SIGUSR1 re-enters at the reset vector."""
self.proc.send_signal(signal.SIGUSR1)
def power_fail(self):
"""SIGTERM: the runner dumps its flash and exits — the image a
restart resumes from."""
self.stop()
return self.dump
def stop(self):
self.proc.terminate()
try:
self.proc.wait(timeout=5)
except subprocess.TimeoutExpired:
self.proc.kill()
self.log.close()
def run_tool(tool, pty, baud, *args, timeout=180):
result = subprocess.run(
[os.environ.get("PYTHON", "python3"), tool, "--port", pty, "--baud", str(baud), "--wait", "25", *args],
capture_output=True,
text=True,
timeout=timeout,
)
print(result.stdout, end="")
if result.returncode != 0:
raise RuntimeError(f"tool exited {result.returncode}: {result.stderr.strip()}")
return result.stdout