diff options
Diffstat (limited to 'fpga/usrp3/lib/rfnoc/vector_iir.v')
-rw-r--r-- | fpga/usrp3/lib/rfnoc/vector_iir.v | 187 |
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 |