// retroDE_ps2 — tb_top_psmct32_sh3_zrop (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 -> zsched_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_zrop; localparam int FB_PXW=256, FB_H=210; 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+ZBEATS*32)) begin $error("Z AR %h OOR",z_araddr);errors++; end if(z_awvalid && (z_awaddr=ZBASE+ZBEATS*32)) begin $error("Z AW %h OOR",z_awaddr);errors++; end if(c_awvalid && (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; logic [1023:0] line; 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=FB_PXW || y<0 || y>=FB_H) begin $error("[zrop] trace coord OOR ep=%0d x=%0d y=%0d z=%0d col=%08x", ep, x, y, z, col); errors++; end else 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 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>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>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_zrop] PASS"); else $display("[tb_top_psmct32_sh3_zrop] FAIL"); $finish; end initial begin #400000000; $error("[tb_top_psmct32_sh3_zrop] TIMEOUT"); $finish; end endmodule : tb_top_psmct32_sh3_zrop