docs: Ch441 board validation + Ch437 A/B scanout evidence
Preserve the Ch441 timing/renderer board-validation evidence and the Codex-directed follow-up: - FB reconciliation: the Ch437 A/B dump's 8-word first-beat delta was a non-reproducible one-time transient; 3x render+dump and 8x pure-probe re-reads all return golden d0047677 (words 0-7 correct). Memory integrity confirmed deterministic. (fb_reconciliation.md) - Structural audit + A-D diagnostic proposal for LPDDR_STATUS[5]: bit5 ORs sticky CDC-synced underflow with AXI read-errors; dump path (arid=1) is separate from scanout/HDMI (arid=3). Instrumentation proposal only, not implemented. (scanout_status_bit5_proposal.md) - Raw board logs, status samples, RBF hashes, A/B FB dump, board PNG. No RTL edits, no compile, no builds. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
55b23b94f51f1aab271d91aa4fa766561c044b9633146f25f4242b431f6ce902 retroDE_ps2.ch435.core.rbf
|
||||
3dd624cd34e56cf553fc92c47c3a0d47dc6a9c0513f37cbf72f2c4f6f9c71559 retroDE_ps2.ch437.candidate.rbf
|
||||
9463602dd40f224330de125a68f96d377c28210d3f7dcce3e9adf775e548db5f retroDE_ps2.ch441.core.rbf
|
||||
56e45346338907eb6c307320757774ac65efa56dabb644906a5600f5ea73a982 retroDE_ps2.core.rbf
|
||||
6fd81615af95cf51e1a893e04ca34fa1bad7a69c933efcb7fca5c9c091e18a65 retroDE_ps2.pre_ch417b.core.rbf
|
||||
17d1c1b49aac4d893ed56285cf0703bf375db2ee9f472ea36862cca376ae63fe retroDE_ps2.pre_ch435.core.rbf
|
||||
55b23b94f51f1aab271d91aa4fa766561c044b9633146f25f4242b431f6ce902 retroDE_ps2.pre_ch436.core.rbf
|
||||
1c8945e3e8ad85d91a682aa7a068cee487a1459a7f2b899600b24ff7d9751294 retroDE_ps2.pre_ch437.core.rbf
|
||||
@@ -0,0 +1,7 @@
|
||||
Ch441 board A/B scanout comparison (2026-07-21)
|
||||
|
||||
Candidate Ch441 = 9463602d... : replay DONE rc=0, 311 drains, zero drops, FB SHA d0047677 (byte-identical golden). LPDDR_STATUS[5] SET ~15/16 (0xF1).
|
||||
Accepted Ch437 = 3dd624cd... : replay DONE rc=0, 311 drains. LPDDR_STATUS[5] SET 16/16 (0xF1) — SAME underflow.
|
||||
Canonical retroDE_ps2.core.rbf = 56e45346... = Ch439b (previously REJECTED for this exact 0xF1).
|
||||
|
||||
VERDICT: both Ch441 and the ACCEPTED Ch437 underflow identically -> reproduced BASELINE scanout defect (LPDDR_STATUS[5]), NOT a Ch441 regression. Ch441 remains the timing-clean / renderer-correct candidate; scanout defect stays OPEN. Ch441 NOT promoted to canonical.
|
||||
@@ -0,0 +1,59 @@
|
||||
# Ch441 board validation — framebuffer reconciliation (zsrt139f52)
|
||||
|
||||
Scope: reconcile the one framebuffer-content discrepancy seen during the Ch441↔Ch437
|
||||
A/B, and record the Codex-authorized zero-RTL re-dump triage that classifies it.
|
||||
|
||||
## The A/B FB discrepancy (as first observed)
|
||||
|
||||
During the Ch441-vs-Ch437 A/B, the Ch437 `--dump-fb` readback logged
|
||||
`sum32=0xaf7b63f3 xor32=0x33178b8f`, differing from the accepted golden
|
||||
`sum32=0xaad0b94d xor32=0x33138181`. Local word-level diff localized the entire
|
||||
difference to **exactly 8 words — the first 256-bit LPDDR beat, row 0, x=0..7, y=0**
|
||||
(307192/307200 = 99.997% byte-identical). In all 8 words the **alpha byte matched**;
|
||||
only RGB differed (golden ≈ 0x0b dark, dump ≈ 0xb6 bright, a uniform ~+0xaa lift).
|
||||
Those 8 words fully account for the logged sum32/xor32 deltas and nothing else.
|
||||
|
||||
Artifacts:
|
||||
- `sh3_zsrt139f52_ch437_ab_fb.mem` SHA-256 `8fd30cdb926ab7a73c39827a2e3352b72c92af78ebaa7b4d69fd9715bf693aec`
|
||||
- golden / prior-Ch437 / Ch441 A/B all: SHA-256 `d0047677371a0f6e4e319458926f604a8599c92baaf34d09b8e1fd452e31662b`
|
||||
|
||||
Key architectural fact: `--dump-fb` reads LPDDR via `gs_lpddr_rd_probe` (**arid=1**),
|
||||
a different AXI master from the scanout line-buffer (`gs_lpddr_scanout_lb`, **arid=3**).
|
||||
So this discrepancy lives on the probe-readback / write path, NOT the scanout/HDMI path.
|
||||
|
||||
## Zero-RTL re-dump triage (Codex-authorized; no RTL / Quartus / builds)
|
||||
|
||||
Image under test: `retroDE_ps2.ch437.candidate.rbf` (the anomaly image), CORE_ID 0x50533200.
|
||||
Two independent axes, on the same f52 replay (`sh3_zsrt139f52_epochs.txt`):
|
||||
|
||||
1. **Write-side determinism** — three full render+dumps (A/B/C).
|
||||
2. **Readback determinism** — repeated pure-probe re-reads of words 0..15 via the
|
||||
`OFF_LPDDR_RDADDR` (0x03C) latch, with NO re-render, on the frame left by dump C.
|
||||
|
||||
### Result — anomaly did NOT reproduce
|
||||
|
||||
| dump | SHA-256 | sum32 | words 0..7 |
|
||||
|------|---------|-------|------------|
|
||||
| A (render+dump) | `d0047677…` | `0xaad0b94d` | golden (dark) |
|
||||
| B (render+dump) | `d0047677…` | `0xaad0b94d` | golden (dark) |
|
||||
| C (render+dump) | `d0047677…` | `0xaad0b94d` | golden (dark) |
|
||||
|
||||
First 16 words, identical across A/B/C:
|
||||
`040c0c0b 030c0b0b 030a0909 030b0b0b 030d0b0b 040e0c0c 050f0e0e 050f0e0e 050b0a0a 050a0909 05090808 050a0a0a 050e0d0d 050d0d0c 050d0c0b 050d0b0b`
|
||||
|
||||
Pure-probe re-reads (8 rounds, no re-render) returned those same values every round —
|
||||
words 0..7 stable and golden, no bright first beat.
|
||||
|
||||
### Classification
|
||||
|
||||
The earlier 8-word bright first-beat was a **non-reproducible one-time transient** in that
|
||||
single A/B dump — not a stable property of the stored framebuffer and not a deterministic
|
||||
write or readback defect. Three fresh renders + repeated probe re-reads are byte-identical
|
||||
to golden across the full frame, including words 0..7. **Framebuffer-memory integrity for
|
||||
this scene is confirmed deterministic (`d0047677…`).** Evidence integrity is resolved.
|
||||
|
||||
This triage does NOT diagnose `LPDDR_STATUS[5]`; that is the scanout/HDMI (arid=3) path,
|
||||
addressed separately (see `scanout_status_bit5_proposal.md`).
|
||||
|
||||
### Raw logs
|
||||
`sh3_zsrt139f52_ch437_redump_[ABC].log`, `redump_triage_console.log`.
|
||||
@@ -0,0 +1,34 @@
|
||||
=== render+dump A ===
|
||||
[sched] epoch 309: zero fragment drops (snap=0, sticky=0)
|
||||
[sched] epoch 310: zero fragment drops (snap=0, sticky=0)
|
||||
[sched] dump-fb: wrote 307200 words -> ch437_redump_A.mem sum32=0xaad0b94d xor32=0x33138181 nonzero=245155 bounds=(0,0)..(511,479) FB=640x480
|
||||
[sched] DONE rc=0 (all gates passed)
|
||||
d0047677371a0f6e4e319458926f604a8599c92baaf34d09b8e1fd452e31662b ch437_redump_A.mem
|
||||
first16(A):
|
||||
040c0c0b 030c0b0b 030a0909 030b0b0b 030d0b0b 040e0c0c 050f0e0e 050f0e0e 050b0a0a 050a0909 05090808 050a0a0a 050e0d0d 050d0d0c 050d0c0b 050d0b0b
|
||||
=== render+dump B ===
|
||||
[sched] epoch 309: zero fragment drops (snap=0, sticky=0)
|
||||
[sched] epoch 310: zero fragment drops (snap=0, sticky=0)
|
||||
[sched] dump-fb: wrote 307200 words -> ch437_redump_B.mem sum32=0xaad0b94d xor32=0x33138181 nonzero=245155 bounds=(0,0)..(511,479) FB=640x480
|
||||
[sched] DONE rc=0 (all gates passed)
|
||||
d0047677371a0f6e4e319458926f604a8599c92baaf34d09b8e1fd452e31662b ch437_redump_B.mem
|
||||
first16(B):
|
||||
040c0c0b 030c0b0b 030a0909 030b0b0b 030d0b0b 040e0c0c 050f0e0e 050f0e0e 050b0a0a 050a0909 05090808 050a0a0a 050e0d0d 050d0d0c 050d0c0b 050d0b0b
|
||||
=== render+dump C ===
|
||||
[sched] epoch 309: zero fragment drops (snap=0, sticky=0)
|
||||
[sched] epoch 310: zero fragment drops (snap=0, sticky=0)
|
||||
[sched] dump-fb: wrote 307200 words -> ch437_redump_C.mem sum32=0xaad0b94d xor32=0x33138181 nonzero=245155 bounds=(0,0)..(511,479) FB=640x480
|
||||
[sched] DONE rc=0 (all gates passed)
|
||||
d0047677371a0f6e4e319458926f604a8599c92baaf34d09b8e1fd452e31662b ch437_redump_C.mem
|
||||
first16(C):
|
||||
040c0c0b 030c0b0b 030a0909 030b0b0b 030d0b0b 040e0c0c 050f0e0e 050f0e0e 050b0a0a 050a0909 05090808 050a0a0a 050e0d0d 050d0d0c 050d0c0b 050d0b0b
|
||||
=== pure-probe re-reads of words 0-15, 8 rounds, NO re-render (frame left by dump C) ===
|
||||
round0: 0x040C0C0B 0x030C0B0B 0x030A0909 0x030B0B0B 0x030D0B0B 0x040E0C0C 0x050F0E0E 0x050F0E0E 0x050B0A0A 0x050A0909 0x05090808 0x050A0A0A 0x050E0D0D 0x050D0D0C 0x050D0C0B 0x050D0B0B
|
||||
round1: 0x040C0C0B 0x030C0B0B 0x030A0909 0x030B0B0B 0x030D0B0B 0x040E0C0C 0x050F0E0E 0x050F0E0E 0x050B0A0A 0x050A0909 0x05090808 0x050A0A0A 0x050E0D0D 0x050D0D0C 0x050D0C0B 0x050D0B0B
|
||||
round2: 0x040C0C0B 0x030C0B0B 0x030A0909 0x030B0B0B 0x030D0B0B 0x040E0C0C 0x050F0E0E 0x050F0E0E 0x050B0A0A 0x050A0909 0x05090808 0x050A0A0A 0x050E0D0D 0x050D0D0C 0x050D0C0B 0x050D0B0B
|
||||
round3: 0x040C0C0B 0x030C0B0B 0x030A0909 0x030B0B0B 0x030D0B0B 0x040E0C0C 0x050F0E0E 0x050F0E0E 0x050B0A0A 0x050A0909 0x05090808 0x050A0A0A 0x050E0D0D 0x050D0D0C 0x050D0C0B 0x050D0B0B
|
||||
round4: 0x040C0C0B 0x030C0B0B 0x030A0909 0x030B0B0B 0x030D0B0B 0x040E0C0C 0x050F0E0E 0x050F0E0E 0x050B0A0A 0x050A0909 0x05090808 0x050A0A0A 0x050E0D0D 0x050D0D0C 0x050D0C0B 0x050D0B0B
|
||||
round5: 0x040C0C0B 0x030C0B0B 0x030A0909 0x030B0B0B 0x030D0B0B 0x040E0C0C 0x050F0E0E 0x050F0E0E 0x050B0A0A 0x050A0909 0x05090808 0x050A0A0A 0x050E0D0D 0x050D0D0C 0x050D0C0B 0x050D0B0B
|
||||
round6: 0x040C0C0B 0x030C0B0B 0x030A0909 0x030B0B0B 0x030D0B0B 0x040E0C0C 0x050F0E0E 0x050F0E0E 0x050B0A0A 0x050A0909 0x05090808 0x050A0A0A 0x050E0D0D 0x050D0D0C 0x050D0C0B 0x050D0B0B
|
||||
round7: 0x040C0C0B 0x030C0B0B 0x030A0909 0x030B0B0B 0x030D0B0B 0x040E0C0C 0x050F0E0E 0x050F0E0E 0x050B0A0A 0x050A0909 0x05090808 0x050A0A0A 0x050E0D0D 0x050D0D0C 0x050D0C0B 0x050D0B0B
|
||||
=== triage done ===
|
||||
@@ -0,0 +1,74 @@
|
||||
# LPDDR_STATUS[5] scanout underflow — structural audit + diagnostic proposal
|
||||
|
||||
Read-only audit (no RTL edits, no compile). Purpose: define the smallest instrumentation
|
||||
that separates the three causes folded into `LPDDR_STATUS[5]`, before any RTL is written.
|
||||
Instrumentation implementation is NOT yet authorized — this is the proposal for review.
|
||||
|
||||
## What bit5 actually is
|
||||
|
||||
`de25_nano_psmct32_raster_demo_top.sv:2694`:
|
||||
|
||||
```systemverilog
|
||||
assign scan_err_w = scanout_lb_eff ? (lb_underflow_w | (lb_rd_errs_w != 32'd0))
|
||||
: (fc_rd_errs_w != 32'd0);
|
||||
```
|
||||
|
||||
Bit5 is a **logical OR of two independent signals**, and one of them is itself ambiguous:
|
||||
|
||||
- `lb_rd_errs_w` — cumulative non-OKAY AXI read responses (real bus error). A 32-bit
|
||||
counter that already exists but is never surfaced (folded into bit5).
|
||||
- `lb_underflow_w` — a **sticky** latch (`gs_lpddr_scanout_lb.sv:767-778`) set when
|
||||
`scan_y >= nf_v`, where `nf_v` is `next_fetch` 2-FF-synchronized into the video domain.
|
||||
`next_fetch` is a **multi-bit binary counter with NO gray coding** (`:765-768`).
|
||||
|
||||
So bit5 conflates **three** roots:
|
||||
|
||||
1. **Real prefetch starvation** — the AXI row FSM (`L_IDLE→L_AR→L_R→L_C`, single-beat,
|
||||
throttled to `disp_row+1`) falls behind the raster. Prime suspect: cold-start row 0
|
||||
— at `frame_start`, `next_fetch` resets and the video side begins scanning almost
|
||||
immediately (this is the frame-origin location; note it coincides with where the now-
|
||||
resolved memory transient appeared, but that was arid=1, a different master).
|
||||
2. **AXI read error** — `lb_rd_errs_w != 0`, a genuine non-OKAY response.
|
||||
3. **CDC false positive** — during a multi-bit `next_fetch` increment (e.g. 127→128 flips
|
||||
8 bits) the ungray-coded 2-FF sync can momentarily present a *low* `nf_v`; the sticky
|
||||
latch then sets `underflow` permanently off one transient cycle. The RTL comment even
|
||||
assumes "a 1-off transient is harmless" — but a sticky latch makes it not harmless.
|
||||
|
||||
## What the current evidence can and cannot say
|
||||
|
||||
- The FB dump / PNG is a **probe (arid=1)** read of LPDDR memory. It proves memory content,
|
||||
and says NOTHING about the **scanout/HDMI path (arid=3)** where bit5 lives.
|
||||
- We have therefore never measured the actual scanout output. bit5 could be a real HDMI
|
||||
starvation, a real AXI read error, or a pure CDC status artifact — currently indistinguishable.
|
||||
|
||||
## Proposed instrumentation (A–D) — proposal only, not authorized
|
||||
|
||||
**(A) Split the OR — near-zero cost.** Surface `lb_rd_errs_w` in its own status register
|
||||
(precedent: tex_rd_errs@0x068, reload_rd_errs@0x090) and give `lb_underflow` its own bit.
|
||||
Immediately separates cause #2 (nonzero rd_errs ⇒ real bus error) from #1/#3
|
||||
(underflow-set with rd_errs==0 ⇒ starvation-or-CDC).
|
||||
|
||||
**(B) First-failure latch (video domain, ~3 small regs).** On the 0→1 edge of `underflow_v`,
|
||||
latch `fail_scan_y`, `fail_nf_v`, a free-running `frame_counter`, and 1 bit for which
|
||||
sub-term tripped (`scan_y>=nf_v` vs the filter-lookahead term). Captures where/when/which.
|
||||
`fail_scan_y==0 & fail_frame==0` ⇒ cold-start row-0 starvation; large `scan_y` with `nf_v`
|
||||
momentarily small ⇒ CDC glitch.
|
||||
|
||||
**(C) Disambiguate #1 vs #3 without touching the CDC.** Also latch `nf_s0` and the next
|
||||
cycle's `nf_v` at first failure: a one-cycle dip that self-heals = CDC transient; a persistent
|
||||
`scan_y>=nf_v` = real starvation. (Definitive later fix, not now: gray-code `next_fetch`
|
||||
across the sync, or require the compare to hold 2 consecutive video cycles before setting
|
||||
sticky.)
|
||||
|
||||
**(D) HDMI-vs-memory — the key measurement.** Add a per-frame **scanout CRC**: rolling
|
||||
sum32/xor32 over the in-window `r/g/b` stream, latched per frame, exposed via status
|
||||
(~2×32-bit accumulators + frame latch). Compare against a golden scanout-CRC computed from
|
||||
the same FB in sim. This exercises the actual HDMI-producing path independent of the probe,
|
||||
and is the one measurement that separates "memory right, HDMI wrong" (real scanout/underflow)
|
||||
from "memory wrong" (write path — already ruled out for this scene by the re-dump triage).
|
||||
|
||||
## Recommended smallest first step
|
||||
|
||||
(A) + (B) together are minimal, add no wide arithmetic, and already partition the three
|
||||
causes for the observed persistent 0xF1. (D) is the follow-on if (A)/(B) point at the
|
||||
scanout path rather than a status artifact. Selection is deferred to owner/Codex.
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,17 @@
|
||||
# Ch437 LPDDR_STATUS 0x02C active-scanout sampling (video_src=1)
|
||||
sample 1: 0x000000F1 bit5=32
|
||||
sample 2: 0x000000F1 bit5=32
|
||||
sample 3: 0x000000F1 bit5=32
|
||||
sample 4: 0x000000F1 bit5=32
|
||||
sample 5: 0x000000F1 bit5=32
|
||||
sample 6: 0x000000F1 bit5=32
|
||||
sample 7: 0x000000F1 bit5=32
|
||||
sample 8: 0x000000F1 bit5=32
|
||||
sample 9: 0x000000F1 bit5=32
|
||||
sample 10: 0x000000F1 bit5=32
|
||||
sample 11: 0x000000F1 bit5=32
|
||||
sample 12: 0x000000F1 bit5=32
|
||||
sample 13: 0x000000F1 bit5=32
|
||||
sample 14: 0x000000F1 bit5=32
|
||||
sample 15: 0x000000F1 bit5=32
|
||||
sample 16: 0x000000F1 bit5=32
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
After Width: | Height: | Size: 441 KiB |
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,17 @@
|
||||
# Ch441 LPDDR_STATUS 0x02C active-scanout sampling (video_src=1, CTRL 0x018=0x5)
|
||||
sample 1: 0x000000F1 bit5=32
|
||||
sample 2: 0x000000F1 bit5=32
|
||||
sample 3: 0x000000F1 bit5=32
|
||||
sample 4: 0x000000F1 bit5=32
|
||||
sample 5: 0x000000F1 bit5=32
|
||||
sample 6: 0x000000F1 bit5=32
|
||||
sample 7: 0x000000F1 bit5=32
|
||||
sample 8: 0x000000D1 bit5=0
|
||||
sample 9: 0x000000F1 bit5=32
|
||||
sample 10: 0x000000F1 bit5=32
|
||||
sample 11: 0x000000F1 bit5=32
|
||||
sample 12: 0x000000F1 bit5=32
|
||||
sample 13: 0x000000F1 bit5=32
|
||||
sample 14: 0x000000F1 bit5=32
|
||||
sample 15: 0x000000F1 bit5=32
|
||||
sample 16: 0x000000F1 bit5=32
|
||||
Reference in New Issue
Block a user