188 lines
7.3 KiB
Verilog
188 lines
7.3 KiB
Verilog
//
|
|
// Copyright 2018 Ettus Research, A National Instruments Company
|
|
//
|
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
|
//
|
|
// Module: vector_iir
|
|
// Description:
|
|
// This module implements an IIR filter with a variable length delay line.
|
|
// Transfer Function: beta
|
|
// H(z) = ------------------
|
|
// 1 - alpha*z^-delay
|
|
// where:
|
|
// - beta is the feedforward tap
|
|
// - alpha is the feedback tap
|
|
// - delay (aka vector_len) is the feedback tap delay
|
|
//
|
|
// Parameters:
|
|
// - MAX_VECTOR_LEN: Maximum value for delay (vector_len)
|
|
// - IN_W: Input sample width for a real sample which includes the sign bit.
|
|
// The actual input of the module will be 2*IN_W because it handles
|
|
// complex data.
|
|
// - OUT_W: Output sample width for a real sample which includes the sign bit.
|
|
// The actual output of the module will be 2*OUT_W because it handles
|
|
// complex data.
|
|
// - ALPHA_W: Width of the alpha parameter (signed)
|
|
// - BETA_W: Width of the beta parameter (signed)
|
|
// - FEEDBACK_W: Number of bits in the feedback delay line (optimal = 25)
|
|
// - ACCUM_HEADROOM: Number of bits of headroom in the feedback accumulator
|
|
// Signals:
|
|
// - i_* : Input sample stream (AXI-Stream)
|
|
// - o_* : Output sample stream (AXI-Stream)
|
|
// - set_*: Static settings
|
|
//
|
|
|
|
module vector_iir #(
|
|
parameter MAX_VECTOR_LEN = 1023,
|
|
parameter IN_W = 16,
|
|
parameter OUT_W = 16,
|
|
parameter ALPHA_W = 16,
|
|
parameter BETA_W = 16,
|
|
parameter FEEDBACK_W = 25,
|
|
parameter ACCUM_HEADROOM = 4
|
|
)(
|
|
input wire clk,
|
|
input wire reset,
|
|
input wire [$clog2(MAX_VECTOR_LEN+1)-1:0] set_vector_len,
|
|
input wire [BETA_W-1:0] set_beta,
|
|
input wire [ALPHA_W-1:0] set_alpha,
|
|
input wire [IN_W*2-1:0] i_tdata,
|
|
input wire i_tlast,
|
|
input wire i_tvalid,
|
|
output wire i_tready,
|
|
output wire [OUT_W*2-1:0] o_tdata,
|
|
output wire o_tlast,
|
|
output wire o_tvalid,
|
|
input wire o_tready
|
|
);
|
|
|
|
// There are four registers between the input and output
|
|
// - Input pipeline (in_X_reg)
|
|
// - Feedforward product (ff_prod_X_reg)
|
|
// - Feedback sum (fb_sum_X_reg)
|
|
// - Output pipeline (dsp_data_out)
|
|
localparam IN_TO_OUT_LATENCY = 4;
|
|
|
|
// The feedback path has 3 cycles of delay
|
|
// - Feedback sum (fb_sum_X_reg)
|
|
// - variable_delay_line (2 cyc)
|
|
// - Scaled feedback (fb_sum_scaled_X_reg)
|
|
localparam MIN_FB_DELAY = 4;
|
|
|
|
// Pipeline settings for timing
|
|
reg [$clog2(MAX_VECTOR_LEN-MIN_FB_DELAY)-1:0] reg_fb_delay;
|
|
reg signed [ BETA_W-1:0] reg_beta;
|
|
reg signed [ ALPHA_W-1:0] reg_alpha;
|
|
|
|
always @(posedge clk) begin
|
|
reg_fb_delay <= set_vector_len - MIN_FB_DELAY - 1; //Adjust for pipeline delay
|
|
reg_beta <= set_beta;
|
|
reg_alpha <= set_alpha;
|
|
end
|
|
|
|
//-----------------------------------------------------------
|
|
// AXI-Stream wrapper
|
|
//-----------------------------------------------------------
|
|
wire [(IN_W*2)-1:0] dsp_data_in;
|
|
reg [(OUT_W*2)-1:0] dsp_data_out = 0;
|
|
wire [IN_TO_OUT_LATENCY-1:0] chain_en;
|
|
|
|
// We are implementing an N-cycle DSP operation without AXI-Stream handshaking.
|
|
// Use an axis_shift_register and the associated strobes to drive clock enables
|
|
// on the DSP regs to ensure that data/valid/last sync up.
|
|
axis_shift_register #(
|
|
.WIDTH(IN_W*2), .NSPC(1), .LATENCY(IN_TO_OUT_LATENCY),
|
|
.SIDEBAND_DATAPATH(1), .PIPELINE("NONE")
|
|
) axis_shreg_i (
|
|
.clk(clk), .reset(reset),
|
|
.s_axis_tdata(i_tdata), .s_axis_tkeep(1'b1), .s_axis_tlast(i_tlast),
|
|
.s_axis_tvalid(i_tvalid), .s_axis_tready(i_tready),
|
|
.m_axis_tdata(o_tdata), .m_axis_tkeep(), .m_axis_tlast(o_tlast),
|
|
.m_axis_tvalid(o_tvalid), .m_axis_tready(o_tready),
|
|
.stage_stb(chain_en), .stage_eop(),
|
|
.m_sideband_data(dsp_data_in), .m_sideband_keep(),
|
|
.s_sideband_data(dsp_data_out)
|
|
);
|
|
|
|
//-----------------------------------------------------------
|
|
// DSP datapath
|
|
//-----------------------------------------------------------
|
|
localparam FF_PROD_W = IN_W + BETA_W - 1;
|
|
localparam FB_PROD_W = FEEDBACK_W + ALPHA_W - 1;
|
|
|
|
reg signed [IN_W-1:0] in_i_reg = 0, in_q_reg = 0;
|
|
reg signed [FF_PROD_W-1:0] ff_prod_i_reg = 0, ff_prod_q_reg = 0;
|
|
reg signed [FB_PROD_W-1:0] fb_sum_i_reg = 0, fb_sum_q_reg = 0;
|
|
wire signed [FB_PROD_W-1:0] fb_trunc_i, fb_trunc_q;
|
|
wire signed [FEEDBACK_W-1:0] fb_sum_del_i, fb_sum_del_q;
|
|
reg signed [FB_PROD_W-1:0] fb_sum_scaled_i_reg = 0, fb_sum_scaled_q_reg = 0;
|
|
wire signed [OUT_W-1:0] out_i_rnd, out_q_rnd;
|
|
|
|
always @(posedge clk) begin
|
|
if (reset) begin
|
|
{in_i_reg, in_q_reg} <= 0;
|
|
ff_prod_i_reg <= 0;
|
|
ff_prod_q_reg <= 0;
|
|
fb_sum_i_reg <= 0;
|
|
fb_sum_q_reg <= 0;
|
|
fb_sum_scaled_i_reg <= 0;
|
|
fb_sum_scaled_q_reg <= 0;
|
|
dsp_data_out <= 0;
|
|
end else begin
|
|
if (chain_en[0]) begin
|
|
// Input pipeline register
|
|
{in_i_reg, in_q_reg} <= dsp_data_in;
|
|
end
|
|
if (chain_en[1]) begin
|
|
// Feedforward product (x[n] * beta)
|
|
ff_prod_i_reg <= in_i_reg * reg_beta;
|
|
ff_prod_q_reg <= in_q_reg * reg_beta;
|
|
// Compute scaled, delayed feedback (y[n-D] * alpha)
|
|
fb_sum_scaled_i_reg <= fb_sum_del_i * reg_alpha;
|
|
fb_sum_scaled_q_reg <= fb_sum_del_q * reg_alpha;
|
|
end
|
|
if (chain_en[2]) begin
|
|
// Sum of feedforward product and scaled, delayed feedback
|
|
// y[n] = (alpha * y[n-D]) + (x[n] * beta)
|
|
fb_sum_i_reg <= fb_sum_scaled_i_reg + (ff_prod_i_reg <<< (FB_PROD_W - FF_PROD_W - ACCUM_HEADROOM));
|
|
fb_sum_q_reg <= fb_sum_scaled_q_reg + (ff_prod_q_reg <<< (FB_PROD_W - FF_PROD_W - ACCUM_HEADROOM));
|
|
end
|
|
if (chain_en[3]) begin
|
|
// Output pipeline register
|
|
dsp_data_out <= {out_i_rnd, out_q_rnd};
|
|
end
|
|
end
|
|
end
|
|
|
|
// Truncate feedback to the requested FEEDBACK_W
|
|
assign fb_trunc_i = (fb_sum_i_reg >>> (FB_PROD_W - FEEDBACK_W));
|
|
assign fb_trunc_q = (fb_sum_q_reg >>> (FB_PROD_W - FEEDBACK_W));
|
|
|
|
// A variable delay line will be used to store the feedback
|
|
// This delay line stores "reg_fb_delay" worth of samples which
|
|
// allows each element in the vector to have it's own independent state
|
|
variable_delay_line #(
|
|
.WIDTH(FEEDBACK_W * 2), .DEPTH(MAX_VECTOR_LEN - MIN_FB_DELAY),
|
|
.DYNAMIC_DELAY(1), .DEFAULT_DATA(0), .OUT_REG(1)
|
|
) delay_line_inst (
|
|
.clk(clk), .clk_en(chain_en[1]), .reset(reset),
|
|
.stb_in(1'b1),
|
|
.data_in({fb_trunc_i[FEEDBACK_W-1:0], fb_trunc_q[FEEDBACK_W-1:0]}),
|
|
.delay(reg_fb_delay),
|
|
.data_out({fb_sum_del_i, fb_sum_del_q})
|
|
);
|
|
|
|
// Round the accumulator output to produce the final output
|
|
round #(
|
|
.bits_in(FB_PROD_W-ACCUM_HEADROOM), .bits_out(OUT_W)
|
|
) out_round_i_inst (
|
|
.in(fb_sum_i_reg[FB_PROD_W-ACCUM_HEADROOM-1:0]), .out(out_i_rnd), .err()
|
|
);
|
|
round #(
|
|
.bits_in(FB_PROD_W-ACCUM_HEADROOM), .bits_out(OUT_W)
|
|
) out_round_q_inst (
|
|
.in(fb_sum_q_reg[FB_PROD_W-ACCUM_HEADROOM-1:0]), .out(out_q_rnd), .err()
|
|
);
|
|
|
|
endmodule // vector_iir
|