1//
2// Copyright 2016 Ettus Research
3// Copyright 2018 Ettus Research, a National Instruments Company
4//
5// SPDX-License-Identifier: LGPL-3.0-or-later
6//
7// AXI-Stream multiplexer with select line
8//
9
10module axi_mux_select #(
11  parameter WIDTH = 32,
12  parameter PRE_FIFO_SIZE = 0,
13  parameter POST_FIFO_SIZE = 0,
14  parameter SWITCH_ON_LAST = 0, // Wait until tlast is asserted before updating
15  parameter SIZE = 4)
16(
17  input clk, input reset, input clear,
18  input [$clog2(SIZE)-1:0] select,
19  input [SIZE*WIDTH-1:0] i_tdata, input [SIZE-1:0] i_tlast, input [SIZE-1:0] i_tvalid, output [SIZE-1:0] i_tready,
20  output [WIDTH-1:0] o_tdata, output o_tlast, output o_tvalid, input o_tready
21);
22
23  wire [WIDTH*SIZE-1:0] i_tdata_int;
24  wire [WIDTH-1:0] i_tdata_arr[0:SIZE-1];
25  wire [SIZE-1:0] i_tlast_int, i_tvalid_int, i_tready_int;
26
27  wire [WIDTH-1:0] o_tdata_int;
28  wire o_tlast_int, o_tvalid_int, o_tready_int;
29
30  genvar n;
31  generate
32    if (PRE_FIFO_SIZE == 0) begin : gen_no_pre_fifo
33      assign i_tdata_int  = i_tdata;
34      assign i_tlast_int  = i_tlast;
35      assign i_tvalid_int = i_tvalid;
36      assign i_tready     = i_tready_int;
37    end else begin : gen_pre_fifo
38      for (n = 0; n < SIZE; n = n + 1) begin
39        axi_fifo #(.WIDTH(WIDTH+1), .SIZE(PRE_FIFO_SIZE)) axi_fifo (
40          .clk(clk), .reset(reset), .clear(clear),
41          .i_tdata({i_tlast[n],i_tdata[WIDTH*(n+1)-1:WIDTH*n]}), .i_tvalid(i_tvalid[n]), .i_tready(i_tready[n]),
42          .o_tdata({i_tlast_int[n],i_tdata_int[WIDTH*(n+1)-1:WIDTH*n]}), .o_tvalid(i_tvalid_int[n]), .o_tready(i_tready_int[n]),
43          .space(), .occupied());
44      end
45    end
46  endgenerate
47
48  // Make arrays for easier muxing
49  genvar i;
50  generate
51    for (i = 0; i < SIZE; i = i + 1) begin : gen_muxing
52      assign i_tdata_arr[i] = i_tdata_int[WIDTH*(i+1)-1:WIDTH*i];
53    end
54  endgenerate
55
56  // Switch select line either immediately or if we're not in the middle of a
57  // packet.
58  reg [$clog2(SIZE)-1:0] select_hold = 0;
59
60  generate
61    if (SWITCH_ON_LAST) begin : gen_switch_on_last
62      reg  in_packet_reg = 1'b0;
63      wire in_packet;
64      wire end_of_packet;
65      wire enable_switch;
66
67      // Create a signal to indicate if we're in the middle of a packet
68      assign in_packet = in_packet_reg || o_tvalid_int;
69
70      // Create a signal to indicate if this is the last transfer of the packet
71      assign end_of_packet = o_tlast_int & o_tvalid_int & o_tready_int;
72
73      // Create a signal that indicates when it's OK to switch the mux select.
74      // We can switch if we're not in the middle of outputting a packet, or if
75      // we're on the last transfer of a packet.
76      assign enable_switch = !in_packet || end_of_packet;
77
78      always @(posedge clk) begin
79        if (reset | clear) begin
80          select_hold   <= 0;
81          in_packet_reg <= 1'b0;
82        end else begin
83          // Use in_packet_reg to indicate if we're in a packet. But this
84          // register is delayed by a clock cycle, so we need the in_pakcet
85          // signal above to add the first clock cycle of a packet.
86          if (end_of_packet) begin
87            in_packet_reg <= 1'b0;
88          end else if (o_tvalid_int) begin
89            in_packet_reg <= 1'b1;
90          end
91
92          if (enable_switch) begin
93            select_hold <= select;
94          end
95        end
96      end
97    end else begin : gen_no_switch_on_last
98      always @(*) begin
99        select_hold <= select;
100      end
101    end
102  endgenerate
103
104  // Mux
105  assign o_tdata_int  = i_tdata_arr[select_hold];
106  assign o_tlast_int  = i_tlast_int[select_hold];
107  assign o_tvalid_int = i_tvalid_int[select_hold];
108  assign i_tready_int = (1'b1 << select_hold) & {SIZE{o_tready_int}};
109
110  generate
111    if(POST_FIFO_SIZE == 0) begin : gen_no_post_fifo
112      assign o_tdata = o_tdata_int;
113      assign o_tlast = o_tlast_int;
114      assign o_tvalid = o_tvalid_int;
115      assign o_tready_int = o_tready;
116    end else begin : gen_post_fifo
117      axi_fifo #(.WIDTH(WIDTH+1),.SIZE(POST_FIFO_SIZE)) axi_fifo (
118        .clk(clk), .reset(reset), .clear(clear),
119        .i_tdata({o_tlast_int,o_tdata_int}), .i_tvalid(o_tvalid_int), .i_tready(o_tready_int),
120        .o_tdata({o_tlast,o_tdata}), .o_tvalid(o_tvalid), .o_tready(o_tready),
121        .space(), .occupied());
122    end
123  endgenerate
124
125endmodule