# Ch438 — source-space 3x3 binomial scanout (pre-fit) ## Why this chapter Ch437 proved exact horizontal and vertical linear presentation, but the complete f52 framebuffer still contains high-frequency speckle that the PCSX2 reference does not. Adding more draws does not address that deficit. An offline filter sweep identified a source-space separable `[1 2 1]/4` low-pass followed by the authentic nearest presentation map as the best small, bounded scanout change. The renderer and LPDDR framebuffer are unchanged. Ch438 only changes pixels at the final HDMI scanout boundary, so the accepted f52 framebuffer SHA remains the rollback and equivalence anchor. ## RTL architecture - `gs_lpddr_scanout_lb.sv` adds opt-in `BINOMIAL_3X3_FILTER`. - SH3 full-frame scanout disables the Ch436/437 linear filters and enables the binomial filter. - A third rotating line buffer retains source rows `y-1`, `y`, and `y+1`. - Each physical line RAM still has one unconditional registered video read, the Quartus-safe inference form established in Ch437. - Six 256-bit register-cache slots retain the even/odd beats for all three rows. - Horizontal and vertical passes each compute `(a + 2*b + c + 2) >> 2`. - Horizontal edges clamp at source columns 0 and 511. Vertical edges clamp at source rows 32 and 479. - The filtered source pixel is presented with the captured nearest maps: `sx=floor(x*4/5)` and `sy=32+floor(y*14/15)`. - The AXI prefetcher still stays one source row ahead; its physical write target now rotates 0/1/2 rather than alternating 0/1. Expected fit delta from the accepted Ch437 build is one 640-pixel physical RAM bank (approximately five M20Ks in the established 256-bit implementation), two additional 256-bit beat-cache registers, and small shift/add logic. Ch437 used 344/358 RAM blocks, so the expected result is about 349/358. ## Exact preview result Input framebuffer: - `captures/gs/silenthill3/extracted/sh3_zsrt139f52_ch435_board.png` - Its underlying f52 board dump is byte-identical to the accepted Ch436/437 framebuffer (`d0047677371a0f6e4e319458926f604a8599c92baaf34d09b8e1fd452e31662b`). Reference: `captures/gs/silenthill3/pcsx2_ref_224139.png`. | Scanout | MAE | RMSE | |---|---:|---:| | Ch437 separable linear | 13.9525 | 20.6568 | | Ch438 3x3 binomial + nearest | 13.1111 | 19.1051 | Preview: `captures/gs/silenthill3/extracted/recon/sh3_zsrt139f52_ch438_binomial_preview.png` Preview SHA-256: `5a0402c523b64e252746067488177b08f0d23ab8a4a7dc09857e914c07f269f1` The preview is generated by `tools/preview_scanout_ch438.py`, which reproduces the RTL's rounding after each separable pass. ## Pre-fit verification - `make -C sim tb_gs_lpddr_scanout_lb_binomial` - 2,400/2,400 output pixels exact - zero underflow - zero read errors - `make -C sim tb_gs_lpddr_scanout_lb_hstretch` - Ch437 compatibility: 2,400/2,400 exact - zero underflow/read errors - `make -C sim tb_gs_lpddr_scanout_lb` - legacy scanout PASS under its established one-pixel-per-line simulation alignment allowance - zero underflow/read errors - Targeted Verilator lint of the binomial parameterization: no errors, latches, multidriven signals, combinational loops, or range errors. - Repository `git diff --check`: clean. The repository-wide `make lint` still stops at the pre-existing unsupported default-valued `rewind_i` input in `sif_dma_ee_ram_bridge_stub.sv`; the targeted scanout lint is clean and this unrelated baseline issue was not changed. ## Owner fit gate Ready for a fresh Quartus 26.1 GUI compile. Accept only if: 1. fit succeeds; 2. all setup/hold/recovery/removal/pulse-width checks are clean; 3. RAM use remains at or below 358 blocks; 4. all three scanline arrays infer as RAM rather than register banks. ## First fit and timing-root correction The first Ch438 GUI fit completed successfully and confirmed the scanout resource projection: - 38,904 ALMs - 57,173 registers - 349/358 RAM blocks - 160 DSP blocks All non-setup checks were clean, but EMIF setup failed at `-0.120 ns`, TNS `-3.122`, across 64 endpoints. The actual top-ten family was unrelated to the new filter datapath: - launch: `u_zc_emit|u_req|raddr_q[6]` - capture: the deep request FIFO's packed `mem_rtl_0` port-B address banks - launch-register fanout: 744 - data path: one 2.886 ns route, zero logic levels This is the established 93-bit x 16K request-FIFO physical-address family. The additional scanout RAM changed placement enough to expose it again. The correction keeps the full 16K request depth and banks only the payload into two independently inferred RAM arrays. Each half has a separate preserved registered read address, reducing the single 744-load launch net to two roughly half-sized physical trees. FIFO order, pointer CDC, and registered-read latency are unchanged. The banked form is enabled only on `u_zc_emit.u_req`; all other `gs_async_fifo` users retain the monolithic default. Post-cut verification: - default async FIFO stress: 3,285 writes/reads, zero errors; - monolithic registered-read stress: 3,285 writes/reads, zero errors; - banked registered-read stress: 3,285 writes/reads, zero errors; - full f52 production Verilator replay: - 311 epochs; - 2,339,914 fragments fed; - 2,286,292 passed; - zero drops, color overflow, BRESP errors, Z mismatches, or color mismatches; - PASS; - post-cut framebuffer SHA-256: `d0047677371a0f6e4e319458926f604a8599c92baaf34d09b8e1fd452e31662b`, byte-identical to the accepted Ch437 board framebuffer. The first timing-red RBF (`7fa4724f...`) was not deployed. A fresh GUI compile is required for the banked-address correction.