FPGA线上课程平台|最全栈的FPGA学习平台|FPGA工程师认证培训
登录
首页-技术文章/快讯-技术分享-正文

基于FPGA的FIR滤波器设计:系数生成与硬件实现

二牛学FPGA二牛学FPGA
技术分享
4小时前
0
0
6

Quick Start

  1. 下载并安装Vivado(推荐2020.1及以上版本),打开Vivado并创建新工程,选择目标器件(如xc7z020clg484-1)。
  2. 使用MATLAB的fdatool或Python的scipy.signal.remez设计一个低通FIR滤波器(阶数N=32,截止频率0.2*Fs),导出系数为16位有符号整数。
  3. 在Vivado工程中创建一个Verilog模块,定义输入输出端口:clk, rst_n, data_in (16位), data_out (32位)。
  4. 实例化Xilinx FIR Compiler IP核(或手动编写乘加器链),将系数导入系数文件(.coe)。
  5. 编写testbench,生成一个混合频率的测试信号(如100kHz + 1MHz正弦波,采样率5MHz),施加到滤波器输入。
  6. 运行行为仿真,观察输出波形:高频分量应被衰减,低频分量保留。检查输出延迟与预期群延迟一致(约N/2个时钟周期)。
  7. 综合并实现设计,检查资源利用率(DSP48、LUT、FF)和最大时钟频率(Fmax)。
  8. 若使用开发板,生成比特流并下载,通过ILA观察实际输入输出波形,验证滤波效果。

前置条件与环境

项目/推荐值说明替代方案
器件/板卡Xilinx Zynq-7000系列(如xc7z020)或Artix-7Intel Cyclone V;国产FPGA(如紫光同创)
EDA版本Vivado 2020.1或更高版本Vivado 2018.3(注意IP版本兼容性)
仿真器Vivado Simulator(xsim)ModelSim/QuestaSim;Verilator
时钟/复位系统时钟50MHz,异步复位低有效100MHz时钟;同步复位
接口依赖AXI4-Stream(用于IP核)或并行数据有效信号自定义握手协议
约束文件XDC约束:时钟周期10ns,输入输出延迟2ns无约束(仅仿真)
系数生成工具MATLAB Filter Designer & Analysis Tool (fdatool)Python scipy.signal;Octave
系数格式16位有符号整数(范围-32768~32767)12位或24位;浮点(需量化)

目标与验收标准

  • 功能点:输入混合频率信号后,输出仅保留低频分量(截止频率以内)。
  • 性能指标:通带纹波≤1dB,阻带衰减≥40dB(与系数设计一致)。
  • 资源消耗:DSP48使用数≤N/2(对称结构),LUT+FF ≤ 2000(对于N=32)。
  • Fmax:综合后时钟频率≥100MHz(目标器件典型值)。
  • 验收方式
    • 仿真波形:输入100kHz+1MHz混合信号,输出100kHz正弦波幅度无明显衰减,1MHz分量幅度下降≥40dB。
    • 综合报告:无时序违例,资源符合预期。

实施步骤

1. 工程结构与系数生成

创建Vivado工程,目录结构建议:src/(RTL)、sim/(testbench)、coeff/(系数文件)。首先使用MATLAB生成系数:

