// retroDE_ps2 — tb_top_psmct32_sh3_zint640c12 (Ch360 — four-epoch real-raster persistent-Z integration) // // Codex's pre-fit gate: prove the persistent-Z ROP through the SAME path the de25 top wires (GS_SH3_LPDDR_FB_Z): // REAL demo (feeder + raster, authz sh3_zs640c12 NATIVE-640x480 strong-reject scene) -> gs_lpddr_zc_emit -> REAL de25 arbiters // (gs_lpddr_wr_arb s0=color / s2=Z-write ; gs_lpddr_rd_arb s3=Z-read) -> ONE shared behavioral LPDDR // with randomized backpressure. Unlike tb_top_psmct32_sh3_zarb (which fed a handshake-throttled trace), // the raster CANNOT honor g_ready, so this is the gate that catches request-FIFO fragment DROPS. // // Asserts (Codex list): // * final LPDDR color + Z == independent clamp16 persistent-Z scoreboard over the ACTUAL emitted fragments; // * zero fragment drops (g_valid && !g_ready) — the whole reason this sim exists; // * exactly one ordered scene marker (sh3_fb_flush) per accepted GO; no fragment after a marker within a scene; // * Z cache invalidated (clear_done) after preclear and before the first GO; Z persists across epochs; // * a FRESH ordered frame_drained per epoch; scanout never referenced during render (rd_arb s0/s1/s2 idle); // * merged AW only in color/Z ranges, merged AR only in Z range (disjoint-map monitor). // LOCAL/gitignored fixtures; skip-guard if absent. `timescale 1ns/1ps module tb_top_psmct32_sh3_zint640c12; `include "sh3_zs640c12_params.vh" // FBPXW=640, FBH=480, N_EPOCHS=4, EPk_CRC/EPk_NTRIS, TEX_*, ... localparam int W = FBPXW, H = FBH; localparam int NPX = W*H; localparam int NEP = N_EPOCHS; localparam [31:0] COLBASE = 32'h0000_0000, ZBASE = 32'h0014_0000; localparam int COL_TOP = NPX*4; // color region byte-size (disjoint below ZBASE) localparam int Z_TOP = NPX*2; // Z region byte-size localparam int MEMBEATS = 65536; // shared LPDDR: covers ZBASE + Z_TOP // per-epoch expected CRC / records // Ch360 — four epochs share one texture: EPk_REUSE=1 means the epoch runs on the resident cache without a fill. logic [31:0] EP_CRC [0:3]; int EP_REC [0:3]; bit EP_REUSE [0:3]; int fills; initial begin EP_CRC[0]=EP0_CRC; EP_CRC[1]=EP1_CRC; EP_CRC[2]=EP2_CRC; EP_CRC[3]=EP3_CRC; EP_REC[0]=EP0_NTRIS; EP_REC[1]=EP1_NTRIS; EP_REC[2]=EP2_NTRIS; EP_REC[3]=EP3_NTRIS; EP_REUSE[0]=EP0_REUSE; EP_REUSE[1]=EP1_REUSE; EP_REUSE[2]=EP2_REUSE; EP_REUSE[3]=EP3_REUSE; fills=0; end `ifdef C12_REQ_DEPTH localparam int TB_REQ_DEPTH = `C12_REQ_DEPTH; // Ch359 sim-only depth-sweep variant (Codex-authorized) `else localparam int TB_REQ_DEPTH = 1024; // Ch359 production depth (Codex: cold-Z burst peaks at 478) `endif logic clk=0; always #5 clk=~clk; logic emif_clk=0; always #2 emif_clk=~emif_clk; logic rst_n; logic fb_commit=0; always #13 fb_commit=~fb_commit; int errors; initial errors=0; // clamp16 (vendored PCSX2 PSMZ16S source-Z saturation) function automatic logic [15:0] cl16(input logic [31:0] z); cl16=(|z[31:16])?16'hFFFF:z[15:0]; endfunction // ===== bram-top (FB_LPDDR_ONLY, AUTOSTART off), 384x381 ===== logic core_go; logic [7:0] r,g,b; logic hsync,vsync_o,de; logic core_halt,dma_done_seen,frame_seen,raster_overflow,frame_toggle,dma_done_toggle; logic feeder_go_tb, feeder_ready_tb; logic [15:0] feeder_records_w; logic [31:0] feeder_waits_w; logic gs_tex_rd_en_o; logic [31:0] gs_tex_rd_addr_o; logic [31:0] tex_cache_data; logic tex_cache_ready; logic [31:0] tex_cache_hits, tex_bram_hits; logic flush_emit_w; logic [31:0] flush_addr_w; logic [31:0] flush_color32_w; logic [5:0] flush_psm_w; logic [11:0] flush_x_w, flush_y_w; logic [31:0] flush_z_w; logic stg_we; logic [11:0] stg_waddr; logic [63:0] stg_wdata; top_psmct32_raster_demo_bram #( .H_ACTIVE(FBPXW), .V_ACTIVE(FBH), .VRAM_BYTES(VRAM_BYTES_P), .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), .FEEDER_ENABLE(1'b1), .FEEDER_STG_WORDS(STG_WORDS), .FEEDER_AUTOSTART(1'b0), .PERSPECTIVE_CORRECT(1'b1), .PERSP_RECIP_IDX_BITS(11), .GRAD_SEQ_DIVIDER(1'b1), .GRAD_DIV_CYCLES(4), .GS_LPDDR_TEX(1'b1), .TEX_VRAM_BASE(TEX_VRAM_BASE), .TEX_CACHE_BYTES(TEX_BYTES), .CLUT_CSM1_ENABLE(1'b1), .FB_LPDDR_ONLY(1'b1) ) dut ( .clk(clk), .rst_n(rst_n), .core_go(core_go), .r(r), .g(g), .b(b), .hsync(hsync), .vsync(vsync_o), .de(de), .core_halt(core_halt), .dma_done_seen(dma_done_seen), .frame_seen(frame_seen), .raster_overflow(raster_overflow), .frame_toggle(frame_toggle), .dma_done_toggle(dma_done_toggle), .joy_a_pressed_i(1'b0), .joy_b_pressed_i(1'b0), .feeder_stg_we_i(stg_we), .feeder_stg_waddr_i(stg_waddr), .feeder_stg_wdata_i(stg_wdata), .feeder_go_i(feeder_go_tb), .feeder_ready_o(feeder_ready_tb), .feeder_records_o(feeder_records_w), .feeder_waits_o(feeder_waits_w), .flush_emit_o(flush_emit_w), .flush_addr_o(flush_addr_w), .flush_pix16_o(), .flush_color32_o(flush_color32_w), .flush_psm_o(flush_psm_w), .flush_x_o(flush_x_w), .flush_y_o(flush_y_w), .flush_z_o(flush_z_w), .gs_tex_rd_en_o(gs_tex_rd_en_o), .gs_tex_rd_addr_o(gs_tex_rd_addr_o), .tex_cache_data_i(tex_cache_data), .tex_cache_ready_i(tex_cache_ready), .tex_cache_hits_o(tex_cache_hits), .tex_bram_hits_o(tex_bram_hits) ); // texture cache + behavioral texture LPDDR (reloaded per epoch) logic [29:0] araddr; logic [1:0] arburst; logic [6:0] arid; logic [7:0] arlen; logic [2:0] arsize; logic arvalid, arready; logic [255:0] rdata; logic [1:0] rresp; logic rlast, rvalid, rready; logic fill_start, fill_done; logic [31:0] fill_beats, fill_bytes, tex_rd_errs, fill_crc_w; gs_texture_cache #(.LPDDR_TEX_BASE(LPDDR_TEX_BASE), .TEX_VRAM_BASE(TEX_VRAM_BASE), .TEX_BYTES(TEX_BYTES), .N_BEATS(N_BEATS)) u_cache ( .axi_clk(clk), .axi_rst_n(rst_n), .fill_start(fill_start), .fill_done(fill_done), .fill_beats(fill_beats), .fill_bytes(fill_bytes), .rd_errs(tex_rd_errs), .fill_crc(fill_crc_w), .araddr(araddr), .arburst(arburst), .arid(arid), .arlen(arlen), .arsize(arsize), .arvalid(arvalid), .arready(arready), .rdata(rdata), .rresp(rresp), .rlast(rlast), .rvalid(rvalid), .rready(rready), .sample_clk(clk), .tex_rd_en(gs_tex_rd_en_o), .tex_rd_addr(gs_tex_rd_addr_o), .tex_rd_data(tex_cache_data), .tex_ready(tex_cache_ready) ); logic [31:0] lpddr_mem [0:(TEX_BYTES/4)-1]; typedef enum logic [1:0] { S_IDLE, S_WAIT, S_DATA } sstate_t; sstate_t sst; logic [3:0] tdly; int tbeat; always_ff @(posedge clk) begin if (!rst_n) begin sst<=S_IDLE; arready<=0; rvalid<=0; rlast<=0; rresp<=0; rdata<=0; tdly<=0; end else begin arready<=0; rvalid<=0; rlast<=0; case (sst) S_IDLE: if (arvalid) begin arready<=1; tbeat<=(araddr-LPDDR_TEX_BASE)>>5; tdly<=4'd2; sst<=S_WAIT; end S_WAIT: if (tdly==0) sst<=S_DATA; else tdly<=tdly-1'b1; S_DATA: if (rready) begin for (int w=0;w<8;w++) rdata[w*32 +: 32]<=lpddr_mem[tbeat*8+w]; rresp<=2'b00; rvalid<=1; rlast<=1; sst<=S_IDLE; end endcase end end // ===== render epoch: one ordered scene marker (sh3_fb_flush) per accepted GO ===== logic fb_flush=0, render_inflight=0, feeder_ready_q=0; int eof_count; wire feeder_ready_rise = feeder_ready_tb && !feeder_ready_q; always_ff @(posedge clk or negedge rst_n) begin if (!rst_n) begin render_inflight<=0; feeder_ready_q<=0; fb_flush<=0; eof_count<=0; end else begin feeder_ready_q<=feeder_ready_tb; fb_flush<=1'b0; if (feeder_go_tb && feeder_ready_tb) render_inflight<=1'b1; if (render_inflight && feeder_ready_rise) begin fb_flush<=1'b1; render_inflight<=1'b0; eof_count<=eof_count+1; end end end // ===== gs_lpddr_zc_emit — Z-then-color emit, fed by the REAL raster (NO handshake) ===== logic zc_enable, clear_start, clear_done, frame_drained; wire [15:0] g_zq = cl16(flush_z_w); wire frag_v = flush_emit_w && (flush_psm_w==6'h00); wire g_valid = frag_v || fb_flush; wire g_scene = fb_flush; wire [11:0] g_x = flush_x_w, g_y = flush_y_w; wire [31:0] g_color = flush_color32_w; wire g_ready; // Z AXI 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; // Color AXI 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 zc_idle; gs_lpddr_zc_emit #(.COLBASE(COLBASE), .ZBASE(ZBASE), .FB_PXW(W), .FB_H(H), .REQ_DEPTH(TB_REQ_DEPTH), .COL_DEPTH(128)) u_zc ( .gs_clk(clk), .gs_rst_n(rst_n), .enable(zc_enable), .g_valid(g_valid), .g_ready(g_ready), .g_x(g_x), .g_y(g_y), .g_zq(g_zq), .g_zmsk(1'b0), .g_ztest(1'b1), .g_ztst(2'd2), .g_color(g_color), .g_be(4'hF), .g_scene(g_scene), .axi_clk(emif_clk), .axi_rst_n(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(zc_idle) ); // Ch359 DIAGNOSTIC — request-FIFO occupancy high-water (write-domain binary pointers, hierarchical): // sizes the burst that overflows REQ_DEPTH=256 (would a deeper FIFO absorb it, or is the ep0 dirty-miss // streak service-rate-bound?). Sampled on the write clock; wbin-rbin_sampled is a safe over-estimate. // (Codex correction: occupancy must be computed ENTIRELY in the write clock domain — wbin vs the // write-domain-SYNCHRONIZED read pointer rgray_s2 converted gray->binary — with the modulus derived from the // selected depth, not a hard-coded 2*256.) int req_hiwater; logic hw_clear; function automatic int g2b(input int g); int b; b=g; b^=b>>1; b^=b>>2; b^=b>>4; b^=b>>8; b^=b>>16; return b; endfunction always_ff @(posedge clk or negedge rst_n) begin if (!rst_n) req_hiwater <= 0; else if (hw_clear) req_hiwater <= 0; else begin int occ; occ = (int'(u_zc.u_req.wbin) - g2b(int'(u_zc.u_req.rgray_s2))) % (2*TB_REQ_DEPTH); if (occ < 0) occ += 2*TB_REQ_DEPTH; if (occ > req_hiwater) req_hiwater <= occ; end end // fragment-drop guard (design clk): the request FIFO silently drops when g_ready is low int g_drops; int frags_fed; always_ff @(posedge clk or negedge rst_n) begin if (!rst_n) begin g_drops<=0; frags_fed<=0; end else begin if (frag_v && !g_ready) g_drops<=g_drops+1; if (frag_v && g_ready) frags_fed<=frags_fed+1; end end // clear ordering: clear_done must rise before the first GO. no psm0 fragment before clear_done. logic saw_clear_done=0, first_go_done=0; int pre_clear_frags; always_ff @(posedge clk or negedge rst_n) begin if (!rst_n) begin saw_clear_done<=0; pre_clear_frags<=0; end else begin if (clear_done) saw_clear_done<=1; if (frag_v && !saw_clear_done) pre_clear_frags<=pre_clear_frags+1; end end // ===== REAL de25 write arbiter: s0=color, s2=Z-write ; s1/s3 inert ===== logic [29:0] m_awaddr; logic [1:0] m_awburst; logic [6:0] m_awid; logic [7:0] m_awlen; logic [2:0] m_awsize; logic m_awvalid, m_awready; logic [255:0] m_wdata; logic [31:0] m_wstrb; logic m_wlast, m_wvalid, m_wready; logic m_bvalid, m_bready; logic [1:0] m_bresp; gs_lpddr_wr_arb u_wr_arb ( .clk(emif_clk), .rst_n(rst_n), .s0_awaddr(c_awaddr[29:0]), .s0_awburst(c_awburst), .s0_awid(7'd0), .s0_awlen(c_awlen), .s0_awsize(c_awsize), .s0_awvalid(c_awvalid), .s0_awready(c_awready), .s0_wdata(c_wdata), .s0_wstrb(c_wstrb), .s0_wlast(c_wlast), .s0_wvalid(c_wvalid), .s0_wready(c_wready), .s0_bresp(c_bresp), .s0_bvalid(c_bvalid), .s0_bready(c_bready), .s1_awaddr(30'd0), .s1_awburst(2'b01), .s1_awid(7'd1), .s1_awlen(8'd0), .s1_awsize(3'b101), .s1_awvalid(1'b0), .s1_awready(), .s1_wdata(256'd0), .s1_wstrb(32'd0), .s1_wlast(1'b0), .s1_wvalid(1'b0), .s1_wready(), .s1_bresp(), .s1_bvalid(), .s1_bready(1'b0), .s2_awaddr(z_awaddr[29:0]), .s2_awburst(z_awburst), .s2_awid(7'd2), .s2_awlen(z_awlen), .s2_awsize(z_awsize), .s2_awvalid(z_awvalid), .s2_awready(z_awready), .s2_wdata(z_wdata), .s2_wstrb(z_wstrb), .s2_wlast(z_wlast), .s2_wvalid(z_wvalid), .s2_wready(z_wready), .s2_bresp(z_bresp), .s2_bvalid(z_bvalid), .s2_bready(z_bready), .s3_awaddr(30'd0), .s3_awburst(2'b01), .s3_awid(7'd3), .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'b0), .m_awaddr(m_awaddr), .m_awburst(m_awburst), .m_awid(m_awid), .m_awlen(m_awlen), .m_awsize(m_awsize), .m_awvalid(m_awvalid), .m_awready(m_awready), .m_wdata(m_wdata), .m_wstrb(m_wstrb), .m_wlast(m_wlast), .m_wvalid(m_wvalid), .m_wready(m_wready), .m_bvalid(m_bvalid), .m_bready(m_bready), .m_bresp(m_bresp) ); // ===== REAL de25 read arbiter: s3=Z-read (mirrors de25 reload port) ; s0/s1/s2 idle during render ===== logic [29:0] m_araddr; logic [1:0] m_arburst; logic [6:0] m_arid; logic [7:0] m_arlen; logic [2:0] m_arsize; logic m_arvalid, m_arready; logic [255:0] m_rdata; logic [1:0] m_rresp; logic m_rlast, m_rvalid, m_rready; gs_lpddr_rd_arb u_rd_arb ( .clk(emif_clk), .rst_n(rst_n), .s0_araddr(30'd0), .s0_arburst(2'b01), .s0_arid(7'd0), .s0_arlen(8'd0), .s0_arsize(3'b101), .s0_arvalid(1'b0), .s0_arready(), .s0_rdata(), .s0_rresp(), .s0_rlast(), .s0_rvalid(), .s0_rready(1'b0), .s1_araddr(30'd0), .s1_arburst(2'b01), .s1_arid(7'd1), .s1_arlen(8'd0), .s1_arsize(3'b101), .s1_arvalid(1'b0), .s1_arready(), .s1_rdata(), .s1_rresp(), .s1_rlast(), .s1_rvalid(), .s1_rready(1'b0), .s2_araddr(30'd0), .s2_arburst(2'b01), .s2_arid(7'd2), .s2_arlen(8'd0), .s2_arsize(3'b101), .s2_arvalid(1'b0), .s2_arready(), .s2_rdata(), .s2_rresp(), .s2_rlast(), .s2_rvalid(), .s2_rready(1'b0), .s3_araddr(z_araddr[29:0]), .s3_arburst(z_arburst), .s3_arid(7'd3), .s3_arlen(z_arlen), .s3_arsize(z_arsize), .s3_arvalid(z_arvalid), .s3_arready(z_arready), .s3_rdata(z_rdata), .s3_rresp(z_rresp), .s3_rlast(z_rlast), .s3_rvalid(z_rvalid), .s3_rready(z_rready), .m_araddr(m_araddr), .m_arburst(m_arburst), .m_arid(m_arid), .m_arlen(m_arlen), .m_arsize(m_arsize), .m_arvalid(m_arvalid), .m_arready(m_arready), .m_rdata(m_rdata), .m_rresp(m_rresp), .m_rlast(m_rlast), .m_rvalid(m_rvalid), .m_rready(m_rready) ); logic [15:0] lf=16'hF00D; always_ff @(posedge emif_clk) lf<={lf[14:0], lf[15]^lf[13]^lf[12]^lf[10]}; // ===== ONE shared behavioral LPDDR (write + read), address-decoded, random backpressure ===== logic [255:0] shmem [0:MEMBEATS-1]; logic [29:0] wa; logic [255:0] wd; logic [31:0] ws; logic aw_s, w_s; logic [3:0] bd; logic bp; logic [29:0] ra; logic rp; logic [3:0] rd_dly; always_ff @(posedge emif_clk or negedge rst_n) begin if(!rst_n) begin m_awready<=0; m_wready<=0; m_bvalid<=0; m_bresp<=0; aw_s<=0; w_s<=0; bp<=0; bd<=0; m_arready<=0; m_rvalid<=0; m_rlast<=0; m_rresp<=0; m_rdata<=0; rp<=0; rd_dly<=0; end else begin m_awready<=(!aw_s)?lf[0]:1'b0; m_wready<=(!w_s)?lf[1]:1'b0; if(m_awvalid&&m_awready) begin wa<=m_awaddr; aw_s<=1; m_awready<=0; end if(m_wvalid&&m_wready) begin wd<=m_wdata; ws<=m_wstrb; w_s<=1; m_wready<=0; end if(aw_s&&w_s&&!bp&&!m_bvalid) begin for(int bb=0;bb<32;bb++) if(ws[bb]) shmem[wa>>5][bb*8+:8]<=wd[bb*8+:8]; bp<=1; bd<={1'b0,lf[4:2]}; end if(bp&&!m_bvalid) begin if(bd==0) begin m_bvalid<=1;m_bresp<=0;bp<=0;aw_s<=0;w_s<=0; end else bd<=bd-1; end if(m_bvalid&&m_bready) m_bvalid<=0; m_arready<=(!rp&&!m_rvalid)?lf[8]:1'b0; if(m_arvalid&&m_arready) begin ra<=m_araddr; rp<=1; rd_dly<={1'b0,lf[7:5]}; m_arready<=0; end if(rp&&!m_rvalid) begin if(rd_dly==0) begin m_rdata<=shmem[ra>>5]; m_rresp<=0; m_rvalid<=1; m_rlast<=1; rp<=0; end else rd_dly<=rd_dly-1; end if(m_rvalid&&m_rready) begin m_rvalid<=0; m_rlast<=0; end end end // disjoint-map monitors on the MERGED master streams always_ff @(posedge emif_clk) if(rst_n) begin if(m_awvalid) begin if(!((m_awaddr<30'(COLBASE+COL_TOP)) || (m_awaddr>=30'(ZBASE) && m_awaddr<30'(ZBASE+Z_TOP)))) begin $error("[zint] merged AW %h not in color/Z range",m_awaddr); errors++; end end if(m_arvalid && (m_araddr<30'(ZBASE) || m_araddr>=30'(ZBASE+Z_TOP))) begin $error("[zint] merged AR %h not in Z range",m_araddr); errors++; end end // ===== clamp16 persistent-Z SCOREBOARD over the ACTUAL emitted fragments (design clk) ===== // Mirrors gs_lpddr_z_rmw: GEQUAL vs stored, Z_CLEAR=0. Persists across epochs. Gated active after clear_done. logic [15:0] sb_z [0:NPX-1]; logic [31:0] sb_col[0:NPX-1]; logic sb_wr [0:NPX-1]; integer sb_frag, sb_pass; // no-fragment-after-marker: a fragment must not appear in the same scene after fb_flush (before next GO) logic scene_ended=0; int frag_after_marker; always_ff @(posedge clk or negedge rst_n) begin if (!rst_n) begin sb_frag<=0; sb_pass<=0; scene_ended<=0; frag_after_marker<=0; end else begin if (feeder_go_tb && feeder_ready_tb) scene_ended<=0; // new scene opens if (fb_flush) scene_ended<=1; // ordered marker closes the scene if (frag_v && saw_clear_done) begin int o; logic [15:0] zq; if (scene_ended) frag_after_marker<=frag_after_marker+1; // fragment after this scene's marker = ordering bug o = g_y*W + g_x; zq = cl16(flush_z_w); sb_frag<=sb_frag+1; if (o>=0 && o= sb_z[o]) begin sb_z[o]<=zq; sb_col[o]<=flush_color32_w; sb_wr[o]<=1'b1; sb_pass<=sb_pass+1; end end end end end // FRESH-DRAIN observer (emif domain) logic fd_q; int fd_rises, fd_falls; always_ff @(posedge emif_clk or negedge rst_n) begin if (!rst_n) begin fd_q<=0; fd_rises<=0; fd_falls<=0; end else begin fd_q<=frame_drained; if (frame_drained && !fd_q) fd_rises<=fd_rises+1; if (!frame_drained && fd_q) fd_falls<=fd_falls+1; end end // clear_start: mirror de25 — pulse once at the writer ARM (after preclear, before first GO) logic wr_arm=0, arm_e1, arm_e2, arm_e3; always_ff @(posedge emif_clk or negedge rst_n) begin if (!rst_n) begin arm_e1<=0; arm_e2<=0; arm_e3<=0; clear_start<=0; end else begin arm_e1<=wr_arm; arm_e2<=arm_e1; arm_e3<=arm_e2; clear_start<=(arm_e2 && !arm_e3); end end // ---- feeder staging stream (write port), per epoch ---- logic [63:0] stg_buf [0:STG_WORDS-1]; task automatic stream_list(input int k); string fn; fn=$sformatf("../../data/top_psmct32_raster_demo/feeder_sh3_zs640c12%0d.mem", k); for (int i=0;i%0d, r %0d->%0d) records=%0d drops=%0d fed=%0d pass=%0d", k, f0, fd_falls, r0, fd_rises, feeder_records_w, g_drops, frags_fed, sb_pass); endtask task automatic run_epoch(input int k); string fn; fn=$sformatf("../../data/top_psmct32_raster_demo/sh3_zs640c12%0d_tex_lpddr.mem", k); if (!EP_REUSE[k]) begin $readmemh(fn, lpddr_mem); fill_cache(k, EP_CRC[k]); end else begin // Ch359 — EXPLICIT REUSE: no reload, no fill pulse. Fail-closed residency: the fill CRC register is // only rewritten by a fill, so it must still hold the prior epoch's verified CRC (same shared texture). if (fill_crc_w!==EP_CRC[k]) begin $error("%s epoch %0d: REUSE but resident crc=%08x exp %08x — residency broken", "[zint]", k, fill_crc_w, EP_CRC[k]); errors++; end else $display("%s epoch %0d: REUSE resident texture (crc=0x%08x, no fill)", "[zint]", k, fill_crc_w); end stream_list(k); run_scene(k, EP_REC[k]); endtask initial begin errors=0; feeder_go_tb=1'b0; fill_start=1'b0; wr_arm=0; zc_enable=1'b1; stg_we=0; stg_waddr=0; stg_wdata=0; hw_clear=1'b0; for (int i=0;i clear_start) AFTER preclear, BEFORE first GO. z_rmw's clear_start writes Z_CLEAR to EVERY Z beat // (NBEATS=NPX/16, through wr_arb s2 under random backpressure) then asserts clear_done -> it is the authoritative // Z preclear. Wait for it (the board host likewise gates the first GO on the clear_done status bit). wr_arm<=1'b1; begin int d=0; while (!clear_done && d<600000) begin @(posedge emif_clk); d++; end end if (!clear_done) begin $error("[zint] clear_done never rose after ARM (Z preclear did not finish)"); errors++; end else $display("[zint] Z preclear complete (clear_done), z_beats_written(clear)=%0d", z_beats_written); repeat(40) @(posedge clk); for (int k=0;k>5; zl=o&15; zs=shmem[zb][zl*16+:16]; zchk++; if (zs !== sb_z[o]) begin if (zmis<10) $error("[zint] Z px%0d got %04x exp %04x", o, zs, sb_z[o]); zmis++; end if (sb_wr[o]) begin cs=shmem[o>>3][(o[2:0])*32+:32]; cchk++; if (cs[23:0] !== sb_col[o][23:0]) begin if (cmis<10) $error("[zint] COL px%0d got %06x exp %06x", o, cs[23:0], sb_col[o][23:0]); cmis++; end end end $display("[zint] final compare: Z %0d/%0d mismatch, COLOR %0d/%0d mismatch (written px=%0d)", zmis, zchk, cmis, cchk, cchk); end begin string fdump; if ($value$plusargs("FBDUMP=%s", fdump)) begin int fh; fh = $fopen(fdump, "w"); if (fh == 0) begin $error("[zint] could not open FBDUMP '%s'", fdump); errors++; end else begin for (int o=0;o>3][(o[2:0])*32+:32]; $fdisplay(fh, "%08x", cs); end $fclose(fh); $display("[zint] dumped final COLOR FB (%0dx%0d) -> %s", W, H, fdump); end end end $display("[tb_top_psmct32_sh3_zint640c12] fed=%0d pass=%0d drops=%0d markers=%0d drains(r/f)=%0d/%0d col_ovf=%0d bresp_err=%0d errors=%0d", frags_fed, sb_pass, g_drops, eof_count, fd_rises, fd_falls, col_ovf, bresp_err, errors); $display("[zint] req-FIFO depth=%0d (diagnostic sweep variant when != 1024)", TB_REQ_DEPTH); $display("[zint] map: COLOR 0x%0h..0x%0h Z 0x%0h..0x%0h", COLBASE, COLBASE+COL_TOP, ZBASE, ZBASE+Z_TOP); if (errors==0) $display("[tb_top_psmct32_sh3_zint640c12] PASS"); else $display("[tb_top_psmct32_sh3_zint640c12] FAIL"); $finish; end initial begin #400000000; $error("[tb_top_psmct32_sh3_zint640c12] TIMEOUT"); $finish; end endmodule : tb_top_psmct32_sh3_zint640c12