The Ch443d board per-frame diagnostic identified the real displayed-frame
failure as a BINOMIAL vertical-lookahead starvation: displaying source
row r while interpolating r-1/r/r+1, the prefetch led by only one row
(next_fetch <= disp_row+1), so the lookahead row r+1 was still in flight
when the 3x3 filter read it (board: scan_y=33, nf_v=34, cause_lookahead=1,
read-error=0, deterministic every frame).
Fix (BINOMIAL_3X3_FILTER only; legacy 2/3-buffer, lead-1 paths unchanged):
- Add a 4th rotating line buffer (lb3) with its own RAM-local write/read/
cache registers. The 3x3 filter needs r-1/r/r+1 resident (3 buffers), so
leading by 2 (fetch r+2 while displaying r) without overwriting r-1
requires a 4th buffer.
- Prefetch lead-2 for binomial: disp_row_limit_e = disp_row+2. The in-flight
r+2 lands in the 4th buffer (b+2 mod 4), always distinct from prev/cur/next
(b-1/b/b+1 mod 4), so it never clobbers a row being read.
- Modulo-4 rotation everywhere: V_SOURCE_BUF%4, stretch_buf_q, next_fetch_buf,
reset alignment at V_SOURCE_START, and the read-cache prev/cur/next case
extended to 4 branches with (b-1)/b/(b+1) mod 4 selection + first/last-row
clamps preserved.
New tb_gs_scanout_binomial_lookahead reproduces the board condition under
realistic EMIF latency (LAT=7) + backpressure and proves: NO binomial
lookahead underflow, correct 3x3 output across modulo-4 wrap + clamps (full
oracle, 1280 px), and coverage that mid-frame rows past V_SOURCE_START+1
with vphase!=0 were exercised under prefetch pressure.
All green: binomial (4-buffer, identical output), lookahead (new),
scanout_lb {,_hstretch,_psm32_256,_fb}, scanout_restart, scanout_diag,
ps2_hps_bridge, and the complete f52 replay BYTE-IDENTICAL (Z 0/307200,
COLOR 0/245760). Also commits the Ch443d board evidence that identified
this defect. No Quartus/board/push from here.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
rtl/gif_gs
GIF path and Graphics Synthesizer logic. Matches docs/contracts/gif_gs.md.
Current contents
-
gs_stub.sv— GS shell with two architecturally distinct write ports (Ch75 namespace split):reg_wr_*— privileged-block writes (16-bit offset within0x12000000). LatchesBGCOLOR(offset0x00E0) intobg_{r,g,b}; other offsets emitEV_MODE.gif_reg_*— GIF A+D register-number writes (8-bit reg# + 64-bit data). DecodesPRIM=0x00,RGBAQ=0x01,XYZF2=0x04,XYZ2=0x05,FRAME_1=0x4C,ZBUF_1=0x4Einto per-register 64-bit latches; unknown reg numbers emitEV_MODE.- No VRAM, no drawing yet — that is the next architectural step.
-
gif_path_stub.sv— Wave 2 minimal GIF packet logger; project-local single-qword register-write format. Used bytb_bgcolor_via_dma. -
gif_packed_stub.sv— real PS2 GIFtag parser (Ch72-Ch75). Handles PACKED (FLG=0), REGLIST (FLG=1), IMAGE (FLG=2), DISABLE (FLG=3). TheREAL_AD_REG_MAPparameter selects the A+D dispatch port:REAL_AD_REG_MAP=0(default, back-compat) — drivesgs_stub.reg_wr_*using a project-local 16-bit offset carried inin_data[79:64].REAL_AD_REG_MAP=1— drivesgs_stub.gif_reg_*using the real PS2 8-bit reg# carried inin_data[71:64]. Source-of-truth: PCSX2GSRegs.h.
BGCOLOR reset value
At reset, bg_{r,g,b} default to 0x40 each (mid-grey) rather than black.
Rationale: this makes "gs_stub reset but no BGCOLOR write yet" visually
distinct from "video output disabled / black frame" in Milestone A. Override
is a BGCOLOR write from the test harness.
Pitfall: namespace conflation
Ch74 conflated GIF A+D reg numbers with GS privileged-block offsets and
mapped e.g. 0x14→PMODE@0x0000. That is fiction — those are separate
namespaces. Ch75 split them. ZBUF_1 is 0x4E, not 0x4F (that's
ZBUF_2). When adding a new GIF-context register, source the reg# from
PCSX2 GSRegs.h, never from the privileged-block map.