diff options
Diffstat (limited to 'fpga/usrp3/lib/fifo/axi_fifo_2clk.v')
-rw-r--r-- | fpga/usrp3/lib/fifo/axi_fifo_2clk.v | 184 |
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 |