diff options
Diffstat (limited to 'fpga/usrp3/lib/axi/axis_shift_register.v')
| -rw-r--r-- | fpga/usrp3/lib/axi/axis_shift_register.v | 209 |
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 |
