详解FIR滤波器原理以及Verilog实现和Xilinx FIR IP核的验证

详解FIR滤波器原理以及Verilog实现和Xilinx FIR IP核的验证FIR FiniteImpuls 有限冲激响应 滤波器是一种数字滤波器 其输出信号仅依赖于当前和过去有限数量的输入信号

大家好,欢迎来到IT知识分享网。


一、什么是FIR滤波器?

  FIR(Finite Impulse Response,有限冲激响应)滤波器是一种数字滤波器,其输出信号仅依赖于当前和过去有限数量的输入信号。FIR 滤波器的特点是其冲激响应是有限的,即在输入信号的冲激响应结束后,输出信号会在有限的时间内归零。表达式如下:

y [ n ] = b 0 x [ n ] + b 1 x [ n − 1 ] + b 2 x [ n − 2 ] + . . . . . . . . . . . + b m x [ n − m ] y[n] = b_0x[n]+b_1x[n-1]+b_2x[n-2]+………..+b_mx[n-m] y[n]=b0x[n]+b1x[n1]+b2x[n2]+………..+bmx[nm]

在这里插入图片描述

  其中 b 0 b_0 b0 b 1 b_1 b1 b 2 b_2 b2 b m b_m bm称为滤波器的系数也叫抽头,m是滤波器的阶数,这个公式其实就是离散信号的卷积公式,具体推导请看《卷积的理解、卷积公式的推导以及计算》这篇文章。
  何为滤波器?就是滤除掉信号中不要的频率分量,常见的滤波器分为高通滤波器、低通滤波器、带通滤波器、带阻滤波器。下图为一个加了高斯白噪声的正弦波信号以及它的频谱图:

在这里插入图片描述
  我们可以看到,一个有高斯白噪声的正弦信号在频谱图中每个频率点都有幅度,如果我们想要滤除掉这些噪声从而恢复出我们想要的纯洁信号,只需要找到一个信号在我们想要的频率范围内有幅度,其它频率范围为0,然后相乘就能够滤除,具体如下:

在这里插入图片描述
  例如一个信号的频谱如上所示,它的频谱特性表现为50HZ以下有信号,50HZ以上没信号。所以我们只需要把上面加了高斯白噪声的正弦信号的频谱和这个信号的频谱相乘,就能够过滤出50HZ以上的高频的白噪声,相乘的结果如下所示:

在这里插入图片描述
  然后把频率相乘后的信号经过IFFT就能恢复出原始信号,如下所示:

在这里插入图片描述
  上面就是一个滤波的过程,那么和FIR有什么关系呢? 我们现在知道滤波就是在频域部分相乘进行过滤的,根据卷积公式:频率相乘=时域卷积,因此我们只需要在时域中把我们想要的信号和一个滤波函数的信号进行卷积就能实现过滤。FIR也叫有限冲激响应,它公式中的抽头系数其实就是滤波器的单位冲激响应,我们只需要用有限个单位冲激响应与信号卷积就能实现滤波。

二、Verilog实现FIR低通滤波

  上面我们知道滤波就是时域卷积运算,那么我们就需要设计一个滤波函数,得到它的单位冲击响应,这里使用matlab来设计一个低通滤波器。

2.1 matlab获取抽头系数

  首先再matlab命令栏里输入fdatool,然后弹出滤波器设计工具,界面如下:

在这里插入图片描述
  设计完成后我们点左上角导出系数,这里先选择导出到工作区:

在这里插入图片描述

在这里插入图片描述
  这样我们得到了15阶,采样频率为100MHZ,截止频率为7MHZ的低通滤波抽头,因为FPGA不能处理小数,因此我们需要把抽头适当的放大一下,这里我们放大2048倍:

在这里插入图片描述
  我们可以看到滤波器抽头系数是对称的,因此我们在被Verilog代码里只需要调用8个乘法器,这样可以省下一半的乘法器资源。

2.2 Verilog代码

