Vivado的FIR IP核实现低通滤波器

  本文介绍如何使用Vivado的FIR IP核实现低通滤波器。我们将设计一个采样频率为10MHz,通带0~1MHz,阻带高于2MHz的FIR低通滤波器。测试时,滤波器的输入信号为1MHz和3MHz的正弦波的叠加信号,期望滤波器能输出失真较小的1MHz的正弦信号。

1、用MATLAB的firpm函数设计FIR低通滤波器

  MATLAB的firpm函数能够设计最优FIR滤波器,详细用法可以参考MATLAB的help文档。这里,我们先用采用kaiserord函数获取满足要求的最小滤波器阶数,再用firpm设计滤波器。滤波器的系数采用12bit有符号量化,并输出.coe文件,方便Vivado导入到FIR IP核。下面给出完整MATLAB代码。

%设计一个低通滤波器。
%采样频率fs=10MHz,过渡带fc=[1MHz 2MHz],通带容限0.1,阻带容限0.01。
fs=10*10^6;           %采样频率
qm=12;               %滤波器系数量化位数
fc=[1*10^6 2*10^6];  %过渡带
mag=[1 0];           %窗函数的理想滤波器幅度
%设置通带容限a1及阻带容限a2
%通带衰减ap=-20*log10(1-a1)=0.915dB,阻带衰减为as=-20*log10(a2)=40dB
a1=0.1;a2=0.01;
dev=[a1 a2];
%采用凯塞窗函数获取满足要求的最小滤波器阶数
[n,~,~,~] = kaiserord(fc,mag,dev,fs);
%采用firpm函数设计最优滤波器
fpm=[0 fc(1)*2/fs fc(2)*2/fs 1];  %firpm函数的频段向量
magpm=[1 1 0 0];                  %firpm函数的幅值向量
h_pm=firpm(n,fpm,magpm);          %设计最优滤波器
h_pm = h_pm/max(abs(h_pm));
%量化滤波系数
q_pm=round(h_pm*(2^(qm-1)-1));%12bit有符号整数
%将生成的滤波器系数数据写入FPGA所需的coe文件中
fid=fopen('.\fir_coe.coe','w');
fprintf(fid, "RADIX=10;\n");%生成索引
fprintf(fid, "COEFDATA = \n");
for i = 1:n+1
    if i == n+1
        fprintf(fid,'%d;', q_pm(i));
    else
        fprintf(fid,'%d,', q_pm(i));
    end
end
fclose(fid);
%获取量化前后滤波器的幅频响应数据
m_pm=20*log10(abs(fft(h_pm,1024)));
m_pm=m_pm-max(m_pm);
q_pm=20*log10(abs(fft(q_pm,1024)));
q_pm=q_pm-max(q_pm);
%设置幅频响应的横坐标单位为MHz
x_f = (0:(fs/length(m_pm)):fs/2)/10^6;
%只显示正频率部分的幅频响应
mf_pm=m_pm(1:length(x_f));
mf_qm=q_pm(1:length(x_f));
%绘制幅频响应曲线
plot(x_f,mf_pm,'--',x_f,mf_qm,'-');
xlabel('频率(MHz)');ylabel('幅度(dB)');
legend('未量化','12位量化');
grid;

  下图展示了FIR低通滤波器量化前后的归一化幅频响应,可以看到没有明显的量化误差。
Vivado的FIR IP核实现低通滤波器_第1张图片

2、配置Vivado的FIR IP核

  2.1 新建一个Vivado工程,点击Flow Navigator中的IP Catalog,选择FIR Compiler,打开FIR IP核的配置窗口。
Vivado的FIR IP核实现低通滤波器_第2张图片  2.2 在Filter Options页中向IP核导入MATLAB输出的.coe文件。
Vivado的FIR IP核实现低通滤波器_第3张图片  2.3 在Channel Specification页设置时钟频率为50MHz,采样频率为10MHz(与MATLAB中的设计一致)。
Vivado的FIR IP核实现低通滤波器_第4张图片  2.4 根据前面MATLAB的设计,Implementation页设置FIR滤波器的系数为12bit有符号整数。FIR输入数据设置为16bit有符号整数,FIR输出位宽被自动设置为30bit。其他保持默认设置。
Vivado的FIR IP核实现低通滤波器_第5张图片  2.5 介绍FIR IP核的主要接口。
Vivado的FIR IP核实现低通滤波器_第6张图片

  • aresetn:复位引脚,低电平复位FIR IP核;
  • aclk:时钟引脚,工程中输入50MHz时钟信号;
  • s_axis_data_tdata:输入采样数据;
  • s_axis_data_tready:1 表示IP核准备好接收采样数据;
  • s_axis_data_tvalid:1 表示s_axis_data_tdata输入的采样数据有效;
  • m_axis_data_tdata:输出滤波后的数据;
  • m_axis_data_tvalid:1 表示m_axis_data_tdata输出的数据有效。
    Vivado的FIR IP核实现低通滤波器_第7张图片
    Vivado的FIR IP核实现低通滤波器_第8张图片

