Snapshot: fog implementation + fidelity tooling baseline (pre bilinear-clamp fix)

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>
This commit is contained in:
2026-07-20 19:56:46 -04:00
parent ec82764bef
commit ba74bbd5aa
476 changed files with 696247 additions and 130119 deletions
+409 -15
View File
@@ -365,6 +365,10 @@ module de25_nano_psmct32_raster_demo_top (
// bridge into an unreadable state — only the design domain
// downstream of `core_rst_n` is held.
wire bridge_core_reset_req;
// Declared before every design-domain consumer. The runtime CLUT CDC is
// instantiated above the reset synchronizer, so an inline declaration at
// the assignment site would create an implicit, undriven early net.
wire core_rst_n;
// ---- Ch229 tile broadcast (bridge CLOCK2_50 → tile_ram_cdc) ----
// Driven by `u_hps_bridge` inside USE_QSYS_TOP; tied safe (zero +
@@ -413,6 +417,35 @@ module de25_nano_psmct32_raster_demo_top (
wire feeder_stg_we_pulse_w = fdr_we_sync[2] ^ fdr_we_sync[1]; // 1 design_clk pulse per commit
wire feeder_go_pulse_w = fdr_go_sync[2] ^ fdr_go_sync[1]; // 1 design_clk pulse per retrigger
// Ch367 -- runtime CLUT residency. The bridge writes a small palette
// bank at CLOCK2_50; one commit toggle starts the bounded design-clock
// copy into the existing CLUT writer port.
wire [7:0] clut_stage_waddr_w;
wire [31:0] clut_stage_wdata_w;
wire clut_stage_we_w, clut_commit_tgl_w;
wire [31:0] clut_expected_crc_w;
wire runtime_clut_wr_en_w, runtime_clut_busy_w, runtime_clut_done_tgl_w;
wire [7:0] runtime_clut_wr_idx_w;
wire [31:0] runtime_clut_wr_data_w, runtime_clut_crc_w;
clut_stage_cdc u_runtime_clut (
.bclk (CLOCK2_50),
.breset_n (~ninit_done),
.b_we (clut_stage_we_w),
.b_waddr (clut_stage_waddr_w),
.b_wdata (clut_stage_wdata_w),
.b_commit_tgl (clut_commit_tgl_w),
.b_expected_crc(clut_expected_crc_w),
.dclk (design_clk),
.dreset_n (core_rst_n),
.clut_wr_en (runtime_clut_wr_en_w),
.clut_wr_idx (runtime_clut_wr_idx_w),
.clut_wr_data (runtime_clut_wr_data_w),
.busy (runtime_clut_busy_w),
.done_tgl (runtime_clut_done_tgl_w),
.crc (runtime_clut_crc_w)
);
// ---- Ch248 platform DS2 wired controller --------------------------
// The shared `ds2_controller` runs on sys_clk (= CLOCK2_50 = 50 MHz),
// drives CLK/CMD/ATTN to the controller, samples DATA, and surfaces
@@ -502,7 +535,39 @@ module de25_nano_psmct32_raster_demo_top (
// Ch327a — line-buffer scanout geometry (the SCALABLE display path: O(width) BRAM, not
// O(width*height) like the frame-cache). For the Ch326 PSMCT32 spill FB: 128 px * 4 B =
// 512 B/row = 16 beats. (Default = the Ch321 128x128 PSMCT16 demo: 256 B/row = 8 beats.)
`ifdef GS_TILE_SPILL
`ifdef GS_SH3_LPDDR_FB
`ifdef GS_SH3_LPDDR_FB_Z
`ifdef GS_SH3_LPDDR_FB_640
localparam int SCANOUT_LB_STRIDE = 2560; // Ch358 — persistent-Z at NATIVE 640 px * 4 B PSMCT32 (FBW=10)
localparam int SCANOUT_LB_ROW_BEATS = 80; // 2560 / 32
localparam int SCANOUT_LB_NROWS = 480; // Ch358 — native 640x480, size 0x12C000, 38400 beats/frame
`else
localparam int SCANOUT_LB_STRIDE = 1024; // Ch357 — persistent-Z strong-reject fixture: 256 px * 4 B PSMCT32
localparam int SCANOUT_LB_ROW_BEATS = 32; // 1024 / 32
localparam int SCANOUT_LB_NROWS = 210; // Ch357 — strong-reject primary (256x210), 6720 beats/frame
`endif
`elsif GS_SH3_LPDDR_FB_640
localparam int SCANOUT_LB_STRIDE = 2560; // Ch357 — NATIVE 640 px * 4 B PSMCT32 (FBW=10)
localparam int SCANOUT_LB_ROW_BEATS = 80; // 2560 / 32
localparam int SCANOUT_LB_NROWS = 480; // Ch357 — native 640x480, size 0x12C000, 38400 beats/frame
`elsif GS_SH3_LPDDR_FB_SCHED
localparam int SCANOUT_LB_STRIDE = 1536; // Ch356 — 384 px * 4 B PSMCT32 (FBW=6, N-texture scheduler union)
localparam int SCANOUT_LB_ROW_BEATS = 48; // 1536 / 32
localparam int SCANOUT_LB_NROWS = 381; // Ch356 — 3-epoch union bbox (384x381), 18288 beats/frame
`elsif GS_SH3_LPDDR_FB_MULTITEX
localparam int SCANOUT_LB_STRIDE = 1280; // Ch355 — 320 px * 4 B PSMCT32 (FBW=5, multi-texture A+B union)
localparam int SCANOUT_LB_ROW_BEATS = 40; // 1280 / 32
localparam int SCANOUT_LB_NROWS = 381; // Ch355 — A+B union bbox (320x381)
`elsif GS_SH3_LPDDR_FB_MULTI
localparam int SCANOUT_LB_STRIDE = 1024; // 256 px * 4 B PSMCT32
localparam int SCANOUT_LB_ROW_BEATS = 32;
localparam int SCANOUT_LB_NROWS = 338; // Ch354 — multi-draw union bbox (256x338)
`else
localparam int SCANOUT_LB_STRIDE = 1024; // Ch353 — 256 px * 4 B PSMCT32
localparam int SCANOUT_LB_ROW_BEATS = 32; // 1024 / 32
localparam int SCANOUT_LB_NROWS = 334; // Ch353 — single SH3 draw bounding box (256x334)
`endif
`elsif GS_TILE_SPILL
localparam int SCANOUT_LB_STRIDE = 1024; // Ch327b — 256 px * 4 B PSMCT32
localparam int SCANOUT_LB_ROW_BEATS = 32; // 1024 / 32
localparam int SCANOUT_LB_NROWS = 256;
@@ -510,6 +575,26 @@ module de25_nano_psmct32_raster_demo_top (
localparam int SCANOUT_LB_STRIDE = 256;
localparam int SCANOUT_LB_ROW_BEATS = 8;
localparam int SCANOUT_LB_NROWS = 128;
`endif
// Ch418 — full-frame SH3 profiles preserve the dump's 512-pixel
// FBW=8 source geometry while presenting it across the board's
// 640-pixel VGA/HDMI active line. Other profiles remain 1:1.
`ifdef GS_SH3_LPDDR_FB_640
localparam bit SCANOUT_LB_H_STRETCH_5_TO_4 = 1'b1;
localparam int SCANOUT_LB_V_SOURCE_START = 32;
localparam bit SCANOUT_LB_V_STRETCH_15_TO_14 = 1'b1;
localparam bit SCANOUT_LB_V_LINEAR_FILTER = 1'b0;
localparam bit SCANOUT_LB_H_LINEAR_FILTER = 1'b0;
localparam bit SCANOUT_LB_BINOMIAL_3X3_FILTER = 1'b1;
localparam int SCANOUT_LB_H_SOURCE_PIXELS = 512;
`else
localparam bit SCANOUT_LB_H_STRETCH_5_TO_4 = 1'b0;
localparam int SCANOUT_LB_V_SOURCE_START = 0;
localparam bit SCANOUT_LB_V_STRETCH_15_TO_14 = 1'b0;
localparam bit SCANOUT_LB_V_LINEAR_FILTER = 1'b0;
localparam bit SCANOUT_LB_H_LINEAR_FILTER = 1'b0;
localparam bit SCANOUT_LB_BINOMIAL_3X3_FILTER = 1'b0;
localparam int SCANOUT_LB_H_SOURCE_PIXELS = 0;
`endif
// Initialize at declaration so iverilog doesn't bring up the
// sim with an X on hps_reset_sync[1] — that X would AND into
@@ -529,7 +614,7 @@ module de25_nano_psmct32_raster_demo_top (
end
wire hps_core_reset = hps_reset_sync[1];
wire core_rst_n = rst_sync[1] & ~hps_core_reset;
assign core_rst_n = rst_sync[1] & ~hps_core_reset;
// -------------------------------------------------------------
// core_go pulse — wait GO_DELAY cycles after core_rst_n
@@ -592,6 +677,7 @@ module de25_nano_psmct32_raster_demo_top (
wire demo_flush_emit;
wire [31:0] demo_flush_addr;
wire [15:0] demo_flush_pix16;
wire [3:0] demo_flush_be;
wire [5:0] demo_flush_psm;
wire [31:0] demo_vram_raddr; // Ch320 — PCRTC scanout VRAM byte address
wire demo_pix_window; // Ch320 — high inside the displayed frame window
@@ -622,6 +708,86 @@ module de25_nano_psmct32_raster_demo_top (
wire [31:0] demo_tile_reload_color; // reloaded color for raddr (1-cyc)
wire [31:0] demo_tile_reload_z; // reloaded Z for raddr (1-cyc)
wire [2:0] demo_tile_phase; // Ch323 diag — current tile phase (event-counter source)
// Ch357 — persistent-Z ROP fragment stream (perspective path). u_demo emits per-fragment
// {x,y,z} aligned to the flush color; gs_lpddr_zc_emit (under GS_SH3_LPDDR_FB_Z) does the
// PSMZ16S clamp16 read/compare/update and emits color ONLY on Z-pass. Inert otherwise.
wire [11:0] demo_flush_x;
wire [11:0] demo_flush_y;
wire [31:0] demo_flush_z;
wire demo_flush_abe;
wire [16:0] demo_flush_alpha;
wire demo_flush_ztest;
wire [1:0] demo_flush_ztst;
wire demo_flush_zmsk;
// ============================ Ch353 render epoch (GS_SH3_LPDDR_FB) ============================
// Codex round 5: with FEEDER_AUTOSTART=0 the boot setup ITSELF transitions into C_READY, so a flush gated on a
// feeder_ready RISE alone would enqueue a FALSE end-of-scene EOF at setup — which would assert frame_drained the
// instant the host later arms the writer. Gate the flush on an in-flight render instead:
// sh3_render_inflight = set by an ACCEPTED host GO (feeder_go pulse WHILE ready)
// sh3_fb_flush = sh3_render_inflight && feeder_ready RISE (render+drain complete) -> ONE EOF per render
// sh3_render_inflight = cleared when the flush is issued
// The writer's ordered drain ack (sh3_frame_drained, EMIF domain) then HARD-gates the scanout (no timeout).
// Proven end-to-end by tb_top_psmct32_sh3_lpddr_fb_seq. Inert (flush tied 0) for every other profile.
`ifdef GS_SH3_LPDDR_FB
wire sh3_frame_drained; // driven by u_lpddr_axi .frame_drained (EMIF/axi domain)
logic sh3_fb_flush, sh3_render_inflight, sh3_ready_q;
wire sh3_ready_rise = feeder_ready_w && !sh3_ready_q;
always_ff @(posedge design_clk or negedge core_rst_n) begin
if (!core_rst_n) begin sh3_fb_flush<=1'b0; sh3_render_inflight<=1'b0; sh3_ready_q<=1'b0; end
else begin
sh3_ready_q <= feeder_ready_w;
sh3_fb_flush <= 1'b0;
if (feeder_go_pulse_w && feeder_ready_w) sh3_render_inflight <= 1'b1; // accepted GO opens the epoch
if (sh3_render_inflight && sh3_ready_rise) begin
sh3_fb_flush <= 1'b1; // exactly one EOF per render
sh3_render_inflight <= 1'b0;
end
end
end
`else
wire sh3_fb_flush = 1'b0;
wire sh3_frame_drained = 1'b0; // non-SH3 profiles: tie off (fed to the bridge diagnostic, unused there)
`endif
`ifdef GS_SH3_LPDDR_FB_Z
// ---- Ch357 persistent-Z ROP feed (design_clk) ----
// The raster flush is the fragment producer; sh3_fb_flush (proven one-per-render ordered
// scene-completion marker) rides the SAME request FIFO as the end-of-scene marker. clamp16 is
// the vendored PCSX2 PSMZ16S source-Z saturation: min(z, 0xFFFF).
wire [15:0] zc_g_zq_w = (|demo_flush_z[31:16]) ? 16'hFFFF : demo_flush_z[15:0];
wire zc_frag_v = demo_flush_emit && (demo_flush_psm == 6'h00); // PSMCT32 raster fragment
wire zc_g_valid = zc_frag_v || sh3_fb_flush;
wire zc_g_scene = sh3_fb_flush; // ordered end-of-scene marker
wire [11:0] zc_g_x = demo_flush_x;
wire [11:0] zc_g_y = demo_flush_y;
wire [15:0] zc_g_zq = zc_g_zq_w;
wire zc_g_zmsk = demo_flush_zmsk;
wire zc_g_ztest = demo_flush_ztest;
wire [1:0] zc_g_ztst = demo_flush_ztst;
wire [31:0] zc_g_color = demo_flush_color32;
wire [16:0] zc_g_alpha = demo_flush_alpha;
wire [3:0] zc_g_be = demo_flush_be;
wire zc_g_ready; // driven by zc_emit
// Fragment-drop guard: the request async FIFO SILENTLY drops when g_ready is low and the raster
// cannot stall. Count every dropped request; the bridge snapshots this (0x0EC) + a sticky-nonzero bit
// (0x02C[8]) so the HOST proves zero drops per scene (fail-closed), not just the randomized sim.
reg [31:0] zc_g_drops_q;
always_ff @(posedge design_clk or negedge core_rst_n) begin
if (!core_rst_n) zc_g_drops_q <= 32'd0;
else if (zc_g_valid && !zc_g_ready) zc_g_drops_q <= zc_g_drops_q + 32'd1;
end
wire [31:0] zc_g_drops = zc_g_drops_q;
// Z-cache invalidate once at the initial host ARM (after the host preclears color+Z, before GO 1).
// arm rises exactly once per session; the pulse invalidates gs_lpddr_z_rmw's write-back line so Z
// then persists across every scheduler epoch (only clear_start invalidates; scene markers keep it).
wire zc_clear_done;
reg arm_e1, arm_e2, arm_e3;
always_ff @(posedge emif_clk or negedge emif_reset_n) begin
if (!emif_reset_n) begin arm_e1<=1'b0; arm_e2<=1'b0; arm_e3<=1'b0; end
else begin arm_e1<=lpddr_arm_w; arm_e2<=arm_e1; arm_e3<=arm_e2; end
end
wire zc_clear_start = arm_e2 && !arm_e3; // emif-domain rising edge of arm
`endif
// Ch352 (Codex guardrail #1) — LPDDR texture-cache geometry as PROFILE PARAMETERS, not hardcoded over the
// Ch322 tritex demo. SH3 real-draw crop: full 512x512 PSMT8 (262 KiB) in LPDDR, sampled at TBP=1024
// (0x40000). Every other GS_LPDDR_TEX profile keeps the tritex 8x8 (2 KiB) geometry.
@@ -629,6 +795,10 @@ module de25_nano_psmct32_raster_demo_top (
localparam int TEXC_VRAM_BASE = 32'h0004_0000; // crop TEX_VRAM_BASE = NEW_TBP(1024)*256
localparam int TEXC_BYTES = 262144; // 512x512 PSMT8
localparam int TEXC_NBEATS = 8192; // 262144 / 32 (single-beat 32 B reads)
`elsif GS_SH3_LPDDR_FB
localparam int TEXC_VRAM_BASE = 32'h0004_0000; // Ch353 — same 512x512 PSMT8 texture as the crop profile
localparam int TEXC_BYTES = 262144;
localparam int TEXC_NBEATS = 8192;
`else
localparam int TEXC_VRAM_BASE = 2048; // Ch322 tritex 8x8 PSMCT32
localparam int TEXC_BYTES = 2048;
@@ -642,12 +812,24 @@ module de25_nano_psmct32_raster_demo_top (
// this build path. Removing it retires the Ch159+ critical
// path and frees the timing budget for a faster clock.
.STRIP_HW_DIVIDER (1'b1),
// Ch352 — SEQUENTIAL gradient divider (registered iterations, no combinational cone). This REPLACES the
// combinational divide + the multicycle/false_path SDC approaches, which all made the Quartus fitter
// grind on the divider cone for hours. The sequential divider closes timing with NO SDC exception.
// Bit-exact to `/` (tb_gs_grad_divider) so the SH3 oracle stays 96.2%. GRAD_DIV_CYCLES below is ignored.
.GRAD_SEQ_DIVIDER (1'b1),
.GRAD_DIV_CYCLES (4),
// Ch357 (Codex) — SEQUENTIAL gradient divider (registered iterations, NO combinational cone), now the board
// default. The Ch352 seq-divider "prep trap" (>18 min, never reached Place) was caused by the combinational
// signed `/` remaining UNCONDITIONALLY in the RTL alongside the sequential divider — Quartus inferred BOTH,
// and the giant LPM divide cone is what ground Place/Prep for hours (twice: the SDC multicycle on the ~1157-
// source combinational cone, then the numerator-register variant, both ground). Ch357 makes the two dividers
// MUTUALLY EXCLUSIVE via generate (gs_stub g_combdiv/g_no_combdiv): at GRAD_SEQ_DIVIDER=1 the combinational `/`
// does not exist in the netlist at all, so there is no divider cone and NO gradient SDC exception is applied.
// Ch357 FINAL (Codex, 2026-07-05, after the isolation experiment ROUTED): keep the registered-numerator
// COMBINATIONAL divide (grad_num_sel_q/grad_det_q -> grad_result_q). Ch417 widens the divide from 56/32 to
// 64/35 bits, so use GRAD_DIV_CYCLES=5: design_clk = 40 ns (25 MHz) gives a 200 ns functional window instead
// of relying on the old 160 ns window measured against the former 108 ns routed cone. The setup=5/hold=4
// multicycle is applied STA-ONLY in the SDC (never
// during quartus_fit), so the fitter keeps its known-CONVERGENT "hopeless single-cycle" behavior (places in
// seconds) while final quartus_sta fail-closes at the 200 ns window. SEQUENTIAL divider (=1) is NOT used — it
// traps this Agilex fitter even with the combinational `/` generate-stripped. Bit-exact to `/`; SH3 oracle
// unchanged (tb_gs_grad_divider + the persp board-path equivalence 252/252).
.GRAD_SEQ_DIVIDER (1'b0),
.GRAD_DIV_CYCLES (5),
// Ch163 — strip the PCRTC magnification divider. The demo
// locks MAGH=MAGV=0 so the divisor is constant 1 and the
// `hwin_rel / hmag_factor` / `vwin_rel / vmag_factor` math
@@ -901,6 +1083,52 @@ module de25_nano_psmct32_raster_demo_top (
.CLUT_CSM1_ENABLE (1'b1),
.FEEDER_ENABLE (1'b1),
.FEEDER_STG_WORDS (768) // 68 clipped tris -> ~655 staging words
`elsif GS_SH3_LPDDR_FB
// Ch353 — the SH3 real draw on an LPDDR-ONLY direct PSMCT32 framebuffer (the FULL 256x334 bounding box, NOT
// the Ch352 256x120 BRAM crop). Same feeder-driven SH3 config as GS_SH3_REAL_DRAW_DEMO (real CSM1 CLUT +
// Ch351 widened perspective + GS_LPDDR_TEX cache, GRAD_SEQ_DIVIDER=0 from the shared prefix = the known-
// fitting combinational divide), but: FB_LPDDR_ONLY suppresses the BRAM raster mirror (the flush stream
// drives the PSMCT32 LPDDR writer instead of BRAM), and FEEDER_AUTOSTART=0 so boot setup reaches C_READY
// WITHOUT a render — the host arms the writer THEN pulses GO (0x0E8), and the render-epoch flush emits exactly
// one EOF -> one frame_drained, which HARD-gates the line-buffer scanout (no timeout). VRAM stays 128 KiB /
// CBP=480 (Codex option A — the known-fitting Ch352 config; the CLUT->CBP0 BRAM-shrink is deferred). Proven
// end-to-end by tb_top_psmct32_sh3_lpddr_fb_seq (the mandatory fit gate). GS_LPDDR4B_FB + GS_LPDDR_TEX must
// ALSO be defined (the build profile sets all three).
.VRAM_BYTES (128 * 1024),
.RAM_SIZE_BYTES (32 * 1024),
.VRAM_ENABLE_READ2 (1'b0),
.PSMCT32_SWIZZLE (1'b0),
.COMBINED_TAZ (1'b0),
.TILE_LOCAL (1'b0),
.TILE_COLS (1),
.TILE_ROWS (1),
.TILE_MULTIPRIM (1'b0),
.TILE_PRIM_COUNT (1),
.TILE_FIFO_DEPTH (8),
.BIN_BUFFER_ENABLE (1'b0),
.HEARTBEAT_SPLICE_ENABLE (1'b0),
.PERSPECTIVE_CORRECT(1'b1),
.SUBPIXEL_XY (1'b1), // Ch416 — preserve native XYZ 12.4 and sample pixel centers
.PERSP_RECIP_IDX_BITS(11),
.BILINEAR_ENABLE (1'b1),
.PALETTE_BILINEAR (1'b1),
.CLUT_CSM1_ENABLE (1'b1),
.FEEDER_ENABLE (1'b1),
`ifdef GS_SH3_LPDDR_FB_Z
.FEEDER_STG_WORDS (2048), // Ch402 — character draws split into staging-safe single-draw epochs
`elsif GS_SH3_LPDDR_FB_640
.FEEDER_STG_WORDS (2048), // Ch357 — native 640x480, same runtime-staged per-epoch list (619 words)
`elsif GS_SH3_LPDDR_FB_SCHED
.FEEDER_STG_WORDS (2048), // Ch356 — runtime-staged per-epoch list (68 tris / 619 words) + headroom
`elsif GS_SH3_LPDDR_FB_MULTITEX
.FEEDER_STG_WORDS (2048), // Ch355 — runtime-staged per-scene list (68 tris / 619 words) + headroom
`elsif GS_SH3_LPDDR_FB_MULTI
.FEEDER_STG_WORDS (2048), // Ch354 — 3-draw combined list = 204 tris / 1843 words (Codex-approved)
`else
.FEEDER_STG_WORDS (768), // Ch353 — single draw = 68 tris / ~655 words
`endif
.FEEDER_AUTOSTART (1'b0), // Ch353 — no boot render; host arms writer then GOes
.FB_LPDDR_ONLY (1'b1) // Ch353 — flush -> LPDDR writer, no BRAM FB mirror
`elsif GS_TILE_LATE_DEMO
// Ch316 — LEADING-EMPTY-TILE traversal fix. ONE prim, ONLY in the LAST tile
// t15 of the 4x4 grid; tiles t0..t14 are empty and precede it. Proves the
@@ -1282,6 +1510,7 @@ module de25_nano_psmct32_raster_demo_top (
.flush_emit_o (demo_flush_emit),
.flush_addr_o (demo_flush_addr),
.flush_pix16_o(demo_flush_pix16),
.flush_be_o (demo_flush_be),
.flush_psm_o (demo_flush_psm),
.vram_read_addr_o(demo_vram_raddr), // Ch320 — PCRTC scanout addr for LPDDR4B scanout
.pix_window_o (demo_pix_window), // Ch320 — displayed-frame window gate
@@ -1293,6 +1522,15 @@ module de25_nano_psmct32_raster_demo_top (
.tex_cache_ready_i(demo_tex_cache_ready),
.tex_cache_hits_o(demo_tex_cache_hits),
.tex_bram_hits_o (demo_tex_bram_hits),
// Ch357 — persistent-Z ROP fragment coords + Z (perspective path), aligned to the flush color.
.flush_x_o (demo_flush_x),
.flush_y_o (demo_flush_y),
.flush_z_o (demo_flush_z),
.flush_abe_o (demo_flush_abe),
.flush_alpha_o (demo_flush_alpha),
.flush_ztest_o (demo_flush_ztest),
.flush_ztst_o (demo_flush_ztst),
.flush_zmsk_o (demo_flush_zmsk),
// Ch323 — tile color+Z spill/reload (inert outputs when TILE_SPILL_ENABLE=0).
.flush_color32_o (demo_flush_color32),
.z_flush_emit_o (demo_z_flush_emit),
@@ -1317,7 +1555,11 @@ module de25_nano_psmct32_raster_demo_top (
.feeder_go_i (feeder_go_pulse_w),
.feeder_ready_o (feeder_ready_w),
.feeder_records_o (feeder_records_w),
.feeder_waits_o (feeder_waits_w)
.feeder_waits_o (feeder_waits_w),
.runtime_clut_wr_en_i (runtime_clut_wr_en_w),
.runtime_clut_wr_idx_i (runtime_clut_wr_idx_w),
.runtime_clut_wr_data_i(runtime_clut_wr_data_w),
.runtime_clut_busy_i (runtime_clut_busy_w)
);
`ifndef GS_LPDDR_TEX
// Ch322 — no texture cache: the texel tap returns BRAM (bram-top mux constant-folds
@@ -1592,8 +1834,21 @@ module de25_nano_psmct32_raster_demo_top (
end
end
wire video_src_eff = video_src_dclk;
`ifdef GS_SH3_LPDDR_FB
// Ch353 — HARD frame_drained gate (Codex round 5, NO timeout): blank BLACK until the ordered drain ack, THEN the
// line-buffer scanout serves the LPDDR FB. frame_ready_emif gates the EMIF prefetch enable; frame_ready_r (design)
// gates the HDMI source mux so unfilled line buffers can never show before the frame is drained. Failure (no ack)
// stays black forever — no timeout fallback (a partial FB is exactly the bug this handshake prevents).
logic [2:0] sh3_frd_dsync;
always_ff @(posedge design_clk or posedge async_rst_assert) begin
if (async_rst_assert) sh3_frd_dsync <= 3'd0; else sh3_frd_dsync <= {sh3_frd_dsync[1:0], sh3_frame_drained};
end
wire frame_ready_r = sh3_frd_dsync[2];
wire frame_ready_emif = sh3_frame_drained;
`else
wire frame_ready_r = 1'b1; // other profiles: BRAM mirror is a stable fallback, no gate
wire frame_ready_emif = 1'b1;
`endif
// emif-domain coherent bundled capture (LPDDR scanout enable + reader routing below).
logic [2:0] commit_e_sync; logic video_src_emif, scanout_lb_emif;
always_ff @(posedge emif_clk or negedge emif_reset_n) begin
@@ -2052,7 +2307,10 @@ module de25_nano_psmct32_raster_demo_top (
// Ch324 Brick 3 — LPDDR scanout config: the spill profile scans the 64x64 PSMCT32 color
// FB straight out of LPDDR at COLOR_SPILL_BASE (PCRTC addresses it BRAM-relative so
// VRAM_BASE stays 0). Other LPDDR profiles keep the FB-at-0 PSMCT16 mirror.
`ifdef GS_TILE_SPILL
`ifdef GS_SH3_LPDDR_FB
localparam [29:0] SCANOUT_FB_BASE = 30'd0; // Ch353 — FB at LPDDR base 0 (host sets 0x01C=0)
localparam bit SCANOUT_PSMCT32 = 1'b1; // full-frame PSMCT32
`elsif GS_TILE_SPILL
localparam [29:0] SCANOUT_FB_BASE = COLOR_SPILL_BASE;
localparam bit SCANOUT_PSMCT32 = 1'b1;
`else
@@ -2072,11 +2330,90 @@ module de25_nano_psmct32_raster_demo_top (
wire [7:0] fbw_awlen; wire [2:0] fbw_awsize; wire fbw_awvalid, fbw_awready;
wire [255:0] fbw_wdata; wire [31:0] fbw_wstrb; wire fbw_wlast, fbw_wvalid, fbw_wready;
wire [1:0] fbw_bresp; wire fbw_bvalid, fbw_bready;
gs_lpddr_axi_master #(.FIFO_DEPTH(16)) u_lpddr_axi (
`ifdef GS_SH3_LPDDR_FB_Z
// ================= Ch357 — persistent-Z emit (gs_lpddr_zc_emit) =================
// Replaces the plain color writer with a Z-then-color emitter: Z RMW (read/clamp16-compare/
// update) FIRST, color enqueued ONLY on pass. Three AXI streams, routed onto the shared LPDDR
// through the SAME de25 arbiters proven in tb_top_psmct32_sh3_zarb:
// * color writes (c_*) -> fbw_* == gs_lpddr_wr_arb s0 (FB-writer, priority)
// * Z writes (zw_*) -> gs_lpddr_wr_arb s2 (repurposed spill port; inert in this profile)
// * Z reads (zr_*) -> gs_lpddr_rd_arb s3 (reload port; inert in this profile)
// Fixture geometry: 256x210 strong-reject (Ch357) or NATIVE 640x480 (Ch358, +GS_SH3_LPDDR_FB_640).
// Z base 0x140000 (disjoint from color: 640x480x4=0x12C000 < 0x140000; and tex: Z top 0x1D6000 < 0x200000).
`ifdef GS_SH3_LPDDR_FB_640
localparam int ZFB_PXW = 640, ZFB_H = 480;
`else
localparam int ZFB_PXW = 256, ZFB_H = 210;
`endif
wire [31:0] zw_awaddr; wire [7:0] zw_awlen; wire [2:0] zw_awsize; wire [1:0] zw_awburst;
wire zw_awvalid, zw_awready;
wire [255:0] zw_wdata; wire [31:0] zw_wstrb; wire zw_wlast, zw_wvalid, zw_wready;
wire zw_bvalid, zw_bready; wire [1:0] zw_bresp;
wire [31:0] zr_araddr; wire [7:0] zr_arlen; wire [2:0] zr_arsize; wire [1:0] zr_arburst;
wire zr_arvalid, zr_arready;
wire [255:0] zr_rdata; wire [1:0] zr_rresp; wire zr_rlast, zr_rvalid, zr_rready;
wire [31:0] dr_araddr; wire [7:0] dr_arlen; wire [2:0] dr_arsize; wire [1:0] dr_arburst;
wire dr_arvalid, dr_arready;
wire [255:0] dr_rdata; wire [1:0] dr_rresp; wire dr_rlast, dr_rvalid, dr_rready;
wire [31:0] c_awaddr; wire [7:0] c_awlen; wire [2:0] c_awsize; wire [1:0] c_awburst;
wire c_awvalid;
wire [255:0] c_wdata; wire [31:0] c_wstrb; wire c_wlast, c_wvalid;
wire c_bready;
// Request-FIFO sizing history: Ch368's adjacent-frame runtime-CLUT test needed 3610 entries (4096 passed;
// 2048 clipped). The much larger Ch439 f52 production replay peaks at 6115 entries, establishing the 8192
// production size below. zc_g_drops remains the fail-closed hardware guard.
// (History: Ch357's tb_top_psmct32_sh3_zint showed a depth-64 request FIFO drops a small transient burst
// (2 frags) when the Z-RMW stalls on a run of cache-line misses while the raster — which cannot honor
// g_ready — keeps emitting; 256 sufficed for the zsched/zs640 scenes.)
gs_lpddr_zc_emit #(.COLBASE(32'h0000_0000), .ZBASE(32'h0014_0000),
// Ch439: the complete 311-epoch f52 replay peaks at 6,115
// queued requests. 8K retains 2,077 entries (34%) of
// measured headroom while halving the deep 93-bit FIFO's
// M20K/address-tree footprint at 310 MHz.
.FB_PXW(ZFB_PXW), .FB_H(ZFB_H), .REQ_DEPTH(8192), .COL_DEPTH(128)) u_zc_emit (
.gs_clk(design_clk), .gs_rst_n(core_rst_n), .enable(1'b1),
.g_valid(zc_g_valid), .g_ready(zc_g_ready), .g_x(zc_g_x), .g_y(zc_g_y), .g_zq(zc_g_zq),
.g_zmsk(zc_g_zmsk), .g_ztest(zc_g_ztest), .g_ztst(zc_g_ztst), .g_color(zc_g_color), .g_alpha(zc_g_alpha), .g_be(zc_g_be), .g_scene(zc_g_scene),
.axi_clk(emif_clk), .axi_rst_n(emif_reset_n),
.clear_start(zc_clear_start), .clear_done(zc_clear_done), .frame_drained(sh3_frame_drained),
.z_araddr(zr_araddr), .z_arlen(zr_arlen), .z_arsize(zr_arsize), .z_arburst(zr_arburst),
.z_arvalid(zr_arvalid), .z_arready(zr_arready),
.z_rdata(zr_rdata), .z_rresp(zr_rresp), .z_rlast(zr_rlast), .z_rvalid(zr_rvalid), .z_rready(zr_rready),
.d_araddr(dr_araddr), .d_arlen(dr_arlen), .d_arsize(dr_arsize), .d_arburst(dr_arburst),
.d_arvalid(dr_arvalid), .d_arready(dr_arready),
.d_rdata(dr_rdata), .d_rresp(dr_rresp), .d_rlast(dr_rlast), .d_rvalid(dr_rvalid), .d_rready(dr_rready),
.z_awaddr(zw_awaddr), .z_awlen(zw_awlen), .z_awsize(zw_awsize), .z_awburst(zw_awburst),
.z_awvalid(zw_awvalid), .z_awready(zw_awready),
.z_wdata(zw_wdata), .z_wstrb(zw_wstrb), .z_wlast(zw_wlast), .z_wvalid(zw_wvalid), .z_wready(zw_wready),
.z_bvalid(zw_bvalid), .z_bready(zw_bready), .z_bresp(zw_bresp),
.c_awaddr(c_awaddr), .c_awlen(c_awlen), .c_awsize(c_awsize), .c_awburst(c_awburst),
.c_awvalid(c_awvalid), .c_awready(fbw_awready),
.c_wdata(c_wdata), .c_wstrb(c_wstrb), .c_wlast(c_wlast), .c_wvalid(c_wvalid), .c_wready(fbw_wready),
.c_bvalid(fbw_bvalid), .c_bready(c_bready), .c_bresp(fbw_bresp),
.z_beats_read(), .z_beats_written(lpddr_bursts_w), .c_beats_written(lpddr_bytes_w),
.col_ovf(lpddr_fifo_ovf_w), .bresp_err(lpddr_bresp_err_w), .idle(lpddr_idle_w)
);
// Color master -> fbw_* (gs_lpddr_wr_arb s0). c_* carries no awid; the arb tags s0 internally.
assign fbw_awaddr = c_awaddr[29:0]; assign fbw_awburst = c_awburst; assign fbw_awid = 7'd0;
assign fbw_awlen = c_awlen; assign fbw_awsize = c_awsize; assign fbw_awvalid = c_awvalid;
assign fbw_wdata = c_wdata; assign fbw_wstrb = c_wstrb; assign fbw_wlast = c_wlast;
assign fbw_wvalid = c_wvalid; assign fbw_bready = c_bready;
assign emif_m_awaddr = 32'd0; assign emif_m_awid = 5'd0; // unused under _Z
`else
gs_lpddr_axi_master #(.FIFO_DEPTH(16)
`ifdef GS_SH3_LPDDR_FB
, .PIX_BYTES(4) // Ch353 — PSMCT32 = 8 px / 256-bit beat
`endif
) u_lpddr_axi (
.gs_clk(design_clk), .gs_rst_n(core_rst_n), .enable(1'b1),
.arm(lpddr_arm_w), .canary(lpddr_canary_w), .fb_base(lpddr_fb_base_w), .ctrl_commit(lpddr_ctrl_commit_w),
`ifdef GS_SH3_LPDDR_FB
.px_emit(demo_flush_emit && (demo_flush_psm == 6'h00)), // Ch353 — PSMCT32 raster emits only
.px_addr(demo_flush_addr), .px_pix32(demo_flush_color32), .flush(sh3_fb_flush),
`else
.px_emit(demo_flush_emit && (demo_flush_psm == 6'h02)),
.px_addr(demo_flush_addr), .px_pix16(demo_flush_pix16),
.px_addr(demo_flush_addr), .px_pix32({16'd0, demo_flush_pix16}), .flush(1'b0),
`endif
.axi_clk(emif_clk), .axi_rst_n(emif_reset_n),
.awaddr(emif_m_awaddr), .awlen(fbw_awlen), .awsize(fbw_awsize),
.awburst(fbw_awburst), .awid(emif_m_awid), .awvalid(fbw_awvalid),
@@ -2087,9 +2424,13 @@ module de25_nano_psmct32_raster_demo_top (
.beats_written(lpddr_bytes_w), .bursts_issued(lpddr_bursts_w),
.bresp_err_count(lpddr_bresp_err_w), .fifo_overflow_count(lpddr_fifo_ovf_w),
.idle(lpddr_idle_w)
`ifdef GS_SH3_LPDDR_FB
, .frame_drained(sh3_frame_drained) // Ch353 — ordered drain ack -> hard scanout gate
`endif
);
assign fbw_awaddr = emif_m_awaddr[29:0]; // EMIF addr is 30-bit (base 0)
assign fbw_awid = {2'b00, emif_m_awid}; // EMIF awid is 7-bit
`endif
`ifdef GS_LPDDR_TEX
// Ch322 — HPS write-probe (stage texture words) + 2:1 write arbiter (FB writer = priority).
wire [29:0] wp_awaddr; wire [1:0] wp_awburst; wire [6:0] wp_awid;
@@ -2116,10 +2457,17 @@ module de25_nano_psmct32_raster_demo_top (
.s1_awsize(wp_awsize), .s1_awvalid(wp_awvalid), .s1_awready(wp_awready),
.s1_wdata(wp_wdata), .s1_wstrb(wp_wstrb), .s1_wlast(wp_wlast), .s1_wvalid(wp_wvalid), .s1_wready(wp_wready),
.s1_bresp(wp_bresp), .s1_bvalid(wp_bvalid), .s1_bready(wp_bready),
`ifdef GS_SH3_LPDDR_FB_Z
// Ch357 — s2 = persistent-Z write stream from gs_lpddr_zc_emit (zw_*).
.s2_awaddr(zw_awaddr[29:0]), .s2_awburst(zw_awburst), .s2_awid(7'd6), .s2_awlen(zw_awlen), .s2_awsize(zw_awsize),
.s2_awvalid(zw_awvalid), .s2_awready(zw_awready), .s2_wdata(zw_wdata), .s2_wstrb(zw_wstrb), .s2_wlast(zw_wlast),
.s2_wvalid(zw_wvalid), .s2_wready(zw_wready), .s2_bresp(zw_bresp), .s2_bvalid(zw_bvalid), .s2_bready(zw_bready),
`else
// Ch323 — ports 2/3 (Z spill / HPS write-probe) unused in the GS_LPDDR_TEX build; tie inert.
.s2_awaddr(30'd0), .s2_awburst(2'b01), .s2_awid(7'd6), .s2_awlen(8'd0), .s2_awsize(3'b101),
.s2_awvalid(1'b0), .s2_awready(), .s2_wdata(256'd0), .s2_wstrb(32'd0), .s2_wlast(1'b0),
.s2_wvalid(1'b0), .s2_wready(), .s2_bresp(), .s2_bvalid(), .s2_bready(1'b1),
`endif
.s3_awaddr(30'd0), .s3_awburst(2'b01), .s3_awid(7'd5), .s3_awlen(8'd0), .s3_awsize(3'b101),
.s3_awvalid(1'b0), .s3_awready(), .s3_wdata(256'd0), .s3_wstrb(32'd0), .s3_wlast(1'b0),
.s3_wvalid(1'b0), .s3_wready(), .s3_bresp(), .s3_bvalid(), .s3_bready(1'b1),
@@ -2284,6 +2632,8 @@ module de25_nano_psmct32_raster_demo_top (
// line-buffer; other profiles keep the runtime bridge bit (default frame-cache).
`ifdef GS_TILE_SPILL
wire scanout_lb_eff = 1'b1;
`elsif GS_SH3_LPDDR_FB
wire scanout_lb_eff = 1'b1; // Ch353 — always line-buffer (a 256x334 frame-cache = 334 KiB)
`else
wire scanout_lb_eff = scanout_lb_emif; // Ch352 — emif-domain coherent capture (not raw bridge bit)
`endif
@@ -2303,7 +2653,14 @@ module de25_nano_psmct32_raster_demo_top (
);
gs_lpddr_scanout_lb #(.FB_BASE(SCANOUT_FB_BASE), .STRIDE_BYTES(SCANOUT_LB_STRIDE),
.ROW_BEATS(SCANOUT_LB_ROW_BEATS), .N_ROWS(SCANOUT_LB_NROWS),
.PSMCT32(SCANOUT_PSMCT32)) u_lpddr_scan_lb (
.PSMCT32(SCANOUT_PSMCT32),
.H_STRETCH_5_TO_4(SCANOUT_LB_H_STRETCH_5_TO_4),
.V_SOURCE_START(SCANOUT_LB_V_SOURCE_START),
.V_STRETCH_15_TO_14(SCANOUT_LB_V_STRETCH_15_TO_14),
.V_LINEAR_FILTER(SCANOUT_LB_V_LINEAR_FILTER),
.H_LINEAR_FILTER(SCANOUT_LB_H_LINEAR_FILTER),
.H_SOURCE_PIXELS(SCANOUT_LB_H_SOURCE_PIXELS),
.BINOMIAL_3X3_FILTER(SCANOUT_LB_BINOMIAL_3X3_FILTER)) u_lpddr_scan_lb (
.axi_clk(emif_clk), .axi_rst_n(emif_reset_n),
.enable(video_src_emif & scanout_lb_eff & frame_ready_emif),
.video_clk(design_clk), .frame_start(demo_video_vsync),
@@ -2354,6 +2711,9 @@ module de25_nano_psmct32_raster_demo_top (
.s3_arready(reload_ar_arready),
.s3_rdata(reload_r_rdata), .s3_rresp(reload_r_rresp), .s3_rlast(reload_r_rlast),
.s3_rvalid(reload_r_rvalid), .s3_rready(reload_r_rready),
.s4_araddr(dr_araddr[29:0]), .s4_arburst(dr_arburst), .s4_arid(7'd7),
.s4_arlen(dr_arlen), .s4_arsize(dr_arsize), .s4_arvalid(dr_arvalid), .s4_arready(dr_arready),
.s4_rdata(dr_rdata), .s4_rresp(dr_rresp), .s4_rlast(dr_rlast), .s4_rvalid(dr_rvalid), .s4_rready(dr_rready),
.m_araddr(emif_ar_araddr), .m_arburst(emif_ar_arburst), .m_arid(emif_ar_arid),
.m_arlen(emif_ar_arlen), .m_arsize(emif_ar_arsize), .m_arvalid(emif_ar_arvalid),
.m_arready(emif_ar_arready),
@@ -2413,6 +2773,14 @@ module de25_nano_psmct32_raster_demo_top (
.color_o(demo_tile_reload_color), .z_o(demo_tile_reload_z),
.reload_ready(demo_tile_reload_ready)
);
`elsif GS_SH3_LPDDR_FB_Z
// Ch357 — read-port-3 carries the persistent-Z READ stream from gs_lpddr_zc_emit (zr_*).
assign reload_ar_araddr=zr_araddr[29:0]; assign reload_ar_arburst=zr_arburst; assign reload_ar_arid=7'd6;
assign reload_ar_arlen=zr_arlen; assign reload_ar_arsize=zr_arsize; assign reload_ar_arvalid=zr_arvalid;
assign zr_arready=reload_ar_arready;
assign zr_rdata=reload_r_rdata; assign zr_rresp=reload_r_rresp; assign zr_rlast=reload_r_rlast;
assign zr_rvalid=reload_r_rvalid; assign reload_r_rready=zr_rready;
assign reload_color_beats_w=32'd0; assign reload_z_beats_w=32'd0; assign reload_rd_errs_w=32'd0;
`else
// no reload engine — tie read-port-3 inert (arvalid=0, rready=1 drains) + counters 0.
assign reload_ar_araddr=30'd0; assign reload_ar_arburst=2'b01; assign reload_ar_arid=7'd6;
@@ -2434,7 +2802,7 @@ module de25_nano_psmct32_raster_demo_top (
.gs_clk(design_clk), .gs_rst_n(core_rst_n), .enable(1'b1),
.arm(lpddr_arm_w), .canary(lpddr_canary_w), .fb_base(lpddr_fb_base_w), .ctrl_commit(lpddr_ctrl_commit_w),
.px_emit(demo_flush_emit && (demo_flush_psm == 6'h02)),
.px_addr(demo_flush_addr), .px_pix16(demo_flush_pix16),
.px_addr(demo_flush_addr), .px_pix32({16'd0, demo_flush_pix16}), .flush(1'b0),
.axi_clk(CLOCK2_50), .axi_rst_n(~ninit_done),
.awaddr(f2s_awaddr_w), .awlen(f2s_awlen_w), .awsize(f2s_awsize_w),
.awburst(f2s_awburst_w), .awid(f2s_awid_w), .awvalid(f2s_awvalid_w),
@@ -2682,6 +3050,15 @@ module de25_nano_psmct32_raster_demo_top (
.emif_hps_emif_ref_clk_0_clk (LPDDR4A_REFCLK_p)
);
// Ch357 — persistent-Z observability into the bridge (host fail-closed gates). Under GS_SH3_LPDDR_FB_Z the
// zc_emit preclear ack + drop counter; tied 0 for every other profile (the status bits read 0 / clear_done 0).
`ifdef GS_SH3_LPDDR_FB_Z
wire bridge_clear_done = zc_clear_done; // emif domain (from gs_lpddr_zc_emit) -> 0x02C[7]
wire [31:0] bridge_frag_drops = zc_g_drops; // design_clk domain -> 0x0EC snapshot + 0x02C[8] sticky
`else
wire bridge_clear_done = 1'b0;
wire [31:0] bridge_frag_drops = 32'd0;
`endif
// The minimal null AXI4 slave for the hps2fpga bridge. Same port
// signature as `splash_hps_bridge` so a future "real" ps2 bridge
// can be dropped in without re-plumbing the top.
@@ -2714,6 +3091,9 @@ module de25_nano_psmct32_raster_demo_top (
.lpddr_bresp_err_i(lpddr_bresp_err_w),
.lpddr_fifo_ovf_i (lpddr_fifo_ovf_w),
.lpddr_idle_i (lpddr_idle_w),
.frame_drained_i (sh3_frame_drained), // Ch353 — 0x02C[6] host-visible ordered drain ack (0 for non-SH3)
.clear_done_i (bridge_clear_done), // Ch357 — 0x02C[7] persistent-Z preclear done (0 for non-Z)
.frag_drops_i (bridge_frag_drops), // Ch357 — 0x0EC snapshot + 0x02C[8] sticky drop flag (0 for non-Z)
.lpddr_rd_addr_o (lpddr_rd_addr_w),
.lpddr_rd_pulse_o (lpddr_rd_pulse_w),
.lpddr_rd_data_i (lpddr_rd_data_w),
@@ -2849,7 +3229,16 @@ module de25_nano_psmct32_raster_demo_top (
.feeder_go_tgl_o (feeder_go_tgl_w),
.feeder_ready_i (feeder_ready_w),
.feeder_records_i (feeder_records_w),
.feeder_waits_i (feeder_waits_w)
.feeder_waits_i (feeder_waits_w),
.clut_stage_waddr_o (clut_stage_waddr_w),
.clut_stage_wdata_o (clut_stage_wdata_w),
.clut_stage_we_o (clut_stage_we_w),
.clut_commit_tgl_o (clut_commit_tgl_w),
.clut_expected_crc_o (clut_expected_crc_w),
.clut_busy_i (runtime_clut_busy_w),
.clut_done_tgl_i (runtime_clut_done_tgl_w),
.clut_crc_i (runtime_clut_crc_w)
);
`else
@@ -2886,6 +3275,11 @@ module de25_nano_psmct32_raster_demo_top (
assign feeder_go_tgl_w = 1'b0;
assign feeder_stg_waddr_w = 12'd0;
assign feeder_stg_wdata_w = 64'd0;
assign clut_stage_waddr_w = 8'd0;
assign clut_stage_wdata_w = 32'd0;
assign clut_stage_we_w = 1'b0;
assign clut_commit_tgl_w = 1'b0;
assign clut_expected_crc_w = 32'd0;
`endif // USE_QSYS_TOP
// Suppress unused-input warnings on lint (CLOCK0_50 / CLOCK1_50