Delays a pipeline with variable shift-registers to adjust the latency
from input to output. The latency, in clock cycles, from input to output is
selected by the tap_number control input, and has a maximum of
PIPE_DEPTH cycles.
Each cycle shift_data is high, the pipeline shifts one word from
input_data towards output_data. Pulse tap_number_load to set a new
tap_number. clear sets all registers and the tap_number to zero.
NOTE: PIPE_DEPTH must be 16 or 32 to match the underlying AMD/Xilinx
FPGA shift-register LUTs (SRLs). This should be trivial to port to other
FPGA families.
The tap_number is zero-indexed, so a tap_number of 0 selects the output
of the first pipeline stage, and PIPE_DEPTH-1 selects the output of the
last pipeline stage. It is not possible to select the input directly.
Changing the tap_number immediately changes the selected tap, and begins
to output whatever data is at that point in the pipeline. Thus, you may
skip data or (re)read old data.
`default_nettype none
module Register_Pipeline_Variable
#(
parameter WORD_WIDTH = 0,
parameter PIPE_DEPTH = 0, // 16 or 32 only
// Do not set at instantiation, except in Vivado IPI
parameter ADDR_WIDTH = clog2(PIPE_DEPTH)
)
(
input wire clock,
input wire clear,
input wire tap_number_load,
input wire [ADDR_WIDTH-1:0] tap_number,
input wire shift_data,
input wire [WORD_WIDTH-1:0] input_data,
output wire [WORD_WIDTH-1:0] output_data
);
`include "clog2_function.vh"
localparam ADDR_ZERO = {ADDR_WIDTH{1'b0}};
First, store the tap selection address.
wire [ADDR_WIDTH-1:0] tap_number_current;
Register
#(
.WORD_WIDTH (ADDR_WIDTH),
.RESET_VALUE (ADDR_ZERO)
)
tap_number_storage
(
.clock (clock),
.clock_enable (tap_number_load),
.clear (clear),
.data_in (tap_number),
.data_out (tap_number_current)
);
Then instantiate SRL dynamic shift registers.
We use the Xilinx SRLs because implementing this as a Register Pipeline and a Multiplexer consumes a very large amount of area for what it does.
generate
genvar i;
for (i = 0; i < WORD_WIDTH; i=i+1) begin: per_bit
if (PIPE_DEPTH == 32) begin: per_SRL32
SRLC32E
#(
.INIT(32'h00000000) // Initial Value of Shift Register
)
data_pipeline
(
.Q (output_data [i]), // SRL data output
// verilator lint_off PINCONNECTEMPTY
.Q31 (), // SRL cascade output pin
// verilator lint_on PINCONNECTEMPTY
.A (tap_number_current), // 5-bit shift depth select input
.CE (shift_data), // Clock enable input
.CLK (clock), // Clock input
.D (input_data [i]) // SRL data input
);
end
else if (PIPE_DEPTH == 16) begin: per_SRL16
SRL16E
#(
.INIT(16'h0000) // Initial Value of Shift Register
)
data_pipeline
(
.Q (output_data [i]), // SRL data output
.A0 (tap_number_current [0]), // Select[0] input
.A1 (tap_number_current [1]), // Select[1] input
.A2 (tap_number_current [2]), // Select[2] input
.A3 (tap_number_current [3]), // Select[3] input
.CE (shift_data), // Clock enable input
.CLK (clock), // Clock input
.D (input_data [i]) // SRL data input
);
end
end
endgenerate
endmodule