build: the libavr pin advances past phase 6
The renames land (interrupt_guard, consume_reset_cause, set_duty), the sampler binds its input in the new converter shape (the input pack plus in<>::start() as free-running's one kick), and the console states .allow_baud_error = true for the 115200-at-16-MHz this board has always spoken - the receiver-tolerance table libavr now enforces is stricter than the rate's own +2.1 %. The loader probe reads through avr::flash_load instead of raw pgmspace, the terminal's line buffer is std::array with backspace and delete named, the tree is reformatted under InsertBraces, and the sources are ASCII. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# The board has no reset line and no programming header, so the loader-entry
|
||||
# route in the emitted image is the only thing standing between a firmware change
|
||||
# and an unreflashable board. It has been wrong before — see the script.
|
||||
# and an unreflashable board. It has been wrong before - see the script.
|
||||
find_package(Python3 COMPONENTS Interpreter)
|
||||
if(Python3_FOUND)
|
||||
add_test(NAME fantemp.reachability
|
||||
@@ -8,5 +8,5 @@ if(Python3_FOUND)
|
||||
--objdump ${CMAKE_OBJDUMP} --elf $<TARGET_FILE:fantemp>
|
||||
--image $<TARGET_FILE_DIR:fantemp>/fantemp.bin)
|
||||
else()
|
||||
message(STATUS "Python not found — the reachability check is skipped")
|
||||
message(STATUS "Python not found - the reachability check is skipped")
|
||||
endif()
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
This board has no reset line and no programming header. The single route to the
|
||||
bootloader is the running firmware's `bootloader` command, so a firmware that
|
||||
gets that route wrong is a board that cannot be reflashed — and the failure is
|
||||
gets that route wrong is a board that cannot be reflashed - and the failure is
|
||||
silent, because everything else still works.
|
||||
|
||||
It has been wrong before. The firmware this one replaces probed and jumped to
|
||||
@@ -15,7 +15,7 @@ hardware, which is what this replaces.
|
||||
Three properties, all read out of the disassembly rather than the source:
|
||||
|
||||
1. The image ends below the boot section. `hfuse d4` puts that at 0x7c00, so an
|
||||
application reaching into it would be overwritten by the loader — or worse,
|
||||
application reaching into it would be overwritten by the loader - or worse,
|
||||
executed at reset, since BOOTRST points there.
|
||||
2. The hand-over targets the loader base. A word address of 0x3f00 is byte
|
||||
0x7e00; anything else is the 0x7800 bug again.
|
||||
@@ -60,7 +60,7 @@ def main() -> int:
|
||||
text = subprocess.run([args.objdump, "-d", str(args.elf)],
|
||||
capture_output=True, text=True, check=True).stdout
|
||||
|
||||
# The address the hand-over actually targets, read at its call sites — not
|
||||
# The address the hand-over actually targets, read at its call sites - not
|
||||
# "does the image contain this byte somewhere", which proves nothing: 0x3f is
|
||||
# an ordinary constant that appears in the curve tables, so a check like that
|
||||
# passes just as happily on the 0x7800 bug it is supposed to catch.
|
||||
@@ -83,7 +83,7 @@ def main() -> int:
|
||||
|
||||
want = LOADER_BASE // 2
|
||||
if not sites:
|
||||
failures.append("no call to bootloader::call with a loaded target — the "
|
||||
failures.append("no call to bootloader::call with a loaded target - the "
|
||||
"hand-over could not be read out of the image")
|
||||
elif wrong := [a for a in sites if a != want]:
|
||||
failures.append(f"the hand-over targets word {[hex(a) for a in wrong]} "
|
||||
@@ -95,13 +95,13 @@ def main() -> int:
|
||||
|
||||
# An icall/ijmp has to exist for that address to be jumped to indirectly.
|
||||
if not re.search(r"\b(icall|ijmp)\b", text):
|
||||
failures.append("no icall/ijmp — the hand-over cannot reach across flash")
|
||||
failures.append("no icall/ijmp - the hand-over cannot reach across flash")
|
||||
else:
|
||||
print(" ok an indirect call exists (a relative one cannot reach)")
|
||||
|
||||
# What actually reaches WDTCSR, not what the image happens to load somewhere.
|
||||
# A timed disable writes WDCE|WDE (0x18) and then zero. Arming writes WDE
|
||||
# *without* WDCE — including 0x08, a 16 ms timeout with every prescaler bit
|
||||
# *without* WDCE - including 0x08, a 16 ms timeout with every prescaler bit
|
||||
# clear, which is precisely what the legacy route used and is why this cannot
|
||||
# be a check for "a prescaler is present".
|
||||
WDCE, WDE = 0x10, 0x08
|
||||
@@ -114,14 +114,14 @@ def main() -> int:
|
||||
values.append(0 if reg == "r1" else held.get(reg))
|
||||
armed = [v for v in values if v is not None and (v & WDE) and not (v & WDCE)]
|
||||
if armed:
|
||||
failures.append(f"WDTCSR is written {[hex(v) for v in armed]} — WDE without "
|
||||
failures.append(f"WDTCSR is written {[hex(v) for v in armed]} - WDE without "
|
||||
f"WDCE is arming the watchdog, and a reset-based hand-over "
|
||||
f"opens no pureboot window")
|
||||
elif not values:
|
||||
print(" ok the watchdog is never written")
|
||||
else:
|
||||
print(f" ok WDTCSR writes are {[hex(v) if v is not None else '?' for v in values]}"
|
||||
f" — unlock and clear, never an arm")
|
||||
f" - unlock and clear, never an arm")
|
||||
|
||||
for line in failures:
|
||||
print(f" FAIL {line}")
|
||||
|
||||
Reference in New Issue
Block a user