///////////////////////////////////////////////////////////////////// // // Copyright 2017 Ettus Research, A National Instruments Company // SPDX-License-Identifier: LGPL-3.0-or-later // // Module: axi_fifo_2clk.v // // Purpose: // An asynchronous clock crossing for AXI-Stream buses // The width (WIDTH) and depth (SIZE) of the FIFO is configurable // For depths less than the technology's SRL threshold, an SRL // will be instantiated. For depths less the minimum RAM block // depth (that corresponds to the max width), a single BRAM block // will be instantiated. For other larger depths, a BRAM block // plus a regular axi_fifo will be instantiated. The depth of the // combined FIFO in that case will be larger than the user request. // // Requirements: // Implementation for fifo_short_2clk, fifo_4k_2clk that infer SRL // and BRAM based clock-crossing FIFOs respectively // ////////////////////////////////////////////////////////////////////// module axi_fifo_2clk #( parameter WIDTH = 69, // Width of input/output data word parameter SIZE = 9, // log2 of the depth of the FIFO parameter PIPELINE = "NONE", // Which ports to pipeline? {NONE, IN, OUT, INOUT} parameter DEVICE = "7SERIES" // FPGA technology identifier (for optimal inference) )( input wire reset, input wire i_aclk, input wire [WIDTH-1:0] i_tdata, input wire i_tvalid, output wire i_tready, input wire o_aclk, output wire [WIDTH-1:0] o_tdata, output wire o_tvalid, input wire o_tready ); wire i_arst, o_arst; synchronizer #(.INITIAL_VAL(1'b1)) i_rst_sync_i ( .clk(i_aclk), .rst(1'b0), .in(reset), .out(i_arst) ); synchronizer #(.INITIAL_VAL(1'b1)) o_rst_sync_i ( .clk(o_aclk), .rst(1'b0), .in(reset), .out(o_arst) ); //---------------------------------------------- // Pipeline Logic //---------------------------------------------- wire [WIDTH-1:0] i_pipe_tdata, o_pipe_tdata; wire i_pipe_tvalid, o_pipe_tvalid; wire i_pipe_tready, o_pipe_tready; generate if (PIPELINE == "IN" || PIPELINE == "INOUT") begin axi_fifo_flop2 #(.WIDTH(WIDTH)) in_pipe_i ( .clk(i_aclk), .reset(i_arst), .clear(1'b0), .i_tdata(i_tdata), .i_tvalid(i_tvalid), .i_tready(i_tready), .o_tdata(i_pipe_tdata), .o_tvalid(i_pipe_tvalid), .o_tready(i_pipe_tready), .space(), .occupied() ); end else begin assign {i_pipe_tdata, i_pipe_tvalid} = {i_tdata, i_tvalid}; assign i_tready = i_pipe_tready; end if (PIPELINE == "OUT" || PIPELINE == "INOUT") begin axi_fifo_flop2 #(.WIDTH(WIDTH)) out_pipe_i ( .clk(o_aclk), .reset(o_arst), .clear(1'b0), .i_tdata(o_pipe_tdata), .i_tvalid(o_pipe_tvalid), .i_tready(o_pipe_tready), .o_tdata(o_tdata), .o_tvalid(o_tvalid), .o_tready(o_tready), .space(), .occupied() ); end else begin assign {o_tdata, o_tvalid} = {o_pipe_tdata, o_pipe_tvalid}; assign o_pipe_tready = o_tready; end endgenerate //---------------------------------------------- // FIFO Logic //---------------------------------------------- wire [WIDTH-1:0] o_ext_tdata; wire o_ext_tvalid; wire o_ext_tready; // Derive constants based on device. // First triple of values is for Intel's MAX10 FPGAs. The FIFO generator for // those devices supports embedded memory only (SRL_THRESHOLD = 0). // The later triple has been optimized for Xilinx 7Series FPGAs. They also // work for Spartan6 but may not be optimal. localparam BASE_WIDTH = (DEVICE == "MAX10") ? 36 : 72; localparam SRL_THRESHOLD = (DEVICE == "MAX10") ? 0 : 5; localparam RAM_THRESHOLD = (DEVICE == "MAX10") ? 8 : 9; // How many parallel FIFOs to instantiate to fit WIDTH localparam NUM_FIFOS = ((WIDTH-1)/BASE_WIDTH)+1; localparam INT_WIDTH = BASE_WIDTH * NUM_FIFOS; wire [INT_WIDTH-1:0] wr_data, rd_data; wire [NUM_FIFOS-1:0] full, empty; wire wr_en, rd_en; // Read/write logic for FIFO sections assign wr_data = {{(INT_WIDTH-WIDTH){1'b0}}, i_pipe_tdata}; assign wr_en = i_pipe_tready & i_pipe_tvalid; assign i_pipe_tready = &(~full); assign o_ext_tdata = rd_data[WIDTH-1:0]; assign o_ext_tvalid = &(~empty); assign rd_en = o_ext_tready & o_ext_tvalid; // FIFO IP instantiation genvar i; generate for (i = 0; i < NUM_FIFOS; i = i + 1) begin: fifo_section if (SIZE <= SRL_THRESHOLD) begin fifo_short_2clk impl_srl_i ( .rst (i_arst), .wr_clk (i_aclk), .din (wr_data[((i+1)*BASE_WIDTH)-1:i*BASE_WIDTH]), .wr_en (wr_en), .full (full[i]), .wr_data_count(), .rd_clk (o_aclk), .dout (rd_data[((i+1)*BASE_WIDTH)-1:i*BASE_WIDTH]), .rd_en (rd_en), .empty (empty[i]), .rd_data_count() ); end else begin fifo_4k_2clk impl_bram_i ( .rst (i_arst), .wr_clk (i_aclk), .din (wr_data[((i+1)*BASE_WIDTH)-1:i*BASE_WIDTH]), .wr_en (wr_en), .full (full[i]), .wr_data_count(), .rd_clk (o_aclk), .dout (rd_data[((i+1)*BASE_WIDTH)-1:i*BASE_WIDTH]), .rd_en (rd_en), .empty (empty[i]), .rd_data_count() ); end end endgenerate //---------------------------------------------- // Extension FIFO (for large sizes) //---------------------------------------------- generate if (SIZE > RAM_THRESHOLD) begin wire [WIDTH-1:0] ext_pipe_tdata; wire ext_pipe_tvalid; wire ext_pipe_tready; // Add a register slice between BRAM cascades axi_fifo_flop2 #(.WIDTH(WIDTH)) ext_fifo_pipe_i ( .clk(o_aclk), .reset(o_arst), .clear(1'b0), .i_tdata(o_ext_tdata), .i_tvalid(o_ext_tvalid), .i_tready(o_ext_tready), .o_tdata(ext_pipe_tdata), .o_tvalid(ext_pipe_tvalid), .o_tready(ext_pipe_tready), .space(), .occupied() ); // Bolt on an extension FIFO if the requested depth is larger than the BRAM // 2clk FIFO primitive (IP) axi_fifo_bram #(.WIDTH(WIDTH), .SIZE(SIZE)) ext_fifo_i ( .clk(o_aclk), .reset(o_arst), .clear(1'b0), .i_tdata(ext_pipe_tdata), .i_tvalid(ext_pipe_tvalid), .i_tready(ext_pipe_tready), .o_tdata(o_pipe_tdata), .o_tvalid(o_pipe_tvalid), .o_tready(o_pipe_tready), .space(), .occupied() ); end else begin assign {o_pipe_tdata, o_pipe_tvalid} = {o_ext_tdata, o_ext_tvalid}; assign o_ext_tready = o_pipe_tready; end endgenerate endmodule