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;%采样频率为5MHz
N=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文件
end
fclose(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, 所以和为26bits
reg [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<=(sum>>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/1ns
module 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 ;//5MHz
always @(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滤波的仿真波形图