Ch443: combined timing repair for both Ch442 setup families

Ch442's fit exposed two failing setup families (plus a stale max-skew).
Attack both structurally; no reseeding.

Family 1 - AWREADY -> Z-FSM (-0.410): gen_p2c_ff[23] (EMIF AWREADY)
reached u_zc_emit|u_z's S_SFLUSH_AW/S_FILL_R next-state combinationally.
Ch441 registered only the W channel; add the AW twin:
- new gs_axi_aw_regbuf (one-entry fully-registered AW buffer), inserted
  in zc_emit between u_z's AW output and the arbiter s2 AW port. The FSM
  now sees registered occupancy, never EMIF's combinational AWREADY.

Family 2 - texcache drain_idx_q -> tex_mem (-0.370, x7): a single index
fanned across the whole 65536x32, 128-M20K macro. Split by WIDTH into
four 65536x8 banks, each with its own (* preserve, dont_merge *) write-
address launch register; write the four byte lanes together in F_WRITE;
reconstruct the sample word by concatenating four registered read bytes.
Selector structure and 1-cycle read latency unchanged; total M20Ks
unchanged (4x32 == 128); fill_crc still sums the full 32-bit word.

Max-skew: relax ONLY the Ch357 tile-write CDC set_max_skew 2.0 -> 2.5
(quasi-static bundle, >=2 dclk stability window); retain set_net_delay
2.0 (the real arrival bound). SDC comment updated.

Tests: new tb_gs_axi_aw_regbuf (AW scoreboard: exactly-once/order/no-
combinational-AWREADY-bypass/stable-while-stalled); tb_gs_texture_cache
strengthened to distinct-per-byte-bank data + per-bank + full-word
checks. All pass: aw/w regbuf, texture_cache, texture_psmt8_clut,
scanout_diag, ps2_hps_bridge, and the complete f52 replay BYTE-IDENTICAL
(Z 0/307200, COLOR 0/245760).

No Quartus, board, or push from here. Ready for one owner GUI fit.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-22 11:19:09 -04:00
parent 194f45bd05
commit 27dfd0b0cf
8 changed files with 316 additions and 15 deletions
+19 -3
View File
@@ -37,8 +37,17 @@ module tb_gs_texture_cache;
logic [31:0] tex_rd_addr, tex_rd_data;
logic tex_ready;
// golden source: 64 words. tex_word(i) = 0xC0DE_0000 | i (distinct per lane).
function automatic [31:0] tex_word(input int i); tex_word = 32'hC0DE_0000 | i[31:0]; endfunction
// golden source. Ch443: every byte lane carries DISTINCT data — different per index
// AND different across the four banks (distinct XOR keys + distinct bases) — so the
// four-bank tex_mem split is genuinely exercised: a bank swap, misroute, or dropped
// bank produces a wrong reconstructed byte that the full-word + per-bank checks catch.
// (The old 0xC0DE_0000|i had constant bytes 2/3, which a bank fault could hide.)
function automatic [31:0] tex_word(input int i);
tex_word = { 8'(((i*4 + 3) & 32'hFF) ^ 32'hA3),
8'(((i*4 + 2) & 32'hFF) ^ 32'h5C),
8'(((i*4 + 1) & 32'hFF) ^ 32'h91),
8'(((i*4 + 0) & 32'hFF) ^ 32'h2E) };
endfunction
// force a bad rresp on a chosen beat to exercise rd_errs (set <0 to disable)
int err_beat = -1;
@@ -122,9 +131,16 @@ module tb_gs_texture_cache;
check(tex_ready, "tex_ready never synced");
// ---- verify every word via the sampler 1-cycle read port ----
// Full reconstructed word AND per-bank byte pinpoint (Ch443 four-bank split).
for (int i=0; i<TEX_WORDS; i++) begin
logic [31:0] exp;
read_word(i, got);
check(got == tex_word(i), $sformatf("word[%0d]=%08x exp %08x", i, got, tex_word(i)));
exp = tex_word(i); // temp: iverilog forbids part-select on a function-call result
check(got == exp, $sformatf("word[%0d]=%08x exp %08x", i, got, exp));
check(got[7:0] === exp[7:0], $sformatf("bank0 byte[%0d]=%02x exp %02x", i, got[7:0], exp[7:0]));
check(got[15:8] === exp[15:8], $sformatf("bank1 byte[%0d]=%02x exp %02x", i, got[15:8], exp[15:8]));
check(got[23:16] === exp[23:16], $sformatf("bank2 byte[%0d]=%02x exp %02x", i, got[23:16], exp[23:16]));
check(got[31:24] === exp[31:24], $sformatf("bank3 byte[%0d]=%02x exp %02x", i, got[31:24], exp[31:24]));
end
$display("[texcache] clean: fill_done=%0d beats=%0d bytes=%0d rd_errs=%0d words_checked=%0d errors=%0d",