build: the libavr pin advances past phase 6, at byte parity everywhere
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>
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
*board*: that the loader actually installed on it answers, that the memories
|
||||
round-trip over the real link, that the application it flashes runs afterwards,
|
||||
and that the refusals which keep a 512-byte slot alive still fire. Run it once
|
||||
when a board is brought up, and again whenever the deployment moves — a new
|
||||
when a board is brought up, and again whenever the deployment moves - a new
|
||||
clock, a new backend, new pins.
|
||||
|
||||
Every check derives its bounds from the info block the loader itself reports, so
|
||||
@@ -63,7 +63,7 @@ class Suite:
|
||||
info = loader.info
|
||||
self.check("identity read", True, info.describe())
|
||||
return info
|
||||
except Exception as error: # noqa: BLE001 — a dead link is a result
|
||||
except Exception as error: # noqa: BLE001 - a dead link is a result
|
||||
self.check("identity read", False, str(error)[:70])
|
||||
return None
|
||||
finally:
|
||||
@@ -75,7 +75,7 @@ class Suite:
|
||||
def scan(self) -> None:
|
||||
"""The --scan walk against real termios and a real oscillator: every
|
||||
probe rate must open a port (the off-nominal rates exist only through
|
||||
termios2), and one probe must answer — the nominal on a healthy board,
|
||||
termios2), and one probe must answer - the nominal on a healthy board,
|
||||
a neighbor on a drifted one. The rig injects the one reset per probe
|
||||
the operator supplies in the field; this is the rate physics the
|
||||
simulator cannot arbitrate (a pty carries bytes at any rate), pinned
|
||||
@@ -102,7 +102,7 @@ class Suite:
|
||||
continue
|
||||
finally:
|
||||
port.close()
|
||||
except Exception as error: # noqa: BLE001 — a rig hiccup is a result
|
||||
except Exception as error: # noqa: BLE001 - a rig hiccup is a result
|
||||
self.check("scan walks the probe ladder", False, str(error)[:70])
|
||||
return
|
||||
self.check("scan finds the board's rate", found is not None,
|
||||
@@ -139,7 +139,7 @@ class Suite:
|
||||
|
||||
if marker:
|
||||
# The tool hands over as it ends its session, so the application is
|
||||
# already running — but only on a board whose DTR is unwired, where
|
||||
# already running - but only on a board whose DTR is unwired, where
|
||||
# opening a port simply listens. Where DTR *is* wired to reset (an
|
||||
# Arduino, most USB-serial dev boards), this open resets the part
|
||||
# and the activation window comes first, so a marker emitted once at
|
||||
@@ -152,7 +152,7 @@ class Suite:
|
||||
sample = "".join(chr(b) if 32 <= b < 127 else "." for b in data[:40])
|
||||
self.check(f"application runs (emits {marker!r})", seen,
|
||||
f"|{sample}|" if seen or data else
|
||||
f"nothing in {marker_wait:g} s — if this board resets when its port "
|
||||
f"nothing in {marker_wait:g} s - if this board resets when its port "
|
||||
f"opens, that wait has to outlast the activation window")
|
||||
|
||||
back = self.work / "app-back.bin"
|
||||
@@ -166,7 +166,7 @@ class Suite:
|
||||
|
||||
Prefers ISP, because an independent reader is the only one that can
|
||||
testify about a loader just asked to erase around itself. Where no
|
||||
programmer is attached the link answers instead — which is weaker for
|
||||
programmer is attached the link answers instead - which is weaker for
|
||||
exactly the reason it is worth having, a destroyed loader being unable
|
||||
to report anything at all. The two are never printed under one word:
|
||||
an absent probe is a fact about the bench, a wrong byte is a verdict on
|
||||
@@ -189,8 +189,8 @@ class Suite:
|
||||
loader.connect(self.rig.d.wait)
|
||||
return (loader.read_flash(0, limit),
|
||||
loader.read_flash(info.base, slot_length),
|
||||
"the link, no probe attached — the loader's own account")
|
||||
except Exception as error: # noqa: BLE001 — a dead link is a result
|
||||
"the link, no probe attached - the loader's own account")
|
||||
except Exception as error: # noqa: BLE001 - a dead link is a result
|
||||
print(f" skip slot checks: no programmer, and the link did not "
|
||||
f"answer either ({str(error)[:60]})")
|
||||
return None, None, ""
|
||||
@@ -226,7 +226,7 @@ class Suite:
|
||||
|
||||
pureboot 9 has no running-slot guard: what stops a mangled command from
|
||||
erasing the loader is the seal and nothing else. So this aims the worst
|
||||
command the protocol has — an SPM erase at the loader's own first page —
|
||||
command the protocol has - an SPM erase at the loader's own first page -
|
||||
and damages one header byte at a time. Every one must come back NAK with
|
||||
the slot untouched and the session still in step.
|
||||
|
||||
@@ -271,10 +271,10 @@ class Suite:
|
||||
|
||||
# And the slot itself, read back over the link: the loader is the
|
||||
# thing that would have been erased, so its own account of its
|
||||
# first bytes is a real witness — an erased page reads all 0xff.
|
||||
# first bytes is a real witness - an erased page reads all 0xff.
|
||||
head = loader.read_flash(info.base, 16)
|
||||
self.check("loader slot intact", set(head) != {0xFF}, head[:8].hex())
|
||||
except Exception as error: # noqa: BLE001 — a dead link is a result
|
||||
except Exception as error: # noqa: BLE001 - a dead link is a result
|
||||
self.check("seal checks", False, str(error)[:70])
|
||||
finally:
|
||||
try:
|
||||
@@ -284,7 +284,7 @@ class Suite:
|
||||
|
||||
@staticmethod
|
||||
def _sealed(module, op, space, address, count, damage=None):
|
||||
"""A sealed header, damaged after sealing — the shape a link fault has."""
|
||||
"""A sealed header, damaged after sealing - the shape a link fault has."""
|
||||
head = bytearray((op, module.selector(space, address), address & 0xFF,
|
||||
(address >> 8) & 0xFF, count & 0xFF))
|
||||
seal = module.SEAL
|
||||
@@ -344,7 +344,7 @@ class Suite:
|
||||
def main(argv: list[str] | None = None) -> int:
|
||||
parser = argparse.ArgumentParser(
|
||||
description="hardware acceptance suite for one pureboot deployment",
|
||||
epilog="overwrites the board's application flash and EEPROM — back them up first")
|
||||
epilog="overwrites the board's application flash and EEPROM - back them up first")
|
||||
pbrig.Deployment.add_arguments(parser)
|
||||
parser.add_argument("--app", type=pathlib.Path,
|
||||
help="application image to flash (test/pbapp.cpp built for this deployment)")
|
||||
|
||||
Reference in New Issue
Block a user