1//
2// Copyright 2018 Ettus Research, A National Instruments Company
3//
4// SPDX-License-Identifier: LGPL-3.0-or-later
5//
6// Module: axis_shift_register
7// Description:
8//   This module implements a chain of flip-flops in connected
9//   using AXI-Stream. It can be used in the following ways:
10//   * As a AXI-Stream shift register. The tready path is
11//     combinatorial from the output to the input so backpressure
12//     is immediate. The same behavior makes this module non-ideal
13//     to actually break timing critical paths.
14//   * An AXI-Stream wrapper module for a multi-cycle operation
15//     with clock-enables. This can most commonly be used with DSP
16//     operations like filters. Enable the sideband datapath to
17//     let the module handle handshaking while processing samples
18//     outside it.
19//
20// Parameters:
21//   - WIDTH: The bitwidth of a sample on the data bus.
22//   - NSPC: The number of parallel samples per cycle to process. The
23//       total width of the data bus will be WIDTH*NSPC.
24//   - LATENCY: Number of stages in the shift register
25//   - SIDEBAND_DATAPATH: If SIDEBAND_DATAPATH==1 then tdata is managed
26//       outside this module and imported from s_sideband_data.
27//       If SIDEBAND_DATAPATH=0, then tdata is managed internally and
28//       the sideband signals are unused.
29//       Useful when using this module to manage a DSP pipeline where the
30//       data could be changing in each stage.
31//   - GAPLESS: After the shift register has filled up, should gaps be
32//       allowed? If set to 1, then if s_axis_tvalid goes low then the
33//       pipeline will stall and all bits in stage_stb will immediately go low
34//       to ensure all stages in the shift register have valid data.
35//       NOTE: This GAPLESS=1 will not allow the final "LATENCY" samples
36//       to exit the shift register.
37//   - PIPELINE: Which ports to pipeline? {NONE, IN, OUT, INOUT}
38//
39// Signals:
40//   - s_axis_* : Input sample stream (AXI-Stream)
41//   - m_axis_* : Output sample stream (AXI-Stream)
42//   - stage_stb : Transfer strobe for each stage
43//   - stage_eop : Transfer end-of-packet out. bit[i] = stage[i]
44//   - m_sideband_data : Sideband data out for external consumer
45//   - m_sideband_keep : Sideband keep signal out for external consumer
46//   - s_sideband_data : Sideband data in from external producer
47
48module axis_shift_register #(
49  parameter WIDTH             = 32,
50  parameter NSPC              = 1,
51  parameter LATENCY           = 3,
52  parameter SIDEBAND_DATAPATH = 0,
53  parameter GAPLESS           = 0,
54  parameter PIPELINE          = "NONE"
55)(
56  // Clock, reset and settings
57  input  wire                     clk,              // Clock
58  input  wire                     reset,            // Reset
59  // Serial Data In (AXI-Stream)
60  input  wire [(WIDTH*NSPC)-1:0]  s_axis_tdata,     // Input stream tdata
61  input  wire [NSPC-1:0]          s_axis_tkeep,     // Input stream tkeep (used as a sample qualifier)
62  input  wire                     s_axis_tlast,     // Input stream tlast
63  input  wire                     s_axis_tvalid,    // Input stream tvalid
64  output wire                     s_axis_tready,    // Input stream tready
65  // Serial Data Out (AXI-Stream)
66  output wire [(WIDTH*NSPC)-1:0]  m_axis_tdata,     // Output stream tdata
67  output wire [NSPC-1:0]          m_axis_tkeep,     // Output stream tkeep (used as a sample qualifier)
68  output wire                     m_axis_tlast,     // Output stream tlast
69  output wire                     m_axis_tvalid,    // Output stream tvalid
70  input  wire                     m_axis_tready,    // Output stream tready
71  // Signals for the sideband data path
72  output wire [LATENCY-1:0]       stage_stb,        // Transfer strobe out. bit[i] = stage[i]
73  output wire [LATENCY-1:0]       stage_eop,        // Transfer end-of-packet out. bit[i] = stage[i]
74  output wire [(WIDTH*NSPC)-1:0]  m_sideband_data,  // Sideband data out for external consumer
75  output wire [NSPC-1:0]          m_sideband_keep,  // Sideband keep signal out for external consumer
76  input  wire [(WIDTH*NSPC)-1:0]  s_sideband_data   // Sideband data in from external producer
77);
78  // Shift register width depends on whether the datapath is internal
79  localparam SHREG_WIDTH     = SIDEBAND_DATAPATH[0] ? (NSPC + 1) : ((WIDTH*NSPC) + NSPC + 1);
80  localparam SHREG_TLAST_LOC = SHREG_WIDTH-1;
81  localparam SHREG_TKEEP_HI  = SHREG_WIDTH-2;
82  localparam SHREG_TKEEP_LO  = SHREG_WIDTH-NSPC-1;
83
84  //----------------------------------------------
85  // Pipeline Logic
86  // (fifo_flop2 is used because it breaks timing
87  //  path going both ways: valid and ready)
88  //----------------------------------------------
89  wire [(WIDTH*NSPC)-1:0] i_tdata,  o_tdata;
90  wire [NSPC-1:0]         i_tkeep,  o_tkeep;
91  wire                    i_tlast,  o_tlast;
92  wire                    i_tvalid, o_tvalid;
93  wire                    i_tready, o_tready;
94
95  generate
96    // Input pipeline register if requested
97    if (PIPELINE == "IN" || PIPELINE == "INOUT") begin
98      axi_fifo_flop2 #(.WIDTH((WIDTH*NSPC) + NSPC + 1)) in_pipe_i (
99        .clk(clk), .reset(reset), .clear(1'b0),
100        .i_tdata({s_axis_tlast, s_axis_tkeep, s_axis_tdata}),
101        .i_tvalid(s_axis_tvalid), .i_tready(s_axis_tready),
102        .o_tdata({i_tlast, i_tkeep, i_tdata}), .o_tvalid(i_tvalid), .o_tready(i_tready),
103        .space(), .occupied()
104      );
105    end else begin
106      assign {i_tlast, i_tkeep, i_tdata} = {s_axis_tlast, s_axis_tkeep, s_axis_tdata};
107      assign i_tvalid = s_axis_tvalid;
108      assign s_axis_tready = i_tready;
109    end
110
111    // Output pipeline register if requested
112    if (PIPELINE == "OUT" || PIPELINE == "INOUT") begin
113      axi_fifo_flop2 #(.WIDTH((WIDTH*NSPC) + NSPC + 1)) out_pipe_i (
114        .clk(clk), .reset(reset), .clear(1'b0),
115        .i_tdata({o_tlast, o_tkeep, o_tdata}), .i_tvalid(o_tvalid), .i_tready(o_tready),
116        .o_tdata({m_axis_tlast, m_axis_tkeep, m_axis_tdata}),
117        .o_tvalid(m_axis_tvalid), .o_tready(m_axis_tready),
118        .space(), .occupied()
119      );
120    end else begin
121      assign {m_axis_tlast, m_axis_tkeep, m_axis_tdata} = {o_tlast, o_tkeep, o_tdata};
122      assign m_axis_tvalid = o_tvalid;
123      assign o_tready = m_axis_tready;
124    end
125  endgenerate
126
127  assign m_sideband_data = i_tdata;
128  assign m_sideband_keep = i_tkeep;
129
130  //----------------------------------------------
131  // Shift register stages
132  //----------------------------------------------
133  genvar i;
134  generate
135    if (GAPLESS == 0) begin
136      // Individual stage wires
137      wire [SHREG_WIDTH-1:0]  stg_tdata [0:LATENCY];
138      wire                    stg_tvalid[0:LATENCY];
139      wire                    stg_tready[0:LATENCY];
140      // Shift register input
141      assign stg_tdata[0] = SIDEBAND_DATAPATH[0] ? {i_tlast, i_tkeep} : {i_tlast, i_tkeep, i_tdata};
142      assign stg_tvalid[0] = i_tvalid;
143      assign i_tready = stg_tready[0];
144      // Shift register output
145      assign o_tlast = stg_tdata[LATENCY][SHREG_TLAST_LOC];
146      assign o_tkeep = stg_tdata[LATENCY][SHREG_TKEEP_HI:SHREG_TKEEP_LO];
147      assign o_tdata = SIDEBAND_DATAPATH[0] ? s_sideband_data : stg_tdata[LATENCY][(WIDTH*NSPC)-1:0];
148      assign o_tvalid = stg_tvalid[LATENCY];
149      assign stg_tready[LATENCY] = o_tready;
150
151      for (i = 0; i < LATENCY; i=i+1) begin: stages
152        axi_fifo_flop #(.WIDTH(SHREG_WIDTH)) reg_i (
153          .clk(clk), .reset(reset), .clear(1'b0),
154          .i_tdata(stg_tdata[i  ]), .i_tvalid(stg_tvalid[i  ]), .i_tready(stg_tready[i  ]),
155          .o_tdata(stg_tdata[i+1]), .o_tvalid(stg_tvalid[i+1]), .o_tready(stg_tready[i+1]),
156          .occupied(), .space()
157        );
158        assign stage_stb[i] = stg_tvalid[i] & stg_tready[i];
159        assign stage_eop[i] = stage_stb[i] & stg_tdata[i][SHREG_TLAST_LOC];
160      end
161    end else begin // if (GAPLESS == 0)
162      wire [(WIDTH*NSPC)-1:0] o_tdata_fifo;
163      wire [NSPC-1:0]         o_tkeep_fifo;
164      wire                    o_tlast_fifo, o_tvalid_fifo, o_tready_fifo;
165
166      // Shift register to hold valids
167      reg  [LATENCY-1:0]     stage_valid = {LATENCY{1'b0}};
168      // Shift register to hold data/last
169      reg  [SHREG_WIDTH-1:0] stage_shreg[0:LATENCY-1];
170      wire [SHREG_WIDTH-1:0] shreg_input = SIDEBAND_DATAPATH[0] ? {i_tlast, i_tkeep} : {i_tlast, i_tkeep, i_tdata};
171      wire                   shreg_ce = i_tready & i_tvalid;
172
173      assign i_tready      = o_tready_fifo;
174      assign o_tvalid_fifo = stage_valid[LATENCY-1] & shreg_ce;
175      assign o_tlast_fifo  = stage_shreg[LATENCY-1][SHREG_TLAST_LOC];
176      assign o_tkeep_fifo  = stage_shreg[LATENCY-1][SHREG_TKEEP_HI:SHREG_TKEEP_LO];
177      assign o_tdata_fifo  = SIDEBAND_DATAPATH[0] ? s_sideband_data : stage_shreg[LATENCY-1][(WIDTH*NSPC)-1:0];
178
179      for (i = 0; i < LATENCY; i=i+1) begin
180        // Initialize shift register
181        initial begin
182          stage_shreg[i] <= {SHREG_WIDTH{1'b0}};
183        end
184        // Shift register logic
185        always @(posedge clk) begin
186          if (reset) begin
187            stage_shreg[i] <= {SHREG_WIDTH{1'b0}};
188            stage_valid[i] <= 1'b0;
189          end else if (shreg_ce) begin
190            stage_shreg[i] <= (i == 0) ? shreg_input : stage_shreg[i-1];
191            stage_valid[i] <= (i == 0) ? 1'b1        : stage_valid[i-1];
192          end
193        end
194        // Outputs
195        assign stage_stb[i] = ((i == 0) ? 1'b1 : stage_valid[i-1]) & shreg_ce;
196        assign stage_eop[i] = stage_stb[i] & ((i == 0) ? i_tlast : stage_shreg[i-1][SHREG_TLAST_LOC]);
197      end
198
199      // The "gapless" logic violates AXI-Stream by having an o_tready -> o_tvalid dependency,
200      // so we add a FIFO downstream to prevent deadlocks.
201      axi_fifo #(.WIDTH((WIDTH*NSPC) + NSPC + 1), .SIZE($clog2(LATENCY))) out_fifo_i (
202        .clk(clk), .reset(reset), .clear(1'b0),
203        .i_tdata({o_tlast_fifo, o_tkeep_fifo, o_tdata_fifo}), .i_tvalid(o_tvalid_fifo), .i_tready(o_tready_fifo),
204        .o_tdata({o_tlast, o_tkeep, o_tdata}), .o_tvalid(o_tvalid), .o_tready(o_tready),
205        .space(), .occupied()
206      );
207    end
208  endgenerate
209endmodule // axis_shift_register
210