pureboot.py: an update follows the staging copy onto its own link
--update-loader works by entering copies of the *new* image and letting them rewrite the resident. Those copies speak the link they were built for, but the host went on knocking with the session's baud and backend — the resident's. Where the image changed either, the staging copy was installed and then never answered: resident untouched, and on a 1 KiB tiny the staging slot is the whole application region, so the application was already gone. The wire cannot be probed for it. 512 bytes of position-independent code carry no header saying what rate they were built for, so the operator declares it: --staged-baud and --staged-autobaud, applied from the jump into the staging copy onward. Retuning goes through the open port — SetCommState or tcsetattr on the live handle, never a reopen — because a DTR pulse would reset the copy being talked to. Undeclared against a changed link it still cannot work, but the error now names that as the cause instead of reporting the bare activation timeout that sent the operator looking at wiring. The README's idempotence claim needed the same qualification: from step 2 a re-run must reach the new image, and after step 3 word 0 points at the staging copy, so on a patched-vector part the resident's link reaches nothing at all. Found on an ATtiny13A, where two controls differing only in the activation window updated cleanly and so isolated the link as the variable. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -328,6 +328,23 @@ with any pureboot build — a re-timed window, a newer version — using the
|
||||
loader itself as its own staging loader. The image is the loader's own 512
|
||||
bytes as a raw binary, or the Intel HEX the build emits beside it.
|
||||
|
||||
One thing the image cannot tell the host: **which link it speaks.** The update
|
||||
works by entering copies of the *new* image (steps 3 and 4 below), so a build
|
||||
made for another baud or another backend answers on that one and not on the
|
||||
session's — and 512 bytes of position-independent code carry no header to read
|
||||
it from. Where the new image's link differs, name it:
|
||||
|
||||
```sh
|
||||
# a 57600 fixed-baud resident, replaced by an autobaud build
|
||||
pureboot.py --port … --baud 57600 --update-loader ab.bin --staged-autobaud
|
||||
# …or by a 38400 build of the same backend
|
||||
pureboot.py --port … --baud 57600 --update-loader sw38400.bin --staged-baud 38400
|
||||
```
|
||||
|
||||
The host retunes on the open port, so no DTR pulse resets the copy it is talking
|
||||
to. Omit them against a changed link and the update stops after installing the
|
||||
staging copy, saying so and naming this as the cause.
|
||||
|
||||
The preflight refuses an image built for another chip: the stamp every pureboot
|
||||
binary carries must resolve to the device's own geometry, and the error names
|
||||
both. Die revisions share their base signature and geometry, so their images
|
||||
@@ -348,10 +365,15 @@ are interchangeable — as the silicon is.
|
||||
content, and the state file is discarded.
|
||||
|
||||
Every phase is idempotent and keyed off the actual flash state, so re-running
|
||||
the same command after any interruption resumes and completes. The state file
|
||||
carries the only bytes not recoverable from the device; losing it mid-update
|
||||
still completes the update, and the staging region comes back by reflashing
|
||||
the application. A boot-sectioned mega needs its fuses for the preflight — read
|
||||
the same command after any interruption resumes and completes — with one
|
||||
qualification, which is the link again: from step 2 on, the copy the re-run has
|
||||
to reach is the *new* image, so a resumed run needs the same `--staged-*` as the
|
||||
first one. On a patched-vector part step 3 also re-aims word 0 at the staging
|
||||
copy, so after that point a reset reaches the new image's link and **only** that
|
||||
one; a re-run on the resident's link finds nothing at all. The state file carries
|
||||
the only bytes not recoverable from the device; losing it mid-update still
|
||||
completes the update, and the staging region comes back by reflashing the
|
||||
application. A boot-sectioned mega needs its fuses for the preflight — read
|
||||
from the device, or supplied with `--assume-fuses` where reading is impossible
|
||||
(simulators).
|
||||
|
||||
@@ -425,6 +447,11 @@ Per chip preset, `ctest` runs:
|
||||
- `pureboot.handshake` — the host tool's activation must not hang on a target
|
||||
that never falls quiet: the drain after a prompt is bounded by the handshake
|
||||
deadline, and a well-behaved loader still connects;
|
||||
- `pureboot.updatelink` — an update whose image changes the baud or the backend
|
||||
must follow the staging copy onto *its* link, since that copy is the new image;
|
||||
and where nothing was declared, the failure must name the link rather than
|
||||
report a bare activation timeout, because by then the staging slot is written
|
||||
and on a 1 KiB tiny that was the application;
|
||||
- `pureboot.planner` — the host tool's pure logic: programming orders and their
|
||||
recovery properties, the surgery, the staging composition, the boot-fuse
|
||||
decode, the update preflight over synthetic fuse bytes, and the repairing
|
||||
|
||||
Reference in New Issue
Block a user