From 4ac688dfa5154772560d682000ae905ee0b7573d Mon Sep 17 00:00:00 2001 From: Cyd Date: Sun, 19 Jul 2026 15:40:57 -0500 Subject: [PATCH] Docs: record E0-threshold bench PASS (E0000105) + corrected counter semantics MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The display observation recalibrates the counters: $3184 = give-up cycles (once per exhausted retry sequence, not per retransmit), $3185 = teardowns of any reply cycle needing at least one retransmission — the counter behind a lone E0000001. Also recorded in the error-handling inventory: 5 retransmits per cycle, reply-await arms only after the complete frame, NAK forces exactly one counted retransmit, late ACKs cannot rescue a cycle. (Previous commit's doc edits had silently missed their anchors; applied properly this time.) Co-Authored-By: Claude Fable 5 --- docs/hardware/display-board-1408.md | 9 +++++--- rio-firmware/RIOv4_2-ANALYSIS.md | 34 ++++++++++++++++++++--------- tools/RioSerialMonitor/E0Test.cs | 27 ++++++++++++++--------- 3 files changed, 47 insertions(+), 23 deletions(-) diff --git a/docs/hardware/display-board-1408.md b/docs/hardware/display-board-1408.md index a42b3af..356757d 100644 --- a/docs/hardware/display-board-1408.md +++ b/docs/hardware/display-board-1408.md @@ -99,9 +99,12 @@ also the RIO's **error annunciator**: - **`E0` serial-diagnostics readout** (`$D5F2`) — `E0xxyyzz` where `xx` = TX-ring overflow count (`$3187`, outbound byte dropped because - the ring was full, `$D66A`), `yy` = reply-retransmit count (`$3184`, - the game failed to ACK a reply, `$D96A`), `zz` = reply-teardown/give-up - count (`$3185`, `$DA2F`). Each increment site immediately re-renders, + the ring was full, `$D66A`), `yy` = give-up count (`$3184`, `$D96A` — + reply cycles whose 5 timeout retransmits all went unACKed), `zz` = + reply-teardown count (`$3185`, `$DA2F` — any reply cycle that needed + at least one retransmission; the counter behind a lone `E0000001`). + Semantics bench-confirmed 2026-07-19: display read `E0000105` after + one give-up + five teardowns. Each increment site immediately re-renders, so the display live-updates as serial trouble accumulates. If a cockpit's display reads `E0…` instead of `F0000000`, the serial link has been dropping or retrying. Note there is **no threshold in stock diff --git a/rio-firmware/RIOv4_2-ANALYSIS.md b/rio-firmware/RIOv4_2-ANALYSIS.md index 96e78ff..c311564 100644 --- a/rio-firmware/RIOv4_2-ANALYSIS.md +++ b/rio-firmware/RIOv4_2-ANALYSIS.md @@ -419,11 +419,19 @@ detail in [`docs/hardware/display-board-1408.md`](../docs/hardware/display-board `$FD`** (`$3170`). The TX ISR sends pending ACK/NAK ahead of reply data (`$D8A6`). Control bytes `$FE` RESTART / `$FF` IDLE. - **Outbound reply delivery**: replies must be ACKed by the game; the - retry machine (`$D90E`/`$D9BE`) retransmits up to 4 times - (`INC $3184` per retry), then gives up — sends `$FE` RESTART, - `INC $317A`, teardown at `$DA2F` (`INC $3185`). The give-up teardown - is where the `$2521` wedge leak lives (see above; fixed in the patched - images). + retry machine (`$D90E`/`$D9BE`) retransmits — **5 times** (bench + 2026-07-19; the earlier code reading of "4" was wrong) — then gives + up: sends `$FE` RESTART, `INC $317A`, `INC $3184` (give-up counter), + teardown at `$DA2F` (`INC $3185` — teardowns: ANY reply cycle that + needed at least one retransmission ends here, including NAK-recovered + ones, so `$3185` is the counter behind a lone cockpit `E0000001`). + The give-up teardown is where the `$2521` wedge leak lives (see + above; fixed in the patched images). Bench-observed await behavior: + the ACK wait arms only after the COMPLETE reply frame is sent + (mid-frame ACK/NAK/RESTART is ignored); a game NAK of the completed + frame triggers exactly one immediate counted retransmit; once the + first retry timeout lapses the cycle runs blind to give-up — a late + ACK cannot rescue it. - **RX overrun: unhandled.** The RX ISR reads SCSR/SCDR but never acts on the overrun flag — dropped bytes just corrupt the frame and surface as checksum NAKs. This is why the 125000-baud bench run @@ -443,7 +451,7 @@ detail in [`docs/hardware/display-board-1408.md`](../docs/hardware/display-board `$DB07-$DB3D`: silent hang, no display, no recovery. - **Error counters** `$3173-$317B` (ACK/NAK/retry/give-up bookkeeping) and the three displayed ones `$3187/$3184/$3185` — rendered as - `E0 ` by `$D5F2` at every increment. + `E0 ` by `$D5F2` at every increment. ### Hardware (RIO_1407 sheet 2) @@ -482,7 +490,13 @@ Built variants (wedge fix + threshold 5): | `RIOv4_2_patched_e0t5.bin` | 9600, native-game compatible | `9c21ac7199fb` | | `RIOv4_2_patched_31250v2_e0t5.bin` | 31250 + widened ACK-wait (FastRIO) | `b43032b016d7` | -**Not yet burned or bench-verified.** To verify on hardware: burn, then -force reply give-ups (e.g. kill the host mid-poll repeatedly) — the -display must stay `F0000000` through the 4th event and flip to -`E00000 05` on the 5th; `RIO_TAP`/mash regression as usual. +**Bench-verified 2026-07-19 (9600 image, burned): PASS.** Driven by +`RioSerialMonitor --e0test` (DTR reset → clean handshake → four +NAK-then-ACK sub-threshold teardown cycles → one unACKed give-up +cycle): the display held `F0000000` through the handshake and all four +sub-threshold teardowns — stock paints `E0…` at the first — and flipped +exactly at the 5th teardown to **`E0000105`** (`$3187`=00 overflows, +`$3184`=01 give-up, `$3185`=05 teardowns), confirmed by eye on the +cockpit display. Mash regression still to run. The 31250v2_e0t5 image +is the same patch on the certified FastRIO base and is expected to +behave identically. diff --git a/tools/RioSerialMonitor/E0Test.cs b/tools/RioSerialMonitor/E0Test.cs index b150d60..708ddba 100644 --- a/tools/RioSerialMonitor/E0Test.cs +++ b/tools/RioSerialMonitor/E0Test.cs @@ -7,15 +7,22 @@ namespace RioSerialMonitor; /// E0-threshold firmware verification (rio-firmware `--e0thresh` images). /// /// Two phases against a DTR-reset board (counters zeroed, display F0000000): -/// - Phase A (gate hold): analog requests whose reply we ACK only after the -/// first retransmit — each event increments the board's retransmit -/// counter ($3184) by a small sub-threshold amount, calling the display -/// renderer every time. The _e0t5 gate must keep the display F0000000; -/// stock firmware paints E0... at the first retry. -/// - Phase B (flip): analog requests we never ACK — the board runs the full -/// retry cycle (observed: 5 retransmits) and gives up ($3185++, RESTART -/// $FE). The counters cross the threshold and the display must flip to -/// the E0 readout, which the tool predicts from observed traffic. +/// - Phase A (gate hold): analog requests answered NAK-then-ACK — NAKing +/// the COMPLETED reply frame forces exactly one immediate retransmit, +/// ACKing that retransmit ends the cycle. Each event increments the +/// teardown counter $3185, invoking the display renderer sub-threshold. +/// The _e0t5 gate must keep the display F0000000; stock firmware paints +/// E0... at the first event. (Responses must follow the complete frame: +/// the board arms its reply-await only when the frame finishes sending, +/// and once the first retry timeout lapses the cycle runs blind.) +/// - Phase B (flip): analog requests we never ACK — the board runs the +/// full retry cycle (5 retransmits), gives up ($3184++, RESTART $FE) +/// and tears down ($3185++). $3185 crosses the threshold; the display +/// must flip to the E0 readout the tool predicts from observed traffic. +/// +/// Bench 2026-07-19 (9600 e0t5 chip): PASS — display held F0000000 through +/// the handshake and all four phase-A teardowns, flipped exactly at the +/// 5th teardown to E0000105 ($3187=00, $3184=01, $3185=05). /// /// dotnet run --project tools/RioSerialMonitor -- --e0test [port] /// [--baud rate] [--hold n] [--flip n] @@ -111,7 +118,7 @@ internal static class E0Test { buffer.Add(tmp[i]); // Frame tracking: respond only once the FULL reply - // frame is on the wire — the board arms its + // frame is on the wire � the board arms its // reply-await when the frame finishes sending; a // response mid-frame is ignored. if (frameCountdown > 0)