做数字信号处理的同学刚接触 FPGA 实现 IIR 滤波器时总会被一堆专业名词砸晕 —— 差分方程、零点极点、定点化、符号扩展、算术右移…… 看着代码好像每个关键字都认识凑一起就不知道在干啥。这篇文章彻底抛开晦涩术语用生活类比 逐行代码拆解 手算实例带你从零搞懂 IIR 滤波器的 FPGA 实现逻辑看完就能对应到自己的工程代码里。一、先搞懂IIR 滤波器到底是啥1. 滤波器一个 “信号筛子”你可以把滤波器想象成一个信号筛子输入是一段混杂了杂音的信号比如人声 高频电流杂音输出是筛掉杂音后的干净信号我们常说的「低通滤波器」本质就是留下低频的有效信号筛掉高频的噪声干扰。2. FIR 与 IIR 的核心区别有没有 “回头路”两种最常见的数字滤波器核心差异就在有没有反馈机制FIR 滤波器只有一条 “向前走” 的通路信号从左边进一层一层过筛子直接从右边输出没有回头路。优点是绝对稳定不会 “炸机”缺点是要想滤波效果好得堆很多层 “筛子”非常占用 FPGA 资源。IIR 滤波器多了一条 “回头路”—— 把输出的一部分再送回输入端重新参与运算这个机制就叫反馈。优点是只用很少几层 “筛子”就能实现很强的滤波效果省资源缺点是反馈比例没调好的话信号会越放越大最终发散失控。一句话总结IIR 带循环反馈的信号筛子用更少资源实现更强滤波。3. 专业名词大白话对照表把所有劝退人的术语全部翻译成日常表达对照着看再也不懵表格专业名词大白话解释差分方程IIR 的 “计算公式”当前输出 一部分当前 历史输入 - 一部分历史输出零点系数 b前向通路输入侧每层筛子的 “松紧比例”决定输入信号的权重占比极点系数 a反馈通路输出回传侧每层筛子的 “松紧比例”决定反馈信号的权重占比延迟单元 z⁻¹就是一个 “寄存格子”把上一个时刻的数值存起来下一拍再拿出来用在 FPGA 里就是寄存器直接 I 型先走完所有前向筛子再走反馈回路两条通路完全分开结构最直白易懂定点化FPGA 算小数很麻烦于是把所有小数系数先放大 N 倍变成整数算完再缩小 N 倍还原这个过程就叫定点化缩放因子定点化里放大 / 缩小的那个倍数常见的是 2 的整数次幂比如典型代码里常用 5122 的 9 次方符号扩展负数在 FPGA 里用补码表示两个位数不一样的负数相加减要把短位数的符号位复制补齐否则会算错正负右移实现除法除以 2 的 n 次方直接把二进制数往右挪 n 位就能实现比专门做除法器省超多资源是 FPGA 设计的必用技巧二、代码逐行拆解直接 I 型 IIR 的硬件逻辑我们以一段典型的直接 I 型 IIR 顶层代码为例拆成模块逐个讲完全对应上面的 “筛子” 类比。1. 两个子模块两套独立的筛子代码里例化了zero和pole两个模块分别对应前向通路和反馈通路verilogzero U0(.Xin(Din), .Xout(Xout)); // 前向筛子处理输入信号 pole U1(.Yin(Yin), .Yout(Yout)); // 反馈筛子处理反馈回来的历史输出1zero 模块前向通路 / 零点核心功能把「当前输入、上一拍输入、上上拍输入……」分别乘以对应的 b 系数然后全部累加得到前向通路的加权总和Xout。以二阶为例运算逻辑就是Xout b0*当前输入 b1*上一拍输入 b2*上上拍输入为什么输出位宽比输入宽因为 12 位的输入乘以系数、再累加数值会变大位数不够就会溢出出错所以中间运算必须用更宽的位宽预留余量。2pole 模块反馈通路 / 极点核心功能把「上一拍输出、上上拍输出……」分别乘以对应的 a 系数然后全部累加得到反馈通路的加权总和Yout。以二阶为例运算逻辑就是Yout a1*上一拍输出 a2*上上拍输出为什么位宽比 zero 模块更宽因为反馈是循环累加的数值会持续堆叠动态范围更大需要更多的位数防止溢出。2. 核心反馈环路混合 缩放还原这三行是 IIR 滤波器的灵魂对应反馈、定点化还原的完整逻辑verilogwire signed [25:0] Ysum {{5{Xout[20]}},Xout} - Yout; // 减法前向总和 - 反馈总和 wire signed [25:0] Ydiv {{9{Ysum[25]}},Ysum[25:9]}; // 除法除以512还原定点缩放 assign Yin rst ? d0 : Ydiv[11:0]; // 结果回传输出第一步符号扩展 减法求和verilog{{5{Xout[20]}},Xout}这行就是符号扩展操作Xout是 21 位Yout是 26 位位宽不同的有符号数不能直接相加减把Xout的最高位符号位0 代表正数、1 代表负数复制 5 次拼接在高位把 21 位补成 26 位补位后正负属性不变负数运算才不会出错。完成位宽对齐后执行减法前向加权和 - 反馈加权和对应 IIR 差分方程的核心逻辑当前运算结果 输入加权和 - 历史输出加权和第二步算术右移 定点化还原verilog{{9{Ysum[25]}},Ysum[25:9]}这行是算术右移 9 位等价于有符号数除以 2⁹ 512。为什么要除以 512这就是定点化的 “还原操作”前面 zero 和 pole 模块里的 b、a 系数原本是小数比如 0.1、0.2FPGA 做整数运算效率最高所以设计时把所有系数都乘以 512转成整数参与运算全部运算完成后再把结果除以 512还原成真实的物理数值。这里同样做了符号扩展保证负数右移时高位补 1不会出现正负错乱。第三步截位输出 反馈回传verilogassign Yin rst ? d0 : Ydiv[11:0]; assign Dout Yin;复位时强制输出 0让所有寄存器初始状态为 0避免上电状态混乱正常工作时从 26 位的运算结果里截取低 12 位作为最终结果这个结果兵分两路一路送回pole模块输入端参与下一拍的反馈运算这就是 IIR 的 “循环” 本质另一路直接作为整个滤波器的输出。三、手算一遍一阶 IIR 实例彻底搞懂运算逻辑光看代码抽象我们用一个最简单的一阶 IIR 举例子数字全部凑成整数跟着算一遍就全通了。例子设定滤波公式y[n] 0.5*x[n] 0.5*y[n-1]一阶低通实现信号平滑效果定点化规则缩放因子选 22 的 1 次方所有系数乘以 2 转成整数零点系数 b0 0.5 × 2 1极点系数 a1 -0.5 × 2 -1输入序列x [4, 0, 0, 0, 0] 第一个时刻输入 4之后全为 0也就是冲激信号初始状态第 0 拍之前的输出 y [-1] 0逐拍手算过程定点整数版第 0 拍n0输入 x [0]4前向加权和1 × 4 4反馈加权和(-1) × y [-1] (-1) × 0 0运算总和4 - 0 4除以 2 还原缩放4 ÷ 2 2本拍输出 y [0] 2第 1 拍n1输入 x [1]0前向加权和1 × 0 0反馈加权和(-1) × y [0] (-1) × 2 -2运算总和0 - (-2) 2除以 2 还原缩放2 ÷ 2 1本拍输出 y [1] 1第 2 拍n2输入 x [2]0前向加权和1 × 0 0反馈加权和(-1) × y [1] (-1) × 1 -1运算总和0 - (-1) 1除以 2 还原缩放0.5 → 整数截位后为 0本拍输出 y [2] 0你看只输入了一个 4输出是 2、1、0…… 慢慢衰减下去这就是 IIR “无限冲激响应” 的含义 —— 输入消失了输出还在慢慢变化理论上永远不会绝对归零。工程里的高阶 IIR 代码逻辑和这个手算例子完全一致只是阶数更高、位宽更大、缩放因子更大而已。四、FPGA 设计 IIR 的完整步骤通俗版搞懂原理后完整的工程设计流程也很好理解定需求明确滤波目标、采样率、输入输出位宽、通带阻带衰减等指标算系数用 MATLAB 的 Filter Designer 等工具输入需求算出浮点格式的 b、a 系数定点化选择合适的缩放因子把小数系数量化为整数同时规划好每一级运算的位宽搭结构划分前向、反馈通路用寄存器做延迟、乘法器乘系数、加法器做累加防溢出中间运算位宽留足余量输出端可增加饱和逻辑防止溢出反转写代码 仿真编写 Verilog 代码输入测试信号验证滤波效果与精度五、初学者常见疑问解答1. 为什么用减法不用加法标准 IIR 传递函数的分母形式是1 a1 z⁻¹ ...移项整理成差分方程后反馈项就是减法形式因此硬件里用减法实现。2. 为什么一定要定点化不能直接算小数吗FPGA 也可以实现浮点运算但浮点运算器资源占用极大、运行速度慢。滤波器这类需要高速、重复运算的模块全行业都采用定点整数方案性价比最高。3. 定点截位会不会算错精度够吗截位确实会引入微小的量化误差但只要缩放因子选取得当、中间位宽预留充足误差完全在可接受范围内音频处理、工业控制、传感器信号采集等绝大多数场景都能满足要求。写在最后IIR 滤波器看似复杂本质就是 “前向加权 反馈加权 缩放还原” 的循环逻辑。只要理解了反馈的意义、定点化的原因、每一行代码对应的运算就能从 “能跑就行” 进阶到 “知道为什么这么写”。如果是刚入门 FPGA 数字信号处理建议先从一阶、二阶 IIR 入手手写代码 仿真对比 MATLAB 结果上手会非常快。