`timescale 1ns / 1ps module fir_low( input aclk , input aresetn ); //dds wire m_dds_10m_data_tvalid ; wire signed [15:0] m_dds_10m_data_tdata ; wire m_dds_5m_data_tvalid ; wire signed [15:0] m_dds_5m_data_tdata ; wire signed [31:0] dds_mix ; reg m_dds_mix_data_valid ; // fir reg signed [31:0] signal_reg [0:15]; //定义16个寄存器缓存待滤波的信号 //定位第几个寄存器 wire [11:0] fir_para [0:7]; //抽头系数 reg signed [32:0] first_end_signal_add[0:7] ;//8个寄存器,存放16个信号的首尾之和 wire signed [44:0] mult_out [0:7]; //乘法器后的结果 reg signed [46:0] sum1 ; reg signed [46:0] sum2 ; reg signed [47:0] sum_out ; //assign  assign fir_para[0] = -12'd11 ; assign fir_para[1] = 12'd23 ; assign fir_para[2] = 12'd65 ; assign fir_para[3] = 12'd112 ; assign fir_para[4] = 12'd158 ; assign fir_para[5] = 12'd199 ; assign fir_para[6] = 12'd229 ; assign fir_para[7] = 12'd245 ; //ip dds_10m u0_dds_10m ( .aclk(aclk), // input wire aclk .aresetn(aresetn), // input wire aresetn .m_axis_data_tvalid(m_dds_10m_data_tvalid), // output wire m_axis_data_tvalid .m_axis_data_tdata(m_dds_10m_data_tdata) // output wire [15 : 0] m_axis_data_tdata ); dds_5m u0_dds_5m ( .aclk(aclk), // input wire aclk .aresetn(aresetn), // input wire aresetn .m_axis_data_tvalid(m_dds_5m_data_tvalid), // output wire m_axis_data_tvalid .m_axis_data_tdata(m_dds_5m_data_tdata) // output wire [15 : 0] m_axis_data_tdata ); mult_dds u0_mult_dds ( .CLK(aclk), // input wire CLK .A(m_dds_10m_data_tdata), // input wire [15 : 0] A .B(m_dds_5m_data_tdata), // input wire [15 : 0] B .P(dds_mix) // output wire [31 : 0] P ); //混频信号经过乘法器后会延迟一拍 always @(posedge aclk) begin if(aresetn == 1'b0) m_dds_mix_data_valid <= 1'b0; else m_dds_mix_data_valid <= (m_dds_10m_data_tvalid && m_dds_5m_data_tvalid); end integer i; //使用for语句循环移位,将信号分别暂存到对应的寄存器里 always @(posedge aclk) begin if(aresetn == 1'b0)begin for (i=0; i<16; i = i+1)begin signal_reg[i] <= 'd0; end end else if(m_dds_mix_data_valid == 1'b1)begin signal_reg[0] <= dds_mix; for (i=0; i<15; i = i+1)begin signal_reg[i+1] <= signal_reg[i]; end end end integer j; //因为fir系数对称,所以只需要调用阶数的一半的乘法器即可,调用乘法器之前先把首位寄存器相加 always @(posedge aclk) begin if(aresetn == 1'b0)begin for(j=0 ; j<8 ; j= j+1)begin first_end_signal_add[j] <= 'd0; end end else begin for(j=0 ; j<8 ; j= j+1)begin first_end_signal_add[j] <= signal_reg[j] + signal_reg[15-j]; end end end //调用8个有符号乘法器 genvar k; generate for(k=0; k<8; k = k +1)begin mult_33_12 u_mult_33_12 ( .CLK(aclk), // input wire CLK .A(first_end_signal_add[k]), // input wire [32 : 0] A .B(fir_para[k]), // input wire [11 : 0] B .P(mult_out[k]) // output wire [44 : 0] P ); end endgenerate always @(posedge aclk) begin if(aresetn == 1'b0) begin sum1 <= 'd0; sum2 <= 'd0; sum_out <= 'd0; end else begin sum1 = mult_out[0] + mult_out[1] + mult_out[2] + mult_out[3]; sum2 = mult_out[4] + mult_out[5] + mult_out[6] + mult_out[7]; sum_out = sum1 + sum2; end end endmodule 

  在代码里,我们把滤波器抽头系数固定好,因为是对称的,所以我们只固定前八个即可。

2.3 仿真观察

在这里插入图片描述
  我们使用dds将一个10MHZ的信号和一个5MHZ的信号进行混频后再经过FIR滤波器,因为我们的低通滤波截至频率为7MHZ,因此我们经过滤波器后10MHZ的信号将被滤掉,只剩下5MHZ的信号,仿真结果正确。

三、Xilinx FIR IP核的说明以及使用

3.1 端口信号

在这里插入图片描述
  FIR IP核的端口如上所示,不同的配置接出来的端口不一样,端口说明如下:

端口名称 输入方向 端口说明
aclk 输入 上升沿有效
aclken 输入 时钟使能信号,高电平有效
aresetn 输入 低电平有效同步复位信号。 需要至少两个周期的 aresetn 有效脉冲
s_axis_config_tvalid 输入 配置数据有效信号
s_axis_config_tready 输出 配置数据准备信号
s_axis_config_tdata 输入 配置数据
s_axis_config_tlast 输入 一包配置数据的最后一个数据指示信号
s_axis_reload_tvalid 输入 重载数据有效信号
s_axis_reload_tready 输出 重载数据准备信号
s_axis_reload_tdata 输入 重载数据
s_axis_reload_tlast 输入 输入最后一个数据指示信号
s_axis_data_tvalid 输入 输入数据有效信号
s_axis_data_tready 输出 输入数据准备信号
s_axis_data_tdata 输入 输入数据,传送要过滤的数据流
s_axis_data_tlast 输入 输入最后一个数据指示信号
s_axis_data_tuser 输入 输入用户信号
端口名称 输入方向 端口说明
m_axis_data_tvalid 输出 输出数据有效信号
m_axis_data_tready 输入 输出数据准备信号
m_axis_data_tdata 输出 输出数据
m_axis_data_tuser 输出 用于输出数据通道。可选择从输入数据 tuser 端口传送用户字段和/或 chan ID 字段,以识别当前样本属于哪个 TDM 通道。
m_axis_data_tlast 输出 输出最后一个数据指示信号
端口名称 输入方向 端口说明
event_s_data_tlast_missing 输出 表示输入 DATA tlast 未按内部通道计数器预期的那样被置位。
event_s_data_tlast_unexpected 输出 表示当内部通道计数器未预期输入 DATA tlast 时,该输入被置位
event_s_data_chanid_incorrect 输出 代表输入 DATA tuser 端口的 chan ID 字段与内部计数器的值不匹配
event_s_reload_tlast_missing 输出 表示 RELOAD tlast 没有按照内部计数器的预期被置位
event_s_reload_tlast_unexpected 输出 表示当内部计数器未预期到 RELOAD tlast 时,该信号被置位
event_s_config_tlast_missing 输出 表示当内部计数器期望时 CONFIG tlast 未被断言
event_s_config_tlast_unexpected 输出 表示当内部计数器未预期到 CONFIG tlast 时,该信号被置位

3.2 TDATA结构

  输入输出数据结构一致,只是位宽不一样

在这里插入图片描述

3.3 IP调用

  IP文档其它的等以后用到了再详细看,这里先配置快速用起来,配置步骤如下:

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

  其它默认即可。

3.4 Verilog代码

  我们删除前面自己写的代码,只保留dds以及FIR IP核,代码如下:

`timescale 1ns / 1ps module fir_low_1( input aclk , input aresetn ); //dds wire m_dds_10m_data_tvalid ; wire signed [15:0] m_dds_10m_data_tdata ; wire m_dds_5m_data_tvalid ; wire signed [15:0] m_dds_5m_data_tdata ; wire signed [31:0] dds_mix ; reg m_dds_mix_data_valid ; wire m_axis_data_tvalid ; wire [55:0] m_axis_data_tdata ; //ip dds_10m u0_dds_10m ( .aclk(aclk), // input wire aclk .aresetn(aresetn), // input wire aresetn .m_axis_data_tvalid(m_dds_10m_data_tvalid), // output wire m_axis_data_tvalid .m_axis_data_tdata(m_dds_10m_data_tdata) // output wire [15 : 0] m_axis_data_tdata ); dds_5m u0_dds_5m ( .aclk(aclk), // input wire aclk .aresetn(aresetn), // input wire aresetn .m_axis_data_tvalid(m_dds_5m_data_tvalid), // output wire m_axis_data_tvalid .m_axis_data_tdata(m_dds_5m_data_tdata) // output wire [15 : 0] m_axis_data_tdata ); mult_dds u0_mult_dds ( .CLK(aclk), // input wire CLK .A(m_dds_10m_data_tdata), // input wire [15 : 0] A .B(m_dds_5m_data_tdata), // input wire [15 : 0] B .P(dds_mix) // output wire [31 : 0] P ); always @(posedge aclk) begin if(aresetn == 1'b0) m_dds_mix_data_valid <= 1'b0; else m_dds_mix_data_valid <= (m_dds_10m_data_tvalid && m_dds_5m_data_tvalid); end fir_low u0_fir_low ( .aclk(aclk), // input wire aclk .s_axis_data_tvalid(m_dds_mix_data_valid), // input wire s_axis_data_tvalid .s_axis_data_tready(), // output wire s_axis_data_tready .s_axis_data_tdata(dds_mix), // input wire [31 : 0] s_axis_data_tdata .m_axis_data_tvalid(m_axis_data_tvalid), // output wire m_axis_data_tvalid .m_axis_data_tdata(m_axis_data_tdata) // output wire [55 : 0] m_axis_data_tdata ); endmodule 

3.5 仿真观察

  我们直接的打开仿真

在这里插入图片描述
  和我们自己写的代码仿真一致。

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/119496.html

(0)
上一篇 2025-11-05 08:00
下一篇 2025-11-05 08:15

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信