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:
@@ -0,0 +1,119 @@
|
||||
// Focused proof for the runtime CLUT staging transaction.
|
||||
`timescale 1ns/1ps
|
||||
|
||||
module tb_clut_stage_cdc;
|
||||
logic bclk = 1'b0, dclk = 1'b0;
|
||||
always #5 bclk = ~bclk;
|
||||
always #7 dclk = ~dclk;
|
||||
|
||||
logic breset_n, dreset_n;
|
||||
logic b_we;
|
||||
logic [7:0] b_waddr;
|
||||
logic [31:0] b_wdata;
|
||||
logic b_commit_tgl;
|
||||
logic [31:0] b_expected_crc;
|
||||
logic clut_wr_en;
|
||||
logic [7:0] clut_wr_idx;
|
||||
logic [31:0] clut_wr_data;
|
||||
logic busy, done_tgl;
|
||||
logic [31:0] crc;
|
||||
logic [31:0] observed [0:255];
|
||||
logic [31:0] expected [0:255];
|
||||
logic seen_done;
|
||||
int writes, errors;
|
||||
logic [31:0] expected_sum;
|
||||
|
||||
clut_stage_cdc u_dut (
|
||||
.bclk, .breset_n, .b_we, .b_waddr, .b_wdata, .b_commit_tgl,
|
||||
.b_expected_crc, .dclk, .dreset_n, .clut_wr_en, .clut_wr_idx,
|
||||
.clut_wr_data, .busy, .done_tgl, .crc
|
||||
);
|
||||
|
||||
always_ff @(posedge dclk) begin
|
||||
if (clut_wr_en) begin
|
||||
observed[clut_wr_idx] <= clut_wr_data;
|
||||
writes <= writes + 1;
|
||||
end
|
||||
end
|
||||
|
||||
task automatic check(input string label, input logic got, input logic want);
|
||||
if (got !== want) begin
|
||||
$error("[%s] got %b expected %b", label, got, want);
|
||||
errors = errors + 1;
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic stage_word(input logic [7:0] addr, input logic [31:0] data);
|
||||
@(posedge bclk);
|
||||
b_waddr <= addr;
|
||||
b_wdata <= data;
|
||||
b_we <= 1'b1;
|
||||
@(posedge bclk);
|
||||
b_we <= 1'b0;
|
||||
endtask
|
||||
|
||||
initial begin
|
||||
breset_n = 1'b0;
|
||||
dreset_n = 1'b0;
|
||||
b_we = 1'b0;
|
||||
b_waddr = 8'd0;
|
||||
b_wdata = 32'd0;
|
||||
b_commit_tgl = 1'b0;
|
||||
b_expected_crc = 32'd0;
|
||||
writes = 0;
|
||||
errors = 0;
|
||||
seen_done = 1'b0;
|
||||
expected_sum = 32'd0;
|
||||
for (int i = 0; i < 256; i++) begin
|
||||
expected[i] = 32'h3C000000 ^ (i * 32'h01020409);
|
||||
observed[i] = 32'd0;
|
||||
expected_sum = expected_sum + expected[i];
|
||||
end
|
||||
|
||||
repeat (3) @(posedge bclk);
|
||||
breset_n = 1'b1;
|
||||
repeat (3) @(posedge dclk);
|
||||
dreset_n = 1'b1;
|
||||
|
||||
for (int i = 0; i < 256; i++)
|
||||
stage_word(i[7:0], expected[i]);
|
||||
|
||||
b_expected_crc = expected_sum;
|
||||
@(posedge bclk);
|
||||
b_commit_tgl <= ~b_commit_tgl;
|
||||
@(posedge bclk);
|
||||
|
||||
// A second commit during the copy is intentionally ignored.
|
||||
wait (busy);
|
||||
@(posedge bclk);
|
||||
b_commit_tgl <= ~b_commit_tgl;
|
||||
@(posedge bclk);
|
||||
|
||||
wait (done_tgl != seen_done);
|
||||
seen_done = done_tgl;
|
||||
repeat (4) @(posedge dclk);
|
||||
check("busy_after_copy", busy, 1'b0);
|
||||
if (writes != 256) begin
|
||||
$error("[write_count] got %0d expected 256", writes);
|
||||
errors = errors + 1;
|
||||
end
|
||||
if (crc !== expected_sum) begin
|
||||
$error("[sum32] got 0x%08x expected 0x%08x", crc, expected_sum);
|
||||
errors = errors + 1;
|
||||
end
|
||||
for (int i = 0; i < 256; i++) begin
|
||||
if (observed[i] !== expected[i]) begin
|
||||
$error("[entry %0d] got 0x%08x expected 0x%08x", i, observed[i], expected[i]);
|
||||
errors = errors + 1;
|
||||
end
|
||||
end
|
||||
repeat (12) @(posedge dclk);
|
||||
if (writes != 256) begin
|
||||
$error("[busy_commit_ignored] writes=%0d expected 256", writes);
|
||||
errors = errors + 1;
|
||||
end
|
||||
if (errors != 0) $fatal(1, "tb_clut_stage_cdc FAILED: %0d errors", errors);
|
||||
$display("tb_clut_stage_cdc PASS: 256 entries, sum32=0x%08x", crc);
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
@@ -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