The pin crosses libavr's phase 6 - the renamed system surface, the named serial configs, the receiver-tolerance table, the paged SPM receipts - and every loader image comes out size-identical: the full matrix on six representative chips (the exhaustive cross product on three of them), the stock and autobaud columns untouched, the four tsb tiers back on their recorded floors at 510/526/638/836. Byte parity was not free, and the two libavr defects it surfaced were fixed there rather than absorbed here. The EEPROM write procedure's step 2 - the SPMEN spin - had landed unconditionally and cost every build six bytes for a wait a polled loader can never take; it is scoped now, and the loaders state the datasheet's own omission clause (spm_interlock::omitted, DS40002061B 8.6.3). The blocking page erase/write grew an internal wait the tiers' settle() already provides, so the tiers issue the command form and pureboot keeps its host-driven sp_spm path. What the port states rather than inherits: the stock 115200 at 16 MHz sits +2.1 % past the receiver-tolerance table libavr now holds rates to, so the hardware links say .allow_baud_error = true - the same 2.5 % envelope pureboot_baud_feasible() has always enforced, proven on silicon across the fleet. rx_ready() reads readable() now. Alongside the pin: rule 33's ASCII sweep over every source (docs keep their typography), rule 34's InsertBraces in .clang-format with the tree reformatted, std::array over the simavr runners' raw buffers, and the stale Studio size in ide/README.md replaced by the claim its check-flags gate actually holds. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
87 lines
3.5 KiB
Python
87 lines
3.5 KiB
Python
#!/usr/bin/env python3
|
|
"""Re-homing acceptance test: an image programmed somewhere other than its
|
|
canonical slot must still be a working loader, and the ordinary
|
|
--update-loader flow must put a build into the top slot from there.
|
|
|
|
Two positions. Address 0, a raw .bin handed to a programmer. And the staging
|
|
slot itself, where a loader already sitting there IS the staging copy -
|
|
recognized by its embedded block and left in place, then streaming the new
|
|
resident like any staged copy.
|
|
|
|
Usage: pbrehome.py <device_bin> <pureboot_elf> <update_bin> <mcu> <hz>
|
|
<base_hex> <page> <baud> <app_bin> <tool_py> <workdir>
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
|
|
|
|
def fail(message):
|
|
print(f"FAIL: {message}")
|
|
sys.exit(1)
|
|
|
|
|
|
def rehome_from(pbsim, pb, device_bin, elf, place_hex, update_bin, base, page, baud, app_bin, workdir,
|
|
mcu, hz):
|
|
"""Place the loader at `place_hex`, heal through --update-loader, flash
|
|
the application, expect the banner."""
|
|
dump = os.path.join(workdir, f"dump-{place_hex}.bin")
|
|
state = os.path.join(workdir, f"rehome-{place_hex}.pbstate")
|
|
if os.path.exists(state):
|
|
os.unlink(state)
|
|
device = pbsim.Device(device_bin, elf, mcu, hz, place_hex, page, baud, dump, reset_hex="0")
|
|
try:
|
|
port = pb.Port(device.pty, baud)
|
|
loader = pb.Loader(port)
|
|
info = loader.connect(25)
|
|
if info.base != base:
|
|
fail(f"the misplaced copy reports base {info.base:#06x} - the info block must stay canonical")
|
|
|
|
# The ordinary update flow puts the build into the top slot.
|
|
pb.op_update_loader(loader, 25, update_bin, state, None)
|
|
update = open(update_bin, "rb").read()
|
|
if loader.read_flash(base, len(update)) != update:
|
|
fail("the canonical slot does not hold the update image")
|
|
|
|
# An application flashed through the healed resident overwrites the
|
|
# stale copy (surgery included) and launches.
|
|
pages = pb.plan_flash(open(app_bin, "rb").read(), loader.info)
|
|
for address in pb.covered(pages, loader.info, skip_blank=False):
|
|
loader.write_page(address, pages[address])
|
|
pb.verify_pages(loader, pages)
|
|
loader.run_application()
|
|
if port.read_exact(3, 5.0) != b"APP":
|
|
fail(f"application does not banner after the re-home from {place_hex}")
|
|
port.close()
|
|
finally:
|
|
device.stop()
|
|
|
|
|
|
def main():
|
|
(device_bin, elf, update_bin, mcu, hz, base_hex, page, baud, app_bin, tool, workdir) = sys.argv[1:]
|
|
base, page, baud = int(base_hex, 0), int(page), int(baud)
|
|
sys.path.insert(0, os.path.dirname(os.path.abspath(tool)))
|
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
import pbsim
|
|
import pureboot as pb
|
|
|
|
os.makedirs(workdir, exist_ok=True)
|
|
|
|
# Address 0: the raw-.bin-to-a-programmer accident.
|
|
rehome_from(pbsim, pb, device_bin, elf, "0x0", update_bin, base, page, baud, app_bin, workdir, mcu, hz)
|
|
print("re-home from address 0: converged")
|
|
|
|
# The staging slot: erased flash with the loader sitting exactly where
|
|
# a staging copy would - the tool must leave it in place and let it
|
|
# stream the (different) update build into the resident slot.
|
|
stage = base - pb.SLOT
|
|
rehome_from(pbsim, pb, device_bin, elf, hex(stage), update_bin, base, page, baud, app_bin, workdir,
|
|
mcu, hz)
|
|
print("re-home from the staging slot: converged")
|
|
|
|
print("pbrehome: a misplaced loader re-homes through the ordinary update flow")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|