// ============================================================================ // tb_gs_tile_reload.sv (Ch323 Brick 2) // // Unit-proves the tile color+Z reload staging engine (the gs_texture_cache clone): // preload a behavioral single-beat EMIF with a 16x16 tile's color (at COLOR_BASE) // and Z (at a DISTINCT Z_BASE), strided STRIDE_BYTES/row; fill; then verify: // (1) reload_done; color_beats=32, z_beats=32 (16 rows x 2 beats each); rd_errs=0 // (2) every served (color,Z) for tile index 0..255 == the source (1-cycle read) // (3) a forced non-OKAY rresp increments rd_errs // ============================================================================ `timescale 1ns/1ps module tb_gs_tile_reload; localparam int TILE_W=16, TILE_H=16, STRIDE_BYTES=256, ROW_BEATS=2, COLOR_W=32; localparam [29:0] COLOR_BASE = 30'd0; localparam [29:0] Z_BASE = 30'h0000_4000; // distinct, small for the TB mem localparam int N_ENTRIES = TILE_W*TILE_H; // 256 logic axi_clk=0, serve_clk=0, axi_rst_n=0; always #5 axi_clk = ~axi_clk; always #7 serve_clk = ~serve_clk; // asynchronous logic reload_start, reload_done; logic [31:0] color_beats, z_beats, rd_errs; 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 [7:0] raddr; logic [31:0] color_o, z_o; logic reload_ready; function automatic [31:0] cval(input int i); cval = 32'hC0DE_0000 | i[31:0]; endfunction function automatic [31:0] zval(input int i); zval = 32'h5A5A_0000 | i[31:0]; endfunction int err_beat = -1; gs_tile_reload #( .COLOR_BASE(COLOR_BASE), .Z_BASE(Z_BASE), .TILE_W(TILE_W), .TILE_H(TILE_H), .STRIDE_BYTES(STRIDE_BYTES), .ROW_BEATS(ROW_BEATS), .COLOR_W(COLOR_W) ) dut ( .axi_clk(axi_clk), .axi_rst_n(axi_rst_n), .reload_start(reload_start), .reload_base(30'd0), .reload_done(reload_done), .color_beats(color_beats), .z_beats(z_beats), .rd_errs(rd_errs), .araddr(araddr), .arburst(arburst), .arid(arid), .arlen(arlen), .arsize(arsize), .arvalid(arvalid), .arready(arready), .rdata(rdata), .rresp(rresp), .rlast(rlast), .rvalid(rvalid), .rready(rready), .serve_clk(serve_clk), .raddr(raddr), .color_o(color_o), .z_o(z_o), .reload_ready(reload_ready) ); // behavioral 256-bit EMIF read model, preloaded with the strided tile color+Z. logic [255:0] mem [0:1023]; initial begin for (int i=0;i<1024;i++) mem[i]=256'd0; for (int row=0; row> 5][((ca >> 2) & 3'd7)*32 +: 32] = cval(idx); mem[za >> 5][((za >> 2) & 3'd7)*32 +: 32] = zval(idx); end end int beat_cnt; typedef enum logic [1:0] { S_IDLE,S_WAIT,S_DATA } sst_t; sst_t sst; logic [29:0] ba; logic [3:0] dly; always_ff @(posedge axi_clk) begin if (!axi_rst_n) begin sst<=S_IDLE; arready<=0; rvalid<=0; rlast<=0; rresp<=0; rdata<=0; dly<=0; beat_cnt<=0; end else begin arready<=0; rvalid<=0; rlast<=0; case (sst) S_IDLE: if (arvalid) begin ba<=araddr; arready<=1; dly<=4'd3; sst<=S_WAIT; end S_WAIT: if (dly==0) sst<=S_DATA; else dly<=dly-1'b1; S_DATA: if (rready) begin rdata <= mem[ba >> 5]; rresp <= (beat_cnt==err_beat) ? 2'b10 : 2'b00; rvalid <= 1; rlast <= 1; beat_cnt <= beat_cnt+1; sst<=S_IDLE; end endcase end end int errors=0; task automatic chk(input bit c, input string m); if (!c) begin errors++; $display("[tilereload] FAIL: %s", m); end endtask task automatic rd(input int idx, output logic [31:0] cg, output logic [31:0] zg); @(posedge serve_clk); raddr <= idx[7:0]; @(posedge serve_clk); @(posedge serve_clk); cg = color_o; zg = z_o; endtask logic [31:0] cg, zg; initial begin reload_start=0; raddr=0; repeat(4) @(posedge axi_clk); axi_rst_n=1; repeat(2) @(posedge axi_clk); // clean fill (PULSE reload_start once — rising-edge strobe) @(posedge axi_clk) reload_start<=1'b1; @(posedge axi_clk) reload_start<=1'b0; begin int g=0; while(!reload_done && g<5000) begin @(posedge axi_clk); g++; end end chk(reload_done, "reload_done never asserted"); chk(color_beats==32, $sformatf("color_beats=%0d exp 32", color_beats)); chk(z_beats==32, $sformatf("z_beats=%0d exp 32", z_beats)); chk(rd_errs==0, $sformatf("rd_errs=%0d exp 0", rd_errs)); begin int g=0; while(!reload_ready && g<200) begin @(posedge serve_clk); g++; end end chk(reload_ready, "reload_ready never synced"); for (int i=0;i rising edge -> refill @(posedge axi_clk) reload_start<=1'b0; begin int g=0; while(!reload_done && g<5000) begin @(posedge axi_clk); g++; end end chk(rd_errs==1, $sformatf("forced-err rd_errs=%0d exp 1", rd_errs)); $display("[tilereload] err-inject: rd_errs=%0d (exp 1)", rd_errs); if (errors==0) $display("[tb_gs_tile_reload] PASS"); else $display("[tb_gs_tile_reload] FAIL (%0d errors)", errors); $finish; end initial begin #800000; $display("[tb_gs_tile_reload] TIMEOUT"); $finish; end endmodule