平台:Vivado2021.1
芯片:xcku115-flva1517-2-i (active)
语言:VerilogHDL
参考文件:pg149.下载地址
FIR Compiler LogiCORE IP Product Guide • FIR Compiler (PG149) • 阅读器 • AMD 自适应计算文档门户 (xilinx.com)
FIR滤波器
最近准备研究以下滤波器。还是从xilinx的官方IP出发,来学习以下这部分。
使用matlab直观的感受以下。输入信号为5khz,和10mhz正弦波叠加。
设置FIR滤波器参数。采样率为50mhz,通带起始频率为100KHz,阻带起始频率为1MHz。
使用matlab打开滤波器设计小工具。产生COE文件。
直接输入filterDesigner。
打开后的界面如下图所示。
我们按照设计参数对滤波器进行设置。
选择响应类型为低通。选择为FIR滤波器。采样率为50mhz,通带起始频率为100KHz,阻带起始频率为1MHz。设置好后选择设置量化参数。
滤波器算法选择定点。分子字长选择16位。设置完成后点击应用。
选择目标。Xilinx系数(.coe)文件。保存在你选定的文件夹中。
这样就生成了需要的FIR滤波器参数。可以在IP设计阶段直接导入到xilinx的IP中。
同时还需要将生成的波形数据进行归一化处理。在量化到2^16次方上去。
所以需要使用matlab产生用于IP核仿真的数据。
%对数据进行归一化
max_abs_value = max(abs(x)); % 获取数据的最大绝对值
normalized_data = x / max_abs_value; % 归一化数据
% 量化为16位数据
simdata = normalized_data;
quantized_data = round(simdata * (2^15 - 1));
% 转换为十六进制数据
hex_data = dec2hex(quantized_data, 4);
%保存数据文件
fileID = fopen('E:\simdata.txt','w');%将数据写入txt。
% fprintf(fileID,'%d\n',quantized_data );%保存为十进制数据
for i = 1:length(hex_data)
fprintf(fileID, '%s\n', hex_data(i, :)); % 将每行的十六进制数据写入文件
end
fclose(fileID);
这里对产生的波形数据进行过归一化后。转换为16进制数据写入到文件simdata.txt中。
下面是整体的matlab代码。
fs = 50000000; % 采样率为50MHz
t = 0:1/fs:0.01; % 时间向量,采样时间为0.01秒
f1 = 5000; % 5kHz正弦波频率
f2 = 10000000; % 10mHz正弦波频率
x = sin(2*pi*f1*t) + sin(2*pi*f2*t) ; % 输入信号,多个正弦波叠加
%对数据进行归一化
max_abs_value = max(abs(x)); % 获取数据的最大绝对值
normalized_data = x / max_abs_value; % 归一化数据
% 量化为16位数据
simdata = normalized_data;
quantized_data = round(simdata * (2^15 - 1));
% 转换为十六进制数据
hex_data = dec2hex(quantized_data, 4);
%保存数据文件
fileID = fopen('E:\CODE\Vivado\KU_TEST\ku_test_sim\flash_test\coe\simdata.txt','w');%将数据写入txt。
% fprintf(fileID,'%d\n',quantized_data );%保存为十进制数据
for i = 1:length(hex_data)
fprintf(fileID, '%s\n', hex_data(i, :)); % 将每行的十六进制数据写入文件
end
fclose(fileID);
order = 10; % FIR滤波器阶数
cutoff = [100000]; % 截止频率
b = fir1(order, cutoff/(fs/2)); % 设计FIR低通滤波器系数
y = filter(b, 1, x); % 应用FIR滤波器
% 时域波形绘制
subplot(2, 2, 1);
plot(t, x);
title('滤波前的时域波形');
xlabel('时间');
ylabel('幅值');
subplot(2, 2, 3);
plot(t, y);
title('滤波后的时域波形');
xlabel('时间');
ylabel('幅值');
% 频域波形绘制
X = fft(x);
Y = fft(y);
f = linspace(0, fs, length(t));
subplot(2, 2, 2);
plot(f, abs(X));
title('滤波前的频域波形');
xlabel('频率');
ylabel('幅值');
subplot(2, 2, 4);
plot(f, abs(Y));
title('滤波后的频域波形');
xlabel('频率');
ylabel('幅值');
使用matlab产生两个波形叠加。通过FIR滤波器后,画出了滤波前后的时域频域波形。
下面是FPGA部分。
IP核的资料是PG149,里面详细的介绍了这个IP资源。这里我们从应用的角度出发来使用这个IP。
接口方面。还是使用了熟悉的AXI接口。
接口非常的简单。只需要输入valid核数据即可。输出端有valid和数据有效。
此IP核的主界面。
在左侧部分分别是,IP核的接口。频率响应曲线。以及详细信息。和系数重载功能。
选择系数源,分别为coefile(COE文件)和Vector(系数矢量)。
这里我们选择coe文件,我们直接从matlab中生成的coe文件。直接选定到文件夹。
Number of Coefficient Sets多个系数集,对于多系数过滤器,单个.coe文件用于指定系数集。 每个系数集应附加到前一组系数。我们这里没有使用。
Number of Coefficients (per set)系数数量(每组):每个滤波器组的滤波器系数数量。自动计算。
Use Reloadable Coefficients使用可重新加载的系数:当选择重新加载选项时,在核心上提供一个系数重新加载接口。这里我们不使用。
Filter Type滤波器类型:支持五种滤波器类型:单速率FIR(有限脉冲响应滤波器)、插值FIR、抽取FIR、Hilbert变换和插值FIR。
我们选择为单速率。
下面的速率变化类型和插值速率抽值速率。分别对应于不同类型的滤波器设置。
下面是通道规范页。
Channel Sequence支持基本的和高级的。我们选择基本的即可。
Number of Channels通道数我们选择一通道。
下面的Select Sequence和Sequence ID List在高级模式中使用。
Select format选择格式。
Sample Period采样周期。
Input Sampling Frequency采样频率。跟你设置的采样频率一致。
Clock Frequency时钟频率。
Coefficient Type系数数据可以指定为有符号或无符号。
Quantization量化方式指定为整数。
Coefficient Width系数位宽,这里选择为16位模式。选择后在matlab中生成coe文件时选择一致。
Coefficient Structure系数结构:支持五种系数结构:非对称、对称、负对称、半带和希尔伯特。
数据路径选择。
同样的,选择输入数类型,数据位宽。这里选择后需要在模拟产生输入数据时,产生一致。
Output Rounding Mode输出数据的精度。默认为全精度。后面的选择可以选择输出数据位宽。
详细页
Goal优化选项。使用最下面积最快速度和custom定制。
Select Optimization和List选择分别定制优化。
下面的Memory Options就是选择缓存的类型。这里可以根据你的需求自己选择或者自动。
DSP Slice Column Options:DSP选择。
Multi-column Support可以选择为自动和定制。
Interface Tab接口页。
Data Channel Options选择AXI接口相关的参数。
TLAST指示AXI需要支持TLAST位不。以及是否使用tuser,tready等。
Configuration Channel Options
CONFIG通道用于选择活动的滤波器系数集。该通道还用于应用重新加载的滤波器系数。
Reload Channel Options和重加载相关。
Control Signals控制信号。选择复位时钟等等。
总结界面。
我们在顶层例化该IP核。
下面我们对该IP核进行仿真。
下面分别是例化代码和仿真tb。
// *********************************************************************************/
// Project Name :
// Author : i_huyi
// Email : i_huyi@qq.com
// Creat Time : 2023/10/20 16:46:10
// File Name : .v
// Module Name :
// Called By :
// Abstract :
//
// CopyRight(c) 2020, xxx xxx xxx Co., Ltd..
// All Rights Reserved
//
// *********************************************************************************/
// Modification History:
// 1. initial
// *********************************************************************************/
// *************************
// MODULE DEFINITION
// *************************
`timescale 1 ns / 1 ps
module fir#(
parameter U_DLY = 1
)
(
//
input wire[15:0] fir_data_in ,
input wire fir_data_inen ,
output wire[39:0] fir_data_out ,
output wire fir_data_outvalid ,
//
input wire clk ,
input wire rst_n
);
//--------------------------------------
// localparam
//--------------------------------------
//--------------------------------------
// register
//--------------------------------------
//--------------------------------------
// wire
//--------------------------------------
wire s_axis_data_tvalid ;
wire[15:0] s_axis_data_tdata ;
wire s_axis_data_tready ;
wire m_axis_data_tvalid ;
wire[39:0] m_axis_data_tdata ;
//--------------------------------------
// assign
//--------------------------------------
assign s_axis_data_tvalid = fir_data_inen;
assign s_axis_data_tdata = fir_data_in;
assign fir_data_out = m_axis_data_tdata;
assign fir_data_outvalid = m_axis_data_tvalid;
//------------------------------------------------------------
//------------------------------------------------------------
fir_compiler_0 u_fir_compiler_0 (
.aresetn (rst_n ),// input wire aresetn
.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
仿真tb
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2023/10/23 09:33:40
// Design Name:
// Module Name: fir_tb
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module fir_tb;
//input
reg [15:0] fir_data_in;
reg fir_data_inen;
reg clk;
reg rst_n;
reg [15:0] simdata [0:32767];
reg [15:0] read_data;
//output
wire[39:0] fir_data_out;
wire fir_data_outvalid;
fir u_fir(
.fir_data_in (fir_data_in ),
.fir_data_inen (fir_data_inen ),
.fir_data_out (fir_data_out ),
.fir_data_outvalid (fir_data_outvalid ),
//
.clk (clk ),
.rst_n (rst_n )
);
//------------------------------------------------------
//复位参数
//------------------------------------------------------
integer i;
//设置复位参数
initial
begin
$display("[%t] : reset begin...", $realtime);
rst_n = 0;
for( i=0 ; i<100 ; i=i+1)
begin
@(posedge clk );
end
$display("[%t] : reset stop...", $realtime);
rst_n = 1;
end
initial
begin
clk = 0;
read_data = 0;
fir_data_in = 0;
fir_data_inen = 0;
wait(rst_n == 1);
fir_data_inen = 1;
$display("[%t] : read data begin...", $realtime);
repeat(32767)
begin
@(posedge clk);
read_data = read_data + 16'd1;
fir_data_in = simdata[read_data];
end
repeat(100)@(posedge clk);
$display("[%t] : read data stop...", $realtime);
$finish(0);
end
initial
begin
$readmemh("E:/CODE/Vivado/KU_TEST/ku_test_sim/flash_test/coe/simdata.txt",simdata);
end
always #2 clk = ~clk;
endmodule
在经过一段时间的仿真后,我们看到通过模拟产生的正弦波数据的高频分量在FIR滤波器的作用下只保留了低频部分。
今天的FIR滤波器就学习到这里。