pureboot 6: a build-time OSCCAL trim, applied ahead of every reset path
The RC-oscillator answer's device half (dev/tasks.md in libavr): OSCCAL joins pureboot_add_loader() as one optional byte, written at the top of run() before the WDRF bail so the watchdog hand-over inherits the corrected clock too. Orthogonal to the backend — an autobaud build may carry it purely for the application. No value, no code: the stock image differs from v5 in exactly the version's two bytes (the stamp and the 'b' immediate). Measured: +6 B where OSCCAL takes sts (328P, 404→410), +4 B in low I/O (t85, 402→406); the tightest image in the space (1284 autobaud on USART pins, 504) carries the sts form at 510 of 512. New gates: the OSCCAL size points on every chip, the wire-observed trim byte on both addressing classes (test/pbosccal.py, red-green), and the autobaud unit pinned to ram_start (test/check_unit.cmake, red-green) — the address --info's measured-clock read is about to rely on. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
23
test/check_unit.cmake
Normal file
23
test/check_unit.cmake
Normal file
@@ -0,0 +1,23 @@
|
||||
# Asserts the autobaud loader's measured unit is the first RAM object: the
|
||||
# host tool reads the bit period from ram_start (--info's measured clock), so
|
||||
# the unit's address is wire contract. Run as
|
||||
# cmake -DOBJDUMP=... -DELF=... -DRAM_START=<data address> -P check_unit.cmake
|
||||
|
||||
execute_process(COMMAND ${OBJDUMP} -t ${ELF} OUTPUT_VARIABLE _syms RESULT_VARIABLE _res)
|
||||
if(NOT _res EQUAL 0)
|
||||
message(FATAL_ERROR "${OBJDUMP} -t ${ELF} failed")
|
||||
endif()
|
||||
|
||||
# The symbol line: "00800100 l O .noinit 00000002 <mangled>unit_E".
|
||||
string(REGEX MATCH "\n0*([0-9a-f]+)[^\n]+[ \t][^ \t\n]*unit_[^ \t\n]*\n" _line "${_syms}")
|
||||
if(NOT _line)
|
||||
message(FATAL_ERROR "no unit_ symbol in ${ELF} — is this the autobaud loader?")
|
||||
endif()
|
||||
|
||||
# AVR data-space symbols carry the 0x800000 VMA offset.
|
||||
math(EXPR _want "0x800000 + ${RAM_START}" OUTPUT_FORMAT HEXADECIMAL)
|
||||
math(EXPR _have "0x${CMAKE_MATCH_1}" OUTPUT_FORMAT HEXADECIMAL)
|
||||
if(NOT _have STREQUAL _want)
|
||||
message(FATAL_ERROR "unit_ sits at ${_have}, ram_start is ${_want} — the host peeks ram_start")
|
||||
endif()
|
||||
message(STATUS "unit_ at ${_have} == ram_start")
|
||||
46
test/pbosccal.py
Normal file
46
test/pbosccal.py
Normal file
@@ -0,0 +1,46 @@
|
||||
#!/usr/bin/env python3
|
||||
"""The build-time OSCCAL trim, observed through the wire: a loader built with
|
||||
the OSCCAL axis holds the trim register at the built byte from its first
|
||||
prompt on — the write sits at the top of run(), ahead of the WDRF bail, so
|
||||
every path out of reset runs on the corrected clock. simavr's clock does not
|
||||
follow OSCCAL, which is what makes the value assertable at all: the register
|
||||
is plain state there, and the peek must return exactly what the build
|
||||
declared rather than whatever the oscillator needed.
|
||||
|
||||
Usage: pbosccal.py <device_bin> <pureboot_elf> <mcu> <hz> <base_hex> <page>
|
||||
<baud> <osccal_addr> <osccal_value> <tool_py> <workdir>
|
||||
[link]
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
def fail(message):
|
||||
print(f"FAIL: {message}")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def main():
|
||||
args = sys.argv[1:]
|
||||
link = args.pop() if len(args) == 12 else None
|
||||
(device_bin, elf, mcu, hz, base_hex, page, baud, addr, value, tool, workdir) = args
|
||||
addr, value, baud = int(addr, 0), int(value, 0), int(baud)
|
||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
import pbsim
|
||||
|
||||
os.makedirs(workdir, exist_ok=True)
|
||||
dump = os.path.join(workdir, "flash_dump.bin")
|
||||
device = pbsim.Device(device_bin, elf, mcu, hz, base_hex, page, baud, dump, link=link)
|
||||
try:
|
||||
out = pbsim.run_tool(tool, device.pty, baud, "--peek", f"{addr:#x}:1")
|
||||
want = f"{addr:#06x} {value:02x}"
|
||||
if want not in out:
|
||||
fail(f"OSCCAL at {addr:#x} did not read back {value:#04x}:\n{out}")
|
||||
finally:
|
||||
device.stop()
|
||||
print("OK")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user