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
|