1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
|
//
// Copyright 2023 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 with width N and splits it
// into M AXI-Stream outputs each with N/M bits from the input bus.
//
// 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 cannot be consumed then the flow-control logic will cause the input
// to stall (i.e., s_axis_tready will deassert).
//
// Parameters:
//
// WIDTH : The bit width of tdata for all ports.
// USER_WIDTH : The bit width of tuser 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_bus #(
parameter WIDTH = 32,
parameter USER_WIDTH = 1,
parameter NUM_PORTS = 4,
parameter INPUT_REG = 0
) (
input wire clk,
input wire reset,
// Input AXI-Stream
input wire [ WIDTH*NUM_PORTS-1:0] s_axis_tdata,
input wire [USER_WIDTH*NUM_PORTS-1:0] s_axis_tuser,
input wire s_axis_tlast,
input wire s_axis_tvalid,
output wire s_axis_tready,
// Output AXI-Streams
output wire [ WIDTH*NUM_PORTS-1:0] m_axis_tdata,
output wire [USER_WIDTH*NUM_PORTS-1:0] m_axis_tuser,
output wire [ NUM_PORTS-1:0] m_axis_tlast,
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 [ WIDTH*NUM_PORTS-1:0] reg_tdata;
wire [USER_WIDTH*NUM_PORTS-1:0] reg_tuser;
wire reg_tlast;
wire reg_tvalid;
wire reg_tready;
// Input to the Output FIFO stage
wire [ WIDTH*NUM_PORTS-1:0] fifo_tdata;
wire [USER_WIDTH*NUM_PORTS-1:0] fifo_tuser;
wire [ NUM_PORTS-1:0] fifo_tlast;
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 (WIDTH+USER_WIDTH+1)
) axi_fifo_flop2_i (
.clk (clk),
.reset (reset),
.clear (1'b0),
.i_tdata ({s_axis_tdata,s_axis_tuser,s_axis_tlast}),
.i_tvalid (s_axis_tvalid),
.i_tready (s_axis_tready),
.o_tdata ({reg_tdata,reg_tuser,reg_tlast}),
.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_tuser = s_axis_tuser;
assign reg_tlast = s_axis_tlast;
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 = reg_tdata;
assign fifo_tuser = reg_tuser;
assign fifo_tlast = { NUM_PORTS {reg_tlast} };
//---------------------------------------------------------------------------
// 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 (WIDTH+USER_WIDTH+1)
) axi_fifo_short_i (
.clk (clk),
.reset (reset),
.clear (1'b0),
.i_tdata ({fifo_tdata[i*WIDTH+:WIDTH],fifo_tuser[i*USER_WIDTH+:USER_WIDTH],fifo_tlast[i]}),
.i_tvalid (fifo_tvalid[i]),
.i_tready (fifo_tready[i]),
.o_tdata ({m_axis_tdata[i*WIDTH+:WIDTH],m_axis_tuser[i*USER_WIDTH+:USER_WIDTH],m_axis_tlast[i]}),
.o_tvalid (m_axis_tvalid[i]),
.o_tready (m_axis_tready[i]),
.space (),
.occupied ()
);
end
endmodule
|