pureboot: the pin axis, and the mute it was hiding
Pins move the image for exactly one reason — a bit-banged link on a USART's own
pins has to release that USART — and the matrix said outright that they were no
axis, so the tightest configuration in the space was one nothing built. Not
subtly, either: the 1284's slot ends at flash end, so that build does not merely
exceed the size test's limit, it fails to link. pureboot_{sw,autobaud}_on_usart
{0,1} are gate points in both matrix modes now, and the exhaustive sweep carries
the pins across its whole cross product. The hand-measured table is the gate's
output: 506 B of 512 for the 1284 autobaud on USART0's pins, 504 on USART1's.
pureboot.mute drives the defect itself — an application hands over with USART0
still enabled and the loader on those pins must still answer. Reaching that
needed the runner to know an enabled USART owns its TxD, which simavr does not
model at all: it wires a USART through IRQs and never takes the pin from the
port. It also brings UCSRnB up with TXEN already set where silicon clears the
register, so the runner restores the reset value for the USART it models — the
mute must come from the application, not from power-on. The fixture stays
silent, since nothing is listening on the USART it brings up.
test_handshake.py, written where no gate could run it, is pureboot.handshake.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -116,6 +116,17 @@ else()
|
||||
math(EXPR _pb_limit "${_pb_slot} - 2")
|
||||
endif()
|
||||
|
||||
# The pins each USART owns. A bit-banged link deployed on them has to release
|
||||
# that USART before it can drive the line, and those instructions are the one
|
||||
# way the choice of pins moves the image — so a size matrix needs them as an
|
||||
# axis even though pins are otherwise immediate operands. Uniform across every
|
||||
# mega libavr covers: USART0 (the classics' un-numbered USART included) on
|
||||
# PD0/PD1, USART1 on PD2/PD3.
|
||||
set(_pb_usart0_rx pd0)
|
||||
set(_pb_usart0_tx pd1)
|
||||
set(_pb_usart1_rx pd2)
|
||||
set(_pb_usart1_tx pd3)
|
||||
|
||||
# simavr names its cores after the base dies; the A revisions run on them
|
||||
# (the 644PA on the 644P core).
|
||||
set(_pb_sim_mcu ${LIBAVR_MCU})
|
||||
@@ -133,6 +144,8 @@ set_property(GLOBAL PROPERTY PUREBOOT_WRAP "${_pb_wrap}")
|
||||
set_property(GLOBAL PROPERTY PUREBOOT_DEFAULT_HZ ${_pb_hz})
|
||||
set_property(GLOBAL PROPERTY PUREBOOT_HAS_USART ${_pb_has_usart})
|
||||
set_property(GLOBAL PROPERTY PUREBOOT_HAS_USART1 ${_pb_has_usart1})
|
||||
set_property(GLOBAL PROPERTY PUREBOOT_USART0_TX ${_pb_usart0_tx})
|
||||
set_property(GLOBAL PROPERTY PUREBOOT_USART1_TX ${_pb_usart1_tx})
|
||||
|
||||
# The port's own build (tests, the size matrix) reads the geometry from the
|
||||
# parent scope; a downstream consumer gets the same variables for free.
|
||||
@@ -145,6 +158,10 @@ set(PUREBOOT_DEFAULT_HZ ${_pb_hz} PARENT_SCOPE)
|
||||
set(PUREBOOT_HAS_USART ${_pb_has_usart} PARENT_SCOPE)
|
||||
set(PUREBOOT_HAS_USART1 ${_pb_has_usart1} PARENT_SCOPE)
|
||||
set(PUREBOOT_SIM_MCU ${_pb_sim_mcu} PARENT_SCOPE)
|
||||
set(PUREBOOT_USART0_RX ${_pb_usart0_rx} PARENT_SCOPE)
|
||||
set(PUREBOOT_USART0_TX ${_pb_usart0_tx} PARENT_SCOPE)
|
||||
set(PUREBOOT_USART1_RX ${_pb_usart1_rx} PARENT_SCOPE)
|
||||
set(PUREBOOT_USART1_TX ${_pb_usart1_tx} PARENT_SCOPE)
|
||||
|
||||
# The rates a default may pick, fastest first.
|
||||
set_property(GLOBAL PROPERTY PUREBOOT_BAUD_LADDER 115200 57600 38400 19200 9600)
|
||||
@@ -200,7 +217,8 @@ endfunction()
|
||||
# The loader target plus its flashable images (<name>.hex for a programmer,
|
||||
# <name>.bin for --update-loader). The resolved deployment is stamped on the
|
||||
# target as PUREBOOT_HZ / PUREBOOT_BAUD / PUREBOOT_LINK (the link spelled
|
||||
# usart0, usart1 or sw:<RX>,<TX>) — what a test harness speaks to it with.
|
||||
# usart0, usart1, or sw:<RX>,<TX> with a trailing @<n> where those pins are a
|
||||
# USART's own) — what a test harness speaks to it with.
|
||||
#
|
||||
# SERIAL autobaud measures the host's bit timing at run time, so the image
|
||||
# carries no clock and no baud: CLOCK and BAUD are not build parameters there,
|
||||
@@ -275,11 +293,21 @@ function(pureboot_add_loader name)
|
||||
else()
|
||||
set(_serial_defines PUREBOOT_SOFT_SERIAL PUREBOOT_RX=${PB_RX} PUREBOOT_TX=${PB_TX})
|
||||
endif()
|
||||
# sw:<RX>,<TX> as port letter and bit, upcased.
|
||||
# sw:<RX>,<TX> as port letter and bit, upcased — with @<n> where
|
||||
# the TX pin is a USART's own TXD, since a harness driving that
|
||||
# link has to know the USART owns the pin until the loader
|
||||
# releases it.
|
||||
string(SUBSTRING ${PB_RX} 1 2 _rx_pin)
|
||||
string(SUBSTRING ${PB_TX} 1 2 _tx_pin)
|
||||
string(TOUPPER "sw:${_rx_pin},${_tx_pin}" _link)
|
||||
string(REPLACE "SW" "sw" _link ${_link})
|
||||
get_property(_tx0 GLOBAL PROPERTY PUREBOOT_USART0_TX)
|
||||
get_property(_tx1 GLOBAL PROPERTY PUREBOOT_USART1_TX)
|
||||
if(_usart AND PB_TX STREQUAL _tx0)
|
||||
set(_link "${_link}@0")
|
||||
elseif(_usart1 AND PB_TX STREQUAL _tx1)
|
||||
set(_link "${_link}@1")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
if(NOT PB_BAUD)
|
||||
|
||||
@@ -28,31 +28,32 @@ it carries the calibration machinery and no clock at all.
|
||||
|
||||
| Chip | Flash | Loader at | Link | Stock | Autobaud |
|
||||
|---|---|---|---|---|---|
|
||||
| ATtiny13, ATtiny13A † | 1 KiB | 0x0200 | software | 402 B | 472 B |
|
||||
| ATtiny25 † | 2 KiB | 0x0600 | software | 406 B | 476 B |
|
||||
| ATtiny45 † | 4 KiB | 0x0e00 | software | 410 B | 480 B |
|
||||
| ATtiny85 † | 8 KiB | 0x1e00 | software | 410 B | 480 B |
|
||||
| ATmega8, 8A | 8 KiB | 0x1e00 | USART0 | 372 B | 486 B |
|
||||
| ATmega16, 16A | 16 KiB | 0x3e00 | USART0 | 374 B | 490 B |
|
||||
| ATmega32, 32A | 32 KiB | 0x7e00 | USART0 | 374 B | 490 B |
|
||||
| ATmega48, 48A, 48P, 48PA † | 4 KiB | 0x0e00 | USART0 | 400 B | 476 B |
|
||||
| ATmega88, 88A, 88P, 88PA | 8 KiB | 0x1e00 | USART0 | 410 B | 486 B |
|
||||
| ATmega168, 168A, 168P, 168PA | 16 KiB | 0x3e00 | USART0 | 412 B | 490 B |
|
||||
| ATmega328, 328P | 32 KiB | 0x7e00 | USART0 | 412 B | 490 B |
|
||||
| ATmega164A, 164P, 164PA | 16 KiB | 0x3e00 | USART0 | 412 B | 490 B |
|
||||
| ATmega324A, 324P, 324PA | 32 KiB | 0x7e00 | USART0 | 412 B | 490 B |
|
||||
| ATmega644, 644A, 644P, 644PA | 64 KiB | 0xfe00 | USART0 | 406 B | 484 B |
|
||||
| ATmega1284, 1284P | 128 KiB | 0x1fe00 | USART0 | 432 B | 510 B |
|
||||
| ATtiny13, ATtiny13A † | 1 KiB | 0x0200 | software | 394 B | 464 B |
|
||||
| ATtiny25 † | 2 KiB | 0x0600 | software | 398 B | 468 B |
|
||||
| ATtiny45 † | 4 KiB | 0x0e00 | software | 402 B | 472 B |
|
||||
| ATtiny85 † | 8 KiB | 0x1e00 | software | 402 B | 472 B |
|
||||
| ATmega8, 8A | 8 KiB | 0x1e00 | USART0 | 364 B | 478 B |
|
||||
| ATmega16, 16A | 16 KiB | 0x3e00 | USART0 | 366 B | 482 B |
|
||||
| ATmega32, 32A | 32 KiB | 0x7e00 | USART0 | 366 B | 482 B |
|
||||
| ATmega48, 48A, 48P, 48PA † | 4 KiB | 0x0e00 | USART0 | 392 B | 468 B |
|
||||
| ATmega88, 88A, 88P, 88PA | 8 KiB | 0x1e00 | USART0 | 402 B | 478 B |
|
||||
| ATmega168, 168A, 168P, 168PA | 16 KiB | 0x3e00 | USART0 | 404 B | 482 B |
|
||||
| ATmega328, 328P | 32 KiB | 0x7e00 | USART0 | 404 B | 482 B |
|
||||
| ATmega164A, 164P, 164PA | 16 KiB | 0x3e00 | USART0 | 404 B | 482 B |
|
||||
| ATmega324A, 324P, 324PA | 32 KiB | 0x7e00 | USART0 | 404 B | 482 B |
|
||||
| ATmega644, 644A, 644P, 644PA | 64 KiB | 0xfe00 | USART0 | 398 B | 476 B |
|
||||
| ATmega1284, 1284P | 128 KiB | 0x1fe00 | USART0 | 424 B | 502 B |
|
||||
|
||||
† No hardware boot section: the host patches the reset vector, and the budget
|
||||
is 510 bytes, since the slot's last word is the trampoline.
|
||||
|
||||
The tightest fit in the whole space is the 1284s' autobaud build, 510 of its
|
||||
512 — they alone carry the far-flash machinery (ELPM reads, RAMPZ page
|
||||
commands) and autobaud alone carries the calibration loop. Everything else has
|
||||
20 B of headroom or more. The flash bank riding in a transfer's selector byte
|
||||
keeps even those chips' addressing the same 16-bit form every other chip uses,
|
||||
which is why they are no longer the outlier they were.
|
||||
The tightest fit in the whole space is the 1284s' autobaud build deployed on a
|
||||
USART's own pins, 506 of its 512 — they alone carry the far-flash machinery
|
||||
(ELPM reads, RAMPZ page commands), autobaud alone carries the calibration loop,
|
||||
and a bit-banged link on a USART's pins alone has to release it (below). The
|
||||
same build on the default pins is 502. The flash bank riding in a transfer's
|
||||
selector byte keeps even those chips' addressing the same 16-bit form every
|
||||
other chip uses, which is why they are no longer the outlier they were.
|
||||
|
||||
The software UART enables the RX pull-up; TX idles high. All multi-byte wire
|
||||
quantities are little-endian.
|
||||
@@ -79,6 +80,13 @@ receiver's 100-cycles-a-bit floor. Whatever is picked or overridden is
|
||||
re-checked in the compile: an infeasible combination, or a USART the chip does
|
||||
not have, fails with a named static assert.
|
||||
|
||||
Putting a bit-banged link on a USART's own pins is a supported deployment, and
|
||||
the usual one where a board's USB bridge is wired to RXD/TXD: the link's `init`
|
||||
clears that USART's `UCSRnB` first, because while its `TXEN` is set the USART —
|
||||
not the port register — owns the TX pin, and a loader entered from an
|
||||
application that left it enabled would receive and obey while answering nothing
|
||||
(§20.2). It costs four bytes, and only on those pins.
|
||||
|
||||
`SERIAL autobaud` takes neither: the loader **measures** the host's bit timing
|
||||
at run time, so `CLOCK` and `BAUD` are not build parameters there and one
|
||||
binary per chip serves every clock and every rate. It is for the deployments
|
||||
@@ -397,8 +405,10 @@ Per chip preset, `ctest` runs:
|
||||
the fastest clock — where a software UART's per-bit spin outgrows its
|
||||
one-register delay loop and takes the 16-bit one. That is the largest image
|
||||
the configuration space produces, and a shape the ladder default (always the
|
||||
*fastest* rate a clock reaches) never picks. Pins are immediate operands and
|
||||
the timeout is a constant: neither is an axis;
|
||||
*fastest* rate a clock reaches) never picks. Pins are an axis for one reason
|
||||
only, and it is enough: a bit-banged link on a USART's own pins has to
|
||||
release that USART, so `pureboot_{sw,autobaud}_on_usart{0,1}` build there
|
||||
too. The timeout is a constant and is no axis;
|
||||
- `pureboot_autobaud.size` — the clock-free build, which has no clock or baud
|
||||
axis of its own: one binary per chip has to serve every point the matrix
|
||||
below sweeps;
|
||||
@@ -412,6 +422,9 @@ Per chip preset, `ctest` runs:
|
||||
flash-resident section but `.text`, and the image byte-identical when linked
|
||||
at a different base — which is position independence itself rather than a
|
||||
proxy for it;
|
||||
- `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.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
|
||||
@@ -434,6 +447,12 @@ Per chip preset, `ctest` runs:
|
||||
- `pureboot.usart1` (644A) — the same suite over the second hardware USART:
|
||||
instance selection is compile-checked everywhere, but only a live session
|
||||
proves the loader polls the USART it claims;
|
||||
- `pureboot.mute` (328P) — a software link on USART0's own pins, entered from an
|
||||
application that handed over with that USART still enabled: the loader must
|
||||
still answer, which it does only because it releases it. The pin ownership is
|
||||
the runner's, not simavr's — simavr wires a USART through IRQs and never takes
|
||||
the pin from the port, so without that model the state under test could not
|
||||
arise at all;
|
||||
- `pureboot.dirty` (328P) — entering the loader from a running application over
|
||||
an SPM buffer it deliberately dirtied, the case the loader declines to guard:
|
||||
a bare verify must see the corruption and the repairing verify must fix it in
|
||||
@@ -449,5 +468,5 @@ Per chip preset, `ctest` runs:
|
||||
exists for. A lone calibration pulse with no knock behind it must still let
|
||||
the application boot, so no wait in activation can be unbounded.
|
||||
|
||||
`size`, `pi` and `planner` are host logic and run anywhere; the
|
||||
`size`, `pi`, `planner` and `handshake` are host logic and run anywhere; the
|
||||
simulator-driven targets need simavr and a pty, so they are POSIX-only.
|
||||
|
||||
Reference in New Issue
Block a user