Files
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

145 lines
4.7 KiB
Systemverilog

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