// ============================================================================
// tb_gs_grad_num_equiv — Ch444
//
// Focused equivalence proof for the SERIALIZED gradient numerator (gs_stub
// grad_num_step). The old design instantiated grad_num_dadx/grad_num_dady for
// every attribute in parallel; Ch444 replaced that bank with ONE mux-selected
// pair of 32x17 multipliers. This TB proves the new inlined arithmetic is
// BIT-IDENTICAL to the original functions for BOTH axes, across every attribute
// operand width AND extreme signed coordinate / overflow-boundary cases that the
// f52 real-data assertion may never exercise:
//   - GOLDEN = the exact original grad_num_dadx/dady (verbatim, pre-Ch444).
//   - DUT    = the exact grad_num_step arithmetic (shared da1/da2 + axis-muxed
//              32x17 products, signed subtract, <<<20).
// Sweeps attributes over {0, 8/11/24/32-bit maxima, sign boundaries, random} and
// vertex coords over {0, 0xFFFF, 0x8000, boundary, random}. Any mismatch fails.
`timescale 1ns/1ps

module tb_gs_grad_num_equiv;
    int errors = 0, checks = 0;

    // ---- GOLDEN: the original functions, copied verbatim from pre-Ch444 gs_stub ----
    function automatic logic signed [63:0] gold_dadx(
            input logic signed [31:0] a0, a1, a2,
            input logic [15:0] x0,y0,x1,y1,x2,y2);
        logic signed [16:0] dy1, dy2; logic signed [63:0] num;
        dy1 = $signed({1'b0, y1}) - $signed({1'b0, y0});
        dy2 = $signed({1'b0, y2}) - $signed({1'b0, y0});
        num = (a1 - a0) * $signed({{47{dy2[16]}}, dy2})
            - (a2 - a0) * $signed({{47{dy1[16]}}, dy1});
        gold_dadx = num <<< 20;
    endfunction
    function automatic logic signed [63:0] gold_dady(
            input logic signed [31:0] a0, a1, a2,
            input logic [15:0] x0,y0,x1,y1,x2,y2);
        logic signed [16:0] dx1, dx2; logic signed [63:0] num;
        dx1 = $signed({1'b0, x1}) - $signed({1'b0, x0});
        dx2 = $signed({1'b0, x2}) - $signed({1'b0, x0});
        num = (a2 - a0) * $signed({{47{dx1[16]}}, dx1})
            - (a1 - a0) * $signed({{47{dx2[16]}}, dx2});
        gold_dady = num <<< 20;
    endfunction

    // ---- DUT: the exact grad_num_step arithmetic (shared 32x17 pair, axis mux) ----
    function automatic logic signed [63:0] dut_step(
            input logic axis,                        // 0 = d/dx, 1 = d/dy
            input logic signed [31:0] a0, a1, a2,
            input logic [15:0] x0,y0,x1,y1,x2,y2);
        logic signed [16:0] dy1,dy2,dx1,dx2;
        logic signed [32:0] da1, da2;
        logic signed [32:0] mA_p, mA_n; logic signed [16:0] mB_p, mB_n;
        logic signed [49:0] prodP, prodN; logic signed [63:0] raw;
        dy1 = $signed({1'b0,y1}) - $signed({1'b0,y0});
        dy2 = $signed({1'b0,y2}) - $signed({1'b0,y0});
        dx1 = $signed({1'b0,x1}) - $signed({1'b0,x0});
        dx2 = $signed({1'b0,x2}) - $signed({1'b0,x0});
        da1 = $signed(a1) - $signed(a0);  da2 = $signed(a2) - $signed(a0);
        if (!axis) begin mA_p=da1; mB_p=dy2; mA_n=da2; mB_n=dy1; end
        else       begin mA_p=da2; mB_p=dx1; mA_n=da1; mB_n=dx2; end
        prodP = mA_p * mB_p;  prodN = mA_n * mB_n;
        raw   = $signed(prodP) - $signed(prodN);
        dut_step = raw <<< 20;
    endfunction

    task automatic chk(input logic signed [31:0] a0,a1,a2,
                       input logic [15:0] x0,y0,x1,y1,x2,y2);
        logic signed [63:0] gx, gy, dx, dy;
        gx = gold_dadx(a0,a1,a2,x0,y0,x1,y1,x2,y2);
        gy = gold_dady(a0,a1,a2,x0,y0,x1,y1,x2,y2);
        dx = dut_step(1'b0, a0,a1,a2,x0,y0,x1,y1,x2,y2);
        dy = dut_step(1'b1, a0,a1,a2,x0,y0,x1,y1,x2,y2);
        checks += 2;
        if (dx !== gx) begin errors++; if (errors<20) $display("[equiv] DADX MISMATCH a=%h,%h,%h xy=%h,%h/%h,%h/%h,%h dut=%h gold=%h",a0,a1,a2,x0,y0,x1,y1,x2,y2,dx,gx); end
        if (dy !== gy) begin errors++; if (errors<20) $display("[equiv] DADY MISMATCH a=%h,%h,%h xy=%h,%h/%h,%h/%h,%h dut=%h gold=%h",a0,a1,a2,x0,y0,x1,y1,x2,y2,dy,gy); end
    endtask

    // attribute corner values spanning every operand width (color8/uv11/stq24/z32)
    logic signed [31:0] AV [];
    logic [15:0]        CV [];
    initial begin
        AV = new[9];
        AV[0]=32'h00000000; AV[1]=32'h000000FF; AV[2]=32'h000007FF; AV[3]=32'h00FFFFFF;
        AV[4]=32'h7FFFFFFF; AV[5]=32'h80000000; AV[6]=32'hFFFFFFFF; AV[7]=32'h00800000; AV[8]=32'h12345678;
        CV = new[6];
        CV[0]=16'h0000; CV[1]=16'hFFFF; CV[2]=16'h8000; CV[3]=16'h7FFF; CV[4]=16'h0001; CV[5]=16'hA5A5;

        // 1) exhaustive over attribute corners with a fixed non-degenerate triangle
        foreach (AV[i]) foreach (AV[j]) foreach (AV[k])
            chk(AV[i],AV[j],AV[k], 16'h0100,16'h0080, 16'h0900,16'h0110, 16'h0300,16'h0A00);

        // 2) exhaustive over coordinate corners with fixed spread attributes
        foreach (CV[a]) foreach (CV[b]) foreach (CV[c]) foreach (CV[d]) foreach (CV[e]) foreach (CV[f])
            chk(32'h00000000, 32'h00ABCDEF, 32'h7F123456, CV[a],CV[b],CV[c],CV[d],CV[e],CV[f]);

        // 3) randomized wide sweep (attributes + coords), catches any residual case
        for (int n=0; n<200000; n++) begin
            logic signed [31:0] ra0,ra1,ra2;
            ra0=$random; ra1=$random; ra2=$random;
            chk(ra0,ra1,ra2, $random,$random,$random,$random,$random,$random);
        end

        $display("[equiv] checks=%0d errors=%0d", checks, errors);
        if (errors==0) $display("[tb_gs_grad_num_equiv] PASS");
        else           $display("[tb_gs_grad_num_equiv] FAIL");
        $finish;
    end
endmodule
