aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/lib/axi/axis_shift_register.v
diff options
context:
space:
mode:
Diffstat (limited to 'fpga/usrp3/lib/axi/axis_shift_register.v')
-rw-r--r--fpga/usrp3/lib/axi/axis_shift_register.v209
1 files changed, 209 insertions, 0 deletions
diff --git a/fpga/usrp3/lib/axi/axis_shift_register.v b/fpga/usrp3/lib/axi/axis_shift_register.v
new file mode 100644
index 000000000..4b3c9f4de
--- /dev/null
+++ b/fpga/usrp3/lib/axi/axis_shift_register.v
@@ -0,0 +1,209 @@
+//
+// Copyright 2018 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: axis_shift_register
+// Description:
+// This module implements a chain of flip-flops in connected
+// using AXI-Stream. It can be used in the following ways:
+// * As a AXI-Stream shift register. The tready path is
+// combinatorial from the output to the input so backpressure
+// is immediate. The same behavior makes this module non-ideal
+// to actually break timing critical paths.
+// * An AXI-Stream wrapper module for a multi-cycle operation
+// with clock-enables. This can most commonly be used with DSP
+// operations like filters. Enable the sideband datapath to
+// let the module handle handshaking while processing samples
+// outside it.
+//
+// Parameters:
+// - WIDTH: The bitwidth of a sample on the data bus.
+// - NSPC: The number of parallel samples per cycle to process. The
+// total width of the data bus will be WIDTH*NSPC.
+// - LATENCY: Number of stages in the shift register
+// - SIDEBAND_DATAPATH: If SIDEBAND_DATAPATH==1 then tdata is managed
+// outside this module and imported from s_sideband_data.
+// If SIDEBAND_DATAPATH=0, then tdata is managed internally and
+// the sideband signals are unused.
+// Useful when using this module to manage a DSP pipeline where the
+// data could be changing in each stage.
+// - GAPLESS: After the shift register has filled up, should gaps be
+// allowed? If set to 1, then if s_axis_tvalid goes low then the
+// pipeline will stall and all bits in stage_stb will immediately go low
+// to ensure all stages in the shift register have valid data.
+// NOTE: This GAPLESS=1 will not allow the final "LATENCY" samples
+// to exit the shift register.
+// - PIPELINE: Which ports to pipeline? {NONE, IN, OUT, INOUT}
+//
+// Signals:
+// - s_axis_* : Input sample stream (AXI-Stream)
+// - m_axis_* : Output sample stream (AXI-Stream)
+// - stage_stb : Transfer strobe for each stage
+// - stage_eop : Transfer end-of-packet out. bit[i] = stage[i]
+// - m_sideband_data : Sideband data out for external consumer
+// - m_sideband_keep : Sideband keep signal out for external consumer
+// - s_sideband_data : Sideband data in from external producer
+
+module axis_shift_register #(
+ parameter WIDTH = 32,
+ parameter NSPC = 1,
+ parameter LATENCY = 3,
+ parameter SIDEBAND_DATAPATH = 0,
+ parameter GAPLESS = 0,
+ parameter PIPELINE = "NONE"
+)(
+ // Clock, reset and settings
+ input wire clk, // Clock
+ input wire reset, // Reset
+ // Serial Data In (AXI-Stream)
+ input wire [(WIDTH*NSPC)-1:0] s_axis_tdata, // Input stream tdata
+ input wire [NSPC-1:0] s_axis_tkeep, // Input stream tkeep (used as a sample qualifier)
+ input wire s_axis_tlast, // Input stream tlast
+ input wire s_axis_tvalid, // Input stream tvalid
+ output wire s_axis_tready, // Input stream tready
+ // Serial Data Out (AXI-Stream)
+ output wire [(WIDTH*NSPC)-1:0] m_axis_tdata, // Output stream tdata
+ output wire [NSPC-1:0] m_axis_tkeep, // Output stream tkeep (used as a sample qualifier)
+ output wire m_axis_tlast, // Output stream tlast
+ output wire m_axis_tvalid, // Output stream tvalid
+ input wire m_axis_tready, // Output stream tready
+ // Signals for the sideband data path
+ output wire [LATENCY-1:0] stage_stb, // Transfer strobe out. bit[i] = stage[i]
+ output wire [LATENCY-1:0] stage_eop, // Transfer end-of-packet out. bit[i] = stage[i]
+ output wire [(WIDTH*NSPC)-1:0] m_sideband_data, // Sideband data out for external consumer
+ output wire [NSPC-1:0] m_sideband_keep, // Sideband keep signal out for external consumer
+ input wire [(WIDTH*NSPC)-1:0] s_sideband_data // Sideband data in from external producer
+);
+ // Shift register width depends on whether the datapath is internal
+ localparam SHREG_WIDTH = SIDEBAND_DATAPATH[0] ? (NSPC + 1) : ((WIDTH*NSPC) + NSPC + 1);
+ localparam SHREG_TLAST_LOC = SHREG_WIDTH-1;
+ localparam SHREG_TKEEP_HI = SHREG_WIDTH-2;
+ localparam SHREG_TKEEP_LO = SHREG_WIDTH-NSPC-1;
+
+ //----------------------------------------------
+ // Pipeline Logic
+ // (fifo_flop2 is used because it breaks timing
+ // path going both ways: valid and ready)
+ //----------------------------------------------
+ wire [(WIDTH*NSPC)-1:0] i_tdata, o_tdata;
+ wire [NSPC-1:0] i_tkeep, o_tkeep;
+ wire i_tlast, o_tlast;
+ wire i_tvalid, o_tvalid;
+ wire i_tready, o_tready;
+
+ generate
+ // Input pipeline register if requested
+ if (PIPELINE == "IN" || PIPELINE == "INOUT") begin
+ axi_fifo_flop2 #(.WIDTH((WIDTH*NSPC) + NSPC + 1)) in_pipe_i (
+ .clk(clk), .reset(reset), .clear(1'b0),
+ .i_tdata({s_axis_tlast, s_axis_tkeep, s_axis_tdata}),
+ .i_tvalid(s_axis_tvalid), .i_tready(s_axis_tready),
+ .o_tdata({i_tlast, i_tkeep, i_tdata}), .o_tvalid(i_tvalid), .o_tready(i_tready),
+ .space(), .occupied()
+ );
+ end else begin
+ assign {i_tlast, i_tkeep, i_tdata} = {s_axis_tlast, s_axis_tkeep, s_axis_tdata};
+ assign i_tvalid = s_axis_tvalid;
+ assign s_axis_tready = i_tready;
+ end
+
+ // Output pipeline register if requested
+ if (PIPELINE == "OUT" || PIPELINE == "INOUT") begin
+ axi_fifo_flop2 #(.WIDTH((WIDTH*NSPC) + NSPC + 1)) out_pipe_i (
+ .clk(clk), .reset(reset), .clear(1'b0),
+ .i_tdata({o_tlast, o_tkeep, o_tdata}), .i_tvalid(o_tvalid), .i_tready(o_tready),
+ .o_tdata({m_axis_tlast, m_axis_tkeep, m_axis_tdata}),
+ .o_tvalid(m_axis_tvalid), .o_tready(m_axis_tready),
+ .space(), .occupied()
+ );
+ end else begin
+ assign {m_axis_tlast, m_axis_tkeep, m_axis_tdata} = {o_tlast, o_tkeep, o_tdata};
+ assign m_axis_tvalid = o_tvalid;
+ assign o_tready = m_axis_tready;
+ end
+ endgenerate
+
+ assign m_sideband_data = i_tdata;
+ assign m_sideband_keep = i_tkeep;
+
+ //----------------------------------------------
+ // Shift register stages
+ //----------------------------------------------
+ genvar i;
+ generate
+ if (GAPLESS == 0) begin
+ // Individual stage wires
+ wire [SHREG_WIDTH-1:0] stg_tdata [0:LATENCY];
+ wire stg_tvalid[0:LATENCY];
+ wire stg_tready[0:LATENCY];
+ // Shift register input
+ assign stg_tdata[0] = SIDEBAND_DATAPATH[0] ? {i_tlast, i_tkeep} : {i_tlast, i_tkeep, i_tdata};
+ assign stg_tvalid[0] = i_tvalid;
+ assign i_tready = stg_tready[0];
+ // Shift register output
+ assign o_tlast = stg_tdata[LATENCY][SHREG_TLAST_LOC];
+ assign o_tkeep = stg_tdata[LATENCY][SHREG_TKEEP_HI:SHREG_TKEEP_LO];
+ assign o_tdata = SIDEBAND_DATAPATH[0] ? s_sideband_data : stg_tdata[LATENCY][(WIDTH*NSPC)-1:0];
+ assign o_tvalid = stg_tvalid[LATENCY];
+ assign stg_tready[LATENCY] = o_tready;
+
+ for (i = 0; i < LATENCY; i=i+1) begin: stages
+ axi_fifo_flop #(.WIDTH(SHREG_WIDTH)) reg_i (
+ .clk(clk), .reset(reset), .clear(1'b0),
+ .i_tdata(stg_tdata[i ]), .i_tvalid(stg_tvalid[i ]), .i_tready(stg_tready[i ]),
+ .o_tdata(stg_tdata[i+1]), .o_tvalid(stg_tvalid[i+1]), .o_tready(stg_tready[i+1]),
+ .occupied(), .space()
+ );
+ assign stage_stb[i] = stg_tvalid[i] & stg_tready[i];
+ assign stage_eop[i] = stage_stb[i] & stg_tdata[i][SHREG_TLAST_LOC];
+ end
+ end else begin // if (GAPLESS == 0)
+ wire [(WIDTH*NSPC)-1:0] o_tdata_fifo;
+ wire [NSPC-1:0] o_tkeep_fifo;
+ wire o_tlast_fifo, o_tvalid_fifo, o_tready_fifo;
+
+ // Shift register to hold valids
+ reg [LATENCY-1:0] stage_valid = {LATENCY{1'b0}};
+ // Shift register to hold data/last
+ reg [SHREG_WIDTH-1:0] stage_shreg[0:LATENCY-1];
+ wire [SHREG_WIDTH-1:0] shreg_input = SIDEBAND_DATAPATH[0] ? {i_tlast, i_tkeep} : {i_tlast, i_tkeep, i_tdata};
+ wire shreg_ce = i_tready & i_tvalid;
+
+ assign i_tready = o_tready_fifo;
+ assign o_tvalid_fifo = stage_valid[LATENCY-1] & shreg_ce;
+ assign o_tlast_fifo = stage_shreg[LATENCY-1][SHREG_TLAST_LOC];
+ assign o_tkeep_fifo = stage_shreg[LATENCY-1][SHREG_TKEEP_HI:SHREG_TKEEP_LO];
+ assign o_tdata_fifo = SIDEBAND_DATAPATH[0] ? s_sideband_data : stage_shreg[LATENCY-1][(WIDTH*NSPC)-1:0];
+
+ for (i = 0; i < LATENCY; i=i+1) begin
+ // Initialize shift register
+ initial begin
+ stage_shreg[i] <= {SHREG_WIDTH{1'b0}};
+ end
+ // Shift register logic
+ always @(posedge clk) begin
+ if (reset) begin
+ stage_shreg[i] <= {SHREG_WIDTH{1'b0}};
+ stage_valid[i] <= 1'b0;
+ end else if (shreg_ce) begin
+ stage_shreg[i] <= (i == 0) ? shreg_input : stage_shreg[i-1];
+ stage_valid[i] <= (i == 0) ? 1'b1 : stage_valid[i-1];
+ end
+ end
+ // Outputs
+ assign stage_stb[i] = ((i == 0) ? 1'b1 : stage_valid[i-1]) & shreg_ce;
+ assign stage_eop[i] = stage_stb[i] & ((i == 0) ? i_tlast : stage_shreg[i-1][SHREG_TLAST_LOC]);
+ end
+
+ // The "gapless" logic violates AXI-Stream by having an o_tready -> o_tvalid dependency,
+ // so we add a FIFO downstream to prevent deadlocks.
+ axi_fifo #(.WIDTH((WIDTH*NSPC) + NSPC + 1), .SIZE($clog2(LATENCY))) out_fifo_i (
+ .clk(clk), .reset(reset), .clear(1'b0),
+ .i_tdata({o_tlast_fifo, o_tkeep_fifo, o_tdata_fifo}), .i_tvalid(o_tvalid_fifo), .i_tready(o_tready_fifo),
+ .o_tdata({o_tlast, o_tkeep, o_tdata}), .o_tvalid(o_tvalid), .o_tready(o_tready),
+ .space(), .occupied()
+ );
+ end
+ endgenerate
+endmodule // axis_shift_register