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:
@@ -214,6 +214,44 @@ module tb_ps2_hps_bridge;
|
||||
logic feeder_ready_i = 1'b0;
|
||||
logic [15:0] feeder_records_i = 16'd0;
|
||||
logic [31:0] feeder_waits_i = 32'd0;
|
||||
logic frame_drained_i = 1'b0; // Ch353 — new bridge diagnostic input; tied off (.* matches by name)
|
||||
logic clear_done_i = 1'b0; // Ch357 — persistent-Z preclear ack (driven by the Ch357 status test)
|
||||
logic [31:0] frag_drops_i = 32'd0; // Ch357 — persistent-Z drop count (driven by the Ch357 status test)
|
||||
// Ch367 -- runtime CLUT staging ports (the data-bank CDC itself is
|
||||
// covered in tb_clut_stage_cdc; this TB pins the bridge ABI/decode).
|
||||
logic [7:0] clut_stage_waddr_o;
|
||||
logic [31:0] clut_stage_wdata_o;
|
||||
logic clut_stage_we_o, clut_commit_tgl_o;
|
||||
logic [31:0] clut_expected_crc_o;
|
||||
logic clut_busy_i = 1'b0, clut_done_tgl_i = 1'b0;
|
||||
logic [31:0] clut_crc_i = 32'd0;
|
||||
integer clut_stage_writes;
|
||||
logic [7:0] clut_stage_last_addr;
|
||||
logic [31:0] clut_stage_last_data;
|
||||
|
||||
always_ff @(posedge clk or negedge reset_n) begin
|
||||
if (!reset_n) begin
|
||||
clut_stage_writes <= 0;
|
||||
clut_stage_last_addr <= 8'd0;
|
||||
clut_stage_last_data <= 32'd0;
|
||||
end else if (clut_stage_we_o) begin
|
||||
clut_stage_writes <= clut_stage_writes + 1;
|
||||
clut_stage_last_addr <= clut_stage_waddr_o;
|
||||
clut_stage_last_data <= clut_stage_wdata_o;
|
||||
end
|
||||
end
|
||||
|
||||
// Ch355 — model the feeder staging RAM exactly as the on-board feeder does: the bridge emits a COMMIT strobe
|
||||
// (feeder_stg_we_tgl_o toggles) with waddr/wdata for each staged word. Capture it here to verify that a
|
||||
// register-write stream lands every word at the correct slot (the board bug put word0 at slot 1).
|
||||
logic [63:0] model_stg [0:63]; logic mstg_tgl_q;
|
||||
always_ff @(posedge clk or negedge reset_n) begin
|
||||
if (!reset_n) mstg_tgl_q <= 1'b0;
|
||||
else begin
|
||||
mstg_tgl_q <= feeder_stg_we_tgl_o;
|
||||
if (feeder_stg_we_tgl_o !== mstg_tgl_q) model_stg[feeder_stg_waddr_o] <= feeder_stg_wdata_o;
|
||||
end
|
||||
end
|
||||
|
||||
ps2_hps_bridge u_dut (
|
||||
.clk (clk),
|
||||
@@ -1183,6 +1221,128 @@ module tb_ps2_hps_bridge;
|
||||
axi_read32(38'h0E8, rd); check_eq("FEEDER_waits", rd, 32'd7);
|
||||
end
|
||||
|
||||
// --------------------------------------------------------
|
||||
// Ch355 — BRIDGE FEEDER-STAGING address path (closes the gap that hid the board bug: the multi-texture TB
|
||||
// staged via $readmemh and never exercised the real 0x0D8 set-addr / 0x0DC-lo / 0x0E4-hi-commit protocol).
|
||||
// Stage two complete lists EXCLUSIVELY through bridge register writes; verify every word (esp word 0), the
|
||||
// final address, and the records readback; plus a negative test that reproduces the exact board bug.
|
||||
// --------------------------------------------------------
|
||||
begin
|
||||
logic [63:0] la [0:9]; logic [63:0] lb [0:5]; logic [31:0] rd;
|
||||
la[0]=64'h0000000100000044; la[1]=64'hDEADBEEF00000001; la[2]=64'h1111111122222222; la[3]=64'h3333333344444444;
|
||||
la[4]=64'h5555555566666666; la[5]=64'h7777777788888888; la[6]=64'h99999999000000AA; la[7]=64'hAAAAAAAABBBBBBBB;
|
||||
la[8]=64'hCCCCCCCCDDDDDDDD; la[9]=64'hEEEEEEEEFFFFFFFF;
|
||||
lb[0]=64'h0000000100000032; lb[1]=64'h0102030405060708; lb[2]=64'h1122334455667788; lb[3]=64'h99AABBCCDDEEFF00;
|
||||
lb[4]=64'hFEDCBA9876543210; lb[5]=64'hCAFEBABE0BADF00D;
|
||||
|
||||
// ---- SCENE A: reset addr to 0 (0x0D8=0), stream {lo(0x0DC), hi(0x0E4)} per word ----
|
||||
for (int i=0;i<64;i++) model_stg[i]=64'd0;
|
||||
axi_write32(38'h0D8, 32'd0);
|
||||
for (int i=0;i<10;i++) begin axi_write32(38'h0DC, la[i][31:0]); axi_write32(38'h0E4, la[i][63:32]); end
|
||||
repeat(8) @(posedge clk);
|
||||
for (int i=0;i<10;i++) check_eq($sformatf("bridge_stgA[%0d]",i), model_stg[i][31:0], la[i][31:0]);
|
||||
if (model_stg[0] !== la[0]) begin $error("[bridge_stg] A: WORD0 not at slot 0 (0x0D8 addr-reset broken)"); errors=errors+1; end
|
||||
axi_read32(38'h0DC, rd); check_eq("A staged_addr (=nwords)", rd, 32'd10);
|
||||
|
||||
// ---- NEGATIVE test: 0x0D8 SETS the addr to the written value; writing 1 mis-slots word0 to slot 1 (the
|
||||
// EXACT board failure that produced 0 rendered beats). This pins the register semantics. ----
|
||||
for (int i=0;i<64;i++) model_stg[i]=64'd0;
|
||||
axi_write32(38'h0D8, 32'd1);
|
||||
axi_write32(38'h0DC, la[0][31:0]); axi_write32(38'h0E4, la[0][63:32]);
|
||||
repeat(8) @(posedge clk);
|
||||
if (model_stg[0][31:0] !== 32'd0) begin $error("[bridge_stg] neg: slot0 should stay empty when addr set to 1"); errors=errors+1; end
|
||||
if (model_stg[1] !== la[0]) begin $error("[bridge_stg] neg: word0 must land at slot 1 when 0x0D8=1 (bug repro)"); errors=errors+1; end
|
||||
|
||||
// ---- SCENE B: re-stage a DIFFERENT list after resetting addr to 0 (A -> B, no shortcut) ----
|
||||
for (int i=0;i<64;i++) model_stg[i]=64'd0;
|
||||
axi_write32(38'h0D8, 32'd0);
|
||||
for (int i=0;i<6;i++) begin axi_write32(38'h0DC, lb[i][31:0]); axi_write32(38'h0E4, lb[i][63:32]); end
|
||||
repeat(8) @(posedge clk);
|
||||
for (int i=0;i<6;i++) check_eq($sformatf("bridge_stgB[%0d]",i), model_stg[i][31:0], lb[i][31:0]);
|
||||
if (model_stg[0] !== lb[0]) begin $error("[bridge_stg] B: WORD0 not at slot 0"); errors=errors+1; end
|
||||
axi_read32(38'h0DC, rd); check_eq("B staged_addr (=nwords)", rd, 32'd6);
|
||||
|
||||
// ---- records_emitted readback (0x0E4 R reflects the feeder count; board expected 68/scene) ----
|
||||
feeder_records_i = 16'd68; repeat(4) @(posedge clk);
|
||||
axi_read32(38'h0E4, rd); check_eq("records_emitted readback", rd, 32'd68);
|
||||
$display("[bridge_stg] OK: two lists staged via bridge writes; word0@slot0, addr=nwords, addr-set semantics pinned, records=68");
|
||||
end
|
||||
|
||||
// --------------------------------------------------------
|
||||
// Ch357 — persistent-Z observability status: clear_done (0x02C[7]), drop snapshot (0x0EC),
|
||||
// sticky drops-nonzero (0x02C[8]). These are the host's fail-closed gates for the Z fit.
|
||||
// --------------------------------------------------------
|
||||
begin
|
||||
frame_drained_i = 1'b0; clear_done_i = 1'b0; frag_drops_i = 32'd0; repeat(4) @(posedge clk);
|
||||
axi_read32(38'h02C, rd); check_eq("Ch357 clear_done idle 0x02C[7]=0", {31'd0, rd[7]}, 32'd0);
|
||||
axi_read32(38'h0EC, rd); check_eq("Ch357 FRAG_DROPS idle=0", rd, 32'd0);
|
||||
axi_read32(38'h02C, rd); check_eq("Ch357 drops_nz idle 0x02C[8]=0", {31'd0, rd[8]}, 32'd0);
|
||||
// clear_done asserts -> synced high at 0x02C[7]
|
||||
clear_done_i = 1'b1; repeat(4) @(posedge clk);
|
||||
axi_read32(38'h02C, rd); check_eq("Ch357 clear_done synced 0x02C[7]=1", {31'd0, rd[7]}, 32'd1);
|
||||
// drop snapshot latches ONLY while drained (quiescent). Drained + drops=0 -> snapshot 0, sticky 0.
|
||||
frame_drained_i = 1'b1; frag_drops_i = 32'd0; repeat(4) @(posedge clk);
|
||||
axi_read32(38'h0EC, rd); check_eq("Ch357 FRAG_DROPS drained,0", rd, 32'd0);
|
||||
axi_read32(38'h02C, rd); check_eq("Ch357 drops_nz still 0 0x02C[8]=0", {31'd0, rd[8]}, 32'd0);
|
||||
// inject a drop while drained -> snapshot captures the count, sticky trips
|
||||
frag_drops_i = 32'd5; repeat(4) @(posedge clk);
|
||||
axi_read32(38'h0EC, rd); check_eq("Ch357 FRAG_DROPS snapshot=5", rd, 32'd5);
|
||||
axi_read32(38'h02C, rd); check_eq("Ch357 drops_nz sticky set 0x02C[8]=1",{31'd0, rd[8]},32'd1);
|
||||
// sticky is FAIL-CLOSED: persists even after drops clear and frame_drained drops
|
||||
frag_drops_i = 32'd0; frame_drained_i = 1'b0; repeat(4) @(posedge clk);
|
||||
axi_read32(38'h02C, rd); check_eq("Ch357 drops_nz sticky persists 0x02C[8]=1", {31'd0, rd[8]}, 32'd1);
|
||||
clear_done_i = 1'b0;
|
||||
$display("[bridge_ch357] OK: clear_done sync + drop snapshot + fail-closed sticky-nonzero pinned");
|
||||
end
|
||||
|
||||
// --------------------------------------------------------
|
||||
// Ch367 -- runtime CLUT residency ABI. The 1 KiB palette window
|
||||
// is intentionally outside every legacy side-effect window. Pin
|
||||
// the address/data write pulse, expected-sum round trip, busy
|
||||
// gate, commit toggle, and done/result readback independently of
|
||||
// the actual dual-clock copier.
|
||||
// --------------------------------------------------------
|
||||
begin
|
||||
logic commit_before;
|
||||
integer writes_before;
|
||||
axi_write32(38'h200, 32'h11223344);
|
||||
@(posedge clk);
|
||||
check_eq("CLUT_STAGE data@0", clut_stage_last_data, 32'h11223344);
|
||||
check_eq("CLUT_STAGE addr@0", {24'd0, clut_stage_last_addr}, 32'd0);
|
||||
writes_before = clut_stage_writes;
|
||||
axi_write32(38'h400, 32'h55667788);
|
||||
@(posedge clk);
|
||||
check_eq("CLUT_STAGE write@128", clut_stage_writes, writes_before + 1);
|
||||
check_eq("CLUT_STAGE data@128", clut_stage_last_data, 32'h55667788);
|
||||
check_eq("CLUT_STAGE addr@128", {24'd0, clut_stage_last_addr}, 32'd128);
|
||||
writes_before = clut_stage_writes;
|
||||
axi_write32(38'h5FC, 32'hAABBCCDD);
|
||||
@(posedge clk);
|
||||
check_eq("CLUT_STAGE write@255", clut_stage_writes, writes_before + 1);
|
||||
check_eq("CLUT_STAGE data@255", clut_stage_last_data, 32'hAABBCCDD);
|
||||
check_eq("CLUT_STAGE addr@255", {24'd0, clut_stage_last_addr}, 32'd255);
|
||||
|
||||
axi_write32(38'h1E4, 32'h89ABCDEF);
|
||||
axi_read32(38'h1E4, rd); check_eq("CLUT expected sum", rd, 32'h89ABCDEF);
|
||||
commit_before = clut_commit_tgl_o;
|
||||
axi_write32(38'h1E0, 32'h1);
|
||||
repeat(2) @(posedge clk);
|
||||
check_eq("CLUT commit toggle", clut_commit_tgl_o ^ commit_before, 1'b1);
|
||||
|
||||
clut_busy_i = 1'b1; repeat(3) @(posedge clk);
|
||||
axi_read32(38'h1E0, rd); check_eq("CLUT busy status", rd[0], 1'b1);
|
||||
commit_before = clut_commit_tgl_o;
|
||||
axi_write32(38'h1E0, 32'h1);
|
||||
repeat(2) @(posedge clk);
|
||||
check_eq("CLUT busy blocks commit", clut_commit_tgl_o ^ commit_before, 1'b0);
|
||||
|
||||
clut_busy_i = 1'b0; clut_crc_i = 32'h89ABCDEF; clut_done_tgl_i = ~clut_done_tgl_i;
|
||||
repeat(4) @(posedge clk);
|
||||
axi_read32(38'h1E0, rd); check_eq("CLUT done status", rd[1], 1'b1);
|
||||
axi_read32(38'h1E8, rd); check_eq("CLUT result sum", rd, 32'h89ABCDEF);
|
||||
$display("[bridge_clut] OK: palette window, commit/busy gate, done/result ABI pinned");
|
||||
end
|
||||
|
||||
// --------------------------------------------------------
|
||||
// Done.
|
||||
// --------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user