// retroDE_ps2 — tb_gs_reciprocal (Ch301)
//
// Focused TB for gs_reciprocal_stub — the pipelined range-reduced reciprocal
// unit that backs PERSPECTIVE-CORRECT texture interpolation (recip = 1/q with
// NO divider). Proves:
//
//   PROOF 1 — CORRECTNESS vs software reference: for a sweep of q over the
//             perspective demo range (and powers of two + edges), the hardware
//             recip matches floor(2**SCALE / q) within the 8-bit-mantissa
//             relative tolerance (~1 part in 256).
//
//   PROOF 2 — PIPELINE THROUGHPUT: one result per cycle, back-to-back inputs
//             with no stall (the rasterizer needs ~1 pixel/cycle). Inputs are
//             streamed continuously; outputs are collected in order and matched
//             to their originating q.
//
//   PROOF 3 — q==0 saturates (1/0 -> all-ones), not X / wraparound.

`timescale 1ns/1ps

module tb_gs_reciprocal;

    localparam int Q_W   = 24;
    localparam int SCALE = 24;
    localparam int OUT_W = 25;

    logic clk, rst_n;
    initial clk = 1'b0;
    always #5 clk = ~clk;

    logic              in_valid;
    logic [Q_W-1:0]    q;
    logic              out_valid;
    logic [OUT_W-1:0]  recip;

    gs_reciprocal_stub #(.Q_W(Q_W), .IDX_BITS(8), .SCALE(SCALE), .OUT_W(OUT_W)) dut (
        .clk(clk), .rst_n(rst_n),
        .in_valid(in_valid), .q(q),
        .out_valid(out_valid), .recip(recip)
    );

    // golden reference
    function automatic logic [OUT_W-1:0] ref_recip(input logic [Q_W-1:0] qq);
        if (qq == 0) return {OUT_W{1'b1}};
        else         return OUT_W'((64'd1 << SCALE) / qq);
    endfunction

    // build the input vector: edges + powers of two + a dense sweep of the
    // perspective demo range [256 .. 65536] (q = (1/w)*2**16 for w in [1..256]).
    localparam int NV = 600;
    logic [Q_W-1:0] qin [0:NV-1];
    int n;

    initial begin
        n = 0;
        qin[n++] = 24'd1;
        qin[n++] = 24'd2;
        qin[n++] = 24'd3;
        qin[n++] = 24'd255;
        qin[n++] = 24'd256;
        qin[n++] = 24'd257;
        for (int p = 0; p < Q_W; p++) qin[n++] = 24'(24'd1 << p);   // powers of two
        // dense sweep across the demo 1/w range
        for (int w = 1; w <= 256 && n < NV; w++)
            qin[n++] = 24'((65536) / w);                            // q = floor(2**16 / w)
        // a few extra mid-range values
        while (n < NV) begin qin[n] = 24'(1000 + n*37); n++; end
    end

    // collected outputs, in order
    logic [OUT_W-1:0] rout [0:NV-1];
    int               oc;       // output count

    always_ff @(posedge clk) begin
        if (rst_n && out_valid) begin
            rout[oc] <= recip;
            oc       <= oc + 1;
        end
    end

    int errors;

    initial begin
        errors   = 0;
        oc       = 0;
        in_valid = 1'b0;
        q        = '0;
        rst_n    = 1'b0;
        repeat (4) @(posedge clk);
        rst_n = 1'b1;
        repeat (2) @(posedge clk);

        // PROOF 2: stream all NV inputs back-to-back (in_valid held high).
        for (int i = 0; i < NV; i++) begin
            @(negedge clk);
            in_valid = 1'b1;
            q        = qin[i];
        end
        @(negedge clk);
        in_valid = 1'b0;
        q        = '0;

        // drain the pipeline
        repeat (8) @(posedge clk);

        if (oc !== NV) begin
            $error("[recip] throughput: expected %0d outputs, got %0d (pipeline stalled / dropped)", NV, oc);
            errors = errors + 1;
        end

        // PROOF 1 + 3: compare each output to the software reference.
        for (int i = 0; i < oc && i < NV; i++) begin
            logic [OUT_W-1:0] exp;
            logic [OUT_W:0]   diff;
            logic [OUT_W:0]   tol;
            exp  = ref_recip(qin[i]);
            diff = (rout[i] > exp) ? ({1'b0,rout[i]} - {1'b0,exp})
                                   : ({1'b0,exp}     - {1'b0,rout[i]});
            // 8-bit mantissa -> relative error ~1/256; allow exp/128 + 1.
            tol  = (exp >> 7) + 2;
            if (qin[i] == 0) begin
                if (rout[i] !== {OUT_W{1'b1}}) begin
                    $error("[recip] q=0 not saturated: got %0d", rout[i]); errors++;
                end
            end else if (diff > tol) begin
                $error("[recip] q=%0d got=%0d exp=%0d diff=%0d tol=%0d",
                       qin[i], rout[i], exp, diff, tol);
                errors++;
            end
        end

        $display("[tb_gs_reciprocal] checked %0d values, errors=%0d", oc, errors);
        if (errors == 0) $display("[tb_gs_reciprocal] PASS");
        else             $display("[tb_gs_reciprocal] FAIL");
        $finish;
    end

    initial begin
        #2000000;
        $error("[tb_gs_reciprocal] TIMEOUT");
        $finish;
    end

endmodule : tb_gs_reciprocal
