Per-vertex GS fog end-to-end (gs_stub emit incl. persp_emit5, gs_prim_list_feeder XYZ2->XYZF2 on PRIM.FGE, gs_make_sh3_scheduler_fixture.py F/FGE packing), new fog TBs, fidelity attribution tooling. Functional baseline before removing the dead bilinear lerp8 clamps (Codex: 161-node comb loop -> -0.042ns setup fail). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
6.2 KiB
Ch437 — zero-line-RAM horizontal-linear presentation prefit
Purpose
Ch436 made the captured 15:14 vertical presentation linear while retaining the Ch418 nearest-neighbour 5:4 horizontal map. The accepted framebuffer and board replay are exact, so this chapter changes presentation only: it reconstructs the fractional horizontal samples that nearest presentation repeats.
For output column x, the exact source coordinate is:
x0 = floor(x*4/5);xf = (x*4) mod 5;x1 = min(x0+1, 511).
The existing five-state mapper already produces both x0 and xf; no divider
or new wide coordinate cone is introduced.
Architecture
The rejected Ch436 horizontal prototype mirrored both complete line buffers to
obtain an x+1 read port. Quartus implemented the mirrors as 30,720 registers
and rejected the design at 6,270 required LABs. Ch437 does not restore those
arrays.
Instead, each physical line buffer retains one video read address. Two 256-bit register slots hold the current and next beat for each of the two resident source rows. Horizontal blanking alternately primes beats 0 and 1. When active scanout enters beat K, the retired parity slot is refilled with beat K+1. The 5:4 cadence provides ten output clocks for each eight-source-pixel beat, so the next beat is resident well before the single lane-7 boundary sample needs it.
The interpolation order is separable and matches the software oracle:
- blend row L and L+1 at x0 with denominator 15;
- blend row L and L+1 at x1 with denominator 15;
- blend those two results with denominator 5.
The source-right edge clamps at pixel 511. Other profiles retain their existing nearest/1:1 path through default-off parameters.
Evidence before owner fit
Directed and legacy regressions:
tb_gs_lpddr_scanout_lb_hstretch: 2,400 checked, zero errors, zero underflow, across eight 256-bit beats and the clamped right edge;tb_gs_lpddr_scanout_lb_psm32: 49,152 checked, zero errors/underflow;tb_gs_lpddr_scanout_lb_psm32_256: 196,608 checked, zero errors/underflow;tb_gs_lpddr_scanout_fb: 105,968 checked, zero errors/underflow;- Verilator lint: no errors;
- relevant
git diff --check: pass.
The exact software presentation model improves the accepted f52 framebuffer's PCSX2 comparison again:
- Ch436 vertical-linear: MAE/RMSE
14.2678 / 21.3085; - Ch437 separable-linear: MAE/RMSE
13.9525 / 20.6568.
This is a visible smoothing of the horizontal staircase/noise without changing framebuffer bytes, render ordering, LPDDR traffic, or draw count.
Owner GUI compile acceptance
Rejected first fit
The first owner fit rejected the initial cache implementation at placement: 6,337 LABs were required on the 4,680-LAB device. The synthesis/netlist report, not the summary alone, showed the exact cause:
u_lpddr_scan_lbcontained 32,312 registers;- the SDC line-buffer reset collection found 30,720 destinations;
lb0[0..79]andlb1[0..79]appeared as individual flip-flop nodes;- no line-buffer M20K payload remained in the scanout hierarchy.
The conditional reads used to prime/refill the cache prevented Quartus from recognizing the dual-clock RAM template. This was the same physical failure mode as the rejected Ch436 mirrors, despite the source-level arrays not being duplicated.
Corrected inference-safe candidate
Each physical array now has exactly one unconditional video-clock read:
lb0_video_rd_q <= lb0[video_rd_addr_q];
lb1_video_rd_q <= lb1[video_rd_addr_q];
Address, valid, and beat tag are registered beside those outputs. A separate
control process fills the two-beat register cache only from the registered
RAM outputs. It never indexes lb0 or lb1 directly. This is the canonical
simple-dual-port, dual-clock inference form already proven by the accepted
line-buffer implementation.
The corrected candidate retains identical horizontal results: the expanded
2,400-pixel test, all PSMCT32 legacy regressions, Verilator lint, and relevant
git diff --check pass again.
The corrected RTL was accepted by an owner-controlled Quartus 26.1 GUI compile. The generated core is:
retroDE_ps2.core.rbf, 4,132,864 bytes;- SHA-256
3dd624cd34e56cf553fc92c47c3a0d47dc6a9c0513f37cbf72f2c4f6f9c71559.
All report gates pass:
- fitter successful at 39,077 / 46,800 ALMs (83%), 56,526 registers, 5,327,792 block-memory bits, 344 / 358 RAM blocks, and 163 / 376 DSPs;
- both line buffers are inferred as simple-dual-port, dual-clock
altera_syncraminstances, five M20Ks and 15,360 bits apiece; - the scanout hierarchy contains 1,602 registers rather than the rejected implementation's 32,312;
- EMIF setup slack is +0.069 ns with zero TNS;
- the 25 MHz design-clock setup slack is +5.473 ns with zero TNS;
- all hold classes have zero TNS and minimum slack 0.000 ns;
- minimum recovery, removal, and pulse-width slack are respectively +1.056 ns, +0.164 ns, and +0.200 ns.
Hardware acceptance
The accepted core was installed on the DE25-Nano and loaded successfully. The live fabric reported the expected core ID/ABI, healthy status, active HDMI scanout, an advancing frame counter, and zero raster overflow.
The complete f52 scheduler replay then passed on hardware:
- all 311 epochs completed;
- all fresh texture CRCs matched and resident reuse checks passed;
- every epoch reported zero fragment drops;
DONE rc=0 (all gates passed);- the dump contained 307,200 words,
sum32=0xaad0b94d,xor32=0x33138181, 245,155 nonzero pixels, and bounds(0,0)..(511,479).
The retrieved Ch437 board framebuffer has SHA-256
d0047677371a0f6e4e319458926f604a8599c92baaf34d09b8e1fd452e31662b
and is byte-identical to the accepted Ch436 framebuffer. This is the intended
result: Ch437 changes only the HDMI presentation filter, after framebuffer
storage, while preserving the proven renderer and replay output.
Ch437 is accepted. Its closure criteria were:
- fitter completes within the device's 4,680-LAB capacity;
- line buffers remain inferred M20Ks and total RAM stays at 344 / 358 blocks;
- all setup, hold, recovery, removal, and pulse-width classes are nonnegative;
- the narrow EMIF request-address family remains clean;
- the 25 MHz presentation domain remains clean.
All five criteria passed before deployment.