diff options
-rw-r--r-- | fpga/usrp3/lib/axi/Makefile.srcs | 1 | ||||
-rw-r--r-- | fpga/usrp3/lib/axi/axis_split.v | 128 |
2 files changed, 129 insertions, 0 deletions
diff --git a/fpga/usrp3/lib/axi/Makefile.srcs b/fpga/usrp3/lib/axi/Makefile.srcs index 28f63104d..bf8b3dbd2 100644 --- a/fpga/usrp3/lib/axi/Makefile.srcs +++ b/fpga/usrp3/lib/axi/Makefile.srcs @@ -33,4 +33,5 @@ axis_shift_register.v \ axis_upsizer.v \ axis_downsizer.v \ axis_width_conv.v \ +axis_split.v \ )) diff --git a/fpga/usrp3/lib/axi/axis_split.v b/fpga/usrp3/lib/axi/axis_split.v new file mode 100644 index 000000000..26759eaa2 --- /dev/null +++ b/fpga/usrp3/lib/axi/axis_split.v @@ -0,0 +1,128 @@ +// +// Copyright 2020 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// Module: axis_split +// +// Description: +// +// This module takes a single AXI-Stream input and duplicates it onto +// multiple AXI-Stream outputs. This block correctly handles the somewhat +// tricky flow-control logic so that the AXI-Stream handshake protocol is +// honored at all top-level ports. +// +// The internal buffering is finite, so if the data from any of the output +// ports can't be consumed then the flow-control logic will cause the input +// to stall (i.e., s_axis_tready will deassert). +// +// Parameters: +// +// DATA_W : The bit width of tdata for all ports. +// NUM_PORTS : The number of output ports on which to duplicate the input. +// INPUT_REG : Set to 1 to add an input register stage to break combinatorial +// paths. Set to 0 to allow combinatorial input paths. +// + + +module axis_split #( + parameter DATA_W = 32, + parameter NUM_PORTS = 4, + parameter INPUT_REG = 0 +) ( + input wire clk, + input wire rst, + + // Input AXI-Stream + input wire [DATA_W-1:0] s_axis_tdata, + input wire s_axis_tvalid, + output wire s_axis_tready, + + // Output AXI-Streams + output wire [DATA_W*NUM_PORTS-1:0] m_axis_tdata, + output wire [ NUM_PORTS-1:0] m_axis_tvalid, + input wire [ NUM_PORTS-1:0] m_axis_tready +); + + // Output of the input-register stage + wire [DATA_W-1:0] reg_tdata; + wire reg_tvalid; + wire reg_tready; + + // Input to the Output FIFO stage + wire [DATA_W*NUM_PORTS-1:0] fifo_tdata; + wire [ NUM_PORTS-1:0] fifo_tvalid; + wire [ NUM_PORTS-1:0] fifo_tready; + + // Indicates all output FIFOs are ready for a transfer + wire all_fifo_tready; + + + //--------------------------------------------------------------------------- + // Optional Input Register + //--------------------------------------------------------------------------- + + if (INPUT_REG) begin : gen_input_reg + axi_fifo_flop2 #( + .WIDTH (DATA_W) + ) axi_fifo_flop2_i ( + .clk (clk), + .reset (rst), + .clear (1'b0), + .i_tdata (s_axis_tdata), + .i_tvalid (s_axis_tvalid), + .i_tready (s_axis_tready), + .o_tdata (reg_tdata), + .o_tvalid (reg_tvalid), + .o_tready (reg_tready), + .space (), + .occupied () + ); + end else begin : gen_no_input_reg + assign reg_tdata = s_axis_tdata; + assign reg_tvalid = s_axis_tvalid; + assign s_axis_tready = reg_tready; + end + + + //--------------------------------------------------------------------------- + // Forking Logic + //--------------------------------------------------------------------------- + + assign all_fifo_tready = &fifo_tready; + + // Data transfer occurs when we have valid data on the input and all output + // FIFOs are ready to accept data. Note that having tvalid depend on tready + // is normally not allowed, but the FIFO has been chosen to tolerate this. + assign reg_tready = all_fifo_tready; + assign fifo_tvalid = { NUM_PORTS {all_fifo_tready & reg_tvalid} }; + assign fifo_tdata = { NUM_PORTS {reg_tdata} }; + + + //--------------------------------------------------------------------------- + // Output FIFOs + //--------------------------------------------------------------------------- + + genvar i; + for (i = 0; i < NUM_PORTS; i = i + 1) begin : gen_ports + + // We use axi_fifo_short specifically because it can tolerate tvalid + // de-asserting at any time. This is normally not allowed by AXI-Stream. + axi_fifo_short #( + .WIDTH (DATA_W) + ) axi_fifo_short_i ( + .clk (clk), + .reset (rst), + .clear (1'b0), + .i_tdata (fifo_tdata[i*DATA_W+:DATA_W]), + .i_tvalid (fifo_tvalid[i]), + .i_tready (fifo_tready[i]), + .o_tdata (m_axis_tdata[i*DATA_W+:DATA_W]), + .o_tvalid (m_axis_tvalid[i]), + .o_tready (m_axis_tready[i]), + .space (), + .occupied () + ); + end + +endmodule |