diff --git a/rtl/gif_gs/gs_stub.sv b/rtl/gif_gs/gs_stub.sv index 4378fdf..5c8d58b 100644 --- a/rtl/gif_gs/gs_stub.sv +++ b/rtl/gif_gs/gs_stub.sv @@ -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. diff --git a/sim/Makefile b/sim/Makefile index 0832a4a..219cbfb 100644 --- a/sim/Makefile +++ b/sim/Makefile @@ -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 \ diff --git a/sim/tb/gif_gs/tb_gs_grad_num_equiv.sv b/sim/tb/gif_gs/tb_gs_grad_num_equiv.sv new file mode 100644 index 0000000..2249e6b --- /dev/null +++ b/sim/tb/gif_gs/tb_gs_grad_num_equiv.sv @@ -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