VIVADO IP核之FIR低通滤波仿真(含滤波器群延时)
目录
前言
网络上有许多文章介绍FIR低通滤波器的使用,包括仿真。本文将继续深入介绍FIR低通滤波器IP核的使用方法,并在此基础之上去分析VIVADO FIR滤波器的群延时。通过与MATLAB仿真计算的结果比较,说明如何在VIVADO中用FIR滤波器时,考虑上滤波器的这个群延时特点。
提示:以下是本篇文章正文内容,欢迎各位阅读,转载请附上链接。
一、滤波器系数生成
在MATLAB的命令行窗口输入filterDesigner,接着界面就会跳出滤波器设计工具的界面。如下图所示,我们选择低通,FIR滤波器,设置Fs为120000000Hz,Fpass为10000000Hz,Fstop为13000000Hz,Apass为1dB,Astop为50dB。
最后点击设计滤波器按钮就会出现下图设计好的滤波器的幅值响应。
接下来量化滤波器系数,首先点击下图箭头所示的按钮,然后选择滤波器算法为定点,分子子长默认为16就能对滤波器的系数进行量化。然后点击目标——>XILINX系数(.coe)文件——>命名保存为LPF.coe。然后我们打开coe文件即可看到已经量化的滤波器系数。
二、VIVADO FIR滤波器使用
在vivado中搜索FIR滤波器IP核并点进去设置它。
首先IP核命名为FIR_LPF,选择COE File,导入我们刚才生成的coe文件,其他保持默认设置即可。
然后将输入的采样频率和时钟频率均设置为120MHz,因为我们刚才设置滤波器时采样频率微为120000000Hz,其他保持默认设置。
然后coe系数类型选择signed,宽度为16,输入数据类型也为signed,位宽为16,小数位数为13,其他保持默认设置,然后点击左边的Freq.Response就能看见滤波器的幅值响应。
到此就可以了,生成IP核即可,其他均不用设置了。
三、用MATLAB生成仿真数据
运行以下代码即可生成vivado FIR仿真所需要的仿真数据data.txt。
clc;
rng default;
K=1024;
fs=120000000;
t=0:1/fs:(K-1)/fs;
f1=8000000;
f2=16000000;
s=cos(2*pi*f1*t)+cos(2*pi*f2*t);
figure(1);
signal_frequencyspectrum(s,fs);
grid on;
xlabel('Frequency/Hz');ylabel('Amplitude/dB');
%%%%%%%%%%%%%%%%%%%%%%%%% FIR低通滤波 %%%%%%%%%%%%%%%%%%%%%%%%%
lowpass_Fs=fs; % 低通滤波器的采样频率
lowpass_Fpass=10000000; % 低通滤波器的通带截止频率
lowpass_Fstop=13000000; % 低通滤波器的阻带起始频率
% 下一行的lowpass是用fdatool设计的滤波器保存为matlab code自己修改了一下
[lowpass_b,lowpass_a] = tf(lowpass(lowpass_Fs,lowpass_Fpass,lowpass_Fstop));% 得到滤波器系数
s_LPF1=conv(s,lowpass_b);
s_LPF2=conv(s,lowpass_b,"same");
figure(2);
signal_frequencyspectrum(s_LPF2,fs);
grid on;
xlabel('Frequency/Hz');ylabel('Amplitude/dB');
h = fopen('data.txt','w');
for i=1:K
result= fi(s(i), 1, 16, 13).bin;
fprintf(h,'%s\n',result);
end
fclose(h);
figure
plot(s_LPF1)
figure
plot(s_LPF2)
function Hd = lowpass(lowpass_Fs,lowpass_Fpass,lowpass_Fstop)
%LPF 返回离散时间滤波器对象。
% MATLAB Code
% Generated by MATLAB(R) 23.2 and Signal Processing Toolbox 23.2.
% Generated on: 02-Aug-2024 20:04:40
% Equiripple Lowpass filter designed using the FIRPM function.
% All frequency values are in Hz.
Fs = lowpass_Fs; % Sampling Frequency
Fpass = lowpass_Fpass; % Passband Frequency
Fstop = lowpass_Fstop; % Stopband Frequency
Dpass = 0.057501127785; % Passband Ripple
Dstop = 0.0031622776602; % Stopband Attenuation
dens = 20; % Density Factor
% Calculate the order from the parameters using FIRPMORD.
[N, Fo, Ao, W] = firpmord([Fpass, Fstop]/(Fs/2), [1 0], [Dpass, Dstop]);
% Calculate the coefficients using the FIRPM function.
b = firpm(N, Fo, Ao, W, {dens});
Hd = dfilt.dffir(b);
% [EOF]
将data.txt保存到vivado工程的.\FIR_test\FIR_test.sim\sim_1\behav\xsim下方便后续仿真调用。
四、VIVADO FIR滤波器仿真
在工程中建立一个名为FIR_LPF_test的仿真.v文件。其中$readmemb("data.txt", signal)用于从文本中读取二进制数据赋值给signal。
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2024/08/05 10:49:26
// Design Name:
// Module Name: FIR_LPF_test
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module FIR_LPF_test();
reg clk=1;
parameter PERIOD=2;
initial
begin
forever #(PERIOD/2) clk=~clk;
end
reg s_axis_data_tvalid=0;
wire s_axis_data_tready;
reg [15:0] s_axis_data_tdata=0;
wire m_axis_data_tvalid;
wire [39:0] m_axis_data_tdata;
integer i=0;
reg [15:0] signal[1023:0];
initial
begin
$readmemb("data.txt", signal);//从data.txt中读入采样数据
#(PERIOD*5)
forever
begin
@(negedge clk)
begin
if(i<1024)
begin
s_axis_data_tvalid<=1;
s_axis_data_tdata <= signal[i];
i <= i + 1;
end
else
begin
s_axis_data_tvalid<=0;
s_axis_data_tdata <= 0;
end
end
end
end
FIR_LPF u_FIR_LPF (
.aclk(clk), // input wire aclk
.s_axis_data_tvalid(s_axis_data_tvalid), // input wire s_axis_data_tvalid
.s_axis_data_tready(s_axis_data_tready), // output wire s_axis_data_tready
.s_axis_data_tdata(s_axis_data_tdata), // input wire [15 : 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 [39 : 0] m_axis_data_tdata
);
endmodule
然后点击run simulation。将s_axis_data_tdata的数据格式设置为定点有符号数,小数位数为13位。将m_axis_data_tdata的数据格式设置为定点有符号数,小数位数为30位。然后就能看见输入的数据依次为2,...,和MATLAB生成的信号数据是对的上的。滤波后的数据依次为-0.0036,...,和MATLAB滤波后的信号数据也是对的上的。
此时注意s_LPF1=conv(s,lowpass_b); 这就说明了VIVADO的FIR滤波器在滤波时是没有考虑滤波器的群延时的。另外去数vivado滤波后的输出m_axis_data_tdata的个数只有1024个,与输入数据的个数是相等的,说明了VIVADO的FIR滤波器滤波输出取的输入和滤波系数直接卷积后结果的前面与输入数据个数相等的那一部分数据。
五、考虑群延时的VIVADO FIR滤波器仿真
有了上面的分析,我们可以自己手动控制输入数据有效的时钟长度,使得FIR IP核输出完整的卷积结果,然后我们按照掐头去尾的方式即可去掉滤波器的群延时。代码如下所示。
module FIR_LPF_test();
reg clk=1;
parameter PERIOD=2;
initial
begin
forever #(PERIOD/2) clk=~clk;
end
reg s_axis_data_tvalid=0;
wire s_axis_data_tready;
reg [15:0] s_axis_data_tdata=0;
reg m_axis_data_tvalid;
reg [39:0] m_axis_data_tdata;
wire tvalid;
wire [39:0] tdata;
integer i=0;
reg [15:0] signal[1023:0];
initial
begin
$readmemb("data.txt", signal);//从data.txt中读入采样数据
#(PERIOD*5)
forever
begin
@(negedge clk)
begin
if(i<1024)
begin
s_axis_data_tvalid<=1;
s_axis_data_tdata <= signal[i];
i <= i + 1;
end
else if(i<1092) //根据卷积理论输出个数=输入个数1024+滤波器长度69-1=1092
begin
s_axis_data_tvalid<=1;
s_axis_data_tdata <=0;
i <= i + 1;
end
else
begin
s_axis_data_tvalid<=0;
s_axis_data_tdata <= 0;
end
end
end
end
integer j=0;
always @(posedge clk)
begin
if(tvalid)
j<=j+1;
if(34<=j&&j<1058) //掐头去尾
begin
m_axis_data_tvalid<=1;
m_axis_data_tdata<=tdata;
end
else
begin
m_axis_data_tvalid<=0;
m_axis_data_tdata<=0;
end
end
FIR_LPF u_FIR_LPF (
.aclk(clk), // input wire aclk
.s_axis_data_tvalid(s_axis_data_tvalid), // input wire s_axis_data_tvalid
.s_axis_data_tready(s_axis_data_tready), // output wire s_axis_data_tready
.s_axis_data_tdata(s_axis_data_tdata), // input wire [15 : 0] s_axis_data_tdata
.m_axis_data_tvalid(tvalid), // output wire m_axis_data_tvalid
.m_axis_data_tdata(tdata) // output wire [39 : 0] m_axis_data_tdata
);
endmodule
接下来再观察m_axis_data_tdata,发现其输出的第一个数据0.6657就和MATLAB仿真考虑滤波器群延时的数据对应的上了。其中s_LPF2=conv(s,lowpass_b,"same"),考虑了滤波器的群延时。
另外,我们可以让数据以波形的形式显示,发现滤波器滤除了频率为13MHz的正弦波,保留下来了10MHz的正弦波。
六、VIVADO工程下载
总结
本文讲解了VIVADO中FIR滤波器IP核的使用,通过仿真,与MATLAB计算的数据相比较,验证了VIVADO中FIR滤波器本身是没有考虑滤波器的群延时的,但是我们自己可以控制数据有效数据的长度和对输出掐头去尾去除滤波器的群延时,以上的仿真结果很好的说明了如何使用VIIVADO FIR滤波器。