Ch442 review fixes: fs_edge parity, dest snap_valid, sync attrs + SDC

Codex review containment/observability fixes (no behavior change, no redesign):

- scanout_lb: add !fs_edge_v to the diagnostic predicate so capture
  matches the underflow latch's frame-start CLEAR priority exactly
  (no capture on an fs_edge cycle the real latch suppresses).
- bridge: expose a destination snap_valid_q as 0x120[0] — set ON the
  payload-capture edge, cleared on synced source-valid deassert — so
  valid never leads the bundle by a cycle (was the middle sync stage).
- bridge: forced-synchronizer (SYNCHRONIZER_IDENTIFICATION FORCED) +
  dont_merge/preserve on the underflow/read-error/valid chains;
  preserve on the bundle capture regs (both domains).
- SDC: stage-0 async cuts on the three sync[0] inputs + the 37-bit
  stable bundle hold-false-path + 2ns max_skew + 2ns net_delay, with
  fail-closed src==37 / dst!=0 count checks (tile_ram_cdc idiom).
- tb_gs_scanout_diag: +fs_edge-suppression monitor (with coverage that
  the coincidence is exercised), +valid-ordering monitor, +snapshot
  stability after later misses, +production DUT (V_SOURCE_START=32,
  stretch, linear) proving cold-start scan_y=32, +cause/phase packing.
  32/32 checks pass.
- doc: production cold start is source row 32 (not 0), base+lookahead
  may both assert, and one snapshot narrows but does not prove
  starvation vs next_fetch CDC-lag.

Sim set all PASS: focused TB, tb_ps2_hps_bridge, scanout_lb
{binomial,hstretch,psm32_256}, complete f52 replay (FB byte-identical
Z 0/307200, COLOR 0/245760). No Quartus, board, push, or scanout
behavior change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-21 23:50:04 -04:00
parent 6720caee12
commit 194f45bd05
5 changed files with 217 additions and 55 deletions
+10 -4
View File
@@ -798,7 +798,11 @@ module gs_lpddr_scanout_lb #(
// 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));
// NOTE the `!fs_edge_v` term: the sticky latch gives frame-start CLEAR priority
// (`if (!enable || fs_edge_v) ... else if (predicate)`), so on an fs_edge_v cycle it
// never sets. Mirror that exactly here or the diagnostic could capture a frame-start
// coincident miss the real latch suppresses.
wire uf_in_range = enable && !fs_edge_v && 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 ||
@@ -814,9 +818,11 @@ module gs_lpddr_scanout_lb #(
// 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;
// (* preserve *): keep these capture regs as named keepers so the SDC bundled-data
// hold-false/max-skew/net-delay constraint can bind to them (they must not be merged).
(* preserve *) logic diag_valid_q, diag_base_q, diag_look_q, diag_lv_q;
(* preserve *) logic [3:0] diag_vphase_q;
(* preserve *) 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
+17 -5
View File
@@ -651,11 +651,19 @@ module ps2_hps_bridge (
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;
// Forced-synchronizer + dont_merge/preserve on each async chain so Quartus identifies the
// synchronizers (MTBF) and never retimes/merges them; the SDC cuts the async into stage 0.
(* altera_attribute = "-name SYNCHRONIZER_IDENTIFICATION FORCED", dont_merge, preserve *)
logic [1:0] scan_diag_uf_sync;
(* altera_attribute = "-name SYNCHRONIZER_IDENTIFICATION FORCED", dont_merge, preserve *)
logic [1:0] scan_diag_rderr_sync;
(* altera_attribute = "-name SYNCHRONIZER_IDENTIFICATION FORCED", dont_merge, preserve *)
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;
logic scan_diag_snap_valid_q; // DEST valid: set WITH payload capture, cleared on synced source-valid deassert
// (* preserve *): keep the bundle dest regs as named keepers for the SDC bundled-data constraint.
(* preserve *) logic scan_diag_base_q, scan_diag_look_q, scan_diag_lv_q;
(* preserve *) logic [3:0] scan_diag_vphase_q;
(* preserve *) 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
@@ -948,7 +956,7 @@ module ps2_hps_bridge (
// [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]};
scan_diag_rderr_sync[1], scan_diag_uf_sync[1], scan_diag_snap_valid_q};
// 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;
@@ -1115,6 +1123,7 @@ module ps2_hps_bridge (
scan_diag_uf_sync <= 2'b00;
scan_diag_rderr_sync <= 2'b00;
scan_diag_valid_sync <= 3'b000;
scan_diag_snap_valid_q<= 1'b0;
scan_diag_base_q <= 1'b0;
scan_diag_look_q <= 1'b0;
scan_diag_lv_q <= 1'b0;
@@ -1169,6 +1178,9 @@ module ps2_hps_bridge (
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];
scan_diag_snap_valid_q <= 1'b1; // valid asserts ON the payload-capture edge, never before
end else if (!scan_diag_valid_sync[1]) begin
scan_diag_snap_valid_q <= 1'b0; // clear once the synced source valid has deasserted (!enable)
end
// Ch322 — texture-fill / write-probe status sync (emif_clk -> clk).
tex_fill_done_sync <= {tex_fill_done_sync[0], tex_fill_done_i};