pureboot v8: one-wire on every backend
HALF_DUPLEX deploys a shared line per backend. The hardware USART takes the library's .half_duplex turn-around — RXD and TXD tied off-chip, each reply byte held to transmit-complete before the line can be released (m8 404 B, m328P 440, 1284P 460; the window poll runs through the outlined release-line call at 18 or 22 cycles a poll, measured off the built loops and held per chip by pureboot.window.halfduplex). The software and autobaud links fold onto the RX pin — RX == TX spells the same — and cost nothing: the frame's direction wrap is what the dropped second-pin init paid, and the worst image in the space is unchanged at the 1284s' 502 of 512, now with its one-wire twin proven equal across the exhaustive matrix. The host gains --one-wire, the echo discard a shared line requires: the adapter's echo is matched byte for byte and a reply interleaving a blind write — a loader already in session re-prompts inside the knock — is held for the reader. The device runner models the shared line by direction (drives only while the firmware's DDR reads input, decodes only while the firmware owns it, supplies the host-side echo), extends the USART pin-ownership model to RXEN's hold on RXD, and starts the pty USART from the datasheet's zeroed UCSR#B: simavr's TXEN-set reset plus its clear-UDRE-on-TXEN-drop otherwise wedges the first transmitter after a receiver-only program, which the half-duplex window gate caught as a banner that never came. v7 is tagged at its era's last commit; v8 changes nothing on the wire. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -26,8 +26,10 @@ Every axis moves per build — see *Configuration*. The Autobaud column is the
|
||||
worst configuration the space produces for the chip: the clock-free build —
|
||||
it alone carries the calibration machinery — with the `OSCCAL` trim baked
|
||||
and, where the chip has a USART, the link deployed on that USART's own pins,
|
||||
which the loader then has to release (*Pin ownership*). On default pins
|
||||
without the trim the same loaders run 4–10 B smaller.
|
||||
which the loader then has to release (*Pin ownership*). Folding the same
|
||||
build onto a single pin (*One-wire*) measures identically on every chip, so
|
||||
the column covers that twin too. On default pins without the trim the same
|
||||
loaders run 4–10 B smaller.
|
||||
|
||||
| Chip | Flash | Loader at | Link | Stock | Autobaud |
|
||||
|---|---|---|---|---|---|
|
||||
@@ -75,6 +77,7 @@ repo's build and by a downstream project alike:
|
||||
| `RX <pin>`, `TX <pin>` | software-UART pins | `pb0`, `pb1` |
|
||||
| `TIMEOUT <s>` | the activation window | 8 |
|
||||
| `OSCCAL <byte>` | a measured oscillator trim, applied before anything runs | none — no value, no code |
|
||||
| `HALF_DUPLEX` | one-wire: both directions on one line (*One-wire* below) | off |
|
||||
|
||||
The default baud is the fastest of 115200/57600/38400/19200/9600 the clock
|
||||
reaches within 2.5 % — the same U2X-included divisor search libavr's baud
|
||||
@@ -126,6 +129,43 @@ another one and the host's retries eventually catch the pulse. That reads as far
|
||||
more reliable than the same part with an application resident, which gets one
|
||||
window per reset. Measure with an application in place.
|
||||
|
||||
## One-wire
|
||||
|
||||
`HALF_DUPLEX` puts both directions on one line — the deployment for a board
|
||||
with a single spare pin, or a native-UART bootloader's shared-line wiring.
|
||||
Each backend has its shape:
|
||||
|
||||
- **Software and autobaud links** fold onto the RX pin (`RX == TX` spells
|
||||
the same deployment directly). The pin idles as the receiver's pull-up
|
||||
input; each transmitted frame takes the pin's direction and hands it back
|
||||
with the stop bit's level already on the pull-up, so neither flip makes
|
||||
an edge. This costs nothing: the frame's direction wrap is exactly what
|
||||
the dropped second-pin init paid, and the tightest image in the space —
|
||||
the 1284s' autobaud + `OSCCAL` on their USART's RXD — measures the same
|
||||
502 bytes one-wire as two-wire. On a USART's own pin the release applies
|
||||
as ever, RXD included: `RXEN` forces that pin's direction (§20.7.3),
|
||||
which a receive-only link could live with and a driven shared pin cannot.
|
||||
- **The hardware USART** (`SERIAL hardware`/`auto` + `HALF_DUPLEX`) uses
|
||||
libavr's `.half_duplex` turn-around — exactly one direction enabled at a
|
||||
time, each written byte held to transmit-complete before the line can be
|
||||
released — and needs RXD and TXD tied together off-chip. It costs
|
||||
+42…50 B over the stock loader (m8 404, m328P 440, 1284P 460 — all far
|
||||
inside the slot); the activation window is unchanged, its poll merely
|
||||
runs through the release-line test (18 cycles a poll in bit-addressable
|
||||
I/O, 22 in extended — measured, and held per chip by
|
||||
`pureboot.window.halfduplex`).
|
||||
|
||||
Host wiring, for an FTDI-style adapter: **adapter TX through ~1 kΩ to the
|
||||
line, adapter RX and the MCU pin directly on it.** The resistor lets the MCU
|
||||
win the line while it answers; the price is that the adapter reads back every
|
||||
byte it transmits. `pureboot.py --one-wire` consumes that echo byte for byte
|
||||
— a missing echo is reported as the wiring fault it is, and a device reply
|
||||
that lands between the echoes of the knock (a loader already in session
|
||||
re-prompts mid-knock) is held for the reader. The knock is the protocol's
|
||||
one blind multi-byte write, so on real wiring its second byte can be lost to
|
||||
that collision outright; the tool's knock retries absorb it. Everything else
|
||||
is ack-paced and cannot collide.
|
||||
|
||||
A downstream project brings its usual libavr setup (the `libavr` target, the
|
||||
chip via the `LIBAVR_MCU` toolchain preset), consumes this directory, and
|
||||
states its deployment — an ATmega328P on its shipped 1 MHz fuses with the
|
||||
@@ -302,7 +342,12 @@ moves `J` onto the unified decode — it gains the selector byte the table
|
||||
shows, which older loaders do not read, so the tool sends each form to the
|
||||
version that speaks it — and re-homes the autobaud unit into the GPIOR pair
|
||||
on the chips that have one (Session: what must not be written), which is
|
||||
where `--info`'s measured clock now reads it on those parts.
|
||||
where `--info`'s measured clock now reads it on those parts. **8** changes
|
||||
nothing on the wire either: it marks the builds whose deployment may be
|
||||
one-wire (*One-wire* above) — the hardware USART's half-duplex turn-around,
|
||||
or a software link folded onto a single pin. The host-side trace is
|
||||
`--one-wire`, the echo discard a shared line requires of any tool driving
|
||||
it.
|
||||
|
||||
Every closed generation is tagged in this repo at its era's last commit — the
|
||||
commit just before the next version bump, so a tag holds everything its
|
||||
@@ -469,6 +514,11 @@ the loader's bit-period unit, decoded and multiplied by the session rate —
|
||||
which is the number an `OSCCAL` bake or a fixed-baud build for the part is
|
||||
held against; `--clock <hz>` states the drift against a nominal.
|
||||
|
||||
`--one-wire` marks the link as a shared line (*One-wire* above): the tool
|
||||
reads back and verifies its own echoed bytes, whatever the backend.
|
||||
It combines with everything, `--scan` included — undiscarded echoes would
|
||||
answer every rate a scan probes.
|
||||
|
||||
`--scan` is the diagnosis once a fixed-baud loader has gone silent: it walks
|
||||
±10 % around `--baud` in 2 % steps, nearest first, one probe per activation
|
||||
window — reset the target as each probe announces itself (a board with DTR
|
||||
|
||||
Reference in New Issue
Block a user