基于Quartues ii和Modelsim的FIR滤波器仿真
基于Quartues ii和Modelsim的FIR滤波器仿真
设计需求
本设计需要实现基于FPGA的FIR低通滤波,采样频率5MHz,截止频率100kHz,利用Matlab设计FIR滤波器系数,并生成测试数据保存至txt文件。在Quartues ii中编写FIR滤波器模块,联合Modelsim进行功能仿真,观察滤波效果。
设计思路
本设计分为两个部分,一个是MATLAB中测试数据的产生和FIR滤波器的设计及验证;另一部分是Quartues ii中基于Verilog的FIR滤波器模块及testbench编写。
设计过程
MATLAB生成测试数据
用MATLAB产生5kHz和800kHz的混频信号,代码如下:
Fs = 5000000; %采样频率决定了两个正弦波点之间的间隔
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)
得到的波形如图1所示
fidc = fopen('E:\fault_detection_code\FIR_test1\mem.txt','wt');
%将结果写入mem.txt文件,便于modesim使用
for x = 1 : N
fprintf(fidc,'%x\n',round((s(x)/10)*4096));
end
fclose(fidc);
利用filterdesigner工具设计FIR滤波器
在MATLAB中用filterdesigner命令调出滤波器设计工具,界面如图2
选择低通FIR滤波器,选择Hamming窗,设定阶数10阶,采样频率5MHz,截止频率100kHz,点击设计滤波器可以看到滤波器的幅频特性曲线。选择文件->导出,将滤波器系数导出到工作区。设计完成后生成的MATLAB代码,如下:
function Hd = test_filter_kaiser
%TEST_FILTER_KAISER 返回离散时间滤波器对象。
% MATLAB Code
% Generated by MATLAB(R) 9.9 and Signal Processing Toolbox 8.5.
% Generated on: 16-Sep-2022 11:01:37
% FIR Window Lowpass filter designed using the FIR1 function.
% All frequency values are in kHz.
Fs = 5000; % Sampling Frequency
N = 10; % Order
Fc = 100; % Cutoff Frequency
flag = 'scale'; % Sampling Flag
Beta = 0.5; % Window Parameter
% Create the window vector for the design algorithm.
win = kaiser(N+1, Beta);
% Calculate the coefficients using the FIR1 function.
b = fir1(N, Fc/(Fs/2), 'low', win, flag);
Hd = dfilt.dffir(b);
% [EOF]
调用生成的滤波器函数对前文生成的混频信号进行滤波,
H=test_filter1;
d=filter(H,s);
figure(2);
plot(N1,d)
得到结果如图3所示,对比图1和图3,可以看到高频成分衰减了很多。
将设计的FIR滤波器抽头系数导出来。如下:
导出的FIR滤波器抽头系数为
0.0138133379804440 ,
0.0296928583924488 ,
0.0717059583915281 ,
0.124585240157610 ,
0.167915751691329 ,
0.184573706773281 ,
0.167915751691329 ,
0.124585240157610 ,
0.0717059583915281 ,
0.0296928583924488 ,
0.0138133379804440,
对其进行量化处理,乘以2^10后取整,得到抽头系数
14,30,73,128,172,189,172,128,73,30,14
在Quartues ii中编写FIR滤波器模块
代码如下:
module FIR_test1(
input CLK,
input RSTn,
input [11:0]FIR_IN,
output reg [23:0]FIR_OUT
);
reg[11:0] delay_pipeline1;
reg[11:0] delay_pipeline2;
reg[11:0] delay_pipeline3;
reg[11:0] delay_pipeline4;
reg[11:0] delay_pipeline5;
reg[11:0] delay_pipeline6;
reg[11:0] delay_pipeline7;
reg[11:0] delay_pipeline8;
reg[11:0] delay_pipeline9;
reg[11:0] delay_pipeline10;
reg[11:0] delay_pipeline11;
wire[7:0] coeff1 = 8'd14; //抽头系数
wire[7:0] coeff2 = 8'd30;
wire[7:0] coeff3 = 8'd73;
wire[7:0] coeff4 = 8'd128;
wire[8:0] coeff5 = 9'd172;
wire[7:0] coeff6 = 8'd189;
wire[7:0] coeff7 = 8'd172;
wire[7:0] coeff8 = 8'd128;
wire[7:0] coeff9 = 8'd73;
wire[7:0] coeff10 = 8'd30;
wire[7:0] coeff11 = 8'd14;
reg signed [23:0] multi_data1 ;//乘积结果
reg signed [23:0] multi_data2 ;
reg signed [23:0] multi_data3 ;
reg signed [23:0] multi_data4 ;
reg signed [23:0] multi_data5 ;
reg signed [23:0] multi_data6 ;
reg signed [23:0] multi_data7 ;
reg signed [23:0] multi_data8 ;
reg signed [23:0] multi_data9 ;
reg signed [23:0] multi_data10 ;
reg signed [23:0] multi_data11 ;
//每到来一个时钟信号,读入一个数据,更新一次delay_pipeline。
always@(posedge CLK or negedge RSTn)
if(!RSTn)
begin
delay_pipeline1 <= 12'b0 ;
delay_pipeline2 <= 12'b0 ;
delay_pipeline3 <= 12'b0 ;
delay_pipeline4 <= 12'b0 ;
delay_pipeline5 <= 12'b0 ;
delay_pipeline6 <= 12'b0 ;
delay_pipeline7 <= 12'b0 ;
delay_pipeline8 <= 12'b0 ;
delay_pipeline9 <= 12'b0 ;
delay_pipeline10<= 12'b0 ;
delay_pipeline11<= 12'b0 ;
end
else
begin
delay_pipeline1 <= FIR_IN;
delay_pipeline2 <= delay_pipeline1 ;
delay_pipeline3 <= delay_pipeline2 ;
delay_pipeline4 <= delay_pipeline3 ;
delay_pipeline5 <= delay_pipeline4 ;
delay_pipeline6 <= delay_pipeline5 ;
delay_pipeline7 <= delay_pipeline6 ;
delay_pipeline8 <= delay_pipeline7 ;
delay_pipeline9 <= delay_pipeline8 ;
delay_pipeline10 <= delay_pipeline9 ;
delay_pipeline11 <= delay_pipeline10 ;
end
//将输入经过延时的信号和滤波器系数相乘,每到来一个时钟便将一个新的乘积结果更新到multi_data中。这里直接使用“*”,编译后Quartues ii会自动调用乘法器IP核
always@(posedge CLK or negedge RSTn)
begin
if(!RSTn)
multi_data1 <= 23'b0 ;
else
multi_data1 <= delay_pipeline1*coeff1 ;
end
always@(posedge CLK or negedge RSTn)
begin
if(!RSTn)
multi_data2 <= 23'b0 ;
else
multi_data2 <= delay_pipeline2*coeff2 ;
end
always@(posedge CLK or negedge RSTn)
begin
if(!RSTn)
multi_data3 <= 23'b0 ;
else
multi_data3 <= delay_pipeline3*coeff3 ;
end
always@(posedge CLK or negedge RSTn)
begin
if(!RSTn)
multi_data4 <= 23'b0 ;
else
multi_data4 <= delay_pipeline4*coeff4 ;
end
always@(posedge CLK or negedge RSTn)
begin
if(!RSTn)
multi_data5 <= 23'b0 ;
else
multi_data5 <= delay_pipeline5*coeff5 ;
end
always@(posedge CLK or negedge RSTn)
begin
if(!RSTn)
multi_data6 <= 23'b0 ;
else
multi_data6 <= delay_pipeline6*coeff6 ;
end
always@(posedge CLK or negedge RSTn)
begin
if(!RSTn)
multi_data7 <= 23'b0 ;
else
multi_data7 <= delay_pipeline7*coeff7 ;
end
always@(posedge CLK or negedge RSTn)
begin
if(!RSTn)
multi_data8 <= 23'b0 ;
else
multi_data8 <= delay_pipeline8*coeff8 ;
end
always@(posedge CLK or negedge RSTn)
begin
if(!RSTn)
multi_data9 <= 23'b0 ;
else
multi_data9 <= delay_pipeline9*coeff9 ;
end
always@(posedge CLK or negedge RSTn)
begin
if(!RSTn)
multi_data10 <= 23'b0 ;
else
multi_data10 <= delay_pipeline10*coeff10 ;
end
always@(posedge CLK or negedge RSTn)
begin
if(!RSTn)
multi_data11 <= 23'b0 ;
else
multi_data11 <= delay_pipeline11*coeff11 ;
end
//将乘积累加,累加的结果就是滤波后的信号。
always@(posedge CLK or negedge RSTn)
begin
if(!RSTn)
FIR_OUT <= 23'b0 ;
else
FIR_OUT <= (multi_data1 + multi_data2 + multi_data3 + multi_data4 +multi_data5 + multi_data6 + multi_data7 + multi_data8 + multi_data9 + multi_data10 + multi_data11)>>10 ;
end
endmodule
接下来编写testbench,代码如下:
`timescale 1 ns/ 1 ps
module FIR_test1_tb();
// constants
// general purpose registers
//reg eachvec;
// test vector input registers
reg CLK;
reg [11:0] FIR_IN;
reg RSTn;
reg [11:0] mem[1:8192];//用来存放读入的8192个数据
// wires
wire [23:0] FIR_OUT;
reg [13:0] i;
// assign statements (if any)
FIR_test1 i1 (
// port map - connection between master ports and signals/registers
.CLK(CLK),
.FIR_IN(FIR_IN),
.FIR_OUT(FIR_OUT),
.RSTn(RSTn)
);
initial
begin
// code that executes only once
// insert code here --> begin
$readmemh("E:/fault_detection_code/FIR_test1/mem.txt",mem);//读入3.1中产生的波形数据文件mem.txt,文件路径注意不要搞错
RSTn = 0;
CLK = 0;
#50 RSTn = 1;
#1000000 $stop;
// --> end
$display("Running testbench");
end
always #100 CLK=~CLK;
always@(posedge CLK or negedge RSTn) //每次时钟到达,给FIR_IN一个输入数据
begin
if(!RSTn)
FIR_IN <= 12'b0 ;
else
FIR_IN <= mem2[i];
end
always@(posedge CLK or negedge RSTn)
begin
if(!RSTn)
i <= 12'd0;
else
i <= i + 1'd1;
end
//always
// optional sensitivity list
// @(event1 or event2 or .... eventn)
//begin
// code executes for every event on sensitivity list
// insert code here --> begin
//@eachvec;
// --> end
//end
endmodule
FIR滤波器模块和testbench编译完成后,进行RTL仿真,可以得到滤波后的波形,如图4所示,