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:
@@ -84,6 +84,14 @@ module gs_lpddr_scanout_lb #(
|
||||
output logic underflow, // a pixel was read before its row was ready (sticky)
|
||||
output logic [31:0] rd_errs, // non-OKAY read responses (cumulative)
|
||||
|
||||
// ---- Ch442 A+B diagnostic (read-only; NOTHING here feeds fetch/output/arbitration/underflow) ----
|
||||
// Splits the single top-level scan-error bit into independently observable causes and captures
|
||||
// the FIRST raw-underflow event of each video-source-enabled session as a bundled-data snapshot.
|
||||
output logic diag_rderr_nz, // LIVE (rd_errs != 0), reduced+registered in the axi_clk domain (single bit; the raw counter never crosses)
|
||||
output logic diag_valid, // first raw-underflow captured; held stable until video source disabled (!enable)
|
||||
output logic [29:0] diag_first, // SNAPSHOT, stable while diag_valid: {nf_s0[9:0], nf_v[9:0], scan_y[9:0]}
|
||||
output logic [6:0] diag_stat, // SNAPSHOT, stable while diag_valid: {vphase[3:0], line_valid, lookahead_cause, base_cause}
|
||||
|
||||
// ---- AXI4 read channel to the EMIF user port (axi_clk, 256-bit) ----
|
||||
output logic [29:0] araddr,
|
||||
output logic [1:0] arburst,
|
||||
@@ -651,6 +659,10 @@ module gs_lpddr_scanout_lb #(
|
||||
logic [255:0] lb0_wdata_q, lb1_wdata_q, lb2_wdata_q;
|
||||
logic [RB_BITS-1:0] lb0_waddr_q, lb1_waddr_q, lb2_waddr_q;
|
||||
logic lb0_we_q, lb1_we_q, lb2_we_q;
|
||||
// Ch442 diag: registered single-bit reduction of the read-error counter (emif domain).
|
||||
// Diagnostic-only; nothing downstream reads it, so it cannot perturb the fetch FSM.
|
||||
logic rd_err_nz_q;
|
||||
assign diag_rderr_nz = rd_err_nz_q;
|
||||
|
||||
always_ff @(posedge axi_clk) begin
|
||||
if (!axi_rst_n) begin
|
||||
@@ -664,6 +676,7 @@ module gs_lpddr_scanout_lb #(
|
||||
cur_row <= '0; cur_buf <= 2'd0; beat <= '0;
|
||||
line_valid <= 1'b0; rd_errs <= 32'd0; fs_pending <= 1'b0;
|
||||
lb0_we_q <= 1'b0; lb1_we_q <= 1'b0; lb2_we_q <= 1'b0;
|
||||
rd_err_nz_q <= 1'b0;
|
||||
end else begin
|
||||
fs_sync_e <= {fs_sync_e[1:0], frame_start};
|
||||
disp_row_s0 <= disp_row_v; // 2-FF sync of the display row
|
||||
@@ -684,6 +697,9 @@ module gs_lpddr_scanout_lb #(
|
||||
lb0_we_q <= 1'b0;
|
||||
lb1_we_q <= 1'b0;
|
||||
lb2_we_q <= 1'b0;
|
||||
// Ch442 diag: register the read-error-nonzero flag (emif domain). One cycle of lag
|
||||
// vs rd_errs is irrelevant for a stuck-nonzero indicator; keeps a clean launch flop.
|
||||
rd_err_nz_q <= (rd_errs != 32'd0);
|
||||
// vsync: mark a prefetch restart. DEFER it to L_IDLE so an in-flight AXI
|
||||
// read is never aborted mid-handshake (which would deadlock the slave).
|
||||
if (fs_edge_e) fs_pending <= 1'b1;
|
||||
@@ -776,4 +792,47 @@ module gs_lpddr_scanout_lb #(
|
||||
underflow_v <= 1'b1;
|
||||
end
|
||||
assign underflow = underflow_v;
|
||||
|
||||
// ================= Ch442 A+B first-failure diagnostic (read-only) =================
|
||||
// Re-express the EXACT raw-underflow predicate (identical boolean to the sticky latch
|
||||
// above) as combinational wires so the capture can name WHICH sub-cause fired. These
|
||||
// wires and the registers below drive ONLY the diag_* outputs — never the fetch FSM,
|
||||
// the pixel path, arbitration, or underflow_v. The existing latch block is untouched.
|
||||
wire uf_in_range = enable && in_window && (scan_y < ($clog2(N_ROWS)+1)'(N_ROWS));
|
||||
wire uf_base_cond = uf_in_range && (scan_y >= nf_v);
|
||||
wire uf_look_cond = uf_in_range &&
|
||||
(BINOMIAL_3X3_FILTER ||
|
||||
(V_LINEAR_FILTER && (stretch_vphase_q != 4'd0))) &&
|
||||
(scan_y + 1'b1 < ($clog2(N_ROWS)+1)'(N_ROWS)) &&
|
||||
(scan_y + 1'b1 >= nf_v);
|
||||
wire raw_uf_cond = uf_base_cond || uf_look_cond;
|
||||
|
||||
// line_valid is axi_clk-domain; 2-FF into video_clk for coherent capture.
|
||||
logic [1:0] line_valid_vsync;
|
||||
// First-failure snapshot (video_clk). diag_valid holds from the FIRST raw underflow
|
||||
// until the video source drops (!enable) — NOT cleared per frame (unlike underflow_v),
|
||||
// so it names the first failure of the whole enabled session. The bundled snapshot
|
||||
// ({nf_s0,nf_v,scan_y} + cause/line_valid/vphase) stays stable while diag_valid is high,
|
||||
// so the bridge can transfer it coherently with a single synchronized valid.
|
||||
logic diag_valid_q, diag_base_q, diag_look_q, diag_lv_q;
|
||||
logic [3:0] diag_vphase_q;
|
||||
logic [$clog2(N_ROWS):0] diag_scan_y_q, diag_nf_v_q, diag_nf_s0_q;
|
||||
always_ff @(posedge video_clk) begin
|
||||
line_valid_vsync <= {line_valid_vsync[0], line_valid};
|
||||
if (!enable) begin
|
||||
diag_valid_q <= 1'b0;
|
||||
end else if (!diag_valid_q && raw_uf_cond) begin
|
||||
diag_valid_q <= 1'b1;
|
||||
diag_base_q <= uf_base_cond;
|
||||
diag_look_q <= uf_look_cond;
|
||||
diag_lv_q <= line_valid_vsync[1];
|
||||
diag_vphase_q <= stretch_vphase_q;
|
||||
diag_scan_y_q <= scan_y;
|
||||
diag_nf_v_q <= nf_v;
|
||||
diag_nf_s0_q <= nf_s0;
|
||||
end
|
||||
end
|
||||
assign diag_valid = diag_valid_q;
|
||||
assign diag_first = {10'(diag_nf_s0_q), 10'(diag_nf_v_q), 10'(diag_scan_y_q)};
|
||||
assign diag_stat = {diag_vphase_q, diag_lv_q, diag_look_q, diag_base_q};
|
||||
endmodule
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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),
|
||||
|
||||
Reference in New Issue
Block a user