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
+178
View File
@@ -0,0 +1,178 @@
// retroDE_ps2 — tb_top_psmct32_sh3_ztrio (Ch357 — persistent-Z ROP acceptance gate, TRACE-based scoreboard)
//
// Codex option A: verify the LPDDR-Z ROP (gs_lpddr_zc_emit) against the raster's ACTUAL emitted fragments. This TB replays
// the captured fragment trace (tb_top_psmct32_sh3_zsched -> ztrio_frags.txt: "epoch x y persp_z color" per line) through
// zc_emit across two clock domains, with a per-epoch scene marker, and scores it with an INDEPENDENT software scoreboard
// (clamp16 / GEQUAL / persistence). Proves Codex's Ch357 gates:
// * every replayed fragment enters the ROP EXACTLY once (we feed each once and wait g_ready — no drop, no double).
// * the scoreboard predicts every pass/reject and the final packed 16-bit Z; failed depth tests emit NO color.
// * final color + Z LPDDR memories match the scoreboard EXACTLY.
// * the ordered end-of-scene marker drain waits BOTH pipelines (color + Z BRESPs) before frame_drained.
// This is the ROP-correctness gate; the raster's Z-interp fidelity (float-oracle 3971 / 87.6% owner agreement) is a SEPARATE
// recorded limitation, not tested here. Primary fixture = sh3_zsched [8634,12757,145742], FB 256x210, Z base 0x140000.
`timescale 1ns/1ps
module tb_top_psmct32_sh3_ztrio;
localparam int FB_PXW=384, FB_H=381;
localparam int NPX=FB_PXW*FB_H; // 53760
localparam int ZBEATS=(NPX+15)/16; // 3360
localparam int CBEATS=(NPX+7)/8; // 6720
localparam [31:0] COLBASE=32'h0000_0000, ZBASE=32'h0014_0000;
localparam int ZBW=$clog2(ZBEATS), CBW=$clog2(CBEATS);
logic gs_clk=0; always #5 gs_clk=~gs_clk;
logic axi_clk=0; always #2 axi_clk=~axi_clk; // emif faster than design -> ROP drains faster than the trace feeds
logic gs_rst_n, axi_rst_n; int errors; initial errors=0;
logic enable, clear_start, clear_done, frame_drained;
logic g_valid, g_ready; logic [11:0] g_x, g_y; logic [15:0] g_zq; logic g_zmsk, g_ztest, g_scene; logic [31:0] g_color;
logic [31:0] z_araddr; logic [7:0] z_arlen; logic [2:0] z_arsize; logic [1:0] z_arburst; logic z_arvalid, z_arready;
logic [255:0] z_rdata; logic [1:0] z_rresp; logic z_rlast, z_rvalid, z_rready;
logic [31:0] z_awaddr; logic [7:0] z_awlen; logic [2:0] z_awsize; logic [1:0] z_awburst; logic z_awvalid, z_awready;
logic [255:0] z_wdata; logic [31:0] z_wstrb; logic z_wlast, z_wvalid, z_wready; logic z_bvalid, z_bready; logic [1:0] z_bresp;
logic [31:0] c_awaddr; logic [7:0] c_awlen; logic [2:0] c_awsize; logic [1:0] c_awburst; logic c_awvalid, c_awready;
logic [255:0] c_wdata; logic [31:0] c_wstrb; logic c_wlast, c_wvalid, c_wready; logic c_bvalid, c_bready; logic [1:0] c_bresp;
logic [31:0] z_beats_read, z_beats_written, c_beats_written, col_ovf, bresp_err; logic idle;
gs_lpddr_zc_emit #(.COLBASE(COLBASE), .ZBASE(ZBASE), .FB_PXW(FB_PXW), .FB_H(FB_H), .REQ_DEPTH(32), .COL_DEPTH(64)) dut (
.gs_clk(gs_clk), .gs_rst_n(gs_rst_n), .enable(enable),
.g_valid(g_valid), .g_ready(g_ready), .g_x(g_x), .g_y(g_y), .g_zq(g_zq), .g_zmsk(g_zmsk),
.g_ztest(g_ztest), .g_ztst(2'd2), .g_color(g_color), .g_be(4'hF), .g_scene(g_scene),
.axi_clk(axi_clk), .axi_rst_n(axi_rst_n), .clear_start(clear_start), .clear_done(clear_done), .frame_drained(frame_drained),
.z_araddr(z_araddr), .z_arlen(z_arlen), .z_arsize(z_arsize), .z_arburst(z_arburst), .z_arvalid(z_arvalid), .z_arready(z_arready),
.z_rdata(z_rdata), .z_rresp(z_rresp), .z_rlast(z_rlast), .z_rvalid(z_rvalid), .z_rready(z_rready),
.z_awaddr(z_awaddr), .z_awlen(z_awlen), .z_awsize(z_awsize), .z_awburst(z_awburst), .z_awvalid(z_awvalid), .z_awready(z_awready),
.z_wdata(z_wdata), .z_wstrb(z_wstrb), .z_wlast(z_wlast), .z_wvalid(z_wvalid), .z_wready(z_wready),
.z_bvalid(z_bvalid), .z_bready(z_bready), .z_bresp(z_bresp),
.c_awaddr(c_awaddr), .c_awlen(c_awlen), .c_awsize(c_awsize), .c_awburst(c_awburst), .c_awvalid(c_awvalid), .c_awready(c_awready),
.c_wdata(c_wdata), .c_wstrb(c_wstrb), .c_wlast(c_wlast), .c_wvalid(c_wvalid), .c_wready(c_wready),
.c_bvalid(c_bvalid), .c_bready(c_bready), .c_bresp(c_bresp),
.z_beats_read(z_beats_read), .z_beats_written(z_beats_written), .c_beats_written(c_beats_written),
.col_ovf(col_ovf), .bresp_err(bresp_err), .idle(idle)
);
logic [15:0] lf=16'hCAFE; always_ff @(posedge axi_clk) lf<={lf[14:0], lf[15]^lf[13]^lf[12]^lf[10]};
// ---- Z LPDDR slave (256b, random backpressure) ----
logic [255:0] zmem [0:ZBEATS-1];
logic [31:0] zra; logic zrp; logic [3:0] zrd; logic [31:0] zwa; logic [255:0] zwd; logic zaw,zw; logic [3:0] zbd; logic zbp;
always_ff @(posedge axi_clk or negedge axi_rst_n) begin
if(!axi_rst_n) begin z_arready<=0;z_rvalid<=0;z_rlast<=0;z_rresp<=0;z_rdata<=0;zrp<=0;zrd<=0;
z_awready<=0;z_wready<=0;z_bvalid<=0;z_bresp<=0;zaw<=0;zw<=0;zbp<=0;zbd<=0; end
else begin
z_arready<=(!zrp&&!z_rvalid)?lf[0]:1'b0;
if(z_arvalid&&z_arready) begin zra<=z_araddr;zrp<=1;zrd<={1'b0,lf[3:1]};z_arready<=0; end
if(zrp&&!z_rvalid) begin if(zrd==0) begin z_rdata<=zmem[(zra-ZBASE)>>5];z_rresp<=0;z_rvalid<=1;z_rlast<=1;zrp<=0; end else zrd<=zrd-1; end
if(z_rvalid&&z_rready) begin z_rvalid<=0;z_rlast<=0; end
z_awready<=(!zaw)?lf[4]:1'b0; z_wready<=(!zw)?lf[5]:1'b0;
if(z_awvalid&&z_awready) begin zwa<=z_awaddr;zaw<=1;z_awready<=0; end
if(z_wvalid&&z_wready) begin zwd<=z_wdata;zw<=1;z_wready<=0; end
if(zaw&&zw&&!zbp&&!z_bvalid) begin zmem[(zwa-ZBASE)>>5]<=zwd;zbp<=1;zbd<={1'b0,lf[8:6]}; end
if(zbp&&!z_bvalid) begin if(zbd==0) begin z_bvalid<=1;z_bresp<=0;zbp<=0;zaw<=0;zw<=0; end else zbd<=zbd-1; end
if(z_bvalid&&z_bready) z_bvalid<=0;
end
end
// ---- Color LPDDR slave (256b, per-byte wstrb, random backpressure) ----
logic [255:0] cmem [0:CBEATS-1];
logic [31:0] cwa; logic [255:0] cwd; logic [31:0] cws; logic caw,cw; logic [3:0] cbd; logic cbp;
always_ff @(posedge axi_clk or negedge axi_rst_n) begin
if(!axi_rst_n) begin c_awready<=0;c_wready<=0;c_bvalid<=0;c_bresp<=0;caw<=0;cw<=0;cbp<=0;cbd<=0; end
else begin
c_awready<=(!caw)?lf[9]:1'b0; c_wready<=(!cw)?lf[11]:1'b0;
if(c_awvalid&&c_awready) begin cwa<=c_awaddr;caw<=1;c_awready<=0; end
if(c_wvalid&&c_wready) begin cwd<=c_wdata;cws<=c_wstrb;cw<=1;c_wready<=0; end
if(caw&&cw&&!cbp&&!c_bvalid) begin
for(int b=0;b<32;b++) if(cws[b]) cmem[(cwa-COLBASE)>>5][b*8+:8]<=cwd[b*8+:8];
cbp<=1;cbd<={1'b0,lf[14:12]};
end
if(cbp&&!c_bvalid) begin if(cbd==0) begin c_bvalid<=1;c_bresp<=0;cbp<=0;caw<=0;cw<=0; end else cbd<=cbd-1; end
if(c_bvalid&&c_bready) c_bvalid<=0;
end
end
// disjoint-range monitors
always_ff @(posedge axi_clk) if(axi_rst_n) begin
if(z_arvalid && (z_araddr<ZBASE || z_araddr>=ZBASE+ZBEATS*32)) begin $error("Z AR %h OOR",z_araddr);errors++; end
if(z_awvalid && (z_awaddr<ZBASE || z_awaddr>=ZBASE+ZBEATS*32)) begin $error("Z AW %h OOR",z_awaddr);errors++; end
if(c_awvalid && (c_awaddr<COLBASE || c_awaddr>=COLBASE+CBEATS*32)) begin $error("C AW %h OOR",c_awaddr);errors++; end
end
// ---- independent scoreboard (clamp16 persistent-Z over the fed fragments) ----
logic [15:0] sb_z [0:NPX-1]; logic [31:0] sb_col [0:NPX-1]; logic sb_seen [0:NPX-1];
function automatic logic [15:0] cl16(input logic [31:0] z); cl16=(|z[31:16])?16'hFFFF:z[15:0]; endfunction
// ---- feed one fragment: drive g_*, wait acceptance (exactly once), update the scoreboard ----
int nfed;
task automatic feed_frag(input int ep, input int x, input int y, input int z, input logic [31:0] col);
int o; logic [15:0] zq; logic pass;
@(negedge gs_clk);
g_valid<=1; g_scene<=0; g_x<=x[11:0]; g_y<=y[11:0]; g_zq<=cl16(z); g_zmsk<=0; g_ztest<=1; g_color<=col;
@(posedge gs_clk);
while(!(g_valid && g_ready)) @(posedge gs_clk); // wait acceptance — NEVER drop a fragment
@(negedge gs_clk); g_valid<=0;
// scoreboard (in feed order == ROP processing order)
o=y*FB_PXW+x; zq=cl16(z); pass=(zq>=sb_z[o]);
if(pass) begin sb_col[o]=col; sb_seen[o]=1; sb_z[o]=zq; end
nfed++;
if(lf[7]) repeat(1+lf[1:0]) @(posedge gs_clk); // random inter-fragment gap
endtask
task automatic send_marker();
@(negedge gs_clk); g_valid<=1; g_scene<=1; @(posedge gs_clk);
while(!(g_valid && g_ready)) @(posedge gs_clk);
@(negedge gs_clk); g_valid<=0; g_scene<=0;
endtask
int fh, r, ep, x, y, z, cep, drains; logic [31:0] col;
initial begin
errors=0; enable=0; clear_start=0; g_valid=0; g_scene=0; g_x=0; g_y=0; g_zq=0; g_zmsk=0; g_ztest=1; g_color=0; nfed=0; drains=0;
for(int i=0;i<NPX;i++) begin sb_z[i]=16'h0000; sb_col[i]=32'd0; sb_seen[i]=1'b0; end
for(int b=0;b<CBEATS;b++) cmem[b]=256'd0; // host preclears the color FB
gs_rst_n=0; axi_rst_n=0; repeat(6) @(posedge axi_clk); gs_rst_n=1; axi_rst_n=1; repeat(4) @(posedge axi_clk);
enable=1;
@(negedge axi_clk) clear_start=1; @(negedge axi_clk) clear_start=0; // preclear Z once
begin int g=0; while(!clear_done && g<400000) begin @(posedge axi_clk); g++; end end
if(!clear_done) begin $error("[zrop] Z preclear timeout"); errors++; end
fh=$fopen("ztrio_frags.txt","r");
if(fh==0) begin $display("[tb_top_psmct32_sh3_ztrio] SKIP — ztrio_frags.txt absent (run make tb_top_psmct32_sh3_zsched)"); $finish; end
cep=0;
while(!$feof(fh)) begin
r=$fscanf(fh, "%d %d %d %d %h\n", ep, x, y, z, col);
if(r==5) begin
if(ep!=cep) begin // epoch boundary -> ordered scene drain
send_marker();
begin int g=0; while(!frame_drained && g<800000) begin @(posedge axi_clk); g++; end end
if(!frame_drained) begin $error("[zrop] epoch %0d: frame_drained never rose",cep); errors++; end
else drains++;
cep=ep;
end
feed_frag(ep, x, y, z, col);
end else begin
r=$fgetc(fh); // skip a char past a blank/partial line
end
end
$fclose(fh);
// final scene marker (last epoch)
send_marker();
begin int g=0; while(!frame_drained && g<800000) begin @(posedge axi_clk); g++; end end
if(!frame_drained) begin $error("[zrop] final: frame_drained never rose"); errors++; end else drains++;
repeat(60) @(posedge axi_clk);
// ---- gates: final Z + color LPDDR == scoreboard ; suppression ; counts ----
for(int o=0;o<NPX;o++) begin
logic [15:0] zs; zs=zmem[o>>4][(o[3:0])*16+:16];
if(zs!==sb_z[o]) begin if(errors<12)$error("[zrop] Z px%0d=%04x exp %04x",o,zs,sb_z[o]);errors++; end
end
for(int o=0;o<NPX;o++) begin
logic [31:0] cs; cs=cmem[o>>3][(o[2:0])*32+:32];
if(sb_seen[o]) begin if(cs!==sb_col[o]) begin if(errors<12)$error("[zrop] COLOR px%0d=%08x exp %08x",o,cs,sb_col[o]);errors++; end end
else begin if(cs!==32'd0) begin if(errors<12)$error("[zrop] px%0d never-passed but color=%08x (suppression fail)",o,cs);errors++; end end
end
if(col_ovf!==0) begin $error("[zrop] col_ovf=%0d",col_ovf);errors++; end
if(bresp_err!==0)begin $error("[zrop] bresp_err=%0d",bresp_err);errors++; end
$display("[zrop] fed=%0d fragments, scene-drains=%0d, z_wr=%0d c_wr=%0d col_ovf=%0d bresp_err=%0d errors=%0d",
nfed, drains, z_beats_written, c_beats_written, col_ovf, bresp_err, errors);
if(errors==0) $display("[tb_top_psmct32_sh3_ztrio] PASS"); else $display("[tb_top_psmct32_sh3_ztrio] FAIL");
$finish;
end
initial begin #400000000; $error("[tb_top_psmct32_sh3_ztrio] TIMEOUT"); $finish; end
endmodule : tb_top_psmct32_sh3_ztrio