tsb: drive each tier to its size floor

asm 502->498 B (below the oracle's 500): the stack bring-up moves to plain C++,
and a register is reserved for the config-page high byte instead of reloading it
at each app-flash-boundary compare. tricks 808->778 B: shared erase/rww helpers
plus the libavr half-duplex W1C fix. pure 950->896 B and no SRAM: streams
rx->SPM/EEPROM instead of staging a 128 B page buffer. All three keep full oracle
feature parity and stay byte-identical across modes; protocol tests (round-trip +
password + emergency erase) green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-19 18:47:38 +02:00
parent ac9447c07f
commit fc5433fd7d
4 changed files with 74 additions and 65 deletions

View File

@@ -62,10 +62,6 @@ inline constexpr std::array<std::uint8_t, 16> info_data = {
// clang-format on
using info = avr::flash_table<info_data>;
// One page staged in SRAM. Scratch that is always filled before it is read, so
// it lives in .noinit — no startup clear (there is no crt) and no .text bytes.
[[gnu::section(".noinit")]] std::uint8_t buffer[page];
// Blocking byte read/write over the one-wire line: read() releases the line to
// the receiver, write() takes it and holds it until the frame is out.
std::uint8_t rx()
@@ -96,13 +92,6 @@ void send_eeprom(std::uint16_t addr, std::uint16_t count)
tx(ee::read(addr++));
}
// Take one page from the host into the SRAM buffer.
void get_page()
{
for (std::uint16_t i = 0; i < page; ++i)
buffer[i] = rx();
}
// Prompt the host with '?' and report whether it answered '!'.
bool request_confirm()
{
@@ -110,29 +99,40 @@ bool request_confirm()
return rx() == confirm;
}
// Program the SRAM buffer into one already-erased flash page (low byte then
// high, as the SPM word buffer wants).
void write_flash_page(std::uint16_t addr)
// Stream one page from the host straight into the already-erased flash page at
// `addr`, filling the SPM word buffer low byte then high — no SRAM staging, so
// receiving and programming are the same loop.
void store_flash_page(std::uint16_t addr)
{
spm::fill<off>(addr, std::span<const std::uint8_t>{buffer, page});
for (std::uint16_t i = 0; i < page; i += 2) {
std::uint8_t lo = rx();
std::uint8_t hi = rx();
spm::fill<off>(addr + i, static_cast<std::uint16_t>(lo | (hi << 8)));
}
spm::write_page<off>(addr);
spm::wait();
}
// Write the SRAM buffer into EEPROM byte by byte.
void write_eeprom_page(std::uint16_t addr)
// Stream one page from the host straight into EEPROM, byte by byte.
void store_eeprom_page(std::uint16_t addr)
{
for (std::uint16_t i = 0; i < page; ++i)
ee::write<off>(addr + i, buffer[i]);
ee::write<off>(addr + i, rx());
}
// Erase one flash page and wait it out — the erase step shared by the whole-app
// erase, the config-page rewrite and the emergency wipe.
void erase_page(std::uint16_t addr)
{
spm::erase_page<off>(addr);
spm::wait();
}
// Erase the whole application, one page at a time (unwritten pages stay erased).
void erase_application()
{
for (std::uint16_t a = 0; a < app_end; a += page) {
spm::erase_page<off>(a);
spm::wait();
}
for (std::uint16_t a = 0; a < app_end; a += page)
erase_page(a);
spm::rww_enable<off>();
}
@@ -171,19 +171,15 @@ void read_eeprom()
void write_flash()
{
erase_application();
for (std::uint16_t a = 0; request_confirm(); a += page) {
get_page();
write_flash_page(a);
}
for (std::uint16_t a = 0; request_confirm(); a += page)
store_flash_page(a);
}
// 'E': take pages the host offers behind '?' into EEPROM.
void write_eeprom()
{
for (std::uint16_t a = 0; request_confirm(); a += page) {
get_page();
write_eeprom_page(a);
}
for (std::uint16_t a = 0; request_confirm(); a += page)
store_eeprom_page(a);
}
// 'C': replace the config page, then echo it back for the host to verify.
@@ -191,10 +187,8 @@ void write_config()
{
if (!request_confirm())
return;
get_page();
spm::erase_page<off>(app_end);
spm::wait();
write_flash_page(app_end);
erase_page(app_end);
store_flash_page(app_end);
spm::rww_enable<off>();
send_flash(app_end, page);
}
@@ -207,8 +201,7 @@ void emergency_erase()
erase_application();
for (std::uint16_t a = 0; a <= eeprom_end; ++a)
ee::write<off>(a, 0xff);
spm::erase_page<off>(app_end);
spm::wait();
erase_page(app_end);
spm::rww_enable<off>();
}