FPGA 23 - DSP FIR Lowpass Filter with Verilog
ฝัง
- เผยแพร่เมื่อ 3 ต.ค. 2024
- In this episode, we're building a 9-tap finite impulse response (FIR) lowpass filter in Verilog that has a cutoff frequency at ~10MHz with a 100MHz sampling clock.
In order to test this FIR lowpass filter, we're also building a testbench that synthesize two sine waves, one at 2MHz and the other at 30MHz. These two sine waves are added together and the resulting noisy signal is resampled at 100MHz before feeding the FIR lowpass filter.
As you will see, the FIR lowpass filter would attenuate the 30MHz component of the noisy signal and leave the 2MHz component untouched.
#fpga #vivado #verilog #xilinx #dsp #impulseresponse #simulation
Recommended prerequisite:
FPGA 18 - Xilinx Verilog Cordic Sine/Cosine generator
• FPGA 18 - AMD Xilinx V...
FPGA 7 - Verilog Vivado two's complement fixed-point arithmetic
• FPGA 7 - Verilog Vivad...
Thank You
module fir_tb();
localparam CORDIC_CLK_PERIOD = 1250; // Clock period for the CORDIC at 800 Hz
localparam FIR_CLK_PERIOD = 1250; // Clock period for the FIR filter at 800 Hz
localparam signed [15:0] PI_POS = 16'h6488; // Positive PI constant
localparam signed [15:0] PI_NEG = 16'h9878; // Negative PI constant
localparam PHASE_INC_150HZ = 122; // Phase increment for 150 Hz signal at 800 Hz
localparam PHASE_INC_400HZ = 326; // Phase increment for 400 Hz signal at 800 Hz
reg cordic_clk = 1'b0;
reg fir_clk = 1'b0;
reg phase_tvalid = 1'b0;
reg signed [15:0] phase_150Hz = 0;
reg signed [15:0] phase_400Hz = 0;
wire sincos_150Hz_tvalid;
wire signed [15:0] sin_150Hz, cos_150Hz;
wire sincos_400Hz_tvalid;
wire signed [15:0] sin_400Hz, cos_400Hz;
reg signed [15:0] noisy_signal = 0;
wire signed [15:0] filtered_signal;
// Instantiate CORDIC for 150 Hz sine wave generation
// Ensure cordic_0 is defined or imported in your project
cordic_0 cordic_inst_150Hz(
.aclk(cordic_clk),
.s_axis_phase_tvalid(phase_tvalid),
.s_axis_phase_tdata(phase_150Hz),
.m_axis_dout_tvalid(sincos_150Hz_tvalid),
.m_axis_dout_tdata({sin_150Hz, cos_150Hz})
);
// Instantiate CORDIC for 400 Hz sine wave generation
cordic_0 cordic_inst_400Hz(
.aclk(cordic_clk),
.s_axis_phase_tvalid(phase_tvalid),
.s_axis_phase_tdata(phase_400Hz),
.m_axis_dout_tvalid(sincos_400Hz_tvalid),
.m_axis_dout_tdata({sin_400Hz, cos_400Hz})
);
// Phase sweep for 150 Hz and 400 Hz sine wave generation
always @(posedge cordic_clk) begin
phase_tvalid
love you
module fir(
input wire clk,
input wire signed [15:0] noisy_signal,
output wire signed [15:0] filtered_signal
);
// Declare the loop variable here, outside the always block
integer i;
integer j;
// Coefficients for 5-tap FIR filter
//reg signed [15:0] coeff [0:4] = {16'h04F6, 16'h0AE4, 16'h160F, 16'h0AE4, 16'h04F6};
reg signed [15:0] coeff [0:4];
initial begin
coeff[0] = 16'h04F6;
coeff[1] = 16'h0AE4;
coeff[2] = 16'h160F;
coeff[3] = 16'h0AE4;
coeff[4] = 16'h04F6;
end
// Delayed signals for the FIR filter
reg signed [15:0] delayed_signal [0:4];
// Multiplication products
reg signed [31:0] prod [0:4];
// Accumulation stages
reg signed [32:0] sum_0 [0:2];
reg signed [33:0] sum_1;
// Delay line and pipeline the noisy signal
always @(posedge clk) begin
// Declare the loop variable outside the loop
delayed_signal[0]
hey, why do you make 1.3.28 and not 1.2.28 fixed point from multiplying two 1.1.14 and why do you increase the integer part of the number by 1 each time sum index increases? second questions, how do we know that we can only cut the oldest bits at the end? thanks in advance
please can you tell how its actually cofficient convert into hexadecimal value? it will be very help for me
Could you please provide this code
hy, how can i get the coefficient numbers? there is easy way to know them?
could you show me the way to integrate it into SoC System (qsys or platform designer), please? Thank you so much.
Hi, how did you generate coefficients values in hex format and i want to generate 2khz frequency, what changes should i do.
hi! I was just wondering how you determined the frequencies of sine wave by the phase jump in testbench, if there are any formula or some references, I would be very appreciate! thank you!
The phase resolution is fixed in this IP and the total number of steps is 2*PI or 51,472. The equation for synthesizing the exact frequency is Fout = (phase_jump * sampling_frequency) / 51,472 so plug in your required Fout and tune the two parameters in the numerator accordingly.
Hi, how could you know exactly where to put those peplined sum_0, sum_1, sum_2 and sum_3. What will happened with those sum when you increase the tap number? If you have any document about how to put those sum registers together, I would be very appreciate!! Thanks
Good curiosity, the RTL was put together straight from the brain without any intermediate document so don't overthink regarding documents but let me elaborate on what's important. Where to put the sums depend on your pipeline architecture. In this demonstration, the pipeline ensures each multiplication or addition would have no more than two terms so if you look at all the product terms, each pair would then get summed and this drives the sum chains resulting in one sum at the end which is the result of the convolution.
When you increase the tap number, you would have more product terms so you would just need to make sure in the first pipeline stage of the summation all product terms are included and paired up then in subsequent pipeline stages, you continue to break down those sum terms until you end up with one.
Hi, which windoing method you used to generate coefficient values
Kaiser
@@FPGARevolution thanks
Hi, how did you convert decimals coefficients in to 16 bit hex.
Getting error “module cordic_0 not found “please help
There's a step starting at 05:28 for configuring the cordic ip that you mentioned missing.
fir_tb.v
module fir_tb();
localparam CORDIC_CLK_PERIOD = 1250; // Clock period for the CORDIC at 800 Hz
localparam FIR_CLK_PERIOD = 1250; // Clock period for the FIR filter at 800 Hz
localparam signed [15:0] PI_POS = 16'h6488; // Positive PI constant
localparam signed [15:0] PI_NEG = 16'h9878; // Negative PI constant
localparam PHASE_INC_150HZ = 122; // Phase increment for 150 Hz signal at 800 Hz
localparam PHASE_INC_400HZ = 326; // Phase increment for 400 Hz signal at 800 Hz
reg cordic_clk = 1'b0;
reg fir_clk = 1'b0;
reg phase_tvalid = 1'b0;
reg signed [15:0] phase_150Hz = 0;
reg signed [15:0] phase_400Hz = 0;
wire sincos_150Hz_tvalid;
wire signed [15:0] sin_150Hz, cos_150Hz;
wire sincos_400Hz_tvalid;
wire signed [15:0] sin_400Hz, cos_400Hz;
reg signed [15:0] noisy_signal = 0;
wire signed [15:0] filtered_signal;
// Instantiate CORDIC for 150 Hz sine wave generation
// Ensure cordic_0 is defined or imported in your project
cordic_0 cordic_inst_150Hz(
.aclk(cordic_clk),
.s_axis_phase_tvalid(phase_tvalid),
.s_axis_phase_tdata(phase_150Hz),
.m_axis_dout_tvalid(sincos_150Hz_tvalid),
.m_axis_dout_tdata({sin_150Hz, cos_150Hz})
);
// Instantiate CORDIC for 400 Hz sine wave generation
cordic_0 cordic_inst_400Hz(
.aclk(cordic_clk),
.s_axis_phase_tvalid(phase_tvalid),
.s_axis_phase_tdata(phase_400Hz),
.m_axis_dout_tvalid(sincos_400Hz_tvalid),
.m_axis_dout_tdata({sin_400Hz, cos_400Hz})
);
// Phase sweep for 150 Hz and 400 Hz sine wave generation
always @(posedge cordic_clk) begin
phase_tvalid