31250 bench run: wedge patch holds under retry storm; window is byte-scaled

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 <noreply@anthropic.com>
This commit is contained in:
Cyd
2026-07-18 00:36:23 -05:00
co-authored by Claude Fable 5
parent 4bbdaff5ef
commit 56d951cee9
2 changed files with 46 additions and 15 deletions
+24
View File
@@ -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.
+22 -15
View File
@@ -346,23 +346,30 @@ internal static class MashTest
// --- helpers ---------------------------------------------------------
async Task<Dictionary<RioStatusType, int>> 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<RioStatusType, int>();
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<RioStatusType, int>();
foreach (CheckStatus item in statusItems)
map[item.Type] = item.Number;
if (map.Count > 0 || attempt == 1)
return map;
}
}
}