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:
+85
-99
@@ -2582,39 +2582,10 @@ module gs_stub
|
||||
// truncation). Numerator width unchanged (signed 56-bit: see the
|
||||
// SYNTHESIS-WIDTH NOTE above for the bound derivation; the <<<16 is
|
||||
// applied here in the 56-bit numerator domain exactly as before).
|
||||
function automatic logic signed [63:0] grad_num_dadx(
|
||||
input logic signed [31:0] a0,
|
||||
input logic signed [31:0] a1,
|
||||
input logic signed [31:0] a2,
|
||||
input logic [15:0] x0, input logic [15:0] y0,
|
||||
input logic [15:0] x1, input logic [15:0] y1,
|
||||
input logic [15:0] x2, input logic [15:0] 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});
|
||||
// Coordinates are 12.4. Convert dA/d(X/16) to Q16.16:
|
||||
// raw numerator * 16 * 2^16 / det = numerator << 20 / det.
|
||||
grad_num_dadx = num <<< 20;
|
||||
endfunction
|
||||
|
||||
function automatic logic signed [63:0] grad_num_dady(
|
||||
input logic signed [31:0] a0,
|
||||
input logic signed [31:0] a1,
|
||||
input logic signed [31:0] a2,
|
||||
input logic [15:0] x0, input logic [15:0] y0,
|
||||
input logic [15:0] x1, input logic [15:0] y1,
|
||||
input logic [15:0] x2, input logic [15:0] 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});
|
||||
grad_num_dady = num <<< 20;
|
||||
endfunction
|
||||
// Ch444 — grad_num_dadx/grad_num_dady REMOVED. Their exact arithmetic (signed
|
||||
// (a1-a0),(a2-a0) times the y/x vertex deltas, subtract, <<<20) is now inlined
|
||||
// ONCE in grad_num_step's shared multiplier pair (operands mux-selected by axis)
|
||||
// instead of being instantiated GRAD_STEPS times in parallel.
|
||||
|
||||
// Ch87 — raster command FIFO. Holds primitive contexts captured
|
||||
// at close cycles while the FSM is busy. Effective concurrency =
|
||||
@@ -3046,7 +3017,8 @@ module gs_stub
|
||||
logic grad_writing; // Ch352 — write phase: grad_result_q -> fifo (single-cycle)
|
||||
logic [FIFO_PTR_W-1:0] grad_slot;
|
||||
logic signed [63:0] grad_det_q;
|
||||
logic signed [63:0] grad_num_q [0:GRAD_STEPS-1];
|
||||
// Ch444 — grad_num_q[] (the pre-latched 22-wide numerator array) REMOVED; the
|
||||
// current step's numerator is computed on demand by grad_num_step.
|
||||
// Ch357 (Codex) — the SOLE registered numerator feeding the combinational divider on the BOARD path
|
||||
// (GRAD_SEQ_DIVIDER=0 && GRAD_DIV_CYCLES!=1). Registering grad_num_q[grad_step] here removes the 20-way dynamic
|
||||
// numerator mux from the divide's launch cone: the multicycle -from set shrinks from ~1157 regs
|
||||
@@ -3078,7 +3050,8 @@ module gs_stub
|
||||
// by construction). Only ONE slot's worth is computed at a time (mux
|
||||
// by grad_pending_slot), so this is one set of the existing functions,
|
||||
// not FIFO_DEPTH copies — no new wide datapath.
|
||||
logic signed [63:0] grad_load_num [0:GRAD_STEPS-1];
|
||||
// Ch444 — grad_load_num[] (the parallel combinational numerator bank, ~44 wide
|
||||
// multiplies) REMOVED; grad_num_step computes one numerator at a time.
|
||||
logic signed [63:0] grad_load_det;
|
||||
logic [63:0] grad_ld_c0, grad_ld_c1, grad_ld_c2;
|
||||
logic [11:0] grad_ld_v0x, grad_ld_v0y, grad_ld_v1x, grad_ld_v1y, grad_ld_v2x, grad_ld_v2y;
|
||||
@@ -3169,49 +3142,10 @@ module gs_stub
|
||||
grad_ld_u2 = gw_u2v [10:0];
|
||||
grad_ld_v2u = gw_u2v [26:16];
|
||||
grad_load_det = $signed({{29{fifo_grad_det[grad_slot][34]}}, fifo_grad_det[grad_slot]});
|
||||
grad_load_num[0] = grad_num_dadx({24'd0, grad_ld_c0[7:0]}, {24'd0, grad_ld_c1[7:0]}, {24'd0, grad_ld_c2[7:0]},
|
||||
grad_ld_v0x4, grad_ld_v0y4, grad_ld_v1x4, grad_ld_v1y4, grad_ld_v2x4, grad_ld_v2y4);
|
||||
grad_load_num[1] = grad_num_dady({24'd0, grad_ld_c0[7:0]}, {24'd0, grad_ld_c1[7:0]}, {24'd0, grad_ld_c2[7:0]},
|
||||
grad_ld_v0x4, grad_ld_v0y4, grad_ld_v1x4, grad_ld_v1y4, grad_ld_v2x4, grad_ld_v2y4);
|
||||
grad_load_num[2] = grad_num_dadx({24'd0, grad_ld_c0[15:8]}, {24'd0, grad_ld_c1[15:8]}, {24'd0, grad_ld_c2[15:8]},
|
||||
grad_ld_v0x4, grad_ld_v0y4, grad_ld_v1x4, grad_ld_v1y4, grad_ld_v2x4, grad_ld_v2y4);
|
||||
grad_load_num[3] = grad_num_dady({24'd0, grad_ld_c0[15:8]}, {24'd0, grad_ld_c1[15:8]}, {24'd0, grad_ld_c2[15:8]},
|
||||
grad_ld_v0x4, grad_ld_v0y4, grad_ld_v1x4, grad_ld_v1y4, grad_ld_v2x4, grad_ld_v2y4);
|
||||
grad_load_num[4] = grad_num_dadx({24'd0, grad_ld_c0[23:16]}, {24'd0, grad_ld_c1[23:16]}, {24'd0, grad_ld_c2[23:16]},
|
||||
grad_ld_v0x4, grad_ld_v0y4, grad_ld_v1x4, grad_ld_v1y4, grad_ld_v2x4, grad_ld_v2y4);
|
||||
grad_load_num[5] = grad_num_dady({24'd0, grad_ld_c0[23:16]}, {24'd0, grad_ld_c1[23:16]}, {24'd0, grad_ld_c2[23:16]},
|
||||
grad_ld_v0x4, grad_ld_v0y4, grad_ld_v1x4, grad_ld_v1y4, grad_ld_v2x4, grad_ld_v2y4);
|
||||
grad_load_num[6] = grad_num_dadx({24'd0, grad_ld_c0[31:24]}, {24'd0, grad_ld_c1[31:24]}, {24'd0, grad_ld_c2[31:24]},
|
||||
grad_ld_v0x4, grad_ld_v0y4, grad_ld_v1x4, grad_ld_v1y4, grad_ld_v2x4, grad_ld_v2y4);
|
||||
grad_load_num[7] = grad_num_dady({24'd0, grad_ld_c0[31:24]}, {24'd0, grad_ld_c1[31:24]}, {24'd0, grad_ld_c2[31:24]},
|
||||
grad_ld_v0x4, grad_ld_v0y4, grad_ld_v1x4, grad_ld_v1y4, grad_ld_v2x4, grad_ld_v2y4);
|
||||
grad_load_num[8] = grad_num_dadx(grad_ld_z0, grad_ld_z1, grad_ld_z2,
|
||||
grad_ld_v0x4, grad_ld_v0y4, grad_ld_v1x4, grad_ld_v1y4, grad_ld_v2x4, grad_ld_v2y4);
|
||||
grad_load_num[9] = grad_num_dady(grad_ld_z0, grad_ld_z1, grad_ld_z2,
|
||||
grad_ld_v0x4, grad_ld_v0y4, grad_ld_v1x4, grad_ld_v1y4, grad_ld_v2x4, grad_ld_v2y4);
|
||||
// Textured-triangle rung — U (steps 10/11) and V (steps 12/13).
|
||||
// U/V are 11-bit unsigned integer texel coords; zero-extend to
|
||||
// the signed[31:0] attribute input the existing numerator
|
||||
// functions take. Same shared divisor (grad_load_det) → same
|
||||
// affine plane solve as the colour/Z attributes.
|
||||
grad_load_num[10] = grad_num_dadx({21'd0, grad_ld_u0}, {21'd0, grad_ld_u1}, {21'd0, grad_ld_u2},
|
||||
grad_ld_v0x4, grad_ld_v0y4, grad_ld_v1x4, grad_ld_v1y4, grad_ld_v2x4, grad_ld_v2y4);
|
||||
grad_load_num[11] = grad_num_dady({21'd0, grad_ld_u0}, {21'd0, grad_ld_u1}, {21'd0, grad_ld_u2},
|
||||
grad_ld_v0x4, grad_ld_v0y4, grad_ld_v1x4, grad_ld_v1y4, grad_ld_v2x4, grad_ld_v2y4);
|
||||
grad_load_num[12] = grad_num_dadx({21'd0, grad_ld_v0u}, {21'd0, grad_ld_v1u}, {21'd0, grad_ld_v2u},
|
||||
grad_ld_v0x4, grad_ld_v0y4, grad_ld_v1x4, grad_ld_v1y4, grad_ld_v2x4, grad_ld_v2y4);
|
||||
grad_load_num[13] = grad_num_dady({21'd0, grad_ld_v0u}, {21'd0, grad_ld_v1u}, {21'd0, grad_ld_v2u},
|
||||
grad_ld_v0x4, grad_ld_v0y4, grad_ld_v1x4, grad_ld_v1y4, grad_ld_v2x4, grad_ld_v2y4);
|
||||
// GS per-vertex FOG — F (steps GRAD_F_DX/GRAD_F_DY). 8-bit fog coeff
|
||||
// zero-extended to the signed[31:0] attribute input the numerator
|
||||
// functions take. Same shared divisor (grad_load_det) → same affine
|
||||
// plane solve as colour/Z. These indices are ALWAYS the last two of
|
||||
// grad_load_num[0:GRAD_STEPS-1] regardless of the perspective param
|
||||
// (the persp 14..19 block never overlaps them).
|
||||
grad_load_num[GRAD_F_DX] = grad_num_dadx({24'd0, grad_ld_f0}, {24'd0, grad_ld_f1}, {24'd0, grad_ld_f2},
|
||||
grad_ld_v0x4, grad_ld_v0y4, grad_ld_v1x4, grad_ld_v1y4, grad_ld_v2x4, grad_ld_v2y4);
|
||||
grad_load_num[GRAD_F_DY] = grad_num_dady({24'd0, grad_ld_f0}, {24'd0, grad_ld_f1}, {24'd0, grad_ld_f2},
|
||||
grad_ld_v0x4, grad_ld_v0y4, grad_ld_v1x4, grad_ld_v1y4, grad_ld_v2x4, grad_ld_v2y4);
|
||||
// Ch444 — the parallel grad_load_num[0:GRAD_STEPS-1] numerator bank is GONE.
|
||||
// The current step's numerator is computed on-demand by grad_num_step (one
|
||||
// shared multiplier pair) from these same grad_ld_* fields. grad_load_det
|
||||
// (the shared divisor) still latches here into grad_det_q.
|
||||
end
|
||||
|
||||
// Ch301 perspective — per-vertex S/T/Q load + the 6 extra gradient
|
||||
@@ -3236,21 +3170,8 @@ module gs_stub
|
||||
grad_ld_s2 = gw_stq2[23:0];
|
||||
grad_ld_t2 = gw_stq2[47:24];
|
||||
grad_ld_q2 = gw_q2;
|
||||
// S (steps 14/15)
|
||||
grad_load_num[14] = grad_num_dadx({8'd0, grad_ld_s0}, {8'd0, grad_ld_s1}, {8'd0, grad_ld_s2},
|
||||
grad_ld_v0x4, grad_ld_v0y4, grad_ld_v1x4, grad_ld_v1y4, grad_ld_v2x4, grad_ld_v2y4);
|
||||
grad_load_num[15] = grad_num_dady({8'd0, grad_ld_s0}, {8'd0, grad_ld_s1}, {8'd0, grad_ld_s2},
|
||||
grad_ld_v0x4, grad_ld_v0y4, grad_ld_v1x4, grad_ld_v1y4, grad_ld_v2x4, grad_ld_v2y4);
|
||||
// T (steps 16/17)
|
||||
grad_load_num[16] = grad_num_dadx({8'd0, grad_ld_t0}, {8'd0, grad_ld_t1}, {8'd0, grad_ld_t2},
|
||||
grad_ld_v0x4, grad_ld_v0y4, grad_ld_v1x4, grad_ld_v1y4, grad_ld_v2x4, grad_ld_v2y4);
|
||||
grad_load_num[17] = grad_num_dady({8'd0, grad_ld_t0}, {8'd0, grad_ld_t1}, {8'd0, grad_ld_t2},
|
||||
grad_ld_v0x4, grad_ld_v0y4, grad_ld_v1x4, grad_ld_v1y4, grad_ld_v2x4, grad_ld_v2y4);
|
||||
// Q (steps 18/19)
|
||||
grad_load_num[18] = grad_num_dadx({8'd0, grad_ld_q0}, {8'd0, grad_ld_q1}, {8'd0, grad_ld_q2},
|
||||
grad_ld_v0x4, grad_ld_v0y4, grad_ld_v1x4, grad_ld_v1y4, grad_ld_v2x4, grad_ld_v2y4);
|
||||
grad_load_num[19] = grad_num_dady({8'd0, grad_ld_q0}, {8'd0, grad_ld_q1}, {8'd0, grad_ld_q2},
|
||||
grad_ld_v0x4, grad_ld_v0y4, grad_ld_v1x4, grad_ld_v1y4, grad_ld_v2x4, grad_ld_v2y4);
|
||||
// Ch444 — S/T/Q numerators (steps 14..19) now computed on-demand by
|
||||
// grad_num_step from these grad_ld_s/t/q fields (parallel bank removed).
|
||||
end
|
||||
end else begin : g_no_grad_stq
|
||||
// Tie the S/T/Q load wires low so they have a single driver
|
||||
@@ -3261,6 +3182,72 @@ module gs_stub
|
||||
end
|
||||
endgenerate
|
||||
|
||||
// ============================================================================
|
||||
// Ch444 (Codex) — SERIALIZED gradient numerator (DSP resource collapse).
|
||||
// The per-triangle divider is already time-shared across all GRAD_STEPS
|
||||
// attributes, but the numerator bank was NOT: grad_load_num[0:GRAD_STEPS-1]
|
||||
// instantiated every attribute's two wide multiplies IN PARALLEL (~44 mults ->
|
||||
// ~100 physical DSP) only to be consumed one-at-a-time by the shared divider.
|
||||
// Compute ONLY the current grad_step's numerator from ONE mux-selected pair of
|
||||
// signed multipliers. grad_word_q + grad_slot are held stable for the whole
|
||||
// solve, so grad_num_step for grad_step is BIT-IDENTICAL to the old
|
||||
// grad_num_q[grad_step] (same attribute triple a0/a1/a2, same axis, same signed
|
||||
// subtract, same <<<20). Step->attribute map mirrors the old bank exactly:
|
||||
// pair = grad_step>>1 : 0=R 1=G 2=B 3=A 4=Z 5=U 6=V [persp 7=S 8=T 9=Q] F=GRAD_F_DX>>1
|
||||
// axis = grad_step[0] : 0=d/dx 1=d/dy
|
||||
// ============================================================================
|
||||
logic signed [31:0] gnum_a0, gnum_a1, gnum_a2;
|
||||
always_comb begin
|
||||
gnum_a0 = 32'sd0; gnum_a1 = 32'sd0; gnum_a2 = 32'sd0;
|
||||
case (grad_step[4:1])
|
||||
4'd0: begin gnum_a0={24'd0,grad_ld_c0[7:0]}; gnum_a1={24'd0,grad_ld_c1[7:0]}; gnum_a2={24'd0,grad_ld_c2[7:0]}; end
|
||||
4'd1: begin gnum_a0={24'd0,grad_ld_c0[15:8]}; gnum_a1={24'd0,grad_ld_c1[15:8]}; gnum_a2={24'd0,grad_ld_c2[15:8]}; end
|
||||
4'd2: begin gnum_a0={24'd0,grad_ld_c0[23:16]}; gnum_a1={24'd0,grad_ld_c1[23:16]}; gnum_a2={24'd0,grad_ld_c2[23:16]}; end
|
||||
4'd3: begin gnum_a0={24'd0,grad_ld_c0[31:24]}; gnum_a1={24'd0,grad_ld_c1[31:24]}; gnum_a2={24'd0,grad_ld_c2[31:24]}; end
|
||||
4'd4: begin gnum_a0=grad_ld_z0; gnum_a1=grad_ld_z1; gnum_a2=grad_ld_z2; end
|
||||
4'd5: begin gnum_a0={21'd0,grad_ld_u0}; gnum_a1={21'd0,grad_ld_u1}; gnum_a2={21'd0,grad_ld_u2}; end
|
||||
4'd6: begin gnum_a0={21'd0,grad_ld_v0u}; gnum_a1={21'd0,grad_ld_v1u}; gnum_a2={21'd0,grad_ld_v2u}; end
|
||||
default: begin
|
||||
if (PERSPECTIVE_CORRECT && grad_step[4:1]==4'd7) begin gnum_a0={8'd0,grad_ld_s0}; gnum_a1={8'd0,grad_ld_s1}; gnum_a2={8'd0,grad_ld_s2}; end
|
||||
else if (PERSPECTIVE_CORRECT && grad_step[4:1]==4'd8) begin gnum_a0={8'd0,grad_ld_t0}; gnum_a1={8'd0,grad_ld_t1}; gnum_a2={8'd0,grad_ld_t2}; end
|
||||
else if (PERSPECTIVE_CORRECT && grad_step[4:1]==4'd9) begin gnum_a0={8'd0,grad_ld_q0}; gnum_a1={8'd0,grad_ld_q1}; gnum_a2={8'd0,grad_ld_q2}; end
|
||||
else if (grad_step[4:1]==4'(GRAD_F_DX>>1)) begin gnum_a0={24'd0,grad_ld_f0}; gnum_a1={24'd0,grad_ld_f1}; gnum_a2={24'd0,grad_ld_f2}; end
|
||||
end
|
||||
endcase
|
||||
end
|
||||
// ONE shared pair of signed multipliers (33 x 17), operands mux-selected by axis.
|
||||
// NOTE: gnum_da1/da2 are 33-bit, NOT 32-bit. The original grad_num_dadx/dady
|
||||
// evaluated (a1-a0) inside a signed 64-bit expression context, so the attribute
|
||||
// difference never wrapped at 32 bits (matters only for full-32-bit Z whose
|
||||
// a1-a0 can exceed +/-2^31). A 33-bit difference reproduces that exactly; a
|
||||
// 32-bit one wraps and diverges on extreme Z (caught by tb_gs_grad_num_equiv).
|
||||
logic signed [16:0] gnum_dy1, gnum_dy2, gnum_dx1, gnum_dx2;
|
||||
logic signed [32:0] gnum_da1, gnum_da2;
|
||||
logic signed [32:0] gnum_mA_p, gnum_mA_n;
|
||||
logic signed [16:0] gnum_mB_p, gnum_mB_n;
|
||||
logic signed [49:0] gnum_prodP, gnum_prodN;
|
||||
logic signed [63:0] gnum_raw;
|
||||
logic signed [63:0] grad_num_step;
|
||||
always_comb begin
|
||||
gnum_dy1 = $signed({1'b0, grad_ld_v1y4}) - $signed({1'b0, grad_ld_v0y4});
|
||||
gnum_dy2 = $signed({1'b0, grad_ld_v2y4}) - $signed({1'b0, grad_ld_v0y4});
|
||||
gnum_dx1 = $signed({1'b0, grad_ld_v1x4}) - $signed({1'b0, grad_ld_v0x4});
|
||||
gnum_dx2 = $signed({1'b0, grad_ld_v2x4}) - $signed({1'b0, grad_ld_v0x4});
|
||||
gnum_da1 = $signed(gnum_a1) - $signed(gnum_a0); // 33-bit, no 32-bit wrap
|
||||
gnum_da2 = $signed(gnum_a2) - $signed(gnum_a0);
|
||||
if (!grad_step[0]) begin // d/dx: num = da1*dy2 - da2*dy1
|
||||
gnum_mA_p = gnum_da1; gnum_mB_p = gnum_dy2;
|
||||
gnum_mA_n = gnum_da2; gnum_mB_n = gnum_dy1;
|
||||
end else begin // d/dy: num = da2*dx1 - da1*dx2
|
||||
gnum_mA_p = gnum_da2; gnum_mB_p = gnum_dx1;
|
||||
gnum_mA_n = gnum_da1; gnum_mB_n = gnum_dx2;
|
||||
end
|
||||
gnum_prodP = gnum_mA_p * gnum_mB_p; // shared multiplier #1
|
||||
gnum_prodN = gnum_mA_n * gnum_mB_n; // shared multiplier #2
|
||||
gnum_raw = $signed(gnum_prodP) - $signed(gnum_prodN);
|
||||
grad_num_step = gnum_raw <<< 20; // exact: matches num<<<20 in grad_num_dadx/dady
|
||||
end
|
||||
|
||||
// Combinational divide of the currently-selected numerator by the
|
||||
// captured determinant — the SINGLE divider in the triangle setup
|
||||
// path. Mirrors the previous per-call expression exactly:
|
||||
@@ -3278,7 +3265,7 @@ module gs_stub
|
||||
if (!GRAD_SEQ_DIVIDER) begin : g_combdiv
|
||||
// Numerator source: GRAD_DIV_CYCLES!=1 uses the REGISTERED grad_num_sel_q (20-way mux captured a cycle
|
||||
// earlier -> tiny divide cone); GRAD_DIV_CYCLES==1 legacy sim reads grad_num_q[grad_step] BYTE-IDENTICAL.
|
||||
wire signed [63:0] grad_div_num = (GRAD_DIV_CYCLES != 1) ? grad_num_sel_q : grad_num_q[grad_step];
|
||||
wire signed [63:0] grad_div_num = (GRAD_DIV_CYCLES != 1) ? grad_num_sel_q : grad_num_step;
|
||||
// Ch417b — keep Quartus' inferred lpm_divide numerator at its
|
||||
// supported maximum of exactly 64 bits. A signed 64/64 `/`
|
||||
// causes Quartus to prepend a sign-magnitude bit and request
|
||||
@@ -3324,7 +3311,7 @@ module gs_stub
|
||||
if (GRAD_SEQ_DIVIDER) begin : g_seqdiv
|
||||
gs_grad_divider #(.W(64)) u_grad_div (
|
||||
.clk(clk), .rst_n(rst_n), .start(div_start),
|
||||
.num(grad_num_q[grad_step]),
|
||||
.num(grad_num_step),
|
||||
.den(grad_det_q),
|
||||
.quo(div_quo), .busy(div_busy_w), .done(div_done_w)
|
||||
);
|
||||
@@ -6218,7 +6205,6 @@ module gs_stub
|
||||
div_start <= 1'b0;
|
||||
grad_slot <= '0;
|
||||
grad_det_q <= 64'sd0;
|
||||
for (int g = 0; g < GRAD_STEPS; g = g + 1) grad_num_q[g] <= 64'sd0;
|
||||
for (int i = 0; i < FIFO_DEPTH; i = i + 1) begin
|
||||
fifo_v1z [i] <= 32'd0;
|
||||
fifo_v2z [i] <= 32'd0;
|
||||
@@ -6707,7 +6693,7 @@ module gs_stub
|
||||
// divide computing); at CYCLES = capture the settled quotient. grad_step advances only in the write
|
||||
// phase below, so grad_num_q[grad_step] is stable when it is captured here.
|
||||
if (grad_settle == 5'd0) begin
|
||||
grad_num_sel_q <= grad_num_q[grad_step]; // capture the 20-way mux output out of the divide cone
|
||||
grad_num_sel_q <= grad_num_step; // Ch444 — serialized on-demand numerator (was grad_num_q[grad_step])
|
||||
grad_settle <= 5'd1;
|
||||
end else if (grad_settle != 5'(GRAD_DIV_CYCLES)) begin
|
||||
grad_settle <= grad_settle + 5'd1; // hold grad_num_sel_q + grad_det_q stable
|
||||
@@ -6775,8 +6761,8 @@ module gs_stub
|
||||
grad_writing <= 1'b0;
|
||||
if (GRAD_SEQ_DIVIDER) div_start <= 1'b1; // kick the first step's sequential divide
|
||||
grad_det_q <= grad_load_det;
|
||||
for (int g = 0; g < GRAD_STEPS; g = g + 1)
|
||||
grad_num_q[g] <= grad_load_num[g];
|
||||
// Ch444 — no numerator bank to latch; grad_num_step computes each
|
||||
// step's numerator on demand from the (stable) grad_word_q.
|
||||
end else if (grad_prefetching) begin
|
||||
// Ch328 1c — stage 2: the single read port issued attr_ram[grad_slot] last cycle, so
|
||||
// attr_rd_q is valid now → capture it into grad_word_q.
|
||||
|
||||
@@ -1182,6 +1182,15 @@ tb_gs_grad_divider: dirs
|
||||
@echo "=== run tb_gs_grad_divider ==="
|
||||
@cd $(TRACE_DIR) && $(VVP) $(BUILD_DIR)/tb_gs_grad_divider.vvp
|
||||
|
||||
tb_gs_grad_num_equiv: dirs
|
||||
@echo "=== build tb_gs_grad_num_equiv ==="
|
||||
$(IVERILOG) $(IVERILOG_FLGS) \
|
||||
-o $(BUILD_DIR)/tb_gs_grad_num_equiv.vvp \
|
||||
-s tb_gs_grad_num_equiv \
|
||||
$(TB_ROOT)/gif_gs/tb_gs_grad_num_equiv.sv
|
||||
@echo "=== run tb_gs_grad_num_equiv ==="
|
||||
@cd $(TRACE_DIR) && $(VVP) $(BUILD_DIR)/tb_gs_grad_num_equiv.vvp
|
||||
|
||||
tb_gs_lpddr_scanout_fb: dirs
|
||||
@echo "=== build tb_gs_lpddr_scanout_fb ==="
|
||||
$(IVERILOG) $(IVERILOG_FLGS) \
|
||||
@@ -6331,6 +6340,7 @@ run: tb_top_psmct32_sh3_zs640b24_cap tb_top_psmct32_sh3_zint640b24
|
||||
.PHONY: tb_top_psmct32_sh3_zs640c24c_cap tb_top_psmct32_sh3_zint640c24c sh3_zs640c24c_fixture sh3_zs640motionabc_bootlet
|
||||
run: tb_top_psmct32_sh3_zs640c24c_cap tb_top_psmct32_sh3_zint640c24c tb_gs_axi_w_regbuf tb_gs_axi_aw_regbuf tb_gs_axi_r_regbuf
|
||||
run: tb_gs_lpddr_scanout_lb tb_gs_scanout_binomial_lookahead tb_gs_scanout_diag tb_gs_scanout_restart tb_gs_scanout_cdc_qual
|
||||
run: tb_gs_grad_num_equiv
|
||||
|
||||
run: tb_ee_fetch tb_gs tb_intc tb_platform_video tb_bgcolor_via_dma tb_sif_mailbox \
|
||||
tb_sif_command_echo tb_sif_command_echo_rearm tb_sif_negative_path \
|
||||
|
||||
@@ -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