diff options
Diffstat (limited to 'fpga/usrp3/lib/dsp/variable_delay_line.v')
-rw-r--r-- | fpga/usrp3/lib/dsp/variable_delay_line.v | 144 |
1 files changed, 144 insertions, 0 deletions
diff --git a/fpga/usrp3/lib/dsp/variable_delay_line.v b/fpga/usrp3/lib/dsp/variable_delay_line.v new file mode 100644 index 000000000..ccef6172f --- /dev/null +++ b/fpga/usrp3/lib/dsp/variable_delay_line.v @@ -0,0 +1,144 @@ +// +// Copyright 2018 Ettus Research, A National Instruments Company +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// Module: variable_delay_line +// Description: +// This module implements a variable length delay line. It can be used +// in filter implementation where the delay is either variable and/or +// longer than a few flip-flops +// +// Parameters: +// - WIDTH: Width of data_in and data_out +// - DYNAMIC_DELAY: Is the delay variable (configurable at runtime) +// - DEPTH: The depth of the delay line. Must be greater than 2. +// The output delay can be between 0 and DEPTH-1. +// If DYNAMIC_DELAY==0, then this is the static delay +// - DEFAULT_DATA: Data to output if time post-delay is negative +// - OUT_REG: Add an output register. This adds a cycle of latency +// - DEVICE: FPGA device family +// Signals: +// - data_in : Input sample value +// - stb_in : Is input sample valid? +// - delay : Delay value for output (Must be between 0 and DEPTH-1) +// - data_out : Output sample value. data_out is updated 1 clock +// cycle (2 if OUT_REG == 1) after assertion of delay +// + +module variable_delay_line #( + parameter WIDTH = 18, + parameter DEPTH = 256, + parameter DYNAMIC_DELAY = 0, + parameter [WIDTH-1:0] DEFAULT_DATA = 0, + parameter OUT_REG = 0, + parameter DEVICE = "7SERIES" +) ( + input wire clk, + input wire clk_en, + input wire reset, + input wire [WIDTH-1:0] data_in, + input wire stb_in, + input wire [$clog2(DEPTH)-1:0] delay, + output wire [WIDTH-1:0] data_out +); + //FIXME: Change to localparam when Vivado doesn't freak out + // about the use of clog2. + parameter ADDR_W = $clog2(DEPTH+1); + localparam DATA_W = WIDTH; + + //----------------------------------------------------------- + // RAM State Machine: FIFO write, random access read + //----------------------------------------------------------- + wire w_en; + wire [DATA_W-1:0] r_data, w_data; + wire [ADDR_W-1:0] r_addr; + reg [ADDR_W-1:0] w_addr = {ADDR_W{1'b0}}, occupied = {ADDR_W{1'b0}}; + reg [1:0] use_default = 2'b11; + + // FIFO write, random access read + always @(posedge clk) begin + if (reset) begin + w_addr <= {ADDR_W{1'b0}}; + occupied <= {ADDR_W{1'b0}}; + end else if (w_en) begin + w_addr <= w_addr + 1'b1; + if (occupied != DEPTH) begin + occupied <= occupied + 1'b1; + end + end + end + + // Logic to handle negative delays + always @(posedge clk) begin + if (reset) begin + use_default <= 2'b11; + end else if (clk_en && (occupied != 0)) begin + use_default <= {use_default[0], (r_addr >= occupied ? 1'b1 : 1'b0)}; + end + end + + assign w_en = stb_in & clk_en; + assign w_data = data_in; + assign r_addr = (DYNAMIC_DELAY == 0) ? DEPTH : delay; + assign data_out = use_default[OUT_REG] ? DEFAULT_DATA : r_data; + + //----------------------------------------------------------- + // Delay Line RAM Implementation + //----------------------------------------------------------- + // Use a delay line implementation based on the depth. + // The DEVICE parameter is passed in but SPARTAN6, + // 7Series, Ultrascale and Ultrascale+ have the same + // MACROs for SRLs so we don't use the param quite yet. + + genvar i; + generate + if (ADDR_W == 4 || ADDR_W == 5) begin + // SRLs don't have an output register to instantiate + // that plus the pipeline register manually + wire [DATA_W-1:0] r_data_srl; + reg [DATA_W-1:0] r_data_shreg[0:1]; + always @(posedge clk) begin + if (clk_en) + {r_data_shreg[1], r_data_shreg[0]} <= {r_data_shreg[0], r_data_srl}; + end + assign r_data = r_data_shreg[OUT_REG]; + + for (i = 0; i < DATA_W; i = i + 1) begin: bits + // Pick SRL based on address width + if (ADDR_W == 4) begin + SRL16E #( + .INIT(16'h0000), .IS_CLK_INVERTED(1'b0) + ) srl16e_i ( + .CLK(clk), .CE(w_en), + .D(w_data[i]), + .A0(r_addr[0]),.A1(r_addr[1]),.A2(r_addr[2]),.A3(r_addr[3]), + .Q(r_data_srl[i]) + ); + end else begin + SRLC32E #( + .INIT(32'h00000000), .IS_CLK_INVERTED(1'b0) + ) srlc32e_i ( + .CLK(clk), .CE(w_en), + .D(w_data[i]), + .A(r_addr), + .Q(r_data_srl[i]), .Q31() + ); + end + end + end else begin + // For ADDR_W < 4, the RAM should ideally get + // synthesized down to flip-flops. + ram_2port #( + .DWIDTH (DATA_W), .AWIDTH(ADDR_W), + .RW_MODE("NO-CHANGE"), .OUT_REG(OUT_REG) + ) ram_i ( + .clka (clk), .ena(clk_en), .wea(w_en), + .addra(w_addr), .dia(w_data), .doa(), + .clkb (clk), .enb(clk_en), .web(1'b0), + .addrb(w_addr - r_addr - 1), .dib(), .dob(r_data) + ); + end + endgenerate + +endmodule // delay_line
\ No newline at end of file |