pureboot: the detail reaches become the library's own API

The three things this repo took from under libavr's counter are now over
it, and the local copies fold away. The USART release on a software
link's pins is the library's init contract (its guard here becomes a
deletion, byte-identical images held by the gate); the WDRF routing test
is power::peek_reset_cause().watchdog instead of a hand lookup of the
flag's register; the tsb tiers' baud arithmetic is the public solver.
libavr pin advances over those three additions.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-28 16:12:35 +02:00
parent c535c4756c
commit e23e535f86
5 changed files with 5 additions and 35 deletions

2
libavr

Submodule libavr updated: 26b80e262d...911a87538f

View File

@@ -38,13 +38,6 @@ using dev = avr::device<{.clock = avr::hertz_t{PUREBOOT_CLOCK_HZ}}>;
constexpr avr::baud_t wire_baud{PUREBOOT_BAUD}; constexpr avr::baud_t wire_baud{PUREBOOT_BAUD};
#endif #endif
// The watchdog reset flag's home: MCUSR, or the classic megas' MCUCSR.
consteval std::int16_t wdrf_field()
{
auto reg = std::string_view{avr::hw::db.regs[static_cast<std::size_t>(avr::power::detail::reset_reg())].name};
return avr::hw::db.field_index(reg, "WDRF");
}
// The loader owns the top 512 bytes; a staging copy goes in the slot below. // The loader owns the top 512 bytes; a staging copy goes in the slot below.
// Chips without a hardware boot section — the tinies and the m48s, whose SPM // Chips without a hardware boot section — the tinies and the m48s, whose SPM
// runs from anywhere (Atmel-8271 §26) — keep the application's relocated // runs from anywhere (Atmel-8271 §26) — keep the application's relocated
@@ -174,27 +167,6 @@ constexpr char usart_digit = '0' + PUREBOOT_USART;
constexpr char usart_digit = '0'; constexpr char usart_digit = '0';
#endif #endif
// Release a hardware USART the application may have left enabled onto a
// bit-banged link's pins. A software transmitter drives its TX pin through the
// port register, but while that USART's TXEN is set the USART owns the pin and
// the port write does nothing — the loader would receive and obey yet never
// answer. Writing UCSRnB zero hands the pin back to the port. Guarded on the
// pin actually being a USART's TXD, so a link on non-USART pins emits nothing.
template <char Inst, avr::io::pin Tx>
[[gnu::always_inline]] inline void release_usart_on()
{
if constexpr (avr::uart::has_usart<Inst>())
if constexpr (avr::uart::detail::usart_pin<Inst>("TXD") == Tx)
avr::hw::reg_impl<avr::uart::detail::ureg<Inst, "UCSR#B">()>::write(0);
}
template <avr::io::pin Tx>
[[gnu::always_inline]] inline void release_usarts_on()
{
release_usart_on<'0', Tx>();
release_usart_on<'1', Tx>();
}
template <avr::hertz_t C, avr::baud_t B> template <avr::hertz_t C, avr::baud_t B>
struct hardware_link { struct hardware_link {
using uart = avr::uart::usart<usart_digit, C, {.baud = B, .max_baud_error = 2.5_pct}>; using uart = avr::uart::usart<usart_digit, C, {.baud = B, .max_baud_error = 2.5_pct}>;
@@ -241,7 +213,6 @@ struct software_link {
static void init() static void init()
{ {
avr::init<rx_t, tx_t>(); avr::init<rx_t, tx_t>();
release_usarts_on<avr::PUREBOOT_TX>();
} }
static bool pending() static bool pending()
@@ -275,7 +246,6 @@ struct autobaud_link {
static void init() static void init()
{ {
avr::init<uart>(); avr::init<uart>();
release_usarts_on<avr::PUREBOOT_TX>();
} }
static std::uint8_t rx() static std::uint8_t rx()
@@ -498,7 +468,7 @@ void fill_page(std::uint8_t bank, std::uint16_t at)
#endif #endif
// A watchdog reset belongs to the application, whose watchdog stays forced // A watchdog reset belongs to the application, whose watchdog stays forced
// on until it clears WDRF — no activation window in its way. // on until it clears WDRF — no activation window in its way.
if (avr::hw::field_impl<wdrf_field()>::test()) if (avr::power::peek_reset_cause().watchdog)
run_app(); run_app();
link::init(); link::init();

View File

@@ -65,7 +65,7 @@ constexpr std::uint8_t comm_window = 200;
constexpr std::uint16_t build_date = 26 * 512 + 7 * 32 + 20; constexpr std::uint16_t build_date = 26 * 512 + 7 * 32 + 20;
// Fixed 115200 8N1; the library solves UBRR + U2X from clock and baud. // Fixed 115200 8N1; the library solves UBRR + U2X from clock and baud.
constexpr auto baud = avr::uart::detail::solve_baud(16_MHz, 115200_Bd); constexpr auto baud = avr::uart::solve_baud(16_MHz, 115200_Bd);
// The 16-byte device-info block, streamed out on activation. // The 16-byte device-info block, streamed out on activation.
// clang-format off // clang-format off

View File

@@ -200,7 +200,7 @@ extern "C" [[noreturn]] void tsb_app(); // the application's reset vector: --def
// only the divisor low byte and U2X0 need a store. The solver still does // only the divisor low byte and U2X0 need a store. The solver still does
// the datasheet work; the asserts pin the reset-state assumptions. // the datasheet work; the asserts pin the reset-state assumptions.
{ {
constexpr auto sol = avr::uart::detail::solve_baud(dev::clock, 115200_Bd); constexpr auto sol = avr::uart::solve_baud(dev::clock, 115200_Bd);
static_assert(sol.u2x && sol.ubrr < 256, "lean bring-up writes UBRR0L only, with U2X0"); static_assert(sol.u2x && sol.ubrr < 256, "lean bring-up writes UBRR0L only, with U2X0");
avr::hw::reg<"UBRR0">::write(static_cast<std::uint8_t>(sol.ubrr)); avr::hw::reg<"UBRR0">::write(static_cast<std::uint8_t>(sol.ubrr));
avr::hw::ucsr0a::write(avr::hw::ucsr0a::u2x0(1)); avr::hw::ucsr0a::write(avr::hw::ucsr0a::u2x0(1));

View File

@@ -65,7 +65,7 @@ constexpr std::uint8_t comm_window = 200;
constexpr std::uint16_t build_date = 26 * 512 + 7 * 32 + 20; constexpr std::uint16_t build_date = 26 * 512 + 7 * 32 + 20;
// Fixed 115200 8N1; the library solves UBRR + U2X from clock and baud. // Fixed 115200 8N1; the library solves UBRR + U2X from clock and baud.
constexpr auto baud = avr::uart::detail::solve_baud(16_MHz, 115200_Bd); constexpr auto baud = avr::uart::solve_baud(16_MHz, 115200_Bd);
// The 16-byte device-info block, streamed out on activation. // The 16-byte device-info block, streamed out on activation.
// clang-format off // clang-format off