Ch444: serialize gradient numerator bank — reclaim ~half the FPGA's DSP
The per-triangle gradient engine already time-shared ONE divider across all GRAD_STEPS attributes, but computed every grad_load_num[0:GRAD_STEPS-1] numerator IN PARALLEL — ~44 wide multiplies (~100 physical DSP) for once-per-triangle setup consumed one-at-a-time. Pure redundant hardware; the design was DSP-maxed (187/188, 99%) so nothing new could fit (fog needed 191/188). Replace the parallel bank + the grad_num_q[] pre-latch array with grad_num_step: computes ONLY the current grad_step's numerator from ONE mux-selected pair of signed multipliers (attribute triple by grad_step>>1, axis by grad_step[0]; shared da1/da2, two shared products, signed subtract, <<<20). grad_word_q/grad_slot are held stable the whole solve, so it is bit-identical to the old grad_num_q[grad_step]. Removed grad_num_dadx/dady (inlined once). FSM sequencing and throughput unchanged. Width note: da1/da2 are 33-bit (products 50-bit), NOT operand-width 32-bit — the original (a1-a0) lived in a signed-64-bit expression context and never wrapped; full-32-bit Z with |a1-a0|>2^31 needs the wider intermediate. tb_gs_grad_num_equiv (extreme signed corners + 200k random = 494770 checks, 0 errors) caught a 32-bit first cut that f52's real data never exercised. Resource (26.1 Seed-3 fit): DSP needed 168->83 / final placement 187->119, i.e. 99% -> 44%, ~85 blocks reclaimed (Codex gate >=70 met). ALM 40458->38784 (86->83%). RAM 322/358 unchanged. Timing CLEAN: setup +0.077, all classes >=0, 0 violated. Verification: tb_gs_grad_num_equiv 0/494770; f52 replay BYTE-IDENTICAL golden d0047677 (drops=0, occupancy unchanged); gradient/perspective/texture regressions (tri_interp, grad_divider, persp_uv, zbuffer, fog_persp, textured_triangle, triangle/perspective/combined/gouraud demos) all PASS. Byte-identical => the screen is unchanged; this is the resource unlock for fog + coverage. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
// ============================================================================
|
||||
// tb_gs_grad_num_equiv — Ch444
|
||||
//
|
||||
// Focused equivalence proof for the SERIALIZED gradient numerator (gs_stub
|
||||
// grad_num_step). The old design instantiated grad_num_dadx/grad_num_dady for
|
||||
// every attribute in parallel; Ch444 replaced that bank with ONE mux-selected
|
||||
// pair of 32x17 multipliers. This TB proves the new inlined arithmetic is
|
||||
// BIT-IDENTICAL to the original functions for BOTH axes, across every attribute
|
||||
// operand width AND extreme signed coordinate / overflow-boundary cases that the
|
||||
// f52 real-data assertion may never exercise:
|
||||
// - GOLDEN = the exact original grad_num_dadx/dady (verbatim, pre-Ch444).
|
||||
// - DUT = the exact grad_num_step arithmetic (shared da1/da2 + axis-muxed
|
||||
// 32x17 products, signed subtract, <<<20).
|
||||
// Sweeps attributes over {0, 8/11/24/32-bit maxima, sign boundaries, random} and
|
||||
// vertex coords over {0, 0xFFFF, 0x8000, boundary, random}. Any mismatch fails.
|
||||
`timescale 1ns/1ps
|
||||
|
||||
module tb_gs_grad_num_equiv;
|
||||
int errors = 0, checks = 0;
|
||||
|
||||
// ---- GOLDEN: the original functions, copied verbatim from pre-Ch444 gs_stub ----
|
||||
function automatic logic signed [63:0] gold_dadx(
|
||||
input logic signed [31:0] a0, a1, a2,
|
||||
input logic [15:0] x0,y0,x1,y1,x2,y2);
|
||||
logic signed [16:0] dy1, dy2; logic signed [63:0] num;
|
||||
dy1 = $signed({1'b0, y1}) - $signed({1'b0, y0});
|
||||
dy2 = $signed({1'b0, y2}) - $signed({1'b0, y0});
|
||||
num = (a1 - a0) * $signed({{47{dy2[16]}}, dy2})
|
||||
- (a2 - a0) * $signed({{47{dy1[16]}}, dy1});
|
||||
gold_dadx = num <<< 20;
|
||||
endfunction
|
||||
function automatic logic signed [63:0] gold_dady(
|
||||
input logic signed [31:0] a0, a1, a2,
|
||||
input logic [15:0] x0,y0,x1,y1,x2,y2);
|
||||
logic signed [16:0] dx1, dx2; logic signed [63:0] num;
|
||||
dx1 = $signed({1'b0, x1}) - $signed({1'b0, x0});
|
||||
dx2 = $signed({1'b0, x2}) - $signed({1'b0, x0});
|
||||
num = (a2 - a0) * $signed({{47{dx1[16]}}, dx1})
|
||||
- (a1 - a0) * $signed({{47{dx2[16]}}, dx2});
|
||||
gold_dady = num <<< 20;
|
||||
endfunction
|
||||
|
||||
// ---- DUT: the exact grad_num_step arithmetic (shared 32x17 pair, axis mux) ----
|
||||
function automatic logic signed [63:0] dut_step(
|
||||
input logic axis, // 0 = d/dx, 1 = d/dy
|
||||
input logic signed [31:0] a0, a1, a2,
|
||||
input logic [15:0] x0,y0,x1,y1,x2,y2);
|
||||
logic signed [16:0] dy1,dy2,dx1,dx2;
|
||||
logic signed [32:0] da1, da2;
|
||||
logic signed [32:0] mA_p, mA_n; logic signed [16:0] mB_p, mB_n;
|
||||
logic signed [49:0] prodP, prodN; logic signed [63:0] raw;
|
||||
dy1 = $signed({1'b0,y1}) - $signed({1'b0,y0});
|
||||
dy2 = $signed({1'b0,y2}) - $signed({1'b0,y0});
|
||||
dx1 = $signed({1'b0,x1}) - $signed({1'b0,x0});
|
||||
dx2 = $signed({1'b0,x2}) - $signed({1'b0,x0});
|
||||
da1 = $signed(a1) - $signed(a0); da2 = $signed(a2) - $signed(a0);
|
||||
if (!axis) begin mA_p=da1; mB_p=dy2; mA_n=da2; mB_n=dy1; end
|
||||
else begin mA_p=da2; mB_p=dx1; mA_n=da1; mB_n=dx2; end
|
||||
prodP = mA_p * mB_p; prodN = mA_n * mB_n;
|
||||
raw = $signed(prodP) - $signed(prodN);
|
||||
dut_step = raw <<< 20;
|
||||
endfunction
|
||||
|
||||
task automatic chk(input logic signed [31:0] a0,a1,a2,
|
||||
input logic [15:0] x0,y0,x1,y1,x2,y2);
|
||||
logic signed [63:0] gx, gy, dx, dy;
|
||||
gx = gold_dadx(a0,a1,a2,x0,y0,x1,y1,x2,y2);
|
||||
gy = gold_dady(a0,a1,a2,x0,y0,x1,y1,x2,y2);
|
||||
dx = dut_step(1'b0, a0,a1,a2,x0,y0,x1,y1,x2,y2);
|
||||
dy = dut_step(1'b1, a0,a1,a2,x0,y0,x1,y1,x2,y2);
|
||||
checks += 2;
|
||||
if (dx !== gx) begin errors++; if (errors<20) $display("[equiv] DADX MISMATCH a=%h,%h,%h xy=%h,%h/%h,%h/%h,%h dut=%h gold=%h",a0,a1,a2,x0,y0,x1,y1,x2,y2,dx,gx); end
|
||||
if (dy !== gy) begin errors++; if (errors<20) $display("[equiv] DADY MISMATCH a=%h,%h,%h xy=%h,%h/%h,%h/%h,%h dut=%h gold=%h",a0,a1,a2,x0,y0,x1,y1,x2,y2,dy,gy); end
|
||||
endtask
|
||||
|
||||
// attribute corner values spanning every operand width (color8/uv11/stq24/z32)
|
||||
logic signed [31:0] AV [];
|
||||
logic [15:0] CV [];
|
||||
initial begin
|
||||
AV = new[9];
|
||||
AV[0]=32'h00000000; AV[1]=32'h000000FF; AV[2]=32'h000007FF; AV[3]=32'h00FFFFFF;
|
||||
AV[4]=32'h7FFFFFFF; AV[5]=32'h80000000; AV[6]=32'hFFFFFFFF; AV[7]=32'h00800000; AV[8]=32'h12345678;
|
||||
CV = new[6];
|
||||
CV[0]=16'h0000; CV[1]=16'hFFFF; CV[2]=16'h8000; CV[3]=16'h7FFF; CV[4]=16'h0001; CV[5]=16'hA5A5;
|
||||
|
||||
// 1) exhaustive over attribute corners with a fixed non-degenerate triangle
|
||||
foreach (AV[i]) foreach (AV[j]) foreach (AV[k])
|
||||
chk(AV[i],AV[j],AV[k], 16'h0100,16'h0080, 16'h0900,16'h0110, 16'h0300,16'h0A00);
|
||||
|
||||
// 2) exhaustive over coordinate corners with fixed spread attributes
|
||||
foreach (CV[a]) foreach (CV[b]) foreach (CV[c]) foreach (CV[d]) foreach (CV[e]) foreach (CV[f])
|
||||
chk(32'h00000000, 32'h00ABCDEF, 32'h7F123456, CV[a],CV[b],CV[c],CV[d],CV[e],CV[f]);
|
||||
|
||||
// 3) randomized wide sweep (attributes + coords), catches any residual case
|
||||
for (int n=0; n<200000; n++) begin
|
||||
logic signed [31:0] ra0,ra1,ra2;
|
||||
ra0=$random; ra1=$random; ra2=$random;
|
||||
chk(ra0,ra1,ra2, $random,$random,$random,$random,$random,$random);
|
||||
end
|
||||
|
||||
$display("[equiv] checks=%0d errors=%0d", checks, errors);
|
||||
if (errors==0) $display("[tb_gs_grad_num_equiv] PASS");
|
||||
else $display("[tb_gs_grad_num_equiv] FAIL");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
Reference in New Issue
Block a user