ec82764bef
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>
82 lines
3.7 KiB
Systemverilog
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
|