> 技术文档 > 基于FPGA的FIR滤波器设计与仿真验证,Verilog设计,modelsim仿真_0-5mhz的滤波

基于FPGA的FIR滤波器设计与仿真验证,Verilog设计,modelsim仿真_0-5mhz的滤波

1、设计内容

(1)利用MATLAB设计FIR低通滤波器系数,采样频率为5MHz,截止频率为100kHz,并生成测试数据保存至txt文件,并且在MATLAB上验证FIR滤波器的效果。

(2)在Modelsim进行波形仿真,验证FIR的滤波效果。

2、基本原理

有限冲激响应滤波器 ( Finite Impulse Response),即FIR滤波器,其输入x(n)和输出y(n)之间的关系为: 

FIR滤波器的转移函数可以表示为:

FIR滤波器应用比较普遍,有较多优良的品质。FIR滤波器输出对输入没有反馈,并且系数b(r)长度总是有限的,因此系统总是稳定收敛的。当系数b(r)对称时,FIR滤波器具有线性相位,输出等于输入在时间上的移位,不会产生相位失真。

FIR滤波器应用广泛,有各种各样的设计方法,比如窗函数法、频率抽样法、最小二乘法、等纹波法等,本文采用窗函数法进行设计。

MATLAB软件主要实现的工作是产生混频信号、设计FIR滤波器的系数并进行验证。混频信号是5KHz和800KHz的混频,之后将混频信号存储到mem.txt。在MATLAB的命令行窗口输入filterDesigner命令调出滤波器设计工具,接下来选择低通滤波器,选择Hamming窗,设定阶数为10阶,采样频率为5MHz,截至频率为100KHz,之后导出滤波器系数和FIR函数。得到FIR函数后验证FIR滤波的效果,并对系数进行量化——乘以1024后取整,结果为:14    30    73   128   172   189   172   128    73    30    14。之后根据MATLAB设计的系数,在FPGA端进行实现,并采用Modelsim进行仿真。

采用Verilog HDL实现FIR滤波器的程序流程图如图1所示,首先输入时钟、复位、时钟使能和待滤波的混频信号,接下来将MATLAB设计好的系数乘以1024,并定义为参数,同时定义中间变量。接下来对输入数据用11个寄存器缓存,然后将这11个寄存器的输出与定义的11个系数参数相乘,并将相乘的结果相加,相加后的和右移10位后的数据即为模块的输出。这里右移10位的原因是系数已经乘了一个1024,要使数据结果不变的话,输出要对应的除以1024,本文采用移位的方式实现除法,即右移10位等效的结果就是除以1024。

图1 FPGA实现FIR滤波器的程序流程图

3、程序设计

(1)MATLAB端主要生成混频数据、设计并得到FIR滤波器系数、验证FIR滤波器,程序如下:

%% clear;clc;close all;load(\"matlab_FIR.mat\");%% 用MATLAB产生5kHz和800kHz的混频信号Fs=5000000;%采样频率为5MHzN=8192;%采样点数N1=0:1/Fs:N/Fs-1/Fs;%以频率Fs采8192个点的数据s=sin(5000*2*pi*N1)+sin(800000*2*pi*N1)+3;figure(1);plot(N1,s);%% 将波形数据进行量化, 并转换为十六进制保存到mem.txt中fidc=fopen(\'mem.txt\',\'wt\');%将结果写入mem.txt文件,便于modelsim使用for x=1:N y=round((s(x)/10)*4096); fprintf(fidc,\'%x\\n\',y);%四舍五入取整后写入mem.txt文件endfclose(fidc);% test=s*4096/10;% plot(N1,test);%% 在MATLAB中用filterDesigner命令调出滤波器设计工具%选择低通滤波器%选择Hamming窗%设定阶数10阶%采样频率5MHz%截止频率100kHz%选择文件->导出,将滤波器系数和FIR函数导出到工作区%% 检验FIR滤波器滤波效果d=filter(FIR,s);figure(2);plot(N1,d);%%对导出的系数进行量化取整%对导入的系数Num进行量化, 乘以2^10后取整Num_int=round(Num*2^10);disp(Num_int);%需要注意的是, 设计的FIR滤波器的系数有一个特点, 就是系数之和为1%验证sum_Num=sum(Num);disp(sum_Num);

(2)Verilog HDL实现FIR滤波器的思路如图1所示,代码如下:

//FIR 滤波器模块module FIR_top( input wire clk_in , input wire rst_n  , input wire clk_en , input wire [15:0] fir_in , //输入待滤波数据, 16bits output wire [15:0] fir_out //系数为8位宽, 且系数和为1023, 所以和为26bits //和再右移10bits, 所以滤波后输出的数据仅为16bits);localparam [7:0] coeff0 = 8\'d14 ;localparam [7:0] coeff1 = 8\'d30 ;localparam [7:0] coeff2 = 8\'d73 ;localparam [7:0] coeff3 = 8\'d128 ;localparam [7:0] coeff4 = 8\'d172 ;localparam [7:0] coeff5 = 8\'d189 ;localparam [7:0] coeff6 = 8\'d172 ;localparam [7:0] coeff7 = 8\'d128 ;localparam [7:0] coeff8 = 8\'d73 ;localparam [7:0] coeff9 = 8\'d30 ;localparam [7:0] coeff10= 8\'d14 ;reg [15:0] delay_pipeline [0:10] ;wire [23:0] product10,product9,product8,product7,product6,product5,  product4,product3,product2,product1,product0;wire [25:0] sum;//系数之和为1023, 数据为16bits, 所以和为26bitsreg [25:0] fir_temp;always @(posedge clk_in or negedge rst_n ) begin if(!rst_n) begin delay_pipeline[0] <=16\'d0; delay_pipeline[1] <=16\'d0; delay_pipeline[2] <=16\'d0; delay_pipeline[3] <=16\'d0; delay_pipeline[4] <=16\'d0; delay_pipeline[5] <=16\'d0; delay_pipeline[6] <=16\'d0; delay_pipeline[7] <=16\'d0; delay_pipeline[8] <=16\'d0; delay_pipeline[9] <=16\'d0; delay_pipeline[10]<=16\'d0; fir_temp<=26\'d0; end else if(clk_en) begin delay_pipeline[0] <=fir_in  ; delay_pipeline[1] <=delay_pipeline[0] ; delay_pipeline[2] <=delay_pipeline[1] ; delay_pipeline[3] <=delay_pipeline[2] ; delay_pipeline[4] <=delay_pipeline[3] ; delay_pipeline[5] <=delay_pipeline[4] ; delay_pipeline[6] <=delay_pipeline[5] ; delay_pipeline[7] <=delay_pipeline[6] ; delay_pipeline[8] <=delay_pipeline[7] ; delay_pipeline[9] <=delay_pipeline[8] ; delay_pipeline[10]<=delay_pipeline[9] ; fir_temp>10);//系数已经扩大2^10倍, 这里右移10bits, 恢复正常值 end //否则保持不变end //FIR中的乘法assign product0 = delay_pipeline[0 ] *coeff0 ;assign product1 = delay_pipeline[1 ] *coeff1 ;assign product2 = delay_pipeline[2 ] *coeff2 ;assign product3 = delay_pipeline[3 ] *coeff3 ;assign product4 = delay_pipeline[4 ] *coeff4 ;assign product5 = delay_pipeline[5 ] *coeff5 ;assign product6 = delay_pipeline[6 ] *coeff6 ;assign product7 = delay_pipeline[7 ] *coeff7 ;assign product8 = delay_pipeline[8 ] *coeff8 ;assign product9 = delay_pipeline[9 ] *coeff9 ;assign product10= delay_pipeline[10] *coeff10 ;//FIR滤波中的加法assign sum=product10+product9+product8+product7+product6+product5+  product4+product3+product2+product1+product0;//数据输出assign fir_out = fir_temp[15:0];endmodule

(3)仿真TestBench主要用于产生时钟、复位、时钟使能信号,并读取MATLAB产生的混频信号,并将该混频信号作为输入信号注入到FIR设计文件中,代码如下:

//FIR滤波器仿真模块`timescale 1ns/1nsmodule FIR_top_tb ();reg clk_in ;reg rst_n  ;reg clk_en ;reg [15:0] fir_in ; wire [15:0] fir_out ; reg [15:0] mem [0:8191] ;//8192个数reg [12:0] num ;//0~8191循环计数initial $readmemh(\"D:/FPGA_prj/FPGA_class/prj_three/matlab_prj/mem.txt\",mem);//以16进制形式将文本文件mem.txt中的8192个数据读入数组mem中initial begin clk_in=1\'b0; rst_n<=1\'b0; clk_en<=1\'b1; repeat(3) begin @(negedge clk_in) ; end rst_n<=1\'b1; #8000000; $stop;end always #100 clk_in = ~clk_in ;//5MHzalways @(posedge clk_in or negedge rst_n) begin if(!rst_n) num<=13\'d0; else num<=num+1\'b1;end always @(posedge clk_in or negedge rst_n ) begin if(!rst_n) fir_in<=16\'d0; else fir_in<=mem[num];end FIR_top FIR_top_inst( . clk_in ( clk_in ) , . rst_n ( rst_n ) , . clk_en ( clk_en ) , . fir_in ( fir_in ) , //输入待滤波数据, 16bits . fir_out ( fir_out ) //系数为8位宽, 且系数和为1023, 所以和为26bits //和再右移10bits, 所以滤波后输出的数据仅为16bits);endmodule

4、实验结果分析

MATLAB产生的混频信号如图2所示,可以看出,信号比较杂乱,分别是5KHz和800KHz的混频。

图2 MATLAB产生的混频信号

在MATLAB端生成滤波后的图形,如图3所示,800KHz的高频信号基本被滤除,只剩5Khz的低频信号。

图3 MATLAB滤波后的效果

在Modelsim上的仿真波形图如图4所示,fir_in为输入的混频信号,fir_out为输出的FIR滤波后的信号,可以看出,高频信号基本被滤除,达到预期效果。

图4 FIR滤波的仿真波形图