Bound the TXDATA chunk to half the tx_bytes depth
a2e8e5c batched TXDATA writes into 64-byte chunks and broke ACKs outright --
"no ACK after 20 tries". My regression, and CLAUDE.md warns about exactly
this: tx_bytes is 16 deep and BBARegisterFile silently drops bytes when
w_rdy is low, because SPI cannot back-pressure.
Within a single transaction the bytes arrive back-to-back at EXI speed, far
faster than TXFrameDrain starts consuming, so a 64-byte chunk overran the FIFO,
the frame went out truncated, and no valid ACK ever reached the host.
Byte-by-byte worked only because the per-byte EXI overhead happened to pace
the drain -- which is also why it was slow.
Chunk is now 8, half the FIFO depth: it cannot overflow even if the drain
consumes nothing for the whole transaction, while still cutting a 60-byte ACK
from ~60 transactions to 8. Noted in both the source and CLAUDE.md not to
raise it without deepening tx_bytes or giving the TX path real back-pressure.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -246,10 +246,20 @@ Details and the recovery ladder are in `gc_bba_test/README.md`. Key points:
|
|||||||
veth and the frames go nowhere).
|
veth and the frames go nowhere).
|
||||||
- **Stop-and-wait is load-bearing** — see the RBFI note; one frame in flight
|
- **Stop-and-wait is load-bearing** — see the RBFI note; one frame in flight
|
||||||
means the ring is never contended.
|
means the ring is never contended.
|
||||||
- `bba_tx_bytes()` streams in 64-byte chunks, ONE EXI transaction each. Writing
|
- `bba_tx_bytes()` streams in **8-byte** chunks, one EXI transaction each.
|
||||||
byte-by-byte cost a full EXI_Lock/Select/Deselect/Unlock per byte — ~60
|
Writing byte-by-byte cost a full EXI_Lock/Select/Deselect/Unlock per byte —
|
||||||
transactions per ACK — and that latency, not any transmit failure, is what
|
~60 transactions per ACK — and that latency, not any transmit failure, is
|
||||||
made ~⅓ of blocks retry (`txto=0` on hardware: TI always set).
|
what made ~⅓ of blocks retry (`txto=0` on hardware: TI always set).
|
||||||
|
|
||||||
|
⚠️ **The chunk size is bounded by `tx_bytes`, which is 16 deep.** Within one
|
||||||
|
transaction the bytes arrive back-to-back at EXI speed, far faster than
|
||||||
|
`TXFrameDrain` starts consuming, and `BBARegisterFile` silently drops bytes
|
||||||
|
when `w_rdy` is low because SPI cannot back-pressure. A 64-byte chunk
|
||||||
|
overran the FIFO, the frame went out truncated and the host saw no valid ACK
|
||||||
|
at all. Byte-by-byte only worked because the per-byte EXI overhead happened
|
||||||
|
to pace the drain. 8 is half the FIFO depth, so it cannot overflow even if
|
||||||
|
the drain consumes nothing for a whole transaction. **Do not raise it
|
||||||
|
without deepening `tx_bytes` or giving the TX path real back-pressure.**
|
||||||
|
|
||||||
### ⚠️ OPEN: ring descriptor sometimes lands at page offset +1
|
### ⚠️ OPEN: ring descriptor sometimes lands at page offset +1
|
||||||
|
|
||||||
|
|||||||
+20
-10
@@ -125,21 +125,31 @@ int bba_tx_bytes(const u8 *buf, u32 len)
|
|||||||
{
|
{
|
||||||
u32 off = 0;
|
u32 off = 0;
|
||||||
|
|
||||||
/* Stream in 64-byte chunks, ONE EXI transaction each.
|
/* Stream in 8-byte chunks, ONE EXI transaction each.
|
||||||
*
|
*
|
||||||
* This used to write byte by byte with bba_w8(), which means a full
|
* Writing byte by byte with bba_w8() costs a full EXI_Lock / EXI_Select /
|
||||||
* EXI_Lock / EXI_Select / header / EXI_Deselect / EXI_Unlock per BYTE --
|
* header / EXI_Deselect / EXI_Unlock PER BYTE -- about sixty transactions
|
||||||
* about sixty transactions to send one 60-byte ACK. That latency is why
|
* for one 60-byte ACK. That latency is why ~1/3 of netboot blocks were
|
||||||
* roughly a third of netboot blocks were retried: the ACK was always
|
* retried: the ACK always reached the W5100 (txto=0, TI always set) but
|
||||||
* transmitted (txto=0 on hardware, so TI always set) but did not get back
|
* missed the host's timeout.
|
||||||
* to the host inside its timeout. The data phase of a register-space
|
|
||||||
* write runs until CS, so there is no reason to pay the per-byte cost.
|
|
||||||
*
|
*
|
||||||
* 64 is bba_write()'s internal buffer limit, not a protocol limit. */
|
* ⚠️ BUT THE CHUNK SIZE IS BOUNDED BY tx_bytes, WHICH IS 16 DEEP.
|
||||||
|
* BBARegisterFile silently drops bytes when tx_bytes.w_rdy is low,
|
||||||
|
* because SPI cannot back-pressure. Within one transaction the bytes
|
||||||
|
* arrive back-to-back at EXI speed -- far faster than TXFrameDrain can
|
||||||
|
* start consuming -- so a 64-byte chunk overran the FIFO, the frame went
|
||||||
|
* out truncated, and the host saw no valid ACK at all ("no ACK after 20
|
||||||
|
* tries"). Byte-by-byte worked only because the per-byte EXI overhead
|
||||||
|
* happened to give the drain time.
|
||||||
|
*
|
||||||
|
* 8 is half the FIFO depth: it cannot overflow even if the drain consumes
|
||||||
|
* nothing for the whole transaction, while still cutting a 60-byte ACK
|
||||||
|
* from ~60 transactions to 8. Do not raise it without deepening
|
||||||
|
* tx_bytes or giving the TX path real back-pressure. */
|
||||||
while (off < len) {
|
while (off < len) {
|
||||||
u32 n = len - off;
|
u32 n = len - off;
|
||||||
int rc;
|
int rc;
|
||||||
if (n > 64) n = 64;
|
if (n > 8) n = 8;
|
||||||
rc = bba_write(REG_TXDATA, buf + off, n);
|
rc = bba_write(REG_TXDATA, buf + off, n);
|
||||||
if (rc < 0) return rc;
|
if (rc < 0) return rc;
|
||||||
off += n;
|
off += n;
|
||||||
|
|||||||
Reference in New Issue
Block a user