Files
retroDE_ps2/sim/tb/gif_gs/tb_gs_grad_divider.sv
thejayman77 ec82764bef Initial commit: retroDE_ps2 — first-of-its-kind PS2 GS FPGA core (DE25-Nano / Agilex 5)
RTL (GS rasterizer, EE core stub, platform bridge, LPDDR4B path), sim regression
(272 TBs), docs, and tooling. Copyrighted PS2 content (BIOS, game code, GS dumps,
and all dump-derived textures/traces) is excluded via .gitignore and stays local.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 20:10:50 -04:00

82 lines
3.7 KiB
Systemverilog

// tb_gs_grad_divider — proves gs_grad_divider is BIT-EXACT to SystemVerilog signed `/` (Ch352).
// Golden = (den==0) ? 0 : $signed(num)/$signed(den). Covers all 4 sign combinations, zero divisor,
// truncation-toward-zero, most-negative edges, divide-by-1 / by-self, and random operands.
`timescale 1ns/1ps
module tb_gs_grad_divider;
localparam int W = 56;
logic clk=0; always #5 clk=~clk;
logic rst_n, start, busy, done;
logic signed [W-1:0] num, den, quo;
gs_grad_divider #(.W(W)) dut (
.clk(clk), .rst_n(rst_n), .start(start), .num(num), .den(den),
.quo(quo), .busy(busy), .done(done)
);
int errors = 0;
function automatic logic signed [W-1:0] golden(input logic signed [W-1:0] n, d);
// NOTE: use if/else with a signed intermediate — a ternary `? '0 : n/d` poisons n/d into an UNSIGNED
// context (the '0 branch), giving the wrong reference. This is the true signed truncate-toward-zero.
logic signed [W-1:0] q;
if (d == '0) q = '0;
else q = n / d;
golden = q;
endfunction
task automatic do_div(input logic signed [W-1:0] n, d, input string tag);
logic signed [W-1:0] exp;
exp = golden(n,d);
@(negedge clk); num=n; den=d; start=1'b1;
@(negedge clk); start=1'b0;
begin int to=0; while(!done && to<200) begin @(posedge clk); to++; end
if(!done) begin $error("[divtb] TIMEOUT %s", tag); errors++; end end
if (quo !== exp) begin
errors++;
if (errors<20) $error("[divtb] %s: num=%0d den=%0d quo=%0d exp=%0d", tag, n, d, quo, exp);
end
@(negedge clk);
endtask
logic signed [W-1:0] rn, rd;
initial begin
rst_n=0; start=0; num=0; den=0;
repeat(4) @(negedge clk); rst_n=1; repeat(2) @(negedge clk);
// ---- targeted: all sign combinations + truncation toward zero ----
do_div( 56'sd7, 56'sd2, "7/2=3");
do_div(-56'sd7, 56'sd2, "-7/2=-3 (trunc, not -4)");
do_div( 56'sd7, -56'sd2, "7/-2=-3");
do_div(-56'sd7, -56'sd2, "-7/-2=3");
do_div( 56'sd1000000, 56'sd7, "1e6/7 trunc");
do_div(-56'sd1000000, 56'sd7, "-1e6/7 trunc");
// ---- zero divisor -> 0 ----
do_div( 56'sd12345, 56'sd0, "x/0=0");
do_div(-56'sd99999, 56'sd0, "neg/0=0");
// ---- divide by 1 / by self / smaller-than-divisor ----
do_div( 56'sd123456789, 56'sd1, "x/1");
do_div(-56'sd123456789, 56'sd1, "-x/1");
do_div( 56'sd42, 56'sd42, "x/x=1");
do_div( 56'sd3, 56'sd100, "3/100=0");
do_div(-56'sd3, 56'sd100, "-3/100=0");
// ---- edges: most-negative, large magnitudes (the <<16-folded gradient numerators) ----
do_div( {1'b1,{(W-1){1'b0}}}, 56'sd1, "MIN/1");
do_div( {1'b1,{(W-1){1'b0}}}, -56'sd1, "MIN/-1");
do_div( 56'sh0FFFF_FFFF_FFFF, 56'sd131072, "bigpos/2^17");
do_div(-56'sh0FFFF_FFFF_FFFF, 56'sd131072, "bigneg/2^17");
// ---- random sweep (both signs, dividers up to 32-bit like the determinant) ----
for (int i=0;i<4000;i++) begin
rn = {$random,$random}; rn = rn & {W{1'b1}};
rd = $random; // ~32-bit determinant range, both signs
if ((i%37)==0) rd = 0; // sprinkle zero divisors
do_div(rn, rd, $sformatf("rand%0d", i));
end
$display("[tb_gs_grad_divider] errors=%0d", errors);
if (errors==0) $display("[tb_gs_grad_divider] PASS");
else $display("[tb_gs_grad_divider] FAIL");
$finish;
end
initial begin #5000000; $display("[tb_gs_grad_divider] GLOBAL TIMEOUT"); $finish; end
endmodule