// ============================================================================
// 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<TILE_H; row++) for (int col=0; col<TILE_W; col++) begin
            int idx; logic [29:0] ca, za;
            idx = row*TILE_W + col;
            ca  = COLOR_BASE + row*STRIDE_BYTES + col*4;
            za  = Z_BASE     + row*STRIDE_BYTES + col*4;
            mem[ca >> 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<N_ENTRIES;i++) begin
            rd(i, cg, zg);
            chk(cg===cval(i), $sformatf("color[%0d]=%08x exp %08x", i, cg, cval(i)));
            chk(zg===zval(i), $sformatf("z[%0d]=%08x exp %08x", i, zg, zval(i)));
        end
        $display("[tilereload] clean: done=%0d color_beats=%0d z_beats=%0d rd_errs=%0d entries=%0d errors=%0d",
                 reload_done, color_beats, z_beats, rd_errs, N_ENTRIES, errors);

        // err-inject: reset, force SLVERR on beat 5
        axi_rst_n=0; err_beat=5; repeat(3) @(posedge axi_clk); axi_rst_n=1; repeat(2) @(posedge axi_clk);
        @(posedge axi_clk) reload_start<=1'b1;   // PULSE again -> 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
