1//
2// Copyright 2019 Ettus Research, A National Instruments Company
3//
4// SPDX-License-Identifier: LGPL-3.0-or-later
5//
6// Module: axis_data_swap
7// Description:
8//  A generic data swapper module for AXI-Stream. The contents of
9//  tdata are swapped based on the tswap signal. For each bit 'i'
10//  in tswap, adjacent words of width 2^i are swapped if tswap[i]
11//  is high. For example, if tswap[3] = 1, then each byte in tdata
12//  will be swapped with its adjacent neighbor. It is permissible
13//  for tswap to change for each transfer in an AXIS packet.
14//  Swapping can also be configured to be static (zero logic) by
15//  setting DYNAMIC = 0. To reduce area, certain swap stages can
16//  even be disabled. For example, if STAGES_EN[2:0] is set to 0
17//  then the lowest granularity for swaps will be a byte.
18//
19// Parameters:
20//   - DATA_W: Width of the tdata bus in bits
21//   - USER_W: Width of the tuser bus in bits
22//   - STAGES_EN: Which swap stages are enabled.
23//   - DYNAMIC: Dynamic swapping enabled (use tswap)
24//
25// Signals:
26//   - s_axis_*: The input AXI stream
27//   - m_axis_*: The output AXI stream
28//
29
30module axis_data_swap #(
31  parameter integer              DATA_W    = 256,
32  parameter integer              USER_W    = 1,
33  parameter [$clog2(DATA_W)-1:0] STAGES_EN = 'hFFFFFFFF, //@HACK: Vivado does not allow $clog2 in value of this expr
34  parameter [0:0]                DYNAMIC   = 1
35)(
36  // Clock and Reset
37  input  wire                      clk,
38  input  wire                      rst,
39  // Input AXIS
40  input  wire [DATA_W-1:0]         s_axis_tdata,
41  input  wire [$clog2(DATA_W)-2:0] s_axis_tswap,
42  input  wire [USER_W-1:0]         s_axis_tuser,
43  input  wire                      s_axis_tlast,
44  input  wire                      s_axis_tvalid,
45  output wire                      s_axis_tready,
46  // Output AXIS
47  output wire [DATA_W-1:0]         m_axis_tdata,
48  output wire [USER_W-1:0]         m_axis_tuser,
49  output wire                      m_axis_tlast,
50  output wire                      m_axis_tvalid,
51  input  wire                      m_axis_tready
52);
53
54  parameter SWAP_STAGES = $clog2(DATA_W);
55  parameter SWAP_W      = $clog2(DATA_W)-1;
56  genvar s, w;
57
58  wire [DATA_W-1:0] stg_tdata [0:SWAP_STAGES], stg_tdata_swp[0:SWAP_STAGES], stg_tdata_res[0:SWAP_STAGES];
59  wire [SWAP_W-1:0] stg_tswap [0:SWAP_STAGES];
60  wire [USER_W-1:0] stg_tuser [0:SWAP_STAGES];
61  wire              stg_tlast [0:SWAP_STAGES];
62  wire              stg_tvalid[0:SWAP_STAGES];
63  wire              stg_tready[0:SWAP_STAGES];
64
65  // Connect input and output to stage wires
66  generate
67    assign stg_tdata [0] = s_axis_tdata;
68    assign stg_tswap [0] = s_axis_tswap;
69    assign stg_tuser [0] = s_axis_tuser;
70    assign stg_tlast [0] = s_axis_tlast;
71    assign stg_tvalid[0] = s_axis_tvalid;
72    assign s_axis_tready = stg_tready[0];
73
74    assign m_axis_tdata            = stg_tdata [SWAP_STAGES];
75    assign m_axis_tuser            = stg_tuser [SWAP_STAGES];
76    assign m_axis_tlast            = stg_tlast [SWAP_STAGES];
77    assign m_axis_tvalid           = stg_tvalid[SWAP_STAGES];
78    assign stg_tready[SWAP_STAGES] = m_axis_tready;
79  endgenerate
80
81  // Instantiate AXIS flip-flops for each stage
82  generate
83    for (s = 0; s < SWAP_STAGES; s=s+1) begin
84      if (STAGES_EN[SWAP_STAGES-s-1]) begin
85        // Swap Logic
86        for (w = 0; w < (1<<s); w=w+1) begin
87          assign stg_tdata_swp[s][(w*(DATA_W/(1<<s)))+:(DATA_W/(1<<s))] =
88            stg_tdata[s][(((1<<s)-w-1)*(DATA_W/(1<<s)))+:(DATA_W/(1<<s))];
89        end
90        if (DYNAMIC) begin
91          // Honor tswap in DYNAMIC mode.
92          // Also add a flip_flop to break the long start-to-end critical path
93          assign stg_tdata_res[s] = (s > 0 && stg_tswap[s][SWAP_W-s]) ?
94            stg_tdata_swp[s] : stg_tdata[s];
95          // Flip-flop
96          axi_fifo_flop #(.WIDTH(DATA_W+SWAP_W+USER_W+1)) reg_i (
97            .clk(clk), .reset(rst), .clear(1'b0),
98            .i_tdata({stg_tlast[s], stg_tuser[s], stg_tswap[s], stg_tdata_res[s]}),
99            .i_tvalid(stg_tvalid[s]), .i_tready(stg_tready[s]),
100            .o_tdata({stg_tlast[s+1], stg_tuser[s+1], stg_tswap[s+1], stg_tdata[s+1]}),
101            .o_tvalid(stg_tvalid[s+1]), .o_tready(stg_tready[s+1]),
102            .occupied(), .space()
103          );
104        end else begin
105          // Static swapping logic
106          assign stg_tdata [s+1] = stg_tdata_swp[s];
107          assign stg_tswap [s+1] = stg_tswap    [s];
108          assign stg_tuser [s+1] = stg_tuser    [s];
109          assign stg_tlast [s+1] = stg_tlast    [s];
110          assign stg_tvalid[s+1] = stg_tvalid   [s];
111          assign stg_tready[s]   = stg_tready   [s+1];
112        end
113      end else begin
114        // Skip this stage
115        assign stg_tdata [s+1] = stg_tdata [s];
116        assign stg_tswap [s+1] = stg_tswap [s];
117        assign stg_tuser [s+1] = stg_tuser [s];
118        assign stg_tlast [s+1] = stg_tlast [s];
119        assign stg_tvalid[s+1] = stg_tvalid[s];
120        assign stg_tready[s]   = stg_tready[s+1];
121      end
122    end
123  endgenerate
124
125endmodule // axis_data_swap
126