fifo: synchronize read-domain reset handling
Synchronize legacy and XPM reset indications into the FIFO read clock domain instead of feeding cross-domain reset logic into asynchronous PRE/CLR paths. This removes the FIFO-induced CDC-10 findings while preserving legacy reset behavior.
This commit is contained in:
@@ -65,23 +65,80 @@ module fifo_2clk_xpm_core #(
|
|||||||
wire [COUNT_WIDTH-1:0] xpm_wr_data_count;
|
wire [COUNT_WIDTH-1:0] xpm_wr_data_count;
|
||||||
wire [COUNT_WIDTH-1:0] xpm_rd_data_count;
|
wire [COUNT_WIDTH-1:0] xpm_rd_data_count;
|
||||||
|
|
||||||
// Keep the read-side legacy interface quiescent from the instant the old
|
// ------------------------------------------------------------------------
|
||||||
// asynchronous reset is asserted until both XPM reset propagation and the
|
// Read-domain reset/hold handling
|
||||||
// read-domain reset-busy interval have completed. Assertion is asynchronous;
|
// ------------------------------------------------------------------------
|
||||||
// release is synchronized to rd_clk.
|
// Do NOT combine xpm_rst (wr_clk domain) with rd-domain signals and feed the
|
||||||
wire rd_hold_req = rst | xpm_rst | xpm_rd_rst_busy;
|
// result into an asynchronous PRE/CLR. Vivado report_cdc correctly reports
|
||||||
(* ASYNC_REG = "TRUE", SHREG_EXTRACT = "NO" *)
|
// that topology as CDC-10 (combinational logic before a synchronizer).
|
||||||
reg [1:0] rd_hold_sync = 2'b11;
|
//
|
||||||
always @(posedge rd_clk or posedge rd_hold_req) begin
|
// Instead, synchronize each reset indication into rd_clk using dedicated XPM
|
||||||
if (rd_hold_req)
|
// CDC structures, then make all hold/release decisions synchronously in the
|
||||||
rd_hold_sync <= 2'b11;
|
// read domain.
|
||||||
else begin
|
|
||||||
rd_hold_sync[0] <= 1'b0;
|
// Preserve the legacy FIFO Generator behavior at the external interface:
|
||||||
rd_hold_sync[1] <= rd_hold_sync[0];
|
// assertion of rst is visible immediately in the read domain, while release
|
||||||
|
// is synchronized to rd_clk.
|
||||||
|
wire legacy_rd_rst;
|
||||||
|
xpm_cdc_async_rst #(
|
||||||
|
.DEST_SYNC_FF (2),
|
||||||
|
.INIT_SYNC_FF (1),
|
||||||
|
.RST_ACTIVE_HIGH(1)
|
||||||
|
) legacy_rd_rst_sync_i (
|
||||||
|
.src_arst (rst),
|
||||||
|
.dest_clk (rd_clk),
|
||||||
|
.dest_arst(legacy_rd_rst)
|
||||||
|
);
|
||||||
|
|
||||||
|
// xpm_rst is generated synchronously in the write domain. Synchronize that
|
||||||
|
// reset request into rd_clk so the read interface can remain held until the
|
||||||
|
// XPM reset has actually propagated through the write side. INIT=1 keeps the
|
||||||
|
// read side conservative during FPGA configuration/startup.
|
||||||
|
wire xpm_rst_rd;
|
||||||
|
xpm_cdc_sync_rst #(
|
||||||
|
.DEST_SYNC_FF (2),
|
||||||
|
.INIT (1),
|
||||||
|
.INIT_SYNC_FF (1),
|
||||||
|
.SIM_ASSERT_CHK(1)
|
||||||
|
) xpm_rst_to_rd_i (
|
||||||
|
.src_rst (xpm_rst),
|
||||||
|
.dest_clk(rd_clk),
|
||||||
|
.dest_rst(xpm_rst_rd)
|
||||||
|
);
|
||||||
|
|
||||||
|
// A reset event is complete only after the read domain has observed the XPM
|
||||||
|
// reset sequence (xpm_rst_rd and/or rd_rst_busy asserted) and both indicators
|
||||||
|
// have subsequently gone low. This prevents a short external reset from
|
||||||
|
// creating a gap where the legacy interface is released before XPM has
|
||||||
|
// propagated the reset from wr_clk into rd_clk.
|
||||||
|
reg rd_reset_pending = 1'b1;
|
||||||
|
reg rd_reset_seen = 1'b0;
|
||||||
|
reg legacy_rd_rst_d = 1'b1;
|
||||||
|
always @(posedge rd_clk) begin
|
||||||
|
legacy_rd_rst_d <= legacy_rd_rst;
|
||||||
|
|
||||||
|
// Start a fresh acknowledgement cycle for each external reset event.
|
||||||
|
if (legacy_rd_rst && !legacy_rd_rst_d) begin
|
||||||
|
rd_reset_pending <= 1'b1;
|
||||||
|
rd_reset_seen <= 1'b0;
|
||||||
|
end else if (legacy_rd_rst) begin
|
||||||
|
// The XPM reset may assert and even complete while the legacy reset is
|
||||||
|
// still being synchronously released. Remember that it was observed.
|
||||||
|
rd_reset_pending <= 1'b1;
|
||||||
|
if (xpm_rst_rd || xpm_rd_rst_busy)
|
||||||
|
rd_reset_seen <= 1'b1;
|
||||||
|
end else if (rd_reset_pending) begin
|
||||||
|
if (xpm_rst_rd || xpm_rd_rst_busy)
|
||||||
|
rd_reset_seen <= 1'b1;
|
||||||
|
|
||||||
|
if (rd_reset_seen && !xpm_rst_rd && !xpm_rd_rst_busy)
|
||||||
|
rd_reset_pending <= 1'b0;
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
wire rd_hold = rd_hold_sync[1];
|
// All terms below are now local to rd_clk (or an XPM-synchronized reset).
|
||||||
|
wire rd_hold = legacy_rd_rst | rd_reset_pending |
|
||||||
|
xpm_rst_rd | xpm_rd_rst_busy;
|
||||||
wire wr_hold = rst | xpm_rst | xpm_wr_rst_busy;
|
wire wr_hold = rst | xpm_rst | xpm_wr_rst_busy;
|
||||||
|
|
||||||
// XPM requires wr_en/rd_en low while reset or the corresponding reset-busy
|
// XPM requires wr_en/rd_en low while reset or the corresponding reset-busy
|
||||||
|
|||||||
Reference in New Issue
Block a user