// retroDE_ps2 — tb_gs_texture_unit
//
// Verifies gs_texture_unit end-to-end against a stand-in VRAM: preload known
// 32-bit texels, stream a sequence of (u,v) coords in, and check the sampled
// color comes out aligned with out_valid and matches the preloaded texel at
// the address the unit computed. Exercises the read-latency pipeline.

`timescale 1ns/1ps

module tb_gs_texture_unit;

    localparam logic [5:0] PSMCT32 = 6'h00;
    localparam int RD_LATENCY = 1;

    logic        clk = 0, rst_n = 0;
    logic        in_valid;
    logic [10:0] u, v;
    logic [31:0] tbp0_base_bytes;
    logic [13:0] tbw;
    logic [5:0]  psm;
    logic        tex_rd_en;
    logic [31:0] tex_rd_addr;
    logic [31:0] tex_rd_data;
    logic        out_valid;
    logic [31:0] tex_color;

    int errors = 0;

    always #5 clk = ~clk;

    gs_texture_unit #(.RD_LATENCY(RD_LATENCY)) dut (
        .clk(clk), .rst_n(rst_n),
        .in_valid(in_valid), .u(u), .v(v),
        .tbp0_base_bytes(tbp0_base_bytes), .tbw(tbw), .psm(psm),
        .tex_rd_en(tex_rd_en), .tex_rd_addr(tex_rd_addr), .tex_rd_data(tex_rd_data),
        .out_valid(out_valid), .tex_color(tex_color)
    );

    // --- stand-in VRAM: byte-addressed, 32-bit word, registered 1-cycle read ---
    logic [31:0] mem [0:1023];        // 4 KiB window
    always_ff @(posedge clk) begin
        if (tex_rd_en)
            tex_rd_data <= mem[tex_rd_addr[31:2]];  // word index = byte addr >> 2
    end

    // expected-color shadow, delayed to align with out_valid
    logic [31:0] exp_color_q;
    logic        exp_valid_q;

    task automatic feed(input logic [10:0] uu, input logic [10:0] vv,
                        input logic [31:0] expect_color);
        @(negedge clk);
        in_valid    = 1'b1;
        u           = uu;
        v           = vv;
        exp_color_q = expect_color;
        exp_valid_q = 1'b1;
        @(posedge clk);          // address presented; VRAM registers data
        #1;
        in_valid    = 1'b0;
        // out_valid + tex_color valid RD_LATENCY cycles after this posedge
    endtask

    // checker: whenever out_valid, compare against the pipelined expectation
    logic [31:0] exp_pipe;
    logic        expv_pipe;
    always_ff @(posedge clk) begin
        if (!rst_n) begin expv_pipe <= 0; exp_pipe <= 0; end
        else begin
            expv_pipe <= exp_valid_q;
            exp_pipe  <= exp_color_q;
            exp_valid_q <= 1'b0;   // one-shot per feed
        end
    end

    always_ff @(posedge clk) begin
        if (out_valid) begin
            if (tex_color !== exp_pipe) begin
                $display("FAIL: tex_color=%08x expected=%08x (addr=%0d)",
                         tex_color, exp_pipe, tex_rd_addr);
                errors++;
            end else begin
                $display("ok  : tex_color=%08x", tex_color);
            end
        end
    end

    initial begin
        // preload texels. tbw=1 -> 64 texels/row, base 0, PSMCT32 (4 B/texel)
        // texel (u,v) lives at word index v*64 + u.
        mem[2*64 + 3] = 32'hDEADBEEF;   // (u=3, v=2)
        mem[0]        = 32'h11223344;   // (u=0, v=0)
        mem[1*64 + 5] = 32'hA5A5F00D;   // (u=5, v=1)

        tbp0_base_bytes = 0; tbw = 1; psm = PSMCT32;
        in_valid = 0; u = 0; v = 0;

        repeat (3) @(posedge clk);
        rst_n = 1;
        @(negedge clk);

        feed(11'd3, 11'd2, 32'hDEADBEEF);
        feed(11'd0, 11'd0, 32'h11223344);
        feed(11'd5, 11'd1, 32'hA5A5F00D);

        repeat (4) @(posedge clk);

        if (errors == 0)
            $display("\nPASS tb_gs_texture_unit");
        else
            $display("\nFAIL tb_gs_texture_unit (%0d errors)", errors);
        $finish;
    end

endmodule
