Snapshot: fog implementation + fidelity tooling baseline (pre bilinear-clamp fix)

Per-vertex GS fog end-to-end (gs_stub emit incl. persp_emit5, gs_prim_list_feeder
XYZ2->XYZF2 on PRIM.FGE, gs_make_sh3_scheduler_fixture.py F/FGE packing), new fog
TBs, fidelity attribution tooling. Functional baseline before removing the dead
bilinear lerp8 clamps (Codex: 161-node comb loop -> -0.042ns setup fail).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-20 19:56:46 -04:00
parent ec82764bef
commit ba74bbd5aa
476 changed files with 696247 additions and 130119 deletions
+26 -21
View File
@@ -502,9 +502,10 @@ module gs_texture_unit #(
// beat[k]'s 32-bit ABGR word -> capture into tap[k].
// If k<3: k++ and -> ISSUE (next neighbor). If k==3: -> DONE.
// DONE : combinationally lerp the 4 captured taps by u_frac/v_frac
// per channel; assert out_valid for 1 cycle with tex_color;
// drop busy; -> IDLE.
// => total ~ 4*(1+L)+1 cycles per filtered sample. Throughput is NOT a
// per channel and capture the result in tex_color_hold.
// OUT : assert out_valid for 1 cycle with the registered color;
// -> IDLE.
// => total ~ 4*(1+L)+2 cycles per filtered sample. Throughput is NOT a
// goal here (a later texture-cache pass collapses the 4 reads).
//
// Neighbor table (k -> du,dv): 0->(0,0) 1->(1,0) 2->(0,1) 3->(1,1).
@@ -519,12 +520,13 @@ module gs_texture_unit #(
// then defensively clamped to 0..255.
generate
if (BILINEAR_ENABLE) begin : g_bilinear
localparam logic [1:0] BS_IDLE = 2'd0;
localparam logic [1:0] BS_ISSUE = 2'd1;
localparam logic [1:0] BS_WAIT = 2'd2;
localparam logic [1:0] BS_DONE = 2'd3;
localparam logic [2:0] BS_IDLE = 3'd0;
localparam logic [2:0] BS_ISSUE = 3'd1;
localparam logic [2:0] BS_WAIT = 3'd2;
localparam logic [2:0] BS_DONE = 3'd3;
localparam logic [2:0] BS_OUT = 3'd4;
logic [1:0] state;
logic [2:0] state;
logic [1:0] beat; // which neighbor 0..3
logic [31:0] wait_cnt; // counts RD_LATENCY
logic [31:0] tap [0:3]; // captured ABGR per neighbor
@@ -641,7 +643,13 @@ module gs_texture_unit #(
wait_cnt <= wait_cnt + 32'd1;
end
end
default: begin // BS_DONE
BS_DONE: begin
// tex_color_hold captures the finished blend on this
// edge. Keep a distinct OUT cycle so no consumer can
// see the tap->two-lerp combinational cone directly.
state <= BS_OUT;
end
default: begin // BS_OUT
state <= BS_IDLE;
end
endcase
@@ -703,18 +711,15 @@ module gs_texture_unit #(
cv_a = lerp8(top_a, bot_a, lat_vf);
end
// Ch310 — HOLD register for the filtered color. The combined-renderer
// Ch310/Ch422 — HOLD register for the filtered color. The combined-renderer
// FSM (gs_stub CB_TWAIT) may latch the result a cycle or two AFTER the
// out_valid pulse (it steps at half-rate on z_advance beats), so the
// blended ABGR must stay STABLE from out_valid until the next sample.
// tex_color is the LIVE combinational blend during DONE (so an
// out_valid-keyed caller — tb_gs_texture_bilinear — reads the fresh
// value the SAME cycle out_valid pulses, byte-identical to before) and
// the LATCHED copy afterward (so a caller that reads one+ cycles later,
// like CB_TWAIT→CB_T, still sees it). The register captures the blend
// on the clk edge that LEAVES DONE; combining "live during DONE, held
// after" gives a value stable from out_valid until the next sample
// overwrites it at its DONE.
// The register captures the blend on the clk edge that LEAVES DONE.
// Ch422 makes OUT a separate following cycle, so tex_color and
// out_valid expose only this registered value. That is the timing
// boundary between the two-stage bilinear arithmetic and downstream
// TEX0 modulation; callers already wait on out_valid or !busy.
logic [31:0] tex_color_blend;
assign tex_color_blend = {cv_a, cv_b, cv_g, cv_r};
logic [31:0] tex_color_hold;
@@ -724,9 +729,9 @@ module gs_texture_unit #(
else if (state == BS_DONE)
tex_color_hold <= tex_color_blend; // capture the just-blended value
end
// live during the DONE pulse, held (last captured) otherwise
// Registered result, held until the next sample completes.
logic [31:0] tex_color_lin;
assign tex_color_lin = (state == BS_DONE) ? tex_color_blend : tex_color_hold;
assign tex_color_lin = tex_color_hold;
// --- output mux: bilinear FSM owns the outputs for a FILTERED PSMCT32
// sample (do_lin). When do_lin=0 — non-PSMCT32 psm OR MMAG=0 NEAREST —
@@ -738,7 +743,7 @@ module gs_texture_unit #(
// tex_rd_addr is the SAME addr-gen output for both paths (the wrap
// selects beat_u/beat_v vs port u/v); the FSM just gates rd_en.
assign tex_rd_addr = near_rd_addr;
assign out_valid = do_lin ? (state == BS_DONE) : near_out_valid;
assign out_valid = do_lin ? (state == BS_OUT) : near_out_valid;
assign tex_color = do_lin ? tex_color_lin : near_color;
assign busy = do_lin && (state != BS_IDLE);
end else begin : g_nearest