Ch442: A+B scanout diagnostic (split LPDDR_STATUS[5] + first-failure snapshot)

Read-only diagnostic to disambiguate the three causes folded into
LPDDR_STATUS[5]. bit5 (0x02C) semantics are UNCHANGED.

- gs_lpddr_scanout_lb: register a live (rd_errs!=0) flag in the emif
  domain (the raw counter never crosses), and capture the FIRST raw
  underflow of each video-source-enabled session as a bundled-data
  snapshot (scan_y/nf_v/nf_s0 + base-vs-lookahead cause + line_valid +
  vphase), held stable until !enable. The existing sticky underflow_v
  latch, fetch FSM, pixel path and arbitration are untouched.
- ps2_hps_bridge: independently 2-FF sync the two split live flags;
  latch the snapshot on the rising synced-valid edge (coherent bundled
  data). New read-only regs 0x120 SCAN_DIAG_STATUS / 0x124
  SCAN_DIAG_FIRST (window addr[37:5]==9); 0x118/0x11C reserved slots
  untouched.
- top: drive 5 diag nets per scanout arm like scan_err_w; bit5 assign
  unchanged.
- tb_gs_scanout_diag (new): provokes AXI RRESP error, cold-start row-0
  starvation, first-failure capture + coherent bridge readback, and
  clear-via-video-source-disable (17/17 checks).
- Tie off the new bridge inputs in the four .*-instantiating TBs.

Sim set all PASS: focused TB, tb_ps2_hps_bridge, scanout_lb
{binomial,hstretch,psm32_256}, and the complete f52 replay (FB
byte-identical: Z 0/307200, COLOR 0/245760; sum32=0xaad0b94d).

