aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/lib/fifo/axi_fifo_2clk.v
diff options
context:
space:
mode:
Diffstat (limited to 'fpga/usrp3/lib/fifo/axi_fifo_2clk.v')
-rw-r--r--fpga/usrp3/lib/fifo/axi_fifo_2clk.v184
1 files changed, 184 insertions, 0 deletions
diff --git a/fpga/usrp3/lib/fifo/axi_fifo_2clk.v b/fpga/usrp3/lib/fifo/axi_fifo_2clk.v
new file mode 100644
index 000000000..1f3eee924
--- /dev/null
+++ b/fpga/usrp3/lib/fifo/axi_fifo_2clk.v
@@ -0,0 +1,184 @@
+/////////////////////////////////////////////////////////////////////
+//
+// 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;
+
+ // Ideally the following parameters should be technology
+ // specific. For now these values have been optimized for
+ // 7Series FPGAs. They also work for Spartan6 but may not
+ // be optimal. For future generations, make these values
+ // depend on the DEVICE parameter.
+ localparam BASE_WIDTH = 72;
+ localparam SRL_THRESHOLD = 5;
+ localparam RAM_THRESHOLD = 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