3、编写Verilog代码例化FIR IP核

  代码例化一个FIR IP核,引出必要的控制和数据接口。

module project_fir(
    input clk,
    input reset_n,
    input s_axis_data_tvalid,
    output s_axis_data_tready,
    input [15:0] s_axis_data_tdata,
    output m_axis_data_tvalid,
    output [31:0] m_axis_data_tdata
    );
fir_compiler_0 your_instance_name (
      .aresetn(reset_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 [31 : 0] m_axis_data_tdata
    );  
endmodule

4、编写test bench文件

  test bench文件的功能是生成复位信号、50MHz的时钟信号、s_axis_data_tvalid信号等,用于仿真。系统时钟频率为50MHz,采样频率为10MHz,s_axis_data_tvalid需要每五个时钟周期产生一个时钟周期的正脉冲驱动FIR IP核读入采样数据。

`timescale 1ns / 1ps
module tb_fir();
parameter LEN = 1000;
reg clk;
reg reset_n;
reg [15:0] signal[LEN-1:0];
reg [15:0] data_tdata;
reg data_tvalid;
reg [3:0] cnt;
wire s_axis_data_tvalid;
wire s_axis_data_tready;
wire [15:0] s_axis_data_tdata;
wire m_axis_data_tvalid;
wire [31:0] m_axis_data_tdata;
initial begin
    clk = 1'b0;
    reset_n = 1'b0;
    data_tvalid = 1'b0;
    $readmemh("signal.txt", signal);//从signal.txt中读入采样数据
    #200
    reset_n = 1'b1;
    #100
    repeat(5) begin //filter task重复执行5次
        filter;
    end
end
always #10 clk = ~clk; // 50MHz
always @(posedge clk or negedge reset_n) begin
    if(!reset_n) begin
        cnt <= 4'd0;
        data_tvalid <= 1'b0;
    end
    else begin
        cnt <= cnt +4'd1;
        if(cnt == 4) begin
            cnt <= 4'd0;
            data_tvalid <= 1'b1;
        end
        else
            data_tvalid <= 1'b0;
    end
end
assign s_axis_data_tvalid = data_tvalid; //生成 s_axis_data_tvalid 脉冲信号
assign s_axis_data_tdata = data_tdata;
integer k;
task filter;
    begin
        for(k = 0; k < LEN; k = k+1)
            #100 data_tdata = signal[k]; //间隔100ns(10MHz)读入一个采样数据
    end
endtask
project_fir fir( //例化第3节写的模块
    .clk(clk),
    .reset_n(reset_n),
    .s_axis_data_tvalid(s_axis_data_tvalid),
    .s_axis_data_tready(s_axis_data_tready),
    .s_axis_data_tdata(s_axis_data_tdata),
    .m_axis_data_tvalid(m_axis_data_tvalid),
    .m_axis_data_tdata(m_axis_data_tdata)
    );
endmodule

  $readmemh(“signal.txt”, signal);该行代码从signal.txt文件中读取16进制表示的采样数据序列到signal存储器中。signal.txt文件用MATLAB生成,并将signal.txt放到工程的".\project_fir.sim\sim_1\behav\xsim"路径下才能被正确读取。
Vivado的FIR IP核实现低通滤波器_第9张图片  生成signal.txt的MATLAB代码如下:

qm=16;%16bit量化
fs = 10000000;%采样频率10MHz
ts = 1/fs;
len = 1000;
t = (0:len-1)*ts;
y = sin(2*pi*1000000*t)+cos(2*pi*3000000*t);%1MHz和3MHz正弦信号叠加
plot(t,y);
y = y/max(abs(y));
q_y=round(y*(2^(qm-1)-1));
fid=fopen('.\signal.txt','w');
for i = 1:len
    if q_y(i) >= 0
        fprintf(fid,'%X\n', q_y(i));
    else
        fprintf(fid,'%X\n', q_y(i)+2^qm);
    end
end
fclose(fid);

5、功能仿真

  编写好FPGA的文件和test bench文件后,进行Behavioral Simulation。点击SIMULATION->Run Simulation->Run Behavioral Simulation,等待一会儿就能看到仿真波形。将s_axis_data_tdata和m_axis_data_tdata波形设置为有符号数、模拟量显示,就可以看到输入的叠加正弦信号和输出的1MHz正弦信号。
Vivado的FIR IP核实现低通滤波器_第10张图片  从上图可以看到两个tvalid信号以10MHz的频率产生脉冲,复位结束后tready信号一直有效,FIR低通滤波器滤除了3MHz的正弦信号,输出了1MHz的正弦信号。
  QuartusII的FIR IP核使用方法可以参考杜勇的《数字调制解调技术的MATLAB与FPGA实现 Altera Verilog版》。
完整Vivado工程下载:Vivado的FIR IP核实现低通滤波器工程

你可能感兴趣的:(FPGA,fpga开发,matlab)