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