1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 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
|