aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/lib/rfnoc/vector_iir.v
diff options
context:
space:
mode:
Diffstat (limited to 'fpga/usrp3/lib/rfnoc/vector_iir.v')
-rw-r--r--fpga/usrp3/lib/rfnoc/vector_iir.v187
1 files changed, 187 insertions, 0 deletions
diff --git a/fpga/usrp3/lib/rfnoc/vector_iir.v b/fpga/usrp3/lib/rfnoc/vector_iir.v
new file mode 100644
index 000000000..a875f34fe
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/vector_iir.v
@@ -0,0 +1,187 @@
+//
+// 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 = 1024,
+ 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: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)-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