No fix / gray-code / persistence-filter / scanout checksum. No Quartus,
no board, no push.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-21 22:40:07 -04:00
parent 2123e646c8
commit 6720caee12
9 changed files with 374 additions and 1 deletions
+55 -1
View File
@@ -328,7 +328,17 @@ module ps2_hps_bridge (
output logic lpddr_video_src_o, // 0x018[2] — 1 = HDMI sourced from LPDDR4B scanout (default 0 = BRAM)
output logic lpddr_scanout_lb_o, // 0x018[3] — 1 = line-buffer scanout, 0 = frame-cache (Ch321; default 0)
input logic lpddr_scan_valid_i, // scanout frame cache loaded (emif_clk; synced -> 0x02C[4])
input logic lpddr_scan_err_i, // scanout read errors seen (emif_clk; synced -> 0x02C[5])
input logic lpddr_scan_err_i, // scanout read errors seen (emif_clk; synced -> 0x02C[5]) — UNCHANGED (bit5 preserved)
// ---- Ch442 A+B scanout diagnostic (read-only; splits 0x02C[5] causes + first-failure snapshot) ----
// 0x02C[5] keeps its exact meaning (still driven by lpddr_scan_err_i). These add a split view at
// 0x120/0x124. Each single-bit flag is synchronized INDEPENDENTLY; the wide snapshot is transferred
// via a bundled-data handshake (stable payload + one synchronized valid), never raw multi-bit reads.
input logic scan_diag_uf_i, // LIVE line-buffer underflow alone (video_clk sticky; 2-FF -> 0x120[1])
input logic scan_diag_rderr_nz_i,// LIVE (lb_rd_errs != 0), reduced in fabric (emif_clk; 2-FF -> 0x120[2])
input logic scan_diag_valid_i, // first-failure captured, held to !enable (video_clk level; bundled-data valid)
input logic [29:0] scan_diag_first_i, // SNAPSHOT {nf_s0[9:0],nf_v[9:0],scan_y[9:0]} (stable while valid -> 0x124)
input logic [6:0] scan_diag_stat_i, // SNAPSHOT {vphase[3:0],line_valid,lookahead,base} (stable while valid -> 0x120)
// ---- Ch322: LPDDR write-probe (HPS stages texture words) + texture-cache fill ----
// 0x040 LPDDR_WRADDR (W): set the LPDDR byte address (auto-increments +4 per data write).
@@ -640,6 +650,12 @@ module ps2_hps_bridge (
logic [1:0] lpddr_scan_valid_sync, lpddr_scan_err_sync;
assign lpddr_video_src_o = lpddr_video_src_q;
assign lpddr_scanout_lb_o = lpddr_scanout_lb_q;
// Ch442 A+B diagnostic — independently synced live flags + bundled-data snapshot capture.
logic [1:0] scan_diag_uf_sync, scan_diag_rderr_sync;
logic [2:0] scan_diag_valid_sync; // 3-deep so the RISING synced edge latches the snapshot
logic scan_diag_base_q, scan_diag_look_q, scan_diag_lv_q;
logic [3:0] scan_diag_vphase_q;
logic [9:0] scan_diag_scan_y_q, scan_diag_nf_v_q, scan_diag_nf_s0_q;
// Ch322 — LPDDR write-probe + texture-cache fill registers + return-path CDC.
logic [31:0] lpddr_wr_addr_q; // "next write" pointer (auto-increments)
logic [31:0] lpddr_wr_addr_present_q;// the address that goes WITH the current data word
@@ -924,6 +940,19 @@ module ps2_hps_bridge (
3'h5: reg_read = osd_cfg1_q; // 0x114 OSD_CFG1
default: reg_read = 32'd0; // 0x118 / 0x11C reserved-zero
endcase
end else if (addr[37:5] == 33'h09) begin
// Ch442 — scanout A+B diagnostic window (0x120..0x13F). Read-only; distinct
// from the reserved 0x118/0x11C (which stay in the 33'h08 window above).
case (addr[4:2])
// 0x120 SCAN_DIAG_STATUS: [0]valid [1]underflow [2]read-error-nonzero
// [3]cause_base(scan_y>=nf_v) [4]cause_lookahead [5]line_valid [11:8]filter vphase.
3'h0: reg_read = {20'd0, scan_diag_vphase_q, 2'd0,
scan_diag_lv_q, scan_diag_look_q, scan_diag_base_q,
scan_diag_rderr_sync[1], scan_diag_uf_sync[1], scan_diag_valid_sync[1]};
// 0x124 SCAN_DIAG_FIRST: [9:0]scan_y [19:10]nf_v [29:20]nf_s0.
3'h1: reg_read = {2'd0, scan_diag_nf_s0_q, scan_diag_nf_v_q, scan_diag_scan_y_q};
default: reg_read = 32'd0;
endcase
end else if (addr[37:12] == 26'h1) begin
// Ch227 — tile RAM 0x1000..0x1FFF, 1024 × 32-bit.
reg_read = tile_mem[addr[11:2]];
@@ -1083,6 +1112,16 @@ module ps2_hps_bridge (
lpddr_scanout_lb_q <= 1'b0; // Ch321 — default frame-cache (line-buffer = opt-in)
lpddr_scan_valid_sync <= 2'b00;
lpddr_scan_err_sync <= 2'b00;
scan_diag_uf_sync <= 2'b00;
scan_diag_rderr_sync <= 2'b00;
scan_diag_valid_sync <= 3'b000;
scan_diag_base_q <= 1'b0;
scan_diag_look_q <= 1'b0;
scan_diag_lv_q <= 1'b0;
scan_diag_vphase_q <= 4'd0;
scan_diag_scan_y_q <= 10'd0;
scan_diag_nf_v_q <= 10'd0;
scan_diag_nf_s0_q <= 10'd0;
lpddr_wr_addr_q <= 32'd0;
lpddr_wr_addr_present_q <= 32'd0;
lpddr_wr_data_q <= 32'd0;
@@ -1116,6 +1155,21 @@ module ps2_hps_bridge (
// Ch320 — scanout status sync (emif_clk -> clk).
lpddr_scan_valid_sync <= {lpddr_scan_valid_sync[0], lpddr_scan_valid_i};
lpddr_scan_err_sync <= {lpddr_scan_err_sync[0], lpddr_scan_err_i};
// Ch442 — independent 2-FF syncs of the two split live flags (never the raw counter).
scan_diag_uf_sync <= {scan_diag_uf_sync[0], scan_diag_uf_i};
scan_diag_rderr_sync <= {scan_diag_rderr_sync[0], scan_diag_rderr_nz_i};
// Bundled-data capture: sync the valid, and on its RISING synced edge the snapshot
// (held stable in the video domain since first-failure) is quiescent -> latch coherently.
scan_diag_valid_sync <= {scan_diag_valid_sync[1:0], scan_diag_valid_i};
if (scan_diag_valid_sync[1] && !scan_diag_valid_sync[2]) begin
scan_diag_scan_y_q <= scan_diag_first_i[9:0];
scan_diag_nf_v_q <= scan_diag_first_i[19:10];
scan_diag_nf_s0_q <= scan_diag_first_i[29:20];
scan_diag_base_q <= scan_diag_stat_i[0];
scan_diag_look_q <= scan_diag_stat_i[1];
scan_diag_lv_q <= scan_diag_stat_i[2];
scan_diag_vphase_q <= scan_diag_stat_i[6:3];
end
// Ch322 — texture-fill / write-probe status sync (emif_clk -> clk).
tex_fill_done_sync <= {tex_fill_done_sync[0], tex_fill_done_i};
lpddr_wr_busy_sync <= {lpddr_wr_busy_sync[0], lpddr_wr_busy_i};