pureboot: review-pass fixes to the host tool and device runner
pureboot.py: reject an empty image file with a clear error instead of an IndexError deep in the vector-surgery planner; tighten the erase docstring (order is irrelevant there — every target byte is the same value, unlike a real flash where page 0 must go last). pureboot_device.c: the GPIO bridge's bit_cycles used plain truncating division where the firmware computes its own bit period with round-to-nearest (uart.hpp: (Clock.hz + Baud.bd/2)/Baud.bd) — one cycle off per bit on both tinies, harmless in practice but needless drift against a firmware built to a different constant. Matched exactly. Also clear the queued-bytes/decode-in-progress bridge state on the test-only reset signal, so a future reset-mid-transfer scenario can't feed a freshly reset chip bytes queued for its previous life. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -193,6 +193,8 @@ def load_image(path):
|
|||||||
"""Raw binary, or Intel HEX by extension (.hex/.ihx/.ihex)."""
|
"""Raw binary, or Intel HEX by extension (.hex/.ihx/.ihex)."""
|
||||||
data = open(path, "rb").read()
|
data = open(path, "rb").read()
|
||||||
if not path.lower().endswith((".hex", ".ihx", ".ihex")):
|
if not path.lower().endswith((".hex", ".ihx", ".ihex")):
|
||||||
|
if not data:
|
||||||
|
raise Error(f"{path}: empty image")
|
||||||
return data
|
return data
|
||||||
memory = {}
|
memory = {}
|
||||||
for number, line in enumerate(data.decode("ascii", "replace").splitlines(), 1):
|
for number, line in enumerate(data.decode("ascii", "replace").splitlines(), 1):
|
||||||
@@ -284,8 +286,9 @@ def covered(pages, skip_blank):
|
|||||||
|
|
||||||
|
|
||||||
def op_erase_flash(loader):
|
def op_erase_flash(loader):
|
||||||
"""0xff over the whole application area, page 0 first — an interrupted
|
"""0xff over the whole application area. Order does not matter here —
|
||||||
erase leaves the entry word blank and the chip still boots the loader."""
|
every target byte is the same value — and a blank word 0 still falls
|
||||||
|
through to the loader, so an interruption is harmless."""
|
||||||
blank = bytes([0xFF] * loader.info.page)
|
blank = bytes([0xFF] * loader.info.page)
|
||||||
for address in range(0, loader.info.base, loader.info.page):
|
for address in range(0, loader.info.base, loader.info.page):
|
||||||
loader.write_page(address, blank)
|
loader.write_page(address, blank)
|
||||||
|
|||||||
@@ -159,6 +159,18 @@ static void rx_start_next(void)
|
|||||||
avr_cycle_timer_register(avr, bit_cycles, rx_step, NULL);
|
avr_cycle_timer_register(avr, bit_cycles, rx_step, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A reset abandons whatever the bridge was mid-transfer: bytes still queued
|
||||||
|
// for a chip that no longer has the context to receive them meaningfully,
|
||||||
|
// and a decode in progress on a TX line the reset may have already changed.
|
||||||
|
static void bridge_reset(void)
|
||||||
|
{
|
||||||
|
rx_head = rx_tail = 0;
|
||||||
|
rx_active = 0;
|
||||||
|
tx_active = 0;
|
||||||
|
tx_level = 1;
|
||||||
|
avr_raise_irq(rx_pin, 1); // idle line
|
||||||
|
}
|
||||||
|
|
||||||
static void poll_pty(void)
|
static void poll_pty(void)
|
||||||
{
|
{
|
||||||
uint8_t chunk[256];
|
uint8_t chunk[256];
|
||||||
@@ -262,7 +274,7 @@ int main(int argc, char *argv[])
|
|||||||
nvm.io.ioctl = nvm_ioctl;
|
nvm.io.ioctl = nvm_ioctl;
|
||||||
avr_register_io(avr, &nvm.io);
|
avr_register_io(avr, &nvm.io);
|
||||||
|
|
||||||
bit_cycles = avr->frequency / baud;
|
bit_cycles = (avr->frequency + baud / 2) / baud; // matches uart.hpp's own rounding exactly
|
||||||
rx_pin = avr_io_getirq(avr, AVR_IOCTL_IOPORT_GETIRQ('B'), 0);
|
rx_pin = avr_io_getirq(avr, AVR_IOCTL_IOPORT_GETIRQ('B'), 0);
|
||||||
avr_irq_register_notify(avr_io_getirq(avr, AVR_IOCTL_IOPORT_GETIRQ('B'), 1), tx_hook, NULL);
|
avr_irq_register_notify(avr_io_getirq(avr, AVR_IOCTL_IOPORT_GETIRQ('B'), 1), tx_hook, NULL);
|
||||||
avr_raise_irq(rx_pin, 1); // idle line
|
avr_raise_irq(rx_pin, 1); // idle line
|
||||||
@@ -297,6 +309,8 @@ int main(int argc, char *argv[])
|
|||||||
avr_ioctl(avr, AVR_IOCTL_UART_GET_FLAGS('0'), &flags);
|
avr_ioctl(avr, AVR_IOCTL_UART_GET_FLAGS('0'), &flags);
|
||||||
flags &= ~AVR_UART_FLAG_POLL_SLEEP;
|
flags &= ~AVR_UART_FLAG_POLL_SLEEP;
|
||||||
avr_ioctl(avr, AVR_IOCTL_UART_SET_FLAGS('0'), &flags);
|
avr_ioctl(avr, AVR_IOCTL_UART_SET_FLAGS('0'), &flags);
|
||||||
|
} else {
|
||||||
|
bridge_reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!use_uart_pty && ++since_poll >= 2000) {
|
if (!use_uart_pty && ++since_poll >= 2000) {
|
||||||
|
|||||||
Reference in New Issue
Block a user