// 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