
这次我们来看一个IIR数字滤波器的Verilog实现项目。IIR滤波器在数字信号处理中应用广泛相比FIR滤波器它可以用更低的阶数实现更好的频率选择性特别适合FPGA实现。这个项目的重点不是概念多复杂而是如何在FPGA上高效实现IIR滤波器包括结构选择、量化处理、资源占用和实际验证。如果你关心FPGA数字信号处理、滤波器硬件实现、Verilog代码优化和实际测试这篇文章可以直接收藏。本文会详细讲解IIR滤波器的直接I型、直接II型和级联型结构实现给出完整的Verilog代码示例并演示如何在Vivado中进行功能仿真和资源分析。1. 核心能力速览能力项说明滤波器类型IIR低通/高通/带通/带阻滤波器实现结构直接I型、直接II型、级联二阶节量化位数支持8/12/16位定点数可根据需求调整工作频率支持MHz级采样频率具体取决于FPGA型号开发环境Vivado、Quartus、VSCodeVerilog插件验证方式仿真测试、实际信号处理适合场景音频处理、通信系统、生物信号处理等实时滤波2. IIR滤波器基础与FPGA实现优势IIR滤波器的系统函数可以表示为有理分式形式H(z) (b₀ b₁z⁻¹ ... bₘz⁻ᵐ) / (1 a₁z⁻¹ ... aₙz⁻ⁿ)。与FIR滤波器相比IIR滤波器的主要优势是在相同性能要求下阶数更低硬件资源消耗更少。在FPGA中实现IIR滤波器有几个关键考虑首先是结构选择直接型结构简单但系数敏感级联型稳定性更好其次是量化效应定点数运算会引入舍入误差和溢出问题最后是时序约束递归结构需要保证反馈路径的时序满足要求。从实际工程角度看级联二阶节结构是最常用的选择。它将高阶滤波器分解为多个二阶节的级联每个二阶节可以独立优化数值稳定性好便于模块化设计。下面我们会重点介绍这种结构的Verilog实现。3. 级联二阶节IIR滤波器结构级联二阶节Biquad是IIR滤波器实现的核心单元。一个二阶节的差分方程为y[n] b₀x[n] b₁x[n-1] b₂x[n-2] - a₁y[n-1] - a₂y[n-2]。在Verilog中实现时我们需要考虑以下几个关键点数据位宽输入输出位宽、系数位宽、中间结果位宽运算精度乘法累加运算的精度保持策略时序控制流水线设计以提高工作频率溢出处理饱和运算或位宽扩展下面是级联二阶节的基本模块接口设计module iir_biquad #( parameter DATA_WIDTH 16, parameter COEF_WIDTH 16, parameter FRAC_WIDTH 14 ) ( input wire clk, input wire rst_n, input wire signed [DATA_WIDTH-1:0] data_in, input wire data_valid, output reg signed [DATA_WIDTH-1:0] data_out, output reg data_out_valid ); // 滤波器系数定义 reg signed [COEF_WIDTH-1:0] b0, b1, b2, a1, a2; // 延迟寄存器 reg signed [DATA_WIDTH-1:0] x1, x2, y1, y2; // 中间计算结果 wire signed [DATA_WIDTHCOEF_WIDTH-1:0] prod_b0, prod_b1, prod_b2; wire signed [DATA_WIDTHCOEF_WIDTH-1:0] prod_a1, prod_a2; wire signed [DATA_WIDTHCOEF_WIDTH:0] sum_b, sum_a; wire signed [DATA_WIDTHCOEF_WIDTH:0] y_temp; // 系数初始化 initial begin b0 16sd3276; // 0.2 * 2^14 b1 16sd6553; // 0.4 * 2^14 b2 16sd3276; // 0.2 * 2^14 a1 -16sd14745; // -0.9 * 2^14 a2 16sd8192; // 0.5 * 2^14 end // 主要处理逻辑 always (posedge clk or negedge rst_n) begin if (!rst_n) begin // 复位逻辑 x1 0; x2 0; y1 0; y2 0; data_out 0; data_out_valid 0; end else if (data_valid) begin // 更新延迟线 x2 x1; x1 data_in; y2 y1; y1 y_temp[FRAC_WIDTHDATA_WIDTH-1:FRAC_WIDTH]; // 截取整数部分 data_out y_temp[FRAC_WIDTHDATA_WIDTH-1:FRAC_WIDTH]; data_out_valid 1; end else begin data_out_valid 0; end end // 乘法累加运算 assign prod_b0 data_in * b0; assign prod_b1 x1 * b1; assign prod_b2 x2 * b2; assign prod_a1 y1 * a1; assign prod_a2 y2 * a2; assign sum_b prod_b0 prod_b1 prod_b2; assign sum_a prod_a1 prod_a2; assign y_temp sum_b - sum_a; endmodule这个模块实现了一个完整的二阶IIR滤波器节采用直接II型结构节省了延迟寄存器。系数采用定点数表示小数位数通过FRAC_WIDTH参数控制。4. 多阶级联IIR滤波器实现对于高阶IIR滤波器我们需要将多个二阶节级联起来。以4阶低通滤波器为例采样频率8MHz截止频率2MHz阻带衰减40dB的设计要求module iir_cascade_4th_order #( parameter DATA_WIDTH 16, parameter COEF_WIDTH 16, parameter FRAC_WIDTH 14 ) ( input wire clk, input wire rst_n, input wire signed [DATA_WIDTH-1:0] data_in, input wire data_valid, output wire signed [DATA_WIDTH-1:0] data_out, output wire data_out_valid ); // 级联的两个二阶节 wire signed [DATA_WIDTH-1:0] stage1_out; wire stage1_valid; iir_biquad #( .DATA_WIDTH(DATA_WIDTH), .COEF_WIDTH(COEF_WIDTH), .FRAC_WIDTH(FRAC_WIDTH) ) stage1 ( .clk(clk), .rst_n(rst_n), .data_in(data_in), .data_valid(data_valid), .data_out(stage1_out), .data_out_valid(stage1_valid) ); iir_biquad #( .DATA_WIDTH(DATA_WIDTH), .COEF_WIDTH(COEF_WIDTH), .FRAC_WIDTH(FRAC_WIDTH) ) stage2 ( .clk(clk), .rst_n(rst_n), .data_in(stage1_out), .data_valid(stage1_valid), .data_out(data_out), .data_out_valid(data_out_valid) ); endmodule在实际设计中滤波器系数需要通过MATLAB或Python的scipy.signal库计算。例如使用butter函数设计4阶巴特沃斯低通滤波器import scipy.signal as signal import numpy as np # 设计4阶巴特沃斯低通滤波器 fs 8e6 # 采样频率8MHz fc 2e6 # 截止频率2MHz order 4 # 计算归一化截止频率 nyquist fs / 2 Wn fc / nyquist # 设计巴特沃斯滤波器 b, a signal.butter(order, Wn, btypelow) # 转换为二阶节形式 sos signal.tf2sos(b, a) print(二阶节系数:) for i, section in enumerate(sos): print(fSection {i1}: b0{section[0]:.6f}, b1{section[1]:.6f}, b2{section[2]:.6f}, a1{section[4]:.6f}, a2{section[5]:.6f})5. 滤波器系数量化与精度处理系数量化是FPGA实现中的关键步骤。12位量化的示例module coefficient_quantizer #( parameter INPUT_WIDTH 32, parameter OUTPUT_WIDTH 12, parameter FRAC_BITS 10 ) ( input wire signed [INPUT_WIDTH-1:0] coeff_in, output reg signed [OUTPUT_WIDTH-1:0] coeff_out ); // 量化处理舍入到最接近的整数 wire signed [INPUT_WIDTH-1:0] rounded; assign rounded (coeff_in (1 (FRAC_BITS-1))) FRAC_BITS; always (*) begin // 饱和处理防止溢出 if (rounded (1 (OUTPUT_WIDTH-1)) - 1) coeff_out (1 (OUTPUT_WIDTH-1)) - 1; else if (rounded -(1 (OUTPUT_WIDTH-1))) coeff_out -(1 (OUTPUT_WIDTH-1)); else coeff_out rounded[OUTPUT_WIDTH-1:0]; end endmodule量化后的系数需要验证滤波器的频率响应是否满足要求。通常建议保留2-4位的保护位来防止运算溢出。6. 测试平台与功能验证完整的测试平台应该包括信号生成、滤波器实例化和结果分析timescale 1ns/1ps module iir_filter_tb; reg clk; reg rst_n; reg signed [15:0] data_in; reg data_valid; wire signed [15:0] data_out; wire data_out_valid; // 时钟生成 always #5 clk ~clk; // 100MHz时钟 // 实例化被测滤波器 iir_cascade_4th_order uut ( .clk(clk), .rst_n(rst_n), .data_in(data_in), .data_valid(data_valid), .data_out(data_out), .data_out_valid(data_out_valid) ); // 测试信号生成 integer i; real frequency1 1.0e6; // 1MHz信号通带内 real frequency2 3.0e6; // 3MHz信号阻带内 real fs 8.0e6; // 采样频率8MHz initial begin // 初始化 clk 0; rst_n 0; data_in 0; data_valid 0; // 复位 #100 rst_n 1; data_valid 1; // 生成测试信号通带信号阻带信号 for (i 0; i 1000; i i 1) begin #125; // 8MHz采样周期125ns data_in 1000 * $sin(2 * 3.1415926 * frequency1 * i / fs) 500 * $sin(2 * 3.1415926 * frequency2 * i / fs); end #1000 $finish; end // 结果记录 integer fp; initial begin fp $fopen(filter_output.txt, w); forever begin (posedge clk); if (data_out_valid) begin $fdisplay(fp, %d, data_out); end end end endmodule仿真完成后可以使用MATLAB或Python分析输出结果验证滤波效果import numpy as np import matplotlib.pyplot as plt from scipy import signal # 读取仿真输出 data np.loadtxt(filter_output.txt) t np.arange(len(data)) / 8e6 # 时间轴8MHz采样 # 绘制时域波形 plt.figure(figsize(12, 8)) plt.subplot(2, 1, 1) plt.plot(t[:1000], data[:1000]) # 显示前1000个点 plt.title(滤波器输出时域波形) plt.xlabel(时间 (s)) plt.ylabel(幅度) # 计算频率响应 f, Pxx signal.welch(data, 8e6, nperseg1024) plt.subplot(2, 1, 2) plt.semilogy(f, Pxx) plt.title(输出信号功率谱密度) plt.xlabel(频率 (Hz)) plt.ylabel(PSD) plt.tight_layout() plt.show() # 验证滤波效果1MHz成分应该保留3MHz成分应该衰减 f1_power np.mean(Pxx[(f 0.9e6) (f 1.1e6)]) f2_power np.mean(Pxx[(f 2.9e6) (f 3.1e6)]) attenuation 10 * np.log10(f2_power / f1_power) print(f阻带衰减: {attenuation:.2f} dB)7. 资源优化与性能提升策略在实际FPGA实现中资源占用和性能是需要重点优化的方面7.1 乘法器资源优化// 使用DSP48E1资源的优化版本 module optimized_iir_biquad #( parameter DATA_WIDTH 16, parameter COEF_WIDTH 16 ) ( input wire clk, input wire rst_n, input wire signed [DATA_WIDTH-1:0] data_in, input wire data_valid, output reg signed [DATA_WIDTH-1:0] data_out, output reg data_out_valid ); // 使用FPGA的DSP单元 wire signed [DATA_WIDTHCOEF_WIDTH-1:0] dsp_out; reg signed [DATA_WIDTH-1:0] x1, x2, y1, y2; // DSP48E1的配置使用 multiply_accumulate mac_unit ( .clk(clk), .ce(data_valid), .a(data_in), // 当前输入 .b(b0), // b0系数 .c(x1 * b1 x2 * b2 - y1 * a1 - y2 * a2), // 累加项 .p(dsp_out) ); always (posedge clk) begin if (data_valid) begin // 更新延迟寄存器 x2 x1; x1 data_in; y2 y1; y1 dsp_out[DATA_WIDTHCOEF_WIDTH-1:COEF_WIDTH]; // 对齐位宽 data_out dsp_out[DATA_WIDTHCOEF_WIDTH-1:COEF_WIDTH]; data_out_valid 1; end else begin data_out_valid 0; end end endmodule7.2 流水线设计提高工作频率module pipelined_iir_biquad #( parameter DATA_WIDTH 16, parameter COEF_WIDTH 16 ) ( input wire clk, input wire rst_n, input wire signed [DATA_WIDTH-1:0] data_in, input wire data_valid, output wire signed [DATA_WIDTH-1:0] data_out, output wire data_out_valid ); // 三级流水线设计 reg signed [DATA_WIDTH-1:0] stage1_in, stage2_in; reg stage1_valid, stage2_valid; // 第一级输入寄存和系数乘法 reg signed [DATA_WIDTHCOEF_WIDTH-1:0] b0_mul, b1_mul, b2_mul; always (posedge clk) begin if (data_valid) begin stage1_in data_in; b0_mul data_in * b0; b1_mul x1 * b1; b2_mul x2 * b2; end stage1_valid data_valid; end // 第二级反馈项乘法和第一次累加 reg signed [DATA_WIDTHCOEF_WIDTH:0] sum_b, a1_mul, a2_mul; always (posedge clk) begin if (stage1_valid) begin stage2_in stage1_in; sum_b b0_mul b1_mul b2_mul; a1_mul y1 * a1; a2_mul y2 * a2; end stage2_valid stage1_valid; end // 第三级最终累加和输出 reg signed [DATA_WIDTHCOEF_WIDTH:0] final_sum; always (posedge clk) begin if (stage2_valid) begin final_sum sum_b - a1_mul - a2_mul; // 更新延迟寄存器 x2 x1; x1 stage2_in; y2 y1; y1 final_sum[DATA_WIDTHCOEF_WIDTH-1:COEF_WIDTH]; end end assign data_out final_sum[DATA_WIDTHCOEF_WIDTH-1:COEF_WIDTH]; assign data_out_valid stage2_valid; endmodule8. 实际部署与调试技巧8.1 Vivado工程设置创建Vivado项目时需要注意的关键设置语言标准选择Verilog-2005或SystemVerilog目标器件根据性能要求选择适当的FPGA型号约束文件正确设置时钟约束和I/O约束# 时钟约束示例 create_clock -name clk -period 10 [get_ports clk] # I/O约束示例 set_property PACKAGE_PIN AA1 [get_ports clk] set_property IOSTANDARD LVCMOS33 [get_ports clk] # 时序约束 set_input_delay -clock clk 2 [get_ports data_in] set_output_delay -clock clk 2 [get_ports data_out]8.2 在线调试与SignalTap使用对于Altera/Intel FPGA可以使用SignalTap进行实时调试// SignalTap调试信号定义 reg [15:0] debug_data_in; reg [15:0] debug_data_out; reg debug_valid; always (posedge clk) begin debug_data_in data_in; debug_data_out data_out; debug_valid data_out_valid; end8.3 资源使用分析典型的资源占用情况以Xilinx Artix-7为例4阶IIR滤波器约4-8个DSP48E1100-200个LUT50-100个FF工作频率可达200-300MHz取决于具体设计和约束9. 常见问题与解决方案9.1 滤波器不稳定问题现象输出信号幅值不断增大直至饱和原因分析极点位于单位圆外通常是由于系数量化误差引起解决方案使用级联型结构代替直接型增加系数位宽或使用浮点DSP在MATLAB中验证量化后系数的稳定性% 稳定性验证 [z,p,k] tf2zp(b,a); if max(abs(p)) 1 disp(滤波器稳定); else disp(滤波器不稳定需要调整系数); end9.2 运算溢出问题现象输出信号出现截断或异常值原因分析中间结果位宽不足解决方案增加中间结果位宽通常建议比输入位宽多log2(N)位N为滤波器阶数实施饱和运算而非截断使用保护位技术// 饱和运算实现 function signed [OUT_WIDTH-1:0] saturate; input signed [IN_WIDTH-1:0] data_in; begin if (data_in max_value) saturate max_value; else if (data_in min_value) saturate min_value; else saturate data_in; end endfunction9.3 时序违例问题现象建立时间或保持时间违例原因分析组合逻辑路径过长解决方案增加流水线级数使用寄存器重定时优化关键路径10. 扩展应用与进阶优化10.1 可重构滤波器设计通过系数重配置实现不同特性的滤波器module reconfigurable_iir #( parameter DATA_WIDTH 16, parameter COEF_WIDTH 16, parameter NUM_SECTIONS 2 ) ( input wire clk, input wire rst_n, input wire [COEF_WIDTH-1:0] coeffs [0:4*NUM_SECTIONS-1], input wire coeffs_valid, // ...其他端口 ); // 系数更新逻辑 always (posedge clk) begin if (coeffs_valid) begin for (int i 0; i NUM_SECTIONS; i) begin b0[i] coeffs[i*4]; b1[i] coeffs[i*41]; b2[i] coeffs[i*42]; a1[i] coeffs[i*43]; a2[i] coeffs[i*44]; end end end endmodule10.2 多通道滤波器实现通过时分复用实现多通道滤波module multi_channel_iir #( parameter DATA_WIDTH 16, parameter NUM_CHANNELS 4 ) ( input wire clk, input wire rst_n, input wire signed [DATA_WIDTH-1:0] data_in [0:NUM_CHANNELS-1], input wire data_valid, output wire signed [DATA_WIDTH-1:0] data_out [0:NUM_CHANNELS-1], output wire data_out_valid ); // 通道选择计数器 reg [1:0] channel_sel; always (posedge clk) begin if (data_valid) channel_sel channel_sel 1; end // 共享的滤波器核心 iir_biquad filter_core ( .clk(clk), .rst_n(rst_n), .data_in(data_in[channel_sel]), .data_valid(data_valid), .data_out(filter_out) ); // 输出分配 genvar i; for (i 0; i NUM_CHANNELS; i i 1) begin assign data_out[i] (channel_sel i) ? filter_out : 0; end endmodule这个IIR滤波器Verilog实现方案提供了从基础理论到实际工程的完整路径。关键是要理解滤波器结构选择、系数量化影响和时序优化策略。在实际项目中建议先用MATLAB验证算法再逐步移植到FPGA最后进行硬件实测验证。