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
@@ -523,6 +523,10 @@ module de25_nano_psmct32_raster_demo_top (
wire scanout_lb_w; // Ch321 — 1 = line-buffer scanout, 0 = frame-cache
wire [7:0] scan_r_w, scan_g_w, scan_b_w;
wire scan_cache_valid_w, scan_err_w;
// Ch442 A+B scanout diagnostic nets (top-level; driven per scanout arm like scan_err_w).
wire scan_diag_uf_w, scan_diag_rderr_nz_w, scan_diag_valid_w;
wire [29:0] scan_diag_first_w;
wire [6:0] scan_diag_stat_w;
// Ch320/Ch321 — LPDDR scanout frame-cache size: 256 beats (8 KiB, 64x64) by default,
// 1024 beats (32 KiB, 128x128) for the Ch321 larger-frame demo.
`ifdef GS_TILE_LPDDR128_DEMO
@@ -2673,6 +2677,8 @@ module de25_nano_psmct32_raster_demo_top (
.pixel_x(pixel_x), .pixel_y(pixel_y), .in_window(demo_pix_window),
.r(lb_r_w), .g(lb_g_w), .b(lb_b_w),
.line_valid(lb_valid_w), .underflow(lb_underflow_w), .rd_errs(lb_rd_errs_w),
.diag_rderr_nz(scan_diag_rderr_nz_w), .diag_valid(scan_diag_valid_w),
.diag_first(scan_diag_first_w), .diag_stat(scan_diag_stat_w),
.araddr(lb_araddr), .arburst(lb_arburst), .arid(lb_arid),
.arlen(lb_arlen), .arsize(lb_arsize), .arvalid(lb_arvalid),
.arready(scan_ar_arready & scanout_lb_eff),
@@ -2693,6 +2699,9 @@ module de25_nano_psmct32_raster_demo_top (
assign scan_cache_valid_w = scanout_lb_eff ? lb_valid_w : fc_valid_w;
assign scan_err_w = scanout_lb_eff ? (lb_underflow_w | (lb_rd_errs_w != 32'd0))
: (fc_rd_errs_w != 32'd0);
// Ch442 — the split underflow flag (bit5 above is UNCHANGED). Only meaningful on the
// line-buffer path; the frame-cache fallback has no per-row prefetch to underflow.
assign scan_diag_uf_w = scanout_lb_eff ? lb_underflow_w : 1'b0;
// 2:1 read arbiter — scanout (s0, priority) + probe (s1) onto the EMIF read channel.
gs_lpddr_rd_arb u_lpddr_rd_arb (
@@ -2823,6 +2832,8 @@ module de25_nano_psmct32_raster_demo_top (
assign lpddr_rd_data_w=32'd0; assign lpddr_rd_done_w=1'b0; // no read-probe in the f2sdram path
assign scan_r_w=8'd0; assign scan_g_w=8'd0; assign scan_b_w=8'd0;
assign scan_cache_valid_w=1'b0; assign scan_err_w=1'b0; // no LPDDR scanout
assign scan_diag_uf_w=1'b0; assign scan_diag_rderr_nz_w=1'b0; assign scan_diag_valid_w=1'b0;
assign scan_diag_first_w=30'd0; assign scan_diag_stat_w=7'd0; // Ch442 diag tie-off (no LPDDR scanout)
assign tex_fill_done_w=1'b0; assign tex_fill_beats_w=32'd0; assign tex_fill_bytes_w=32'd0;
assign tex_rd_errs_w=32'd0; assign tex_fill_crc_w=32'd0;
assign lpddr_wr_busy_w=1'b0; assign lpddr_wr_done_w=1'b0; assign lpddr_wr_bresp_err_w=32'd0;
@@ -2843,6 +2854,8 @@ module de25_nano_psmct32_raster_demo_top (
assign lpddr_rd_data_w=32'd0; assign lpddr_rd_done_w=1'b0; // no read-probe (inert path)
assign scan_r_w=8'd0; assign scan_g_w=8'd0; assign scan_b_w=8'd0;
assign scan_cache_valid_w=1'b0; assign scan_err_w=1'b0; // no LPDDR scanout
assign scan_diag_uf_w=1'b0; assign scan_diag_rderr_nz_w=1'b0; assign scan_diag_valid_w=1'b0;
assign scan_diag_first_w=30'd0; assign scan_diag_stat_w=7'd0; // Ch442 diag tie-off (no LPDDR scanout)
assign tex_fill_done_w=1'b0; assign tex_fill_beats_w=32'd0; assign tex_fill_bytes_w=32'd0;
assign tex_rd_errs_w=32'd0; assign tex_fill_crc_w=32'd0;
assign lpddr_wr_busy_w=1'b0; assign lpddr_wr_done_w=1'b0; assign lpddr_wr_bresp_err_w=32'd0;
@@ -3108,6 +3121,11 @@ module de25_nano_psmct32_raster_demo_top (
.lpddr_scanout_lb_o(scanout_lb_w),
.lpddr_scan_valid_i(scan_cache_valid_w),
.lpddr_scan_err_i (scan_err_w),
.scan_diag_uf_i (scan_diag_uf_w),
.scan_diag_rderr_nz_i(scan_diag_rderr_nz_w),
.scan_diag_valid_i (scan_diag_valid_w),
.scan_diag_first_i (scan_diag_first_w),
.scan_diag_stat_i (scan_diag_stat_w),
// Ch322 — LPDDR write-probe (HPS stages texture words) + texture-cache fill.
.lpddr_wr_addr_o (lpddr_wr_addr_w),
.lpddr_wr_data_o (lpddr_wr_data_w),