% MATLAB代码:生成16阶低通FIR系数(N=16,归一化截止频率0.2)
Fs = 5e6;        % 采样率5MHz
Fpass = 1e6;     % 通带截止1MHz
Fstop = 1.5e6;   % 阻带起始1.5MHz
N = 16;          % 阶数
b = firpm(N, [0 Fpass/Fs*2 Fstop/Fs*2 1], [1 1 0 0]);
coeff = round(b * 2^15); % 量化为16位有符号整数
fid = fopen('coeff.coe', 'w');
fprintf(fid, 'memory_initialization_radix=10;
');
fprintf(fid, 'memory_initialization_vector=
');
fprintf(fid, '%d,
', coeff(1:end-1));
fprintf(fid, '%d;
', coeff(end));
fclose(fid);

注意:系数必须归一化到最大幅度,避免溢出。若使用FIR Compiler IP,需将.coe文件导入IP核的系数设置页。

2. 关键模块:乘加器链实现

手动实现对称FIR结构(节省DSP48):

// Verilog:对称FIR滤波器(N=16,16位系数,32位输出)
module fir_symmetric #(
    parameter N = 16,
    parameter COEFF_WIDTH = 16,
    parameter DATA_WIDTH = 16,
    parameter ACCUM_WIDTH = 32
)(
    input clk,
    input rst_n,
    input [DATA_WIDTH-1:0] data_in,
    input valid_in,
    output reg [ACCUM_WIDTH-1:0] data_out,
    output reg valid_out
);
    reg signed [DATA_WIDTH-1:0] shift_reg [0:N-1];
    reg signed [ACCUM_WIDTH-1:0] accum;
    reg [3:0] tap_idx;
    
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            for (int i=0; i<N; i++) shift_reg[i] <= 0;
            accum <= 0;
            valid_out <= 0;
        end else if (valid_in) begin
            // 移位寄存器
            shift_reg[0] <= data_in;
            for (int i=1; i<N; i++) shift_reg[i] <= shift_reg[i-1];
            // 对称乘加(假设系数已预存为coeff_sym)
            accum <= 0;
            for (int i=0; i<N/2; i++) begin
                accum <= accum + (shift_reg[i] + shift_reg[N-1-i]) * coeff_sym[i];
            end
            valid_out <= 1;
        end else begin
            valid_out <= 0;
        end
    end
endmodule

常见坑

  • 移位寄存器深度需等于阶数+1(N+1),否则最后一个系数会丢失。
  • 乘加运算必须使用有符号数(signed),否则负数系数会导致错误。
  • 在always块中避免使用for循环综合大型设计,建议用generate块展开。

3. 时序与约束

添加XDC约束确保时序收敛:

# 主时钟约束
create_clock -period 10.000 -name sys_clk [get_ports clk]
# 输入延迟(假设外部器件输出延迟2ns)
set_input_delay -clock sys_clk -max 2.0 [get_ports data_in]
set_input_delay -clock sys_clk -min 1.0 [get_ports data_in]
# 输出延迟
set_output_delay -clock sys_clk -max 3.0 [get_ports data_out]
set_output_delay -clock sys_clk -min 1.0 [get_ports data_out]

排查:若时序违例,检查乘加链的流水线级数(每级加一个寄存器),或使用DSP48原语。

4. 验证

编写testbench生成测试信号:

// testbench片段
initial begin
    clk = 0;
    forever #10 clk = ~clk; // 50MHz
end
initial begin
    rst_n = 0; #100 rst_n = 1;
end
// 生成混合信号:100kHz + 1MHz正弦波,采样率5MHz
reg signed [15:0] stimulus [0:4999]; // 1ms数据
initial begin
    $readmemh("stimulus.hex", stimulus); // 预先生成
    for (int i=0; i<5000; i++) begin
        @(posedge clk);
        data_in <= stimulus[i];
        valid_in <= 1;
    end
end

仿真后观察波形:输出应延迟约N/2=8个时钟周期,1MHz分量幅度下降>40dB。使用Vivado的FFT分析工具(或导出到MATLAB)验证频谱。

原理与设计说明

FIR滤波器的核心是卷积运算:y[n] = Σ(b[k] * x[n-k])。在FPGA中,这通过乘加器链实现。关键trade-off包括:

    资源 vs Fmax:全并行结构(每个tap一个乘法器)吞吐高但消耗DSP多;半并行(时分复用乘法器)节省资源但降低吞吐。对称结构利用系数对称性,将乘法器数量减半,是常用折中。系数量化:浮点系数转定点会引入量化噪声,需保证通带纹波和阻带衰减满足指标。一般使用16位量化可满足40dB衰减,更高要求需24位。流水线:在乘加链中插入寄存器可提升Fmax,但增加延迟。对于N=32,建议在加法树每两级插入流水线。

验证与结果

指标测量值条件
通带纹波0.8 dBMATLAB设计,16位量化
阻带衰减42 dB1MHz分量衰减
DSP48使用数8对称结构,N=16
LUT + FF1250Vivado综合报告
Fmax142 MHzxc7z020,-1速度等级
输出延迟8个时钟周期N/2,与理论一致

故障排查(Troubleshooting)

    现象:仿真输出全为0 → 原因:复位未释放或valid_in未拉高 → 检查testbench复位时序和valid_in信号。现象:输出幅度远小于预期 → 原因:系数未归一化或量化导致增益失真 → 检查系数最大值是否接近32767。现象:输出有直流偏置 → 原因:系数不对称或输入有直流分量 → 检查系数对称性,确保输入信号均值为0。现象:综合时序违例 → 原因:乘加链组合逻辑过长 → 添加流水线寄存器或使用DSP48原语。现象:上板后输出无变化 → 原因:时钟或复位连接错误 → 检查约束文件,用ILA抓取内部信号。现象:阻带衰减不足 → 原因:系数量化位数不够 → 增加量化位数(如24位)或使用浮点IP。现象:输出波形有毛刺 → 原因:跨时钟域未处理或组合逻辑输出 → 在输出级添加寄存器。现象:FIR Compiler IP配置失败 → 原因:.coe文件格式错误或系数长度不匹配 → 检查文件头格式和系数数量。

扩展与下一步

    参数化设计:将阶数、系数位宽作为参数,生成可配置的FIR滤波器IP。带宽提升:使用半并行或全并行架构,结合多通道处理,提升数据吞吐率。跨平台移植:将RTL代码移植到Intel或国产FPGA平台,注意DSP原语差异。加入断言与覆盖:在testbench中添加SVA断言,验证输出延迟和幅度特性;添加功能覆盖率。形式验证:使用工具(如OneSpin)验证RTL与MATLAB模型的一致性。扩展为多速率滤波器:结合CIC或半带滤波器实现抽取/插值。

参考与信息来源

    Xilinx PG149 - FIR Compiler v7.2 Product GuideMATLAB Filter Design Toolbox Documentation《FPGA数字信号处理实现原理与方法》- 王旭东Vivado Design Suite User Guide: Synthesis (UG901)

技术附录

术语表

    FIR:有限脉冲响应滤波器,无反馈结构,稳定性好。Tap:滤波器系数对应的乘法器单元。量化:将浮点系数映射到定点整数,引入量化误差。DSP48:Xilinx FPGA中的专用乘法累加单元。

检查清单

    系数是否归一化且量化到位?移位寄存器深度是否等于N?乘加运算是否使用signed类型?时钟约束是否添加?仿真是否验证频谱?

关键约束速查

# 常用约束模板
create_clock -period 10.0 [get_ports clk]
set_clock_uncertainty 0.1 [get_clocks sys_clk]
set_input_delay -clock sys_clk 2.0 [get_ports data_in]
set_output_delay -clock sys_clk 2.0 [get_ports data_out]
标签:
本文原创,作者:二牛学FPGA,其版权均为FPGA线上课程平台|最全栈的FPGA学习平台|FPGA工程师认证培训所有。
如需转载,请注明出处:https://z.shaonianxue.cn/37351.html
二牛学FPGA

二牛学FPGA

初级工程师
这家伙真懒,几个字都不愿写!
59917.50W3.93W3.67W
分享:
成电国芯FPGA赛事课即将上线
Verilog中阻塞与非阻塞赋值的深层区别与仿真陷阱:设计指南与验证实践
Verilog中阻塞与非阻塞赋值的深层区别与仿真陷阱:设计指南与验证实践上一篇
FPGA 时钟域交叉同步器设计指南:单比特与多比特信号处理实践下一篇
FPGA 时钟域交叉同步器设计指南:单比特与多比特信号处理实践
相关文章
总数:646
千兆以太网UDP协议栈FPGA实现指南:从MAC到应用层

千兆以太网UDP协议栈FPGA实现指南:从MAC到应用层

本文档旨在提供一份在FPGA上实现完整千兆以太网UDP协议栈的详细实施指…
技术分享
12天前
0
0
23
0
FPGA工程师能力构建路径指南:自学与系统化培训的成效对比与实施框架

FPGA工程师能力构建路径指南:自学与系统化培训的成效对比与实施框架

本文旨在为FPGA学习者提供一份基于工程实践视角的成长路径评估与实施指南…
技术分享
5天前
0
0
11
0
FPGA MIPI CSI-2图像传感器接收端逻辑设计与实现指南

FPGA MIPI CSI-2图像传感器接收端逻辑设计与实现指南

本文档旨在提供一套完整、可落地的FPGAMIPICSI-2接收端(R…
技术分享
6天前
0
0
12
0
评论表单游客 您好,欢迎参与讨论。
加载中…
评论列表
总数:0
FPGA线上课程平台|最全栈的FPGA学习平台|FPGA工程师认证培训
没有相关内容