142 lines
4.8 KiB
Verilog
142 lines
4.8 KiB
Verilog
//
|
|
// 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
|
|
);
|
|
localparam 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
|