1// 2// Copyright 2018 Ettus Research, A National Instruments Company 3// 4// SPDX-License-Identifier: LGPL-3.0-or-later 5// 6// Module: variable_delay_line 7// Description: 8// This module implements a variable length delay line. It can be used 9// in filter implementation where the delay is either variable and/or 10// longer than a few flip-flops 11// 12// Parameters: 13// - WIDTH: Width of data_in and data_out 14// - DYNAMIC_DELAY: Is the delay variable (configurable at runtime) 15// - DEPTH: The depth of the delay line. Must be greater than 2. 16// The output delay can be between 0 and DEPTH-1. 17// If DYNAMIC_DELAY==0, then this is the static delay 18// - DEFAULT_DATA: Data to output if time post-delay is negative 19// - OUT_REG: Add an output register. This adds a cycle of latency 20// - DEVICE: FPGA device family 21// Signals: 22// - data_in : Input sample value 23// - stb_in : Is input sample valid? 24// - delay : Delay value for output (Must be between 0 and DEPTH-1) 25// - data_out : Output sample value. data_out is updated 1 clock 26// cycle (2 if OUT_REG == 1) after assertion of delay 27// 28 29module variable_delay_line #( 30 parameter WIDTH = 18, 31 parameter DEPTH = 256, 32 parameter DYNAMIC_DELAY = 0, 33 parameter [WIDTH-1:0] DEFAULT_DATA = 0, 34 parameter OUT_REG = 0, 35 parameter DEVICE = "7SERIES" 36) ( 37 input wire clk, 38 input wire clk_en, 39 input wire reset, 40 input wire [WIDTH-1:0] data_in, 41 input wire stb_in, 42 input wire [$clog2(DEPTH)-1:0] delay, 43 output wire [WIDTH-1:0] data_out 44); 45 localparam ADDR_W = $clog2(DEPTH+1); 46 localparam DATA_W = WIDTH; 47 48 //----------------------------------------------------------- 49 // RAM State Machine: FIFO write, random access read 50 //----------------------------------------------------------- 51 wire w_en; 52 wire [DATA_W-1:0] r_data, w_data; 53 wire [ADDR_W-1:0] r_addr; 54 reg [ADDR_W-1:0] w_addr = {ADDR_W{1'b0}}, occupied = {ADDR_W{1'b0}}; 55 reg [1:0] use_default = 2'b11; 56 57 // FIFO write, random access read 58 always @(posedge clk) begin 59 if (reset) begin 60 w_addr <= {ADDR_W{1'b0}}; 61 occupied <= {ADDR_W{1'b0}}; 62 end else if (w_en) begin 63 w_addr <= w_addr + 1'b1; 64 if (occupied != DEPTH) begin 65 occupied <= occupied + 1'b1; 66 end 67 end 68 end 69 70 // Logic to handle negative delays 71 always @(posedge clk) begin 72 if (reset) begin 73 use_default <= 2'b11; 74 end else if (clk_en && (occupied != 0)) begin 75 use_default <= {use_default[0], (r_addr >= occupied ? 1'b1 : 1'b0)}; 76 end 77 end 78 79 assign w_en = stb_in & clk_en; 80 assign w_data = data_in; 81 assign r_addr = (DYNAMIC_DELAY == 0) ? DEPTH : delay; 82 assign data_out = use_default[OUT_REG] ? DEFAULT_DATA : r_data; 83 84 //----------------------------------------------------------- 85 // Delay Line RAM Implementation 86 //----------------------------------------------------------- 87 // Use a delay line implementation based on the depth. 88 // The DEVICE parameter is passed in but SPARTAN6, 89 // 7Series, Ultrascale and Ultrascale+ have the same 90 // MACROs for SRLs so we don't use the param quite yet. 91 92 genvar i; 93 generate 94 if (ADDR_W == 4 || ADDR_W == 5) begin 95 // SRLs don't have an output register to instantiate 96 // that plus the pipeline register manually 97 wire [DATA_W-1:0] r_data_srl; 98 reg [DATA_W-1:0] r_data_shreg[0:1]; 99 always @(posedge clk) begin 100 if (clk_en) 101 {r_data_shreg[1], r_data_shreg[0]} <= {r_data_shreg[0], r_data_srl}; 102 end 103 assign r_data = r_data_shreg[OUT_REG]; 104 105 for (i = 0; i < DATA_W; i = i + 1) begin: bits 106 // Pick SRL based on address width 107 if (ADDR_W == 4) begin 108 SRL16E #( 109 .INIT(16'h0000), .IS_CLK_INVERTED(1'b0) 110 ) srl16e_i ( 111 .CLK(clk), .CE(w_en), 112 .D(w_data[i]), 113 .A0(r_addr[0]),.A1(r_addr[1]),.A2(r_addr[2]),.A3(r_addr[3]), 114 .Q(r_data_srl[i]) 115 ); 116 end else begin 117 SRLC32E #( 118 .INIT(32'h00000000), .IS_CLK_INVERTED(1'b0) 119 ) srlc32e_i ( 120 .CLK(clk), .CE(w_en), 121 .D(w_data[i]), 122 .A(r_addr), 123 .Q(r_data_srl[i]), .Q31() 124 ); 125 end 126 end 127 end else begin 128 // For ADDR_W < 4, the RAM should ideally get 129 // synthesized down to flip-flops. 130 ram_2port #( 131 .DWIDTH (DATA_W), .AWIDTH(ADDR_W), 132 .RW_MODE("NO-CHANGE"), .OUT_REG(OUT_REG) 133 ) ram_i ( 134 .clka (clk), .ena(clk_en), .wea(w_en), 135 .addra(w_addr), .dia(w_data), .doa(), 136 .clkb (clk), .enb(clk_en), .web(1'b0), 137 .addrb(w_addr - r_addr - 1), .dib(), .dob(r_data) 138 ); 139 end 140 endgenerate 141 142endmodule // delay_line