pureboot: host tool and end-to-end protocol tests, all three chips

pureboot.py (Python stdlib only): images as raw binary or Intel HEX,
flash and EEPROM programming with read-back verify, erase composites,
fuse and info readout, activation-timeout configuration, and the
tinies' reset-vector surgery — the trampoline word below the loader,
page 0 written last.

The test spawns a simavr device (pureboot_device.c) — the mega's USART
as a pty; on the tinies a cycle-timed GPIO<->pty bridge for the polled
software UART plus the NVM module simavr's tiny cores lack (their SPM
opcode ioctls into a void and silently does nothing) — and drives it
with the real tool: knock from reset (erased-flash walk on the tinies),
program and verify both memories, timeout write, session reconnect, an
external reset through the patched vector, hand-over, and the fixture
application's banner. Results are cross-checked against ground-truth
memory dumps and an independent decode of the surgery's rjmp words,
red-verified against a sabotaged encoder.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-20 05:54:15 +02:00
parent 47990349a4
commit 2133627057
7 changed files with 1061 additions and 8 deletions

51
test/pbapp.cpp Normal file
View File

@@ -0,0 +1,51 @@
// Test-fixture application for the pureboot protocol test: prints "APP" on
// the chip's serial link (the same link the loader uses) and idles — the
// proof that the loader's hand-over, and on the tinies the host's
// reset-vector surgery, actually launched it. Linked normally (crt, vectors
// at 0); on the tinies its reset vector is the rjmp the host re-homes.
#include <libavr/libavr.hpp>
using namespace avr::literals;
namespace {
consteval avr::hertz_t clock()
{
if (avr::hw::db.name == "ATtiny13A")
return 9.6_MHz;
if (avr::hw::db.name == "ATtiny85")
return 8_MHz;
return 16_MHz;
}
using dev = avr::device<{.clock = clock()}>;
template <avr::hertz_t C, bool Hardware = avr::hw::db.has_reg("UDR0")>
struct link {
using tx_t = avr::uart::usart0<C, {.baud = 115200_Bd, .max_baud_error = 2.5_pct}>;
static void tx(char c)
{
tx_t::write(static_cast<std::uint8_t>(c));
}
};
template <avr::hertz_t C>
struct link<C, false> {
using tx_t = avr::uart::software_tx<C, avr::pb1, 57600_Bd>;
static void tx(char c)
{
tx_t::write(static_cast<std::uint8_t>(c));
}
};
} // namespace
int main()
{
avr::init<typename link<dev::clock>::tx_t>();
link<dev::clock>::tx('A');
link<dev::clock>::tx('P');
link<dev::clock>::tx('P');
while (true) {
}
}