由上图可知,n阶FIR滤波器就需要n个乘法器。如果设计的是线性相位FIR,则h(n)是对称的,利用对称性可以节省一半的乘法器。
FIR滤波器的设计方法有窗函数法、频率取样法、等波纹切比雪夫逼近法(也叫最优设计法)等等。以上所有的理论知识点在任意一本数字信号处理课本中都有详细的推论,本文节省篇幅不再赘述。
MATLAB设计虽然Quartus和Vivado的FIR IP核中都提供了设计FIR滤波器的功能,但远没有MATLAB设计便捷和强大。设计中通常都是在MATLAB中设计好FIR的单位脉冲响应h(n),或者说滤波器系数,量化后应用到FPGA设计中。
MATLAB提供了基于窗函数设计法的fir1函数、设计任意响应滤波器的fir2函数、最优设计法的firpm函数,以及两个应用程序包“Filter Builder”和“Filter Design&Analysis”,后者通常也被称作FDATOOL。现在最受欢迎的设计方式恐怕就是使用FDATOOL工具,功能强大、界面便捷,且可以直接导出xilinx公司IP核所需的coe文件。
本系列主要是讲述FPGA设计,不详细讨论上述函数及工具的使用,具体情况可以的MATLAB的help中查询。(Ps:博主目前的几个系列都处于开篇阶段,篇幅不多,暂未成体系,目前不再开新坑,等后期应该会出一个“MATLAB数字信号处理系列”)
FPGA设计从MATLAB到FPGA最重要的工作便是滤波器系数的量化。现在的计算机大多都是64位的,然而为了节省资源,FPGA中进行如此高位宽的运算步进浪费资源而且也没有必要。在MATLAB中将滤波器系数量化为指定位宽,会改变滤波器的频率特性,因此需要做好
仿真,确定量化后的系数也能满足FIR的设计需求。
由上节可知FPGA最方便实现的是直接型结构FIR,实现时可以采用并行结构、串行结构、分布式结构,也可以直接使用Quartus和Vivado提供的FIR IP核。本篇先介绍并行FIR滤波器的Verilog设计。设计参考自杜勇老师的《数字滤波器的MATLAB与FPGA实现》。本设计将在Vivado环境下进行仿真。
使用MATLAB设计一个2kHz采样,500Hz截止的15阶低通滤波器(h(n)长度为16),量化位数为12bit,输入信号位宽也为12bit。Verilog设计代码如下。
模块接口:
module Xilinx_FIR_Guide_liuqi( input rst, //复位信号,高电平有效 input clk, //FPGA系统时钟,频率为2kHz input signed [11:0] Xin, //数据输入频率为2khZ output signed [28:0]Yout //滤波后的输出数据);输出信号的29bit位宽是全分辨率输出,没有截位。“并行”FIR指的是多个乘法器并行地进行滤波器系数与输入数据之间的乘法计算,因此代码中我们需要缓存16个数据:
reg signed[11:0] Xin_Reg[15:0]; //[11:0]指单数据12bit位宽;[15:0]指共有16个数据reg [3:0] i,j; always @(posedge clk or posedge rst) if (rst) //初始化寄存器值为0 begin for (i=0; i<15; i=i+1) Xin_Reg
=12'd0; end else begin for (j=0; j<15; j=j+1) //每个时钟移位一个数据 Xin_Reg[j+1] <= Xin_Reg[j]; Xin_Reg[0] <= Xin; end由FIR系数的对称性可知,16个系数只需要8个乘法器即可,因此应该将对称系数多对应的输入数据相加:
reg signed [12:0] Add_Reg[7:0];always @(posedge clk or posedge rst) if (rst) //初始化寄存器值为0 begin for (i=0; i<8; i=i+1) Add_Reg=13'd0; end else begin for (i=0; i<8; i=i+1) //对称系数相加 Add_Reg={Xin_Reg[11],Xin_Reg}+{Xin_Reg[15-i][11],Xin_Reg[15-i]}; end由于加法会增加一个bit位宽,因此相加结构扩充为13bit。由于输入数据为二进制补码带符号数,因此在相加前需要先使用Verilog中的拼接运算符{}扩展符号位到最高位。接下来例化8个乘法器IP核进行乘法运算:
wire signed [11:0] coe[7:0] ; //滤波器为12比特量化数据 wire signed [24:0] Mout[7:0]; //乘法器输出为25比特数据 assign coe[0]=12'h000; assign coe[1]=12'hffd; assign coe[2]=12'h00f; assign coe[3]=12'h02e; assign coe[4]=12'hf8b; assign coe[5]=12'hef9; assign coe[6]=12'h24e; assign coe[7]=12'h7ff; mult_gen_0 Umult0 ( .CLK (clk), .A (coe[0]), .B (Add_Reg[0]), .P (Mout[0])); mult_gen_0 Umult1 ( .CLK (clk), .A (coe[1]), .B (Add_Reg[1]), .P (Mout[1])); mult_gen_0 Umult2 ( .CLK (clk), .A (coe[2]), .B (Add_Reg[2]), .P (Mout[2])); mult_gen_0 Umult3 ( .CLK (clk), .A (coe[3]), .B (Add_Reg[3]), .P (Mout[3])); mult_gen_0 Umult4 ( .CLK (clk), .A (coe[4]), .B (Add_Reg[4]), .P (Mout[4])); mult_gen_0 Umult5 ( .CLK (clk), .A (coe[5]), .B (Add_Reg[5]), .P (Mout[5])); mult_gen_0 Umult6 ( .CLK (clk), .A (coe[6]), .B (Add_Reg[6]), .P (Mout[6])); mult_gen_0 Umult7 ( .CLK (clk), .A (coe[7]), .B (Add_Reg[7]), .P (Mout[7]));12bit的滤波器系数与13bit的输入信号数据相乘结果为25bit。乘法结果累加即为滤波器的输出结果:
reg signed [28:0] sum; reg signed [28:0] yout; reg [3:0] k; always @(posedge clk or posedge rst) if (rst) begin sum = 29'd0; yout <= 29'd0; end else begin yout <= sum; sum = 29'd0; for (k=0; k<8; k=k+1) sum = sum+Mout[k]; //相加输出结果 end assign Yout = yout;8个25bit的数相加,结果可能扩展到29bit,这也是全分辨率输出的结果。可以看到并行结构的FIR乘法、加法运算都是在一个时钟内完成,因此每个时钟都能获得一个输出。
仿真与工程下载使用MATLAB生成一个200Hz+800Hz的混合频率信号,写入txt文件,再生成一个噪声信号写入txt文件。
对正弦信号的滤波如下图所示:
明显看到经过500Hz低通滤波器滤波后,输入的200Hz+800Hz信号只剩下200Hz的频率分量。
对噪声信号的滤波如下图所示: