module axis_ingress_vc_buff #(
  parameter WIDTH     = 64,         // Width of the datapath
  parameter NUM_VCS   = 2,          // Number of virtual channels
  parameter SIZE      = 5,          // Virtual channel buffer size
  parameter ROUTING   = "WORMHOLE", // Routing (switching) method {WORMHOLE, CUT-THROUGH}
  parameter DEST_W    = (NUM_VCS > 1) ? $clog2(NUM_VCS) : 1 // PRIVATE
) (
  input  wire               clk,
  input  wire               reset,
  input  wire [WIDTH-1:0]   s_axis_tdata,
  input  wire [DEST_W-1:0]  s_axis_tdest,
  input  wire               s_axis_tlast,
  input  wire               s_axis_tvalid,
  output wire               s_axis_tready,
  output wire [WIDTH-1:0]   m_axis_tdata,
  output wire               m_axis_tlast,
  output wire               m_axis_tvalid,
  input  wire               m_axis_tready
);

  generate if (NUM_VCS > 1) begin
    //----------------------------------------------------
    // Multiple virtual channels
    //----------------------------------------------------

    wire [(WIDTH*NUM_VCS)-1:0] bufin_tdata , bufout_tdata ;
    wire [NUM_VCS-1:0]         bufin_tlast , bufout_tlast ;
    wire [NUM_VCS-1:0]         bufin_tvalid, bufout_tvalid;
    wire [NUM_VCS-1:0]         bufin_tready, bufout_tready;
  
    axi_demux #(
      .WIDTH(WIDTH), .SIZE(NUM_VCS),
      .PRE_FIFO_SIZE(0 /* must be 0 */), .POST_FIFO_SIZE(0)
    ) vc_demux_i (
      .clk      (clk),
      .reset    (reset),
      .clear    (1'b0),
      .header   (/* unused */),
      .dest     (s_axis_tdest ),
      .i_tdata  (s_axis_tdata ),
      .i_tlast  (s_axis_tlast ),
      .i_tvalid (s_axis_tvalid),
      .i_tready (s_axis_tready),
      .o_tdata  (bufin_tdata),
      .o_tlast  (bufin_tlast),
      .o_tvalid (bufin_tvalid),
      .o_tready (bufin_tready)
    );
  
    genvar vc;
    for (vc = 0; vc < NUM_VCS; vc = vc + 1) begin
      if (ROUTING == "WORMHOLE") begin
        axi_fifo #(
          .WIDTH(WIDTH+1), .SIZE(SIZE)
        ) buf_i (
          .clk      (clk), 
          .reset    (reset), 
          .clear    (1'b0),
          .i_tdata  ({bufin_tlast[vc], bufin_tdata  [(vc*WIDTH)+:WIDTH]}),
          .i_tvalid (bufin_tvalid [vc]),
          .i_tready (bufin_tready [vc]),
          .o_tdata  ({bufout_tlast[vc], bufout_tdata [(vc*WIDTH)+:WIDTH]}),
          .o_tvalid (bufout_tvalid[vc]),
          .o_tready (bufout_tready[vc]),
          .space    (),
          .occupied ()
        );
      end else begin
        axi_packet_gate #(
          .WIDTH(WIDTH), .SIZE(SIZE)
        ) buf_i (
          .clk      (clk), 
          .reset    (reset), 
          .clear    (1'b0),
          .i_tdata  (bufin_tdata[(vc*WIDTH)+:WIDTH]),
          .i_tlast  (bufin_tlast[vc]),
          .i_tvalid (bufin_tvalid[vc]),
          .i_tready (bufin_tready[vc]),
          .i_terror (1'b0),
          .o_tdata  (bufout_tdata[(vc*WIDTH)+:WIDTH]),
          .o_tlast  (bufout_tlast[vc]),
          .o_tvalid (bufout_tvalid[vc]),
          .o_tready (bufout_tready[vc])
        );
      end
    end
  
    axi_mux #(
      .WIDTH(WIDTH), .SIZE(NUM_VCS),
      .PRE_FIFO_SIZE(0), .POST_FIFO_SIZE(1)
    ) vc_mux_i (
      .clk      (clk),
      .reset    (reset),
      .clear    (1'b0),
      .i_tdata  (bufout_tdata ),
      .i_tlast  (bufout_tlast ),
      .i_tvalid (bufout_tvalid),
      .i_tready (bufout_tready),
      .o_tdata  (m_axis_tdata ),
      .o_tlast  (m_axis_tlast ),
      .o_tvalid (m_axis_tvalid),
      .o_tready (m_axis_tready)
    );

  end else begin
    //----------------------------------------------------
    // Single virtual channel
    //----------------------------------------------------
    wire [WIDTH-1:0] pipe_tdata;
    wire             pipe_tlast;
    wire             pipe_tvalid;
    wire             pipe_tready;

    if (ROUTING == "WORMHOLE") begin
      axi_fifo #(
        .WIDTH(WIDTH+1), .SIZE(SIZE)
      ) buf_i (
        .clk      (clk), 
        .reset    (reset), 
        .clear    (1'b0),
        .i_tdata  ({s_axis_tlast, s_axis_tdata}),
        .i_tvalid (s_axis_tvalid ),
        .i_tready (s_axis_tready ),
        .o_tdata  ({pipe_tlast, pipe_tdata}),
        .o_tvalid (pipe_tvalid),
        .o_tready (pipe_tready),
        .space    (),
        .occupied ()
      );
    end else begin
      axi_packet_gate #(
        .WIDTH(WIDTH), .SIZE(SIZE)
      ) buf_i (
        .clk      (clk), 
        .reset    (reset), 
        .clear    (1'b0),
        .i_tdata  (s_axis_tdata),
        .i_tlast  (s_axis_tlast),
        .i_tvalid (s_axis_tvalid),
        .i_tready (s_axis_tready),
        .i_terror (1'b0),
        .o_tdata  (pipe_tdata),
        .o_tlast  (pipe_tlast),
        .o_tvalid (pipe_tvalid),
        .o_tready (pipe_tready)
      );
    end

    axi_fifo #(
      .WIDTH(WIDTH+1), .SIZE(1)
    ) buf_i (
      .clk      (clk), 
      .reset    (reset), 
      .clear    (1'b0),
      .i_tdata  ({pipe_tlast, pipe_tdata}),
      .i_tvalid (pipe_tvalid ),
      .i_tready (pipe_tready ),
      .o_tdata  ({m_axis_tlast, m_axis_tdata}),
      .o_tvalid (m_axis_tvalid),
      .o_tready (m_axis_tready),
      .space    (),
      .occupied ()
    );

  end endgenerate

endmodule