From 56d951cee93d6902f17b97b5c925b506ba89ae27 Mon Sep 17 00:00:00 2001 From: Cyd Date: Sat, 18 Jul 2026 00:36:23 -0500 Subject: [PATCH] 31250 bench run: wedge patch holds under retry storm; window is byte-scaled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 127s mash at 31250: zero wedges — but 5655 framing resyncs, duplicate replies (215% of poll slots) and AbandonCount=65 reveal the board's reply ACK-wait scales with byte time: ~5+ms at 9600 (USB beats it, zero framing) vs ~1.6ms at 31250 (USB cannot). All 65 retry exhaustions self-recovered through the patched give-up path — the strongest field proof of the latch fix yet; unpatched firmware would have wedged on the first. Verdict recorded in ANALYSIS.md: cabinets run the 9600 patched chip; 31250 stays a bench branch until the retry window is widened in firmware. Tool: before-snapshot now retries once (can lose the post-DTR boot race, as this run showed); evidence log archived. Co-Authored-By: Claude Fable 5 --- rio-firmware/RIOv4_2-ANALYSIS.md | 24 +++++++++++++++++++ tools/RioSerialMonitor/MashTest.cs | 37 ++++++++++++++++++------------ 2 files changed, 46 insertions(+), 15 deletions(-) diff --git a/rio-firmware/RIOv4_2-ANALYSIS.md b/rio-firmware/RIOv4_2-ANALYSIS.md index e39409b..e7791b6 100644 --- a/rio-firmware/RIOv4_2-ANALYSIS.md +++ b/rio-firmware/RIOv4_2-ANALYSIS.md @@ -240,3 +240,27 @@ heavier mash is decisive. Remaining (cabinet): overnight idle soak + a native-game session. Both check snapshots also reported a board/lamp fault item — persistent across chips, likely a real tired lamp; inspect separately. + +## 31250-baud bench run (2026-07-18) — patch holds; retry window is byte-scaled + +`testlogs/riomash-patched-31250-20260718-003114.log` (127s mash, FTDI +COM1 @ 31250, latency timer already 1ms): **zero wedges**, but +framing=5655, analog replies at 215% of poll slots (duplicates), and +AbandonCount=65 — a reply-retry storm. Reading: the board's reply +ACK-wait window scales with BYTE TIME, not wall clock. At 9600 the +window (~5+ms) hides USB turnaround entirely (zero framing in both +9600 runs); at 31250 it shrinks to ~1.6ms, which USB cannot reliably +beat, so most replies retransmit and 65 exhausted all 4 retries. + +Two conclusions: +1. **Brutal confirmation of the wedge patch**: 65 trips through the + give-up path in 127s, every one self-recovered ($DFF0 stub). The + unpatched firmware would have latched dead on the first. +2. **31250 needs one more firmware tweak to be clean**: widen the + reply-retry wait (the pacing/limit around the $D90E/$D9BE retry + machine) so the window stays >=10ms of wall clock at 31250. Until + then: cabinets run the 9600 patched chip (validated clean); 31250 + stays a bench branch. Note the duplicate replies also mean the raw + reply rate overstates throughput — unique samples are still capped + by the host's 55ms poll; harvesting the speed needs a faster poll + AFTER the retry window is fixed. diff --git a/tools/RioSerialMonitor/MashTest.cs b/tools/RioSerialMonitor/MashTest.cs index 27e7eda..83b1057 100644 --- a/tools/RioSerialMonitor/MashTest.cs +++ b/tools/RioSerialMonitor/MashTest.cs @@ -346,23 +346,30 @@ internal static class MashTest // --- helpers --------------------------------------------------------- async Task> SnapshotStatusAsync() { - lock (gate) { statusItems.Clear(); statusCollecting = true; } - try + // Two attempts: the first request can lose a race with the board's + // post-DTR boot (port open pulses DTR = board reset), or get eaten + // by a reply collision — seen on the 31250 bench run. + for (int attempt = 0; ; attempt++) { - await link.RequestVersionAsync(); - await link.RequestCheckAsync(); - } - catch { /* port trouble surfaces as an empty snapshot */ } - await Task.Delay(1200); + lock (gate) { statusItems.Clear(); statusCollecting = true; } + try + { + await link.RequestVersionAsync(); + await link.RequestCheckAsync(); + } + catch { /* port trouble surfaces as an empty snapshot */ } + await Task.Delay(1200); - lock (gate) - { - statusCollecting = false; - // Counters arrive one item per type; keep the last value per type. - var map = new Dictionary(); - foreach (CheckStatus item in statusItems) - map[item.Type] = item.Number; - return map; + lock (gate) + { + statusCollecting = false; + // Counters arrive one item per type; keep the last value per type. + var map = new Dictionary(); + foreach (CheckStatus item in statusItems) + map[item.Type] = item.Number; + if (map.Count > 0 || attempt == 1) + return map; + } } }