pureboot: the pin axis, and the mute it was hiding
Pins move the image for exactly one reason — a bit-banged link on a USART's own
pins has to release that USART — and the matrix said outright that they were no
axis, so the tightest configuration in the space was one nothing built. Not
subtly, either: the 1284's slot ends at flash end, so that build does not merely
exceed the size test's limit, it fails to link. pureboot_{sw,autobaud}_on_usart
{0,1} are gate points in both matrix modes now, and the exhaustive sweep carries
the pins across its whole cross product. The hand-measured table is the gate's
output: 506 B of 512 for the 1284 autobaud on USART0's pins, 504 on USART1's.
pureboot.mute drives the defect itself — an application hands over with USART0
still enabled and the loader on those pins must still answer. Reaching that
needed the runner to know an enabled USART owns its TxD, which simavr does not
model at all: it wires a USART through IRQs and never takes the pin from the
port. It also brings UCSRnB up with TXEN already set where silicon clears the
register, so the runner restores the reset value for the USART it models — the
mute must come from the application, not from power-on. The fixture stays
silent, since nothing is listening on the USART it brings up.
test_handshake.py, written where no gate could run it, is pureboot.handshake.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -10,7 +10,9 @@
|
||||
//
|
||||
// The link follows the chip's natural default (USART0 on the megas, the
|
||||
// software UART on PB0/PB1 elsewhere) unless -l overrides it: `-l usart1`
|
||||
// for the second instance, `-l sw:B5,B1` for a software build's RX,TX pins.
|
||||
// for the second instance, `-l sw:B5,B1` for a software build's RX,TX pins,
|
||||
// and `-l sw:D0,D1@0` where those pins are a USART's own — see the pin
|
||||
// ownership the bridge models below.
|
||||
//
|
||||
// simavr's tiny cores decode the SPM opcode but attach no NVM module — SPM
|
||||
// is a silent no-op (the mega's boot section has one, avr_flash). The
|
||||
@@ -46,6 +48,7 @@ static int link_software;
|
||||
static char uart_digit = '0';
|
||||
static char sw_rx_port = 'B', sw_tx_port = 'B';
|
||||
static int sw_rx_bit = 0, sw_tx_bit = 1;
|
||||
static char sw_tx_owner = 0; // the USART whose TXD the software link sits on
|
||||
static const char *dump_path;
|
||||
static uint32_t reset_pc;
|
||||
static volatile sig_atomic_t reset_requested;
|
||||
@@ -61,8 +64,12 @@ static int parse_link(const char *spec)
|
||||
link_software = 1;
|
||||
if (spec[2] == '\0')
|
||||
return 0;
|
||||
if (sscanf(spec + 2, ":%c%d,%c%d", &sw_rx_port, &sw_rx_bit, &sw_tx_port, &sw_tx_bit) == 4)
|
||||
char owner = 0;
|
||||
int fields = sscanf(spec + 2, ":%c%d,%c%d@%c", &sw_rx_port, &sw_rx_bit, &sw_tx_port, &sw_tx_bit, &owner);
|
||||
if (fields == 4 || fields == 5) {
|
||||
sw_tx_owner = owner;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
@@ -198,10 +205,50 @@ static avr_cycle_count_t tx_sample(avr_t *mcu, avr_cycle_count_t when, void *par
|
||||
return 0;
|
||||
}
|
||||
|
||||
// A USART owns its TxD pin whenever its transmitter is enabled, and the port
|
||||
// register cannot drive it (§20.2 / Atmel-8271 §19.2) — which is why a
|
||||
// bit-banged link deployed on those pins is mute until it clears UCSRnB.
|
||||
// simavr wires a USART entirely through IRQs and never touches the port pin
|
||||
// model, so the ownership does not exist there and the mute cannot happen:
|
||||
// supply it, or the very state this models is untestable. The link spec's
|
||||
// trailing @n names the USART; without one the pins are nobody's.
|
||||
static avr_uart_t *tx_owner;
|
||||
|
||||
static int tx_pin_taken(void)
|
||||
{
|
||||
return tx_owner && avr_regbit_get(avr, tx_owner->txen);
|
||||
}
|
||||
|
||||
// simavr leaves TXEN set in UCSRnB out of reset, where silicon clears the
|
||||
// whole register (§20.11.3) — which would hand the pin to a USART no code has
|
||||
// enabled, making a freshly reset chip mute for reasons hardware does not
|
||||
// have. Reset it the way the datasheet does, so the ownership starts from
|
||||
// nobody's and only an application that really enables the USART takes it.
|
||||
static void reset_tx_owner(void)
|
||||
{
|
||||
if (tx_owner)
|
||||
avr_regbit_clear(avr, tx_owner->txen);
|
||||
}
|
||||
|
||||
static void find_tx_owner(void)
|
||||
{
|
||||
for (avr_io_t *io = avr->io_port; io; io = io->next)
|
||||
if (io->kind && strcmp(io->kind, "uart") == 0 && ((avr_uart_t *)io)->name == sw_tx_owner) {
|
||||
tx_owner = (avr_uart_t *)io;
|
||||
reset_tx_owner();
|
||||
return;
|
||||
}
|
||||
fprintf(stderr, "device: no USART%c to own the software link's TX pin\n", sw_tx_owner);
|
||||
}
|
||||
|
||||
static void tx_hook(avr_irq_t *irq, uint32_t value, void *param)
|
||||
{
|
||||
(void)irq;
|
||||
(void)param;
|
||||
if (tx_pin_taken()) { // the USART holds the line; the port write goes nowhere
|
||||
tx_level = 1;
|
||||
return;
|
||||
}
|
||||
int level = value & 1;
|
||||
if (!tx_active && tx_level == 1 && level == 0) { // start edge
|
||||
tx_active = 1;
|
||||
@@ -324,7 +371,8 @@ int main(int argc, char *argv[])
|
||||
fprintf(stderr,
|
||||
"usage: %s [-l link] <pureboot.elf> <mcu> <hz> <base_hex> <page> <baud> <flash_dump>"
|
||||
" [reset_hex] [resume_flash]\n"
|
||||
" -l link: usart0 | usart1 | sw[:B0,B1] (RX,TX); default: the chip's own\n"
|
||||
" -l link: usart0 | usart1 | sw[:B0,B1[@0]] (RX,TX, then the USART owning\n"
|
||||
" them); default: the chip's own\n"
|
||||
" reset_hex: reset vector (default: base with a boot section, else 0)\n"
|
||||
" resume_flash: raw full-flash image loaded instead of the ELF — a prior\n"
|
||||
" run's dump, for power-fail resume tests\n",
|
||||
@@ -410,6 +458,8 @@ int main(int argc, char *argv[])
|
||||
printf("PB_PTY %s\n", uart_pty.pty.slavename);
|
||||
} else {
|
||||
bit_cycles = (avr->frequency + baud / 2) / baud; // matches uart.hpp's own rounding exactly
|
||||
if (sw_tx_owner)
|
||||
find_tx_owner();
|
||||
rx_pin = avr_io_getirq(avr, AVR_IOCTL_IOPORT_GETIRQ(sw_rx_port), (unsigned)sw_rx_bit);
|
||||
avr_irq_register_notify(avr_io_getirq(avr, AVR_IOCTL_IOPORT_GETIRQ(sw_tx_port), (unsigned)sw_tx_bit), tx_hook,
|
||||
NULL);
|
||||
@@ -447,6 +497,7 @@ int main(int argc, char *argv[])
|
||||
avr_ioctl(avr, AVR_IOCTL_UART_SET_FLAGS(uart_digit), &flags);
|
||||
} else {
|
||||
bridge_reset();
|
||||
reset_tx_owner();
|
||||
}
|
||||
}
|
||||
if (link_software && ++since_poll >= 2000) {
|
||||
|
||||
Reference in New Issue
Block a user