pureboot: W addresses a page, not a word in it
The in-page bits of a W address are dropped so the fill always walks from the page base; the wire contract is one page of data for any address inside it, on both the byte- and the word-addressed path. +2 B. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -127,15 +127,15 @@ addresses and all counts are bytes.
|
|||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `b` | — | the 12-byte info block |
|
| `b` | — | the 12-byte info block |
|
||||||
| `R` | addr16, n8 | n flash bytes (n = 0 means 256) |
|
| `R` | addr16, n8 | n flash bytes (n = 0 means 256) |
|
||||||
| `W` | addr16, then one page of data | — (completion = next prompt) |
|
| `W` | addr16 (any address in the page), then one page of data | — (completion = next prompt) |
|
||||||
| `r` | addr16, n8 | n EEPROM bytes (n = 0 means 256) |
|
| `r` | addr16, n8 | n EEPROM bytes (n = 0 means 256) |
|
||||||
| `w` | addr16, n8, then n data bytes | `+` per byte, sent once its write has begun |
|
| `w` | addr16, n8, then n data bytes | `+` per byte, sent once its write has begun |
|
||||||
| `F` | — | 4 bytes: low fuse, lock, extended fuse, high fuse |
|
| `F` | — | 4 bytes: low fuse, lock, extended fuse, high fuse |
|
||||||
| `J` | word address (16-bit) | `+`, then execution continues there |
|
| `J` | word address (16-bit) | `+`, then execution continues there |
|
||||||
| other | — | ignored; the loop re-prompts (send a junk byte, await `+`, to resync) |
|
| other | — | ignored; the loop re-prompts (send a junk byte, await `+`, to resync) |
|
||||||
|
|
||||||
`W` streams exactly one page-aligned SPM page (size from the info block) into
|
`W` streams exactly one SPM page (size from the info block) into the buffer,
|
||||||
the buffer, then erases and programs — except pages inside the 512-byte slot
|
then erases and programs — except pages inside the 512-byte slot
|
||||||
the loader is *running* in, which are drained and left alone, so a broken host
|
the loader is *running* in, which are drained and left alone, so a broken host
|
||||||
cannot brick the running copy and a staged copy may rewrite the resident.
|
cannot brick the running copy and a staged copy may rewrite the resident.
|
||||||
|
|
||||||
|
|||||||
@@ -309,18 +309,20 @@ void store_eeprom(std::uint16_t address, std::uint8_t count)
|
|||||||
// that write clears the condition and the host's read-back rewrites the page.
|
// that write clears the condition and the host's read-back rewrites the page.
|
||||||
void program_flash(std::uint16_t wire_address, std::uint8_t slot_high)
|
void program_flash(std::uint16_t wire_address, std::uint8_t slot_high)
|
||||||
{
|
{
|
||||||
// One induction either way: a byte-addressed wire address walks the page
|
// The address names a page, so its in-page bits are dropped and the walk
|
||||||
// itself (aligned, so the offset bits wrap to zero), while a word one
|
// starts at the page base — one induction either way: a byte-addressed
|
||||||
// becomes a byte cursor once. The slot index is the wire address's high
|
// wire address walks the page itself (the offset bits wrap back to zero),
|
||||||
// byte — on byte-addressed chips the byte address's, with the low bit
|
// while a word one becomes a byte cursor once. The slot index is the wire
|
||||||
// dropped, since a slot is two of those.
|
// address's high byte — on byte-addressed chips the byte address's, with
|
||||||
|
// the low bit dropped, since a slot is two of those.
|
||||||
spm::flash_address_t address;
|
spm::flash_address_t address;
|
||||||
std::uint8_t page_high;
|
std::uint8_t page_high;
|
||||||
if constexpr (word_flash) {
|
if constexpr (word_flash) {
|
||||||
// A page is aligned, so it never crosses 64 KiB: RAMPZ is a per-page
|
// A page is aligned, so it never crosses 64 KiB: RAMPZ is a per-page
|
||||||
// constant and the 16-bit Z's low byte is the whole in-page offset.
|
// constant and the 16-bit Z's low byte is the whole in-page offset.
|
||||||
const std::uint8_t rampz = static_cast<std::uint8_t>(wire_address >> 15);
|
const std::uint8_t rampz = static_cast<std::uint8_t>(wire_address >> 15);
|
||||||
const std::uint16_t z0 = static_cast<std::uint16_t>(wire_address << 1);
|
const std::uint16_t z0 =
|
||||||
|
static_cast<std::uint16_t>(wire_address << 1) & ~static_cast<std::uint16_t>(page - 1);
|
||||||
std::uint16_t z = z0;
|
std::uint16_t z = z0;
|
||||||
do {
|
do {
|
||||||
std::uint8_t low = link::rx();
|
std::uint8_t low = link::rx();
|
||||||
@@ -331,7 +333,7 @@ void program_flash(std::uint16_t wire_address, std::uint8_t slot_high)
|
|||||||
address = (static_cast<spm::flash_address_t>(rampz) << 16) | z0;
|
address = (static_cast<spm::flash_address_t>(rampz) << 16) | z0;
|
||||||
page_high = static_cast<std::uint8_t>(wire_address >> 8);
|
page_high = static_cast<std::uint8_t>(wire_address >> 8);
|
||||||
} else {
|
} else {
|
||||||
address = static_cast<spm::flash_address_t>(wire_address);
|
address = static_cast<spm::flash_address_t>(wire_address & ~static_cast<std::uint16_t>(page - 1));
|
||||||
do {
|
do {
|
||||||
std::uint8_t low = link::rx();
|
std::uint8_t low = link::rx();
|
||||||
std::uint8_t high = link::rx();
|
std::uint8_t high = link::rx();
|
||||||
|
|||||||
@@ -105,6 +105,17 @@ def main():
|
|||||||
# was never told about is a loader it would refuse to speak to.
|
# was never told about is a loader it would refuse to speak to.
|
||||||
if live.version != pb.NEWEST_LOADER:
|
if live.version != pb.NEWEST_LOADER:
|
||||||
fail(f"loader reports pureboot {live.version}, the tool's newest is {pb.NEWEST_LOADER}")
|
fail(f"loader reports pureboot {live.version}, the tool's newest is {pb.NEWEST_LOADER}")
|
||||||
|
|
||||||
|
# A W addressed inside a page rather than at its base must still
|
||||||
|
# consume exactly one page and prompt. The loader's own slot is
|
||||||
|
# the target — it is drained and never programmed — and the
|
||||||
|
# payload is erased-state bytes, so the probe can disturb neither
|
||||||
|
# the image nor the page buffer it leaves behind.
|
||||||
|
wire = wire_base + 1
|
||||||
|
port.write(bytes((ord("W"), wire & 0xFF, wire >> 8)) + b"\xff" * page)
|
||||||
|
if port.read_exact(1, 5.0) != pb.PROMPT:
|
||||||
|
fail("unaligned W did not return to the prompt")
|
||||||
|
|
||||||
loader.run_application()
|
loader.run_application()
|
||||||
banner = port.read_exact(3, 5.0)
|
banner = port.read_exact(3, 5.0)
|
||||||
if banner != b"APP":
|
if banner != b"APP":
|
||||||
|
|||||||
Reference in New Issue
Block a user