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
|
||||
|
||||
Reference in New Issue
Block a user