// SPDX-License-Identifier: LGPL-3.0-or-later // // Vivado/XPM replacement for the legacy Ettus FIFO Generator building blocks // used by axi_fifo_2clk.v on Xilinx 7-series devices. // // This core deliberately provides the old native FIFO interface while using // xpm_fifo_async internally. The external reset may be asynchronous, as it // was for the FIFO Generator instances. XPM's rst input, however, must be // synchronous to wr_clk, so reset assertion is captured asynchronously and // then presented to XPM through a wr_clk synchronizer. Read/write requests // and external flags are held inactive/conservative until XPM reset-busy has // cleared. `timescale 1ns/1ps `default_nettype none module fifo_2clk_xpm_core #( parameter integer FIFO_DEPTH = 32, parameter integer COUNT_WIDTH = 6, parameter integer CDC_SYNC_STAGES = 3, parameter MEMORY_TYPE = "distributed" )( input wire rst, input wire wr_clk, input wire [71:0] din, input wire wr_en, output wire full, output wire [COUNT_WIDTH-1:0] wr_data_count, input wire rd_clk, output wire [71:0] dout, input wire rd_en, output wire empty, output wire [COUNT_WIDTH-1:0] rd_data_count ); // ------------------------------------------------------------------------ // Reset adaptation // ------------------------------------------------------------------------ // Legacy FIFO Generator accepted an asynchronous reset. XPM_FIFO_ASYNC // requires rst to be synchronous to wr_clk. rst_capture catches even a // short asynchronous assertion; xpm_rst_sync then converts it into a reset // signal that changes only on wr_clk edges. reg rst_capture = 1'b1; always @(posedge wr_clk or posedge rst) begin if (rst) rst_capture <= 1'b1; else rst_capture <= 1'b0; end (* ASYNC_REG = "TRUE", SHREG_EXTRACT = "NO" *) reg [1:0] xpm_rst_sync = 2'b11; always @(posedge wr_clk) begin xpm_rst_sync[0] <= rst_capture; xpm_rst_sync[1] <= xpm_rst_sync[0]; end wire xpm_rst = xpm_rst_sync[1]; wire xpm_full; wire xpm_empty; wire xpm_wr_rst_busy; wire xpm_rd_rst_busy; wire [71:0] xpm_dout; wire [COUNT_WIDTH-1:0] xpm_wr_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-busy interval have completed. Assertion is asynchronous; // release is synchronized to rd_clk. wire rd_hold_req = rst | xpm_rst | xpm_rd_rst_busy; (* ASYNC_REG = "TRUE", SHREG_EXTRACT = "NO" *) reg [1:0] rd_hold_sync = 2'b11; always @(posedge rd_clk or posedge rd_hold_req) begin if (rd_hold_req) rd_hold_sync <= 2'b11; else begin rd_hold_sync[0] <= 1'b0; rd_hold_sync[1] <= rd_hold_sync[0]; end end wire rd_hold = rd_hold_sync[1]; wire wr_hold = rst | xpm_rst | xpm_wr_rst_busy; // XPM requires wr_en/rd_en low while reset or the corresponding reset-busy // flag is active. Gating with full/empty also suppresses overflow/underflow // requests from legacy call sites that leave an enable asserted continuously. wire xpm_wr_en = wr_en & ~wr_hold & ~xpm_full; wire xpm_rd_en = rd_en & ~rd_hold & ~xpm_empty; // Match the conservative reset behavior of the Ettus FIFO Generator IP: // full is asserted during reset and empty is asserted until read-side reset // recovery is complete. assign full = wr_hold | xpm_full; assign empty = rd_hold | xpm_empty; assign dout = rd_hold ? 72'b0 : xpm_dout; assign wr_data_count = wr_hold ? {COUNT_WIDTH{1'b0}} : xpm_wr_data_count; assign rd_data_count = rd_hold ? {COUNT_WIDTH{1'b0}} : xpm_rd_data_count; // USE_ADV_FEATURES="0404" enables only wr_data_count (bit 2) and // rd_data_count (bit 10), preserving the legacy ports without enabling the // other optional status logic. xpm_fifo_async #( .CDC_SYNC_STAGES (CDC_SYNC_STAGES), .DOUT_RESET_VALUE ("0"), .ECC_MODE ("no_ecc"), .FIFO_MEMORY_TYPE (MEMORY_TYPE), .FIFO_READ_LATENCY (0), .FIFO_WRITE_DEPTH (FIFO_DEPTH), .FULL_RESET_VALUE (1), .PROG_EMPTY_THRESH (10), .PROG_FULL_THRESH (10), .RD_DATA_COUNT_WIDTH(COUNT_WIDTH), .READ_DATA_WIDTH (72), .READ_MODE ("fwft"), .RELATED_CLOCKS (0), .SIM_ASSERT_CHK (1), .USE_ADV_FEATURES ("0404"), .WAKEUP_TIME (0), .WRITE_DATA_WIDTH (72), .WR_DATA_COUNT_WIDTH(COUNT_WIDTH) ) xpm_fifo_async_i ( .almost_empty (), .almost_full (), .data_valid (), .dbiterr (), .dout (xpm_dout), .empty (xpm_empty), .full (xpm_full), .overflow (), .prog_empty (), .prog_full (), .rd_data_count(xpm_rd_data_count), .rd_rst_busy (xpm_rd_rst_busy), .sbiterr (), .underflow (), .wr_ack (), .wr_data_count(xpm_wr_data_count), .wr_rst_busy (xpm_wr_rst_busy), .din (din), .injectdbiterr(1'b0), .injectsbiterr(1'b0), .rd_clk (rd_clk), .rd_en (xpm_rd_en), .rst (xpm_rst), .sleep (1'b0), .wr_clk (wr_clk), .wr_en (xpm_wr_en) ); endmodule `default_nettype wire