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

138 lines
4.5 KiB
Systemverilog

// retroDE_ps2 — tb_gs_persp_uv (Ch301)
//
// Focused TB for gs_persp_uv — the per-pixel perspective-correct divide
// (recip + multiply + clamp) that recovers integer texel (u,v) from the
// interpolated perspective attributes (u/w, v/w, 1/w). Proves:
//
// PROOF 1 — ROUND-TRIP: for known (u,v,w), feeding uq=(u<<FRAC)/w,
// vq=(v<<FRAC)/w, q=(1<<FRAC)/w returns texel (u,v) within the
// reciprocal's mantissa tolerance (<= ~1 texel for the demo range).
// Sweep covers near (w small) and far (w large) — the regime where
// perspective diverges from affine.
//
// PROOF 2 — THROUGHPUT: back-to-back inputs, one result/cycle, no stall.
//
// PROOF 3 — CLAMP: an over-range result saturates to TEXEL_MAX, not wrap.
`timescale 1ns/1ps
module tb_gs_persp_uv;
localparam int ATTR_W = 24;
localparam int Q_W = 24;
localparam int FRAC = 12;
localparam int SCALE = 24;
localparam int TEXEL_W = 11;
localparam int TEXEL_MAX = 63; // demo texture max coord
logic clk, rst_n;
initial clk = 1'b0;
always #5 clk = ~clk;
logic in_valid;
logic [ATTR_W-1:0] uq, vq;
logic [Q_W-1:0] q;
logic out_valid;
logic [TEXEL_W-1:0] u, v;
gs_persp_uv #(
.ATTR_W(ATTR_W), .Q_W(Q_W), .FRAC(FRAC), .SCALE(SCALE),
.RECIP_W(25), .TEXEL_W(TEXEL_W), .TEXEL_MAX(TEXEL_MAX)
) dut (
.clk(clk), .rst_n(rst_n),
.in_valid(in_valid), .uq(uq), .vq(vq), .q(q),
.out_valid(out_valid), .u(u), .v(v)
);
// input vectors: (u,v,w) triples + their fixed-point attributes
localparam int NV = 512;
int tu [0:NV-1];
int tv [0:NV-1];
int tw [0:NV-1];
logic [ATTR_W-1:0] vuq [0:NV-1];
logic [ATTR_W-1:0] vvq [0:NV-1];
logic [Q_W-1:0] vq_ [0:NV-1];
int n;
initial begin
n = 0;
// sweep texel coords 0..63 across a range of w (1..16 = near..far)
for (int w = 1; w <= 16 && n < NV; w++) begin
for (int c = 0; c <= 63 && n < NV; c += 7) begin
tu[n] = c;
tv[n] = 63 - c;
tw[n] = w;
vuq[n] = ATTR_W'((c * (1 << FRAC)) / w);
vvq[n] = ATTR_W'(((63 - c) * (1 << FRAC)) / w);
vq_[n] = Q_W'((1 << FRAC) / w);
n++;
end
end
// an explicit over-range case to exercise the clamp (u/w*w would be 100>63)
tu[n]=100; tv[n]=0; tw[n]=1;
vuq[n]=ATTR_W'((100*(1<<FRAC))/1); vvq[n]='0; vq_[n]=Q_W'((1<<FRAC)/1); n++;
end
logic [TEXEL_W-1:0] ou [0:NV-1];
logic [TEXEL_W-1:0] ov [0:NV-1];
int oc;
always_ff @(posedge clk) begin
if (rst_n && out_valid) begin
ou[oc] <= u;
ov[oc] <= v;
oc <= oc + 1;
end
end
int errors;
function automatic int iabs(input int x); return (x < 0) ? -x : x; endfunction
initial begin
errors = 0; oc = 0;
in_valid = 1'b0; uq = '0; vq = '0; q = '0;
rst_n = 1'b0;
repeat (4) @(posedge clk);
rst_n = 1'b1;
repeat (2) @(posedge clk);
for (int i = 0; i < n; i++) begin
@(negedge clk);
in_valid = 1'b1; uq = vuq[i]; vq = vvq[i]; q = vq_[i];
end
@(negedge clk); in_valid = 1'b0; uq='0; vq='0; q='0;
repeat (8) @(posedge clk);
if (oc !== n) begin
$error("[persp_uv] throughput: expected %0d outputs got %0d", n, oc); errors++;
end
for (int i = 0; i < oc && i < n; i++) begin
int eu, ev;
// clamp expected to TEXEL_MAX (the over-range case)
eu = (tu[i] > TEXEL_MAX) ? TEXEL_MAX : tu[i];
ev = (tv[i] > TEXEL_MAX) ? TEXEL_MAX : tv[i];
if (iabs(int'(ou[i]) - eu) > 1) begin
$error("[persp_uv] i=%0d (u=%0d,w=%0d) got_u=%0d exp=%0d", i, tu[i], tw[i], ou[i], eu);
errors++;
end
if (iabs(int'(ov[i]) - ev) > 1) begin
$error("[persp_uv] i=%0d (v=%0d,w=%0d) got_v=%0d exp=%0d", i, tv[i], tw[i], ov[i], ev);
errors++;
end
end
$display("[tb_gs_persp_uv] checked %0d, errors=%0d", oc, errors);
if (errors == 0) $display("[tb_gs_persp_uv] PASS");
else $display("[tb_gs_persp_uv] FAIL");
$finish;
end
initial begin
#2000000;
$error("[tb_gs_persp_uv] TIMEOUT");
$finish;
end
endmodule : tb_gs_persp_uv