pureboot 5: one command pair for every memory, and a clock-free backend
R/r/w/F collapse into G and g over a selector byte naming the space — flash,
EEPROM, data, fuse, SPM — with the flash bank in its high nibble. Four command
bodies, four transfer loops and four argument decodes become one of each, and
W joins the same decode instead of keeping an address form of its own. The
loader shrinks while gaining everything below: on the 1284P the stock build
goes 480 -> 432 B and the software one 496 -> 450.
What the freed space buys:
- Data space. On AVR one pointer spans SRAM, the register file and the whole
I/O space, so G over space 2 reads all three. pureboot keeps zero static
RAM and pushes no register, so at loader entry an application's SRAM is
still what the application left there — this is a post-mortem, not just a
poke hole. As its own command it needed a dispatch arm and a loop; as one
more space it is a single ld/st.
- Host-issued SPM. W fills the page buffer and stops; erase, write and RWW
re-enable are writes to space 4, which reach the same fused store-and-SPM
pair through the transfer's own address and data. Any SPM operation, lock
bits included, is now reachable and the loader carries no page-commit logic.
The four-cycle SPMCSR-to-SPM window is why that primitive stays fused: no
host can hit it across a serial link, and that — not the byte count — is
the floor on how low-level a bootloader's primitives can go.
- Byte addresses everywhere. The bank in the selector retires the
word-addressed wire the >64 KiB parts needed, so the 1284s stop being the
outlier.
SERIAL autobaud is a third backend on the same loader, over libavr's
software_autobaud: no clock, no baud, one binary per chip for every F_CPU and
every rate. Activation counts poll iterations rather than seconds and bounds
every wait, so a stray pulse cannot hold an unattended device.
b answers with the version and signature only; the host derives geometry from
the signature, which is what an autobaud build requires anyway. An update image
is a bare slot with no device to ask, so every image carries a six-byte stamp —
the same bytes b answers with, and the source of both — that the loader never
reads from flash and the host refuses to install a mismatch against. The
running-slot write guard moved onto the SPM commit, which covers erase and
write both where guarding W covered neither directly.
The position-independence lint now proves the property instead of a proxy for
it: the image must come out byte-identical linked at a different base.
-fno-move-loop-invariants left the tuned flag set — it was fitted to a command
loop carrying four transfer bodies and costs bytes now that it carries one.
Verified: the exhaustive matrix on all 37 chips (every clock x every baud x
every backend, non-standard rates included, plus the autobaud build) —
8174 size checks, no failures, tightest fit the 1284s' autobaud at 510 of 512.
Behavioral suites green on every chip class: t13a 10/10, t85 11/11, m8 13/13,
m16a 13/13, m48pa 13/13, 328P 23/23, 644A 17/17, 1284P 17/17. Data-space
round trip through --peek/--poke and the autobaud handshake are both red-green
proven.
The two prototype sources and their findings file go; the README carries the
protocol and dev/done.md in libavr carries the reasoning.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -150,7 +150,10 @@ if(PROJECT_IS_TOP_LEVEL)
|
||||
if(Python3_FOUND)
|
||||
add_test(NAME pureboot.pi
|
||||
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test/check_pi.py
|
||||
${CMAKE_OBJDUMP} ${CMAKE_NM} $<TARGET_FILE:pureboot> ${PUREBOOT_BASE_HEX})
|
||||
${CMAKE_OBJDUMP} ${CMAKE_OBJCOPY} ${CMAKE_CXX_COMPILER} ${LIBAVR_MCU}
|
||||
$<TARGET_FILE:pureboot>
|
||||
${CMAKE_BINARY_DIR}/CMakeFiles/pureboot.dir/pureboot/pureboot.cpp.obj
|
||||
${PUREBOOT_BASE_HEX})
|
||||
add_test(NAME pureboot.planner
|
||||
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test/test_planner.py
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/pureboot/pureboot.py)
|
||||
@@ -245,23 +248,14 @@ if(PROJECT_IS_TOP_LEVEL)
|
||||
-DLIMIT=${PUREBOOT_LIMIT} -P ${CMAKE_CURRENT_SOURCE_DIR}/test/check_size.cmake)
|
||||
endfunction()
|
||||
|
||||
# The autobaud loader (pureboot/autobaud.md): one clock-agnostic image per
|
||||
# chip, no clock × baud axis, size-tested against the same per-chip budget on
|
||||
# every chip.
|
||||
#
|
||||
# Only the unified version is built. Its two predecessors —
|
||||
# pureboot_autobaud_pure.cpp at 508 B and pureboot_autobaud_reg.cpp at 512 —
|
||||
# had 4 B and 0 B of margin on the 1284P, and the fix for the activation hang
|
||||
# costs ~22, which puts them at 530 and 534. Neither can ship, so the choice
|
||||
# the branch existed to offer is settled by measurement rather than taste.
|
||||
# The sources stay for the record; autobaud.md carries the numbers.
|
||||
function(pureboot_autobaud_variant name source)
|
||||
pureboot_add_autobaud(${name} ${source})
|
||||
add_test(NAME ${name}.size
|
||||
COMMAND ${CMAKE_COMMAND} -DSIZE_TOOL=${CMAKE_SIZE} -DELF=$<TARGET_FILE:${name}>
|
||||
-DLIMIT=${PUREBOOT_LIMIT} -P ${CMAKE_CURRENT_SOURCE_DIR}/test/check_size.cmake)
|
||||
endfunction()
|
||||
pureboot_autobaud_variant(pureboot_autobaud_uni pureboot_autobaud_uni.cpp)
|
||||
# The autobaud loader: one clock-agnostic image per chip, so it has no
|
||||
# clock x baud axis of its own — the matrix below sweeps those for the
|
||||
# fixed-baud builds, and this one binary has to serve all of them at run
|
||||
# time. Size-tested against the same per-chip budget as every other variant.
|
||||
pureboot_add_loader(pureboot_autobaud SERIAL autobaud)
|
||||
add_test(NAME pureboot_autobaud.size
|
||||
COMMAND ${CMAKE_COMMAND} -DSIZE_TOOL=${CMAKE_SIZE} -DELF=$<TARGET_FILE:pureboot_autobaud>
|
||||
-DLIMIT=${PUREBOOT_LIMIT} -P ${CMAKE_CURRENT_SOURCE_DIR}/test/check_size.cmake)
|
||||
|
||||
# One point of the exhaustive matrix, named from its resolved parameters
|
||||
# so the enumeration cannot collide with itself. Unreachable rates drop
|
||||
@@ -302,16 +296,15 @@ if(PROJECT_IS_TOP_LEVEL)
|
||||
# sites), the largest image the space produces and a shape the ladder
|
||||
# default — always the *fastest* rate a clock reaches — never picks.
|
||||
#
|
||||
# Bounded to one chip per size-bearing class: flash addressing (the
|
||||
# word-addressed 1284), hand-over shape (the patched vector on the tinies
|
||||
# and m48s), page size, and USART inventory. Everything else in the image
|
||||
# is chip-independent code, so a further chip buys builds and no
|
||||
# coverage; every chip outside the set carries the compact matrix.
|
||||
# Every chip runs the full cross product: the size-bearing classes (flash
|
||||
# addressing, hand-over shape, page size, USART inventory) are what make
|
||||
# the image differ, and a chip outside them is expected to match its class
|
||||
# — but "expected" is what a matrix is for, and the whole sweep is cheap
|
||||
# enough to run rather than reason about. PUREBOOT_FULL_MATRIX is what
|
||||
# selects it; the compact matrix below is the per-commit default.
|
||||
get_property(_full_bauds GLOBAL PROPERTY PUREBOOT_BAUD_LADDER)
|
||||
list(APPEND _full_bauds 16000 4800 2400 1200)
|
||||
set(_matrix_spot attiny13a attiny85 atmega48pa atmega8a atmega168pa
|
||||
atmega328p atmega164a atmega644a atmega1284p)
|
||||
if(DEFINED ENV{PUREBOOT_FULL_MATRIX} AND LIBAVR_MCU IN_LIST _matrix_spot)
|
||||
if(DEFINED ENV{PUREBOOT_FULL_MATRIX})
|
||||
foreach(_matrix_hz IN LISTS _full_clocks)
|
||||
foreach(_matrix_baud IN LISTS _full_bauds)
|
||||
pureboot_matrix_point(${_matrix_hz} ${_matrix_baud} software)
|
||||
@@ -409,14 +402,12 @@ if(PROJECT_IS_TOP_LEVEL)
|
||||
add_custom_command(TARGET pbapp_autobaud POST_BUILD
|
||||
COMMAND ${CMAKE_OBJCOPY} -O binary
|
||||
$<TARGET_FILE:pbapp_autobaud> $<TARGET_FILE:pbapp_autobaud>.bin)
|
||||
foreach(_variant uni)
|
||||
add_test(NAME pureboot.autobaud_${_variant}
|
||||
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test/pbautobaud.py
|
||||
${PB_DEVICE} $<TARGET_FILE:pureboot_autobaud_${_variant}> ${PUREBOOT_SIM_MCU}
|
||||
${PUREBOOT_BASE_HEX} ${PUREBOOT_PAGE} $<TARGET_FILE:pbapp_autobaud>.bin
|
||||
1000000 9600 ${CMAKE_CURRENT_SOURCE_DIR}/pureboot/pureboot.py
|
||||
${CMAKE_BINARY_DIR}/pbautobaud-${_variant}-work)
|
||||
set_tests_properties(pureboot.autobaud_${_variant} PROPERTIES TIMEOUT 240)
|
||||
endforeach()
|
||||
add_test(NAME pureboot.autobaud
|
||||
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test/pbautobaud.py
|
||||
${PB_DEVICE} $<TARGET_FILE:pureboot_autobaud> ${PUREBOOT_SIM_MCU}
|
||||
${PUREBOOT_BASE_HEX} ${PUREBOOT_PAGE} $<TARGET_FILE:pbapp_autobaud>.bin
|
||||
1000000 9600 ${CMAKE_CURRENT_SOURCE_DIR}/pureboot/pureboot.py
|
||||
${CMAKE_BINARY_DIR}/pbautobaud-work)
|
||||
set_tests_properties(pureboot.autobaud PROPERTIES TIMEOUT 240)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
Reference in New Issue
Block a user