IIR数字滤波器的Verilog实现:FPGA硬件设计与优化指南

📅 2026/7/19 8:00:42
IIR数字滤波器的Verilog实现:FPGA硬件设计与优化指南
这次我们来看IIR数字滤波器的Verilog实现。对于FPGA开发者来说数字滤波器的硬件实现是个常见需求而IIR滤波器以其较高的效率在实时信号处理中很有价值。本文将重点讨论如何在Verilog中实现IIR滤波器包括核心结构选择、定点数处理、资源优化和实际验证方案。IIR无限脉冲响应滤波器与FIR滤波器相比主要优势是在相同性能要求下需要较少的乘法器资源但缺点是可能引入稳定性问题。在FPGA中实现IIR滤波器时需要特别关注数值精度、溢出处理和流水线设计。1. 核心能力速览能力项说明滤波器类型IIR无限脉冲响应滤波器实现方式直接I型、直接II型、级联型数据精度可配置常用12-16位定点数主要资源乘法器、加法器、寄存器适用场景音频处理、通信系统、实时信号处理稳定性考虑需要系数缩放和溢出保护2. IIR滤波器结构选择在Verilog中实现IIR滤波器时首先需要确定使用哪种结构。常见的结构有直接I型、直接II型和级联型。直接I型结构直观但资源消耗较大直接II型也称典范型节省寄存器但可能存在数值精度问题。对于高阶滤波器推荐使用级联型结构将高阶滤波器分解为多个二阶节Biquad的级联。// 二阶IIR滤波器模块Biquad示例 module iir_biquad #( parameter DATA_WIDTH 16, parameter COEF_WIDTH 16 )( input wire clk, input wire reset_n, input wire signed [DATA_WIDTH-1:0] data_in, output reg signed [DATA_WIDTH-1:0] data_out ); // 滤波器系数 wire signed [COEF_WIDTH-1:0] b0, b1, b2, a1, a2; // 延迟寄存器 reg signed [DATA_WIDTH-1:0] x1, x2, y1, y2; // 系数赋值实际应从外部输入 assign b0 16sd3276; // 0.1 in Q15 assign b1 16sd6553; // 0.2 in Q15 assign b2 16sd3276; // 0.1 in Q15 assign a1 -16sd5898; // -0.18 in Q15 assign a2 16sd3276; // 0.1 in Q15 always (posedge clk or negedge reset_n) begin if (!reset_n) begin x1 0; x2 0; y1 0; y2 0; data_out 0; end else begin // 直接II型实现 data_out (b0 * data_in b1 * x1 b2 * x2 - a1 * y1 - a2 * y2) (COEF_WIDTH-1); // 更新延迟寄存器 x2 x1; x1 data_in; y2 y1; y1 data_out; end end endmodule3. 定点数表示与量化处理FPGA中通常使用定点数进行数字信号处理。需要合理选择整数位和小数位的分配以及处理乘法后的位宽扩展。// 定点数运算处理模块 module fixed_point_multiply #( parameter INPUT_WIDTH 16, parameter COEF_WIDTH 16, parameter OUTPUT_WIDTH 16 )( input wire signed [INPUT_WIDTH-1:0] data, input wire signed [COEF_WIDTH-1:0] coef, output wire signed [OUTPUT_WIDTH-1:0] result ); // 中间结果位宽扩展 wire signed [INPUT_WIDTHCOEF_WIDTH-1:0] product; assign product data * coef; // 舍入处理加0.5后截断 wire signed [INPUT_WIDTHCOEF_WIDTH-1:0] rounded; assign rounded product (1 (COEF_WIDTH-2)); // 截断到输出位宽 assign result rounded[INPUT_WIDTHCOEF_WIDTH-1:COEF_WIDTH-1]; endmodule量化误差是IIR滤波器实现中的重要问题。需要确保系数量化后滤波器仍然稳定且满足频率响应要求。4. 级联型IIR滤波器实现对于4阶或更高阶的IIR滤波器推荐使用级联结构。下面展示一个4阶IIR滤波器的级联实现。module iir_cascade_4th_order #( parameter DATA_WIDTH 16, parameter COEF_WIDTH 16 )( input wire clk, input wire reset_n, input wire signed [DATA_WIDTH-1:0] data_in, output wire signed [DATA_WIDTH-1:0] data_out ); // 级联的两个二阶节之间的信号 wire signed [DATA_WIDTH-1:0] stage1_out; // 第一个二阶节 iir_biquad #( .DATA_WIDTH(DATA_WIDTH), .COEF_WIDTH(COEF_WIDTH) ) stage1 ( .clk(clk), .reset_n(reset_n), .data_in(data_in), .data_out(stage1_out) ); // 第二个二阶节 iir_biquad #( .DATA_WIDTH(DATA_WIDTH), .COEF_WIDTH(COEF_WIDTH) ) stage2 ( .clk(clk), .reset_n(reset_n), .data_in(stage1_out), .data_out(data_out) ); endmodule5. 滤波器系数生成与验证在实际项目中滤波器系数通常通过MATLAB或Python的scipy.signal库生成。下面是通过MATLAB生成4阶IIR低通滤波器系数的示例。% 设计4阶IIR低通滤波器 fs 8000000; % 采样频率8MHz fc 2000000; % 截止频率2MHz order 4; % 设计巴特沃斯滤波器 [b, a] butter(order, fc/(fs/2)); % 转换为二阶节形式 [sos, g] tf2sos(b, a); % 量化到12位 coef_scale 2^11; % Q12格式 sos_quant round(sos * coef_scale); % 输出Verilog可用的系数 fprintf(// 二阶节1系数\n); fprintf(b0 12sd%d;\n, sos_quant(1,1)); fprintf(b1 12sd%d;\n, sos_quant(1,2)); fprintf(b2 12sd%d;\n, sos_quant(1,3)); fprintf(a1 -12sd%d;\n, -sos_quant(1,5)); fprintf(a2 -12sd%d;\n, -sos_quant(1,6));6. 测试平台设计与功能验证完整的Verilog实现需要配套的测试平台。下面是一个基本的测试平台示例用于验证IIR滤波器的功能。module iir_filter_tb; reg clk; reg reset_n; reg signed [15:0] data_in; wire signed [15:0] data_out; // 实例化被测滤波器 iir_cascade_4th_order dut ( .clk(clk), .reset_n(reset_n), .data_in(data_in), .data_out(data_out) ); // 时钟生成 always #5 clk ~clk; // 100MHz时钟 // 测试信号生成 integer i; real frequency1 1000000; // 1MHz real frequency2 3000000; // 3MHz real fs 8000000; // 8MHz采样率 initial begin // 初始化 clk 0; reset_n 0; data_in 0; // 复位 #20 reset_n 1; // 生成测试信号1MHz 3MHz混合 for (i 0; i 1000; i i 1) begin #125; // 对应8MHz采样周期 data_in 1000 * ($sin(2 * 3.14159 * frequency1 * i / fs) 0.5 * $sin(2 * 3.14159 * frequency2 * i / fs)); end #1000 $finish; end // 波形输出 initial begin $dumpfile(iir_filter.vcd); $dumpvars(0, iir_filter_tb); end endmodule7. 资源优化技巧FPGA资源优化是IIR滤波器实现的关键。以下是一些实用的优化技巧7.1 乘法器共享对于对称系数的滤波器可以共享乘法器减少资源使用。// 乘法器共享示例 module shared_multiplier #( parameter WIDTH 16 )( input wire clk, input wire [1:0] sel, input wire signed [WIDTH-1:0] data, input wire signed [WIDTH-1:0] coef0, coef1, coef2, output reg signed [2*WIDTH-1:0] result ); reg signed [2*WIDTH-1:0] product; always (posedge clk) begin case(sel) 2b00: product data * coef0; 2b01: product data * coef1; 2b10: product data * coef2; default: product 0; endcase result product; end endmodule7.2 流水线设计通过插入流水线寄存器提高系统时钟频率。// 流水线型IIR二阶节 module iir_biquad_pipelined #( parameter DATA_WIDTH 16, parameter COEF_WIDTH 16 )( input wire clk, input wire reset_n, input wire signed [DATA_WIDTH-1:0] data_in, output reg signed [DATA_WIDTH-1:0] data_out ); // 流水线寄存器 reg signed [DATA_WIDTH-1:0] stage1, stage2, stage3; always (posedge clk or negedge reset_n) begin if (!reset_n) begin stage1 0; stage2 0; stage3 0; data_out 0; end else begin // 第一级前向路径乘法 stage1 (b0 * data_in b1 * x1 b2 * x2) (COEF_WIDTH-1); // 第二级反馈路径乘法 stage2 stage1 - (a1 * y1 a2 * y2) (COEF_WIDTH-1); // 第三级输出 stage3 stage2; data_out stage3; // 更新延迟寄存器也需要流水线 x2 x1; x1 data_in; y2 y1; y1 data_out; end end endmodule8. 数值稳定性处理IIR滤波器的数值稳定性是需要特别关注的问题。以下措施可以提高稳定性8.1 系数缩放在系数量化前进行适当的缩放避免溢出。// 系数缩放模块 module coef_scaling #( parameter INPUT_WIDTH 16, parameter OUTPUT_WIDTH 16 )( input wire signed [INPUT_WIDTH-1:0] coef_in, output wire signed [OUTPUT_WIDTH-1:0] coef_out ); // 计算缩放因子根据滤波器特性确定 localparam SCALE_FACTOR 4; // 示例值 // 缩放处理保持符号位 assign coef_out coef_in / SCALE_FACTOR; endmodule8.2 饱和算术使用饱和算术防止溢出扩散。// 饱和加法器 module saturated_adder #( parameter WIDTH 16 )( input wire signed [WIDTH-1:0] a, input wire signed [WIDTH-1:0] b, output reg signed [WIDTH-1:0] sum ); wire signed [WIDTH:0] extended_sum; assign extended_sum a b; always (*) begin if (extended_sum (2**(WIDTH-1)-1)) sum 2**(WIDTH-1)-1; // 正饱和 else if (extended_sum -(2**(WIDTH-1))) sum -(2**(WIDTH-1)); // 负饱和 else sum extended_sum[WIDTH-1:0]; end endmodule9. 实际部署考虑在实际FPGA项目中部署IIR滤波器时还需要考虑以下方面9.1 时钟域交叉如果滤波器需要处理不同时钟域的数据需要添加适当的同步逻辑。module clock_domain_crossing #( parameter DATA_WIDTH 16 )( input wire src_clk, input wire dst_clk, input wire [DATA_WIDTH-1:0] data_in, output wire [DATA_WIDTH-1:0] data_out ); reg [DATA_WIDTH-1:0] sync1, sync2, sync3; always (posedge dst_clk) begin sync1 data_in; sync2 sync1; sync3 sync2; end assign data_out sync3; endmodule9.2 动态系数加载支持运行时更新滤波器系数增加设计的灵活性。module dynamic_coef_iir #( parameter DATA_WIDTH 16, parameter COEF_WIDTH 16 )( input wire clk, input wire reset_n, input wire coef_load, // 系数加载使能 input wire [2:0] coef_addr, // 系数地址 input wire signed [COEF_WIDTH-1:0] coef_data, // 系数数据 input wire signed [DATA_WIDTH-1:0] data_in, output wire signed [DATA_WIDTH-1:0] data_out ); // 系数寄存器组 reg signed [COEF_WIDTH-1:0] b0, b1, b2, a1, a2; always (posedge clk or negedge reset_n) begin if (!reset_n) begin b0 0; b1 0; b2 0; a1 0; a2 0; end else if (coef_load) begin case(coef_addr) 3b000: b0 coef_data; 3b001: b1 coef_data; 3b010: b2 coef_data; 3b011: a1 coef_data; 3b100: a2 coef_data; endcase end end // 滤波器逻辑同上 // ... endmodule10. 性能评估与调试完成IIR滤波器实现后需要进行全面的性能评估10.1 频率响应测试通过仿真验证滤波器的频率响应特性。// 频率扫描测试 task automatic frequency_sweep; input integer start_freq; input integer end_freq; input integer step; integer freq; real magnitude; begin for (freq start_freq; freq end_freq; freq freq step) begin // 生成单频测试信号 // 应用滤波器 // 测量输出幅度 $display(频率: %d Hz, 增益: %f, freq, magnitude); end end endtask10.2 资源使用报告综合后分析FPGA资源使用情况确保满足目标器件限制。11. 常见问题与解决方案问题现象可能原因解决方案滤波器不稳定系数量化误差过大增加系数位宽或使用级联结构输出信号失真定点数精度不足增加数据位宽或优化量化策略时序违例组合逻辑路径过长插入流水线寄存器资源使用超标乘法器数量过多使用乘法器共享或时间复用12. 最佳实践建议开始阶段先用浮点仿真验证算法正确性再逐步转为定点实现系数设计使用MATLAB或Python验证系数在量化后的稳定性资源规划根据目标FPGA的DSP资源数量设计滤波器阶数测试验证使用真实信号和仿真信号进行充分测试文档维护详细记录系数含义、数据格式和接口时序IIR滤波器的Verilog实现需要综合考虑算法精度、硬件资源和时序要求。通过合理的结构选择、定点数处理和优化技巧可以在FPGA上实现高性能的数字滤波器。建议在实际项目中先从简单的二阶节开始验证逐步扩展到更复杂的系统。