// retroDE_ps2 — gs_persp_uv (Ch301) // // Per-pixel PERSPECTIVE-CORRECT texture-coordinate divide. Given the three // affinely-interpolated perspective attributes at a pixel — // // uq = (u/w) * 2**FRAC (u-over-w, fixed-point) // vq = (v/w) * 2**FRAC (v-over-w, fixed-point) // q = (1/w) * 2**FRAC (one-over-w, fixed-point) // // — this recovers the integer texel coordinates: // // w_recip = 1/q (= w, via the pipelined gs_reciprocal_stub LUT, NO divider) // u_texel = (uq * w_recip) >> SCALE (= (u/w) * w = u) // v_texel = (vq * w_recip) >> SCALE (= (v/w) * w = v) // // gs_reciprocal_stub returns recip = floor(2**SCALE / q). With q = (1/w)<> SCALE = u. (The FRAC scaling cancels.) // // Pipeline (NO divider, ~1 result/cycle): // recip: RLAT cycles (gs_reciprocal_stub, 3). // uq/vq: delayed RLAT cycles to align with recip. // mul: 1 cycle (uq*recip, vq*recip) + shift + clamp. // total latency = RLAT + 1. // // Output texel coords are clamped to [0, TEXEL_MAX] (saturating), matching the // integer-coord clamp the affine path already applies. `timescale 1ns/1ps module gs_persp_uv #( parameter int ATTR_W = 24, // width of uq/vq ((u/w)<> SCALE; if (shifted > PROD_W'(TEXEL_MAX)) clamp_texel = TEXEL_W'(TEXEL_MAX); else clamp_texel = shifted[TEXEL_W-1:0]; endfunction always_ff @(posedge clk or negedge rst_n) begin if (!rst_n) begin out_valid <= 1'b0; u <= '0; v <= '0; end else begin logic [PROD_W-1:0] u_prod, v_prod; out_valid <= recip_valid; u_prod = uq_pipe[RLAT-1] * w_recip; v_prod = vq_pipe[RLAT-1] * w_recip; u <= clamp_texel(u_prod); v <= clamp_texel(v_prod); end end endmodule : gs_persp_uv