pbhw: the marker check assumed a board whose port-open is not a reset
Its comment said "opening the port does not reset a board whose DTR is unwired, so this simply listens" — true of the tiny it was written against, false of an Arduino, and this is the generic harness. Where DTR is wired to reset, that open resets the part and the activation window comes first, so a fixture emitting its banner once says it on the far side of a wait the suite cannot know the length of: the window is a compile-time constant and nothing on the wire reports it. The suite read the silence as an application that never ran, on a board where it demonstrably had. So --marker-wait, defaulting to the 2.5 s that was hardcoded, and a failure that names the window as the candidate rather than leaving the next person to suspect the loader. The other half is the fixture: PUREBOOT_HEARTBEAT makes the observation independent of when the listener arrives, which is what the rig's own builds now pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -132,18 +132,28 @@ class Suite:
|
||||
got = erased.read_bytes() if erased.exists() else b""
|
||||
self.check("EEPROM erase leaves 0xff", got == b"\xff" * size, f"{len(got)} B")
|
||||
|
||||
def application(self, info, app: pathlib.Path, marker: str) -> None:
|
||||
def application(self, info, app: pathlib.Path, marker: str,
|
||||
marker_wait: float = 2.5) -> None:
|
||||
rc, out = self.rig.pureboot("--flash", str(app), "--verify-flash", str(app))
|
||||
self.check(f"application flash + verify ({app.name})", rc == 0, self._brief(out))
|
||||
|
||||
if marker:
|
||||
# The tool hands over as it ends its session, so the application is
|
||||
# already running; opening the port does not reset a board whose DTR
|
||||
# is unwired, so this simply listens.
|
||||
data = self.rig.capture(seconds=2.5)
|
||||
# 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
|
||||
# startup happens on the far side of a wait this cannot know the
|
||||
# length of: the window is a compile-time constant and nothing on
|
||||
# the wire reports it. Hence --marker-wait, and a fixture that
|
||||
# repeats its banner (PUREBOOT_HEARTBEAT) rather than saying it once.
|
||||
data = self.rig.capture(seconds=marker_wait)
|
||||
seen = marker.encode() in data
|
||||
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}|")
|
||||
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"opens, that wait has to outlast the activation window")
|
||||
|
||||
back = self.work / "app-back.bin"
|
||||
rc, out = self.rig.pureboot("--read-flash", str(back))
|
||||
@@ -190,7 +200,7 @@ class Suite:
|
||||
# ------------------------------------------------------------------- run
|
||||
|
||||
def run(self, app: pathlib.Path | None, loader_image: pathlib.Path | None,
|
||||
marker: str) -> int:
|
||||
marker: str, marker_wait: float = 2.5) -> int:
|
||||
print("identity")
|
||||
info = self.identity()
|
||||
if info is None:
|
||||
@@ -206,7 +216,7 @@ class Suite:
|
||||
|
||||
if app:
|
||||
print("\napplication")
|
||||
self.application(info, app, marker)
|
||||
self.application(info, app, marker, marker_wait)
|
||||
else:
|
||||
print("\nskip application checks (pass --app <image.hex>)")
|
||||
|
||||
@@ -232,6 +242,10 @@ def main(argv: list[str] | None = None) -> int:
|
||||
help="the resident loader's .bin, to prove the slot survives an erase")
|
||||
parser.add_argument("--marker", default="",
|
||||
help="text the application emits when it runs, e.g. APP")
|
||||
parser.add_argument("--marker-wait", type=float, default=2.5,
|
||||
help="seconds to listen for it. On a board whose DTR is wired to "
|
||||
"reset, opening the port resets the part, so this must outlast "
|
||||
"the activation window (default 2.5)")
|
||||
args = parser.parse_args(argv)
|
||||
|
||||
rig = pbrig.Rig(pbrig.Deployment.from_args(args))
|
||||
@@ -239,7 +253,8 @@ def main(argv: list[str] | None = None) -> int:
|
||||
f"{' (autobaud)' if args.autobaud else ''}")
|
||||
print("this overwrites the application flash and EEPROM\n")
|
||||
with tempfile.TemporaryDirectory(prefix="pbhw-") as temporary:
|
||||
return Suite(rig, pathlib.Path(temporary)).run(args.app, args.loader, args.marker)
|
||||
return Suite(rig, pathlib.Path(temporary)).run(args.app, args.loader, args.marker,
|
||||
args.marker_wait)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
Reference in New Issue
Block a user