test: the simavr device stops dumping a flash image nobody reads
Every protocol run wrote the simulated flash to /tmp/tsb_dump.bin on exit - outside the repository, from four tests that run in parallel into one file - for a cross-check no test made. The dump goes, and the device's clock and page size are named. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
aafaf25a47
commit
bc671618a7
2 files changed
+20
-31
No files matched your search
+17
-26
@@ -1,12 +1,8 @@
|
||||
// simavr "device" for the TSB bootloader: load the boot-linked ELF into the
|
||||
// ATmega328P boot section, enter it (BOOTRST is not modelled, so we set PC to
|
||||
// ATmega328P boot section, enter it (BOOTRST is not modelled, so PC is set to
|
||||
// the boot base, exactly as simavr's own board_simduino does), and expose
|
||||
// UART0 as a pty. A host client (Python pyserial, or the real tsbloader) then
|
||||
// speaks the TSB protocol over that pty and actually flashes the device.
|
||||
//
|
||||
// SPM genuinely writes avr->flash on the mega cores, so on exit (or SIGTERM)
|
||||
// we dump the flash image to a file for a ground-truth cross-check against
|
||||
// what the client read back through the bootloader.
|
||||
#include <array>
|
||||
#include <csignal>
|
||||
#include <cstdint>
|
||||
@@ -28,19 +24,16 @@ extern "C" {
|
||||
|
||||
namespace {
|
||||
|
||||
// What the loaders are built for: tsb/*.cpp's device clock and the chip's SPM
|
||||
// page, which is also the config page sitting directly below the boot section.
|
||||
constexpr std::uint32_t clock_hz = 16'000'000;
|
||||
constexpr std::uint32_t page_bytes = 128;
|
||||
|
||||
avr_t *avr;
|
||||
uart_pty_t uart_pty;
|
||||
const char *dump_path;
|
||||
|
||||
[[noreturn]] void finish(int)
|
||||
{
|
||||
if (dump_path) {
|
||||
std::FILE *f = std::fopen(dump_path, "wb");
|
||||
if (f) {
|
||||
std::fwrite(avr->flash, 1, avr->flashend + 1, f);
|
||||
std::fclose(f);
|
||||
}
|
||||
}
|
||||
uart_pty_stop(&uart_pty);
|
||||
_exit(0);
|
||||
}
|
||||
@@ -49,12 +42,11 @@ const char *dump_path;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
if (argc < 3) {
|
||||
std::println(stderr, "usage: {} <tsb.elf> <boot_base_hex> [flash_dump.bin]", argv[0]);
|
||||
if (argc != 3) {
|
||||
std::println(stderr, "usage: {} <tsb.elf> <boot_base_hex>", argv[0]);
|
||||
return 2;
|
||||
}
|
||||
auto boot_base = static_cast<std::uint32_t>(std::strtoul(argv[2], nullptr, 0));
|
||||
dump_path = argc >= 4 ? argv[3] : nullptr;
|
||||
const auto boot_base = static_cast<std::uint32_t>(std::strtoul(argv[2], nullptr, 0));
|
||||
|
||||
avr = avr_make_mcu_by_name("atmega328p");
|
||||
if (!avr) {
|
||||
@@ -62,14 +54,14 @@ int main(int argc, char *argv[])
|
||||
return 1;
|
||||
}
|
||||
avr_init(avr);
|
||||
avr->frequency = 16000000;
|
||||
avr->frequency = clock_hz;
|
||||
// Real flash powers up erased (0xff); the app region must look erased
|
||||
// before the bootloader programs it.
|
||||
std::memset(avr->flash, 0xff, avr->flashend + 1);
|
||||
|
||||
// simavr's ELF loader flattens the flash base to 0 (it expects an app at
|
||||
// 0x0), but it hands back the boot code in fw.flash; place it at the boot
|
||||
// section base ourselves and enter there (BOOTRST is not modelled).
|
||||
// 0x0), but it hands back the boot code in fw.flash, so it is placed at the
|
||||
// boot section base by hand.
|
||||
elf_firmware_t fw{};
|
||||
if (elf_read_firmware(argv[1], &fw) != 0) {
|
||||
std::println(stderr, "device: cannot read {}", argv[1]);
|
||||
@@ -88,12 +80,11 @@ int main(int argc, char *argv[])
|
||||
avr->pc = boot_base;
|
||||
avr->codeend = avr->flashend;
|
||||
|
||||
// Optional: seed the config page (one page below the boot section) with a
|
||||
// hex byte string, so the password gate and emergency erase can be tested.
|
||||
// Optional: seed the config page with a hex byte string, so the password
|
||||
// gate and emergency erase can be tested.
|
||||
// Layout: [appjump lo][appjump hi][timeout][password...][0xff].
|
||||
const char *cfg = std::getenv("TSB_CONFIG");
|
||||
if (cfg) {
|
||||
std::uint32_t app_end = boot_base - 128; // config page sits directly below the boot code
|
||||
if (const char *cfg = std::getenv("TSB_CONFIG")) {
|
||||
const auto app_end = boot_base - page_bytes;
|
||||
for (int i = 0; cfg[i] && cfg[i + 1]; i += 2) {
|
||||
const std::array pair{cfg[i], cfg[i + 1], '\0'};
|
||||
avr->flash[app_end + i / 2] = static_cast<std::uint8_t>(std::strtoul(pair.data(), nullptr, 16));
|
||||
@@ -119,7 +110,7 @@ int main(int argc, char *argv[])
|
||||
std::signal(SIGINT, finish);
|
||||
|
||||
for (;;) {
|
||||
int state = avr_run(avr);
|
||||
const auto state = avr_run(avr);
|
||||
if (state == cpu_Done || state == cpu_Crashed) {
|
||||
break;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user