test: the exhaustive clock x baud x backend size matrix
Every plausible oscillator against every rate it reaches against every backend, on one chip per size-bearing class, under --full only. The baud ladder becomes a reachability predicate the enumeration filters on, so an unreachable point drops out instead of aborting the configure. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
102
CMakeLists.txt
102
CMakeLists.txt
@@ -233,11 +233,11 @@ if(PROJECT_IS_TOP_LEVEL)
|
|||||||
# The size matrix: every configuration axis that could move the image
|
# The size matrix: every configuration axis that could move the image
|
||||||
# size — the serial backend (different code), the USART instance
|
# size — the serial backend (different code), the USART instance
|
||||||
# (different registers), the clock (different constants), and the baud
|
# (different registers), the clock (different constants), and the baud
|
||||||
# through the two shapes its bit timing takes — each combination must
|
# through the shapes its bit timing takes — each combination must still
|
||||||
# still fit the chip's slot budget. Pins are size-neutral (port and bit
|
# fit the chip's slot budget. Pins are size-neutral (port and bit are
|
||||||
# are immediate operands) and the timeout is a constant, so neither adds
|
# immediate operands) and the timeout is a constant, so neither adds an
|
||||||
# an axis. The stock build is one point of this matrix and already has
|
# axis. The stock build is one point of this matrix and already has its
|
||||||
# its test.
|
# test.
|
||||||
function(pureboot_size_variant name)
|
function(pureboot_size_variant name)
|
||||||
pureboot_add_loader(${name} ${ARGN})
|
pureboot_add_loader(${name} ${ARGN})
|
||||||
add_test(NAME ${name}.size
|
add_test(NAME ${name}.size
|
||||||
@@ -245,42 +245,86 @@ if(PROJECT_IS_TOP_LEVEL)
|
|||||||
-DLIMIT=${PUREBOOT_LIMIT} -P ${CMAKE_CURRENT_SOURCE_DIR}/test/check_size.cmake)
|
-DLIMIT=${PUREBOOT_LIMIT} -P ${CMAKE_CURRENT_SOURCE_DIR}/test/check_size.cmake)
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
|
# One point of the exhaustive matrix, named from its resolved parameters
|
||||||
|
# so the enumeration cannot collide with itself. Unreachable rates drop
|
||||||
|
# out here rather than aborting the configure.
|
||||||
|
function(pureboot_matrix_point hz baud link)
|
||||||
|
if(link STREQUAL "software")
|
||||||
|
pureboot_baud_feasible(${hz} ${baud} 1 _ok)
|
||||||
|
set(_args SERIAL software)
|
||||||
|
else()
|
||||||
|
pureboot_baud_feasible(${hz} ${baud} 0 _ok)
|
||||||
|
set(_args USART ${link})
|
||||||
|
endif()
|
||||||
|
if(_ok)
|
||||||
|
pureboot_size_variant(pbm_${hz}_${baud}_${link} CLOCK ${hz} BAUD ${baud} ${_args})
|
||||||
|
endif()
|
||||||
|
endfunction()
|
||||||
|
|
||||||
# Clock points: the shipped-fuse floor (CKDIV8), the calibrated RC, and
|
# Clock points: the shipped-fuse floor (CKDIV8), the calibrated RC, and
|
||||||
# the crystal the stock build assumes (the tiny13's ladder is its own RC
|
# the crystal the stock build assumes (the tiny13's ladder is its own RC
|
||||||
# menu — it has no crystal option).
|
# menu — it has no crystal option).
|
||||||
if(LIBAVR_MCU MATCHES "^attiny13")
|
if(LIBAVR_MCU MATCHES "^attiny13")
|
||||||
set(_matrix_clocks 1200000 4800000 9600000)
|
set(_matrix_clocks 1200000 4800000 9600000)
|
||||||
|
set(_full_clocks 128000 600000 1200000 4800000 9600000)
|
||||||
else()
|
else()
|
||||||
set(_matrix_clocks 1000000 8000000 16000000)
|
set(_matrix_clocks 1000000 8000000 16000000)
|
||||||
|
set(_full_clocks 128000 1000000 1843200 2000000 3686400 4000000 7372800 8000000
|
||||||
|
11059200 12000000 14745600 16000000 18432000 20000000)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# The exhaustive cross product: every clock a deployment plausibly runs
|
||||||
|
# — the internal oscillators, the shipped CKDIV8 floor, the plain
|
||||||
|
# crystals and the UART crystals — against every rate, against every
|
||||||
|
# backend. Beyond the ladder the list carries the slow rates a
|
||||||
|
# sub-megahertz oscillator is left with, which no ladder rate reaches
|
||||||
|
# (16000 Bd is the only rate the 128 kHz oscillator holds exactly); at
|
||||||
|
# the fast clocks those same rates also select the software UART's
|
||||||
|
# 16-bit _delay_loop_2 bit spin (two words more setup at each of its five
|
||||||
|
# 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.
|
||||||
|
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)
|
||||||
|
foreach(_matrix_hz IN LISTS _full_clocks)
|
||||||
|
foreach(_matrix_baud IN LISTS _full_bauds)
|
||||||
|
pureboot_matrix_point(${_matrix_hz} ${_matrix_baud} software)
|
||||||
|
if(PUREBOOT_HAS_USART)
|
||||||
|
pureboot_matrix_point(${_matrix_hz} ${_matrix_baud} 0)
|
||||||
|
endif()
|
||||||
|
if(PUREBOOT_HAS_USART1)
|
||||||
|
pureboot_matrix_point(${_matrix_hz} ${_matrix_baud} 1)
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
endforeach()
|
||||||
|
else()
|
||||||
|
foreach(_matrix_hz IN LISTS _matrix_clocks)
|
||||||
|
math(EXPR _matrix_khz "${_matrix_hz} / 1000")
|
||||||
|
if(PUREBOOT_HAS_USART OR NOT _matrix_hz EQUAL _pb_stock_hz)
|
||||||
|
pureboot_size_variant(pureboot_sw_${_matrix_khz}k CLOCK ${_matrix_hz} SERIAL software)
|
||||||
|
endif()
|
||||||
|
if(PUREBOOT_HAS_USART AND NOT _matrix_hz EQUAL _pb_stock_hz)
|
||||||
|
pureboot_size_variant(pureboot_hw_${_matrix_khz}k CLOCK ${_matrix_hz} SERIAL hardware)
|
||||||
|
endif()
|
||||||
|
if(PUREBOOT_HAS_USART1 AND NOT _matrix_hz EQUAL _pb_stock_hz)
|
||||||
|
pureboot_size_variant(pureboot_usart1_${_matrix_khz}k CLOCK ${_matrix_hz} USART 1)
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
list(GET _matrix_clocks -1 _matrix_top_hz)
|
||||||
|
pureboot_size_variant(pureboot_sw_wide CLOCK ${_matrix_top_hz} BAUD 9600 SERIAL software)
|
||||||
endif()
|
endif()
|
||||||
foreach(_matrix_hz IN LISTS _matrix_clocks)
|
|
||||||
math(EXPR _matrix_khz "${_matrix_hz} / 1000")
|
|
||||||
if(PUREBOOT_HAS_USART OR NOT _matrix_hz EQUAL _pb_stock_hz)
|
|
||||||
pureboot_size_variant(pureboot_sw_${_matrix_khz}k CLOCK ${_matrix_hz} SERIAL software)
|
|
||||||
endif()
|
|
||||||
if(PUREBOOT_HAS_USART AND NOT _matrix_hz EQUAL _pb_stock_hz)
|
|
||||||
pureboot_size_variant(pureboot_hw_${_matrix_khz}k CLOCK ${_matrix_hz} SERIAL hardware)
|
|
||||||
endif()
|
|
||||||
if(PUREBOOT_HAS_USART1 AND NOT _matrix_hz EQUAL _pb_stock_hz)
|
|
||||||
pureboot_size_variant(pureboot_usart1_${_matrix_khz}k CLOCK ${_matrix_hz} USART 1)
|
|
||||||
endif()
|
|
||||||
endforeach()
|
|
||||||
if(PUREBOOT_HAS_USART1)
|
if(PUREBOOT_HAS_USART1)
|
||||||
pureboot_size_variant(pureboot_usart1 USART 1)
|
pureboot_size_variant(pureboot_usart1 USART 1)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# The baud axis, whose one size-bearing shape the ladder never picks: a
|
|
||||||
# software UART spins out each bit with _delay_loop_1 while the count
|
|
||||||
# fits a byte and with the 16-bit _delay_loop_2 beyond it, two words more
|
|
||||||
# setup at every one of its five sites — the largest image the
|
|
||||||
# configuration space produces. The ladder default takes the *fastest*
|
|
||||||
# rate a clock reaches, which always lands in the byte, so the wide form
|
|
||||||
# needs the slowest ladder rate against the fastest clock to appear. The
|
|
||||||
# hardware USART has no such shape: its baud is a divisor constant, and
|
|
||||||
# the ladder's U2X solutions are already its larger form.
|
|
||||||
list(GET _matrix_clocks -1 _matrix_top_hz)
|
|
||||||
pureboot_size_variant(pureboot_sw_wide CLOCK ${_matrix_top_hz} BAUD 9600 SERIAL software)
|
|
||||||
|
|
||||||
# One configured deployment end to end — a real board's shape rather
|
# One configured deployment end to end — a real board's shape rather
|
||||||
# than the stock assumption: the ATmega328P on its shipped 1 MHz fuses,
|
# than the stock assumption: the ATmega328P on its shipped 1 MHz fuses,
|
||||||
# the software UART on hand-picked pins (TX = PB1, RX = PB5), the ladder
|
# the software UART on hand-picked pins (TX = PB1, RX = PB5), the ladder
|
||||||
|
|||||||
@@ -146,36 +146,51 @@ set(PUREBOOT_HAS_USART ${_pb_has_usart} PARENT_SCOPE)
|
|||||||
set(PUREBOOT_HAS_USART1 ${_pb_has_usart1} PARENT_SCOPE)
|
set(PUREBOOT_HAS_USART1 ${_pb_has_usart1} PARENT_SCOPE)
|
||||||
set(PUREBOOT_SIM_MCU ${_pb_sim_mcu} PARENT_SCOPE)
|
set(PUREBOOT_SIM_MCU ${_pb_sim_mcu} PARENT_SCOPE)
|
||||||
|
|
||||||
# The fastest standard rate the clock reaches within 2.5 %, by the same
|
# The rates a default may pick, fastest first.
|
||||||
# best-of-U2X-and-plain divisor search libavr's solve_baud runs, so a default
|
set_property(GLOBAL PROPERTY PUREBOOT_BAUD_LADDER 115200 57600 38400 19200 9600)
|
||||||
|
|
||||||
|
# Whether <baud> is reachable from <clock> within 2.5 %, by the same
|
||||||
|
# best-of-U2X-and-plain divisor search libavr's solve_baud runs, so a build
|
||||||
# never trips the compile-time error it is checked against. A software build
|
# never trips the compile-time error it is checked against. A software build
|
||||||
# also needs the polled receiver's 100-cycles-a-bit floor: at low clocks the
|
# also needs the polled receiver's 100-cycles-a-bit floor: at low clocks the
|
||||||
# U2X divisor reaches rates the bit-banged sampler cannot.
|
# U2X divisor reaches rates the bit-banged sampler cannot.
|
||||||
function(pureboot_default_baud clock software outvar)
|
function(pureboot_baud_feasible clock baud software outvar)
|
||||||
foreach(baud 115200 57600 38400 19200 9600)
|
set(${outvar} 0 PARENT_SCOPE)
|
||||||
math(EXPR _cycles "${clock} / ${baud}")
|
math(EXPR _cycles "${clock} / ${baud}")
|
||||||
if(software AND _cycles LESS 100)
|
if(software AND _cycles LESS 100)
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
foreach(divisor 8 16)
|
||||||
|
math(EXPR _step "${divisor} * ${baud}")
|
||||||
|
math(EXPR _n "(${clock} + ${_step} / 2) / ${_step}")
|
||||||
|
if(_n LESS 1 OR _n GREATER 4096)
|
||||||
continue()
|
continue()
|
||||||
endif()
|
endif()
|
||||||
foreach(divisor 8 16)
|
math(EXPR _actual "${clock} / (${divisor} * ${_n})")
|
||||||
math(EXPR _step "${divisor} * ${baud}")
|
math(EXPR _delta "${_actual} - ${baud}")
|
||||||
math(EXPR _n "(${clock} + ${_step} / 2) / ${_step}")
|
if(_delta LESS 0)
|
||||||
if(_n LESS 1 OR _n GREATER 4096)
|
math(EXPR _delta "-(${_delta})")
|
||||||
continue()
|
endif()
|
||||||
endif()
|
math(EXPR _error_bp "${_delta} * 10000 / ${baud}")
|
||||||
math(EXPR _actual "${clock} / (${divisor} * ${_n})")
|
if(_error_bp LESS_EQUAL 250)
|
||||||
math(EXPR _delta "${_actual} - ${baud}")
|
set(${outvar} 1 PARENT_SCOPE)
|
||||||
if(_delta LESS 0)
|
return()
|
||||||
math(EXPR _delta "-(${_delta})")
|
endif()
|
||||||
endif()
|
|
||||||
math(EXPR _error_bp "${_delta} * 10000 / ${baud}")
|
|
||||||
if(_error_bp LESS_EQUAL 250)
|
|
||||||
set(${outvar} ${baud} PARENT_SCOPE)
|
|
||||||
return()
|
|
||||||
endif()
|
|
||||||
endforeach()
|
|
||||||
endforeach()
|
endforeach()
|
||||||
message(FATAL_ERROR "pureboot: no standard baud rate fits a ${clock} Hz clock within 2.5 %")
|
endfunction()
|
||||||
|
|
||||||
|
# The fastest ladder rate the clock reaches.
|
||||||
|
function(pureboot_default_baud clock software outvar)
|
||||||
|
get_property(_ladder GLOBAL PROPERTY PUREBOOT_BAUD_LADDER)
|
||||||
|
foreach(baud ${_ladder})
|
||||||
|
pureboot_baud_feasible(${clock} ${baud} ${software} _ok)
|
||||||
|
if(_ok)
|
||||||
|
set(${outvar} ${baud} PARENT_SCOPE)
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
message(FATAL_ERROR "pureboot: no standard baud rate fits a ${clock} Hz clock within 2.5 % "
|
||||||
|
"— pass BAUD <rate> to deploy a non-standard one")
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
# pureboot_add_loader(<name> [CLOCK <hz>] [BAUD <bd>]
|
# pureboot_add_loader(<name> [CLOCK <hz>] [BAUD <bd>]
|
||||||
|
|||||||
@@ -309,6 +309,13 @@ Per chip preset, `ctest` runs:
|
|||||||
the configuration space produces, and a shape the ladder default (always the
|
the configuration space produces, and a shape the ladder default (always the
|
||||||
*fastest* rate a clock reaches) never picks. Pins are immediate operands and
|
*fastest* rate a clock reaches) never picks. Pins are immediate operands and
|
||||||
the timeout is a constant: neither is an axis;
|
the timeout is a constant: neither is an axis;
|
||||||
|
- `pbm_*.size` — under `--full`, the exhaustive cross product replacing that
|
||||||
|
compact matrix: every plausible oscillator (the internal ones, the CKDIV8
|
||||||
|
floor, the plain and the UART crystals) × every rate reachable from it ×
|
||||||
|
every backend, unreachable combinations dropping out rather than aborting
|
||||||
|
the configure. Bounded to one chip per size-bearing class — flash
|
||||||
|
addressing, hand-over shape, page size, USART inventory — since everything
|
||||||
|
else in the image is chip-independent code;
|
||||||
- `pureboot.pi` — the position-independence lint: no absolute `jmp`/`call`, the
|
- `pureboot.pi` — the position-independence lint: no absolute `jmp`/`call`, the
|
||||||
info block within the image's first 256 bytes;
|
info block within the image's first 256 bytes;
|
||||||
- `pureboot.planner` — the host tool's pure logic: programming orders and their
|
- `pureboot.planner` — the host tool's pure logic: programming orders and their
|
||||||
|
|||||||
@@ -2,12 +2,14 @@
|
|||||||
# The port's gate: every chip's generated workflow — build, size matrix, and
|
# The port's gate: every chip's generated workflow — build, size matrix, and
|
||||||
# the simulator-driven protocol suites. --full adds the reflect-spot builds
|
# the simulator-driven protocol suites. --full adds the reflect-spot builds
|
||||||
# (libavr's rule: reflect compiles are bounded to its spot set, never the
|
# (libavr's rule: reflect compiles are bounded to its spot set, never the
|
||||||
# full matrix). LIBAVR_ROOT must point at the libavr checkout.
|
# full matrix) and swaps the compact size matrix for the exhaustive
|
||||||
|
# clock × baud × backend cross product. LIBAVR_ROOT must point at the libavr
|
||||||
|
# checkout.
|
||||||
set -e
|
set -e
|
||||||
cd "$(dirname "$0")/.."
|
cd "$(dirname "$0")/.."
|
||||||
|
|
||||||
full=0
|
full=0
|
||||||
[[ "$1" == "--full" ]] && { full=1; shift; }
|
[[ "$1" == "--full" ]] && { full=1; shift; export PUREBOOT_FULL_MATRIX=1; }
|
||||||
|
|
||||||
CHIPS=(attiny13 attiny13a attiny25 attiny45 attiny85
|
CHIPS=(attiny13 attiny13a attiny25 attiny45 attiny85
|
||||||
atmega8 atmega8a atmega16 atmega16a atmega32 atmega32a
|
atmega8 atmega8a atmega16 atmega16a atmega32 atmega32a
|
||||||
|
|||||||
Reference in New Issue
Block a user