3. FIR带通滤波器设计

任务目标

设计一个中心频率可配置的带通FIR滤波器,在FPGA上实现滤波逻辑,并通过仿真验证带外信号被有效抑制。滤波器阶数、采样率、中心频率与带宽作为参数输入,由模块内部自动计算系数。

实现方案

采用窗函数法(Hamming窗)设计带通FIR滤波器,系数在FPGA内部实时计算,避免外部导入系数文件。滤波采用直接I型对称结构,利用系数对称性将乘法器数量减半。中心频率通过寄存器配置,支持运行时调整。

核心代码

module fir_bandpass #(
    parameter DATA_WIDTH = 16,
    parameter COEF_WIDTH = 16,
    parameter TAPS = 32,          // 滤波器阶数(需为偶数)
    parameter ACC_WIDTH = 32
)(
    input  wire clk,
    input  wire rst_n,
    input  wire signed [DATA_WIDTH-1:0] data_in,
    input  wire data_valid,
    // 配置接口
    input  wire [31:0] cfg_fc,     // 中心频率(归一化:fc/fs * 2^32)
    input  wire [31:0] cfg_bw,     // 带宽(归一化:bw/fs * 2^32)
    input  wire cfg_update,        // 系数更新使能
    // 输出
    output reg signed [DATA_WIDTH-1:0] data_out,
    output reg data_out_valid
);

    // 系数存储(对称,只需TAPS/2个)
    reg signed [COEF_WIDTH-1:0] coef [0:TAPS/2-1];
    reg signed [DATA_WIDTH-1:0] shift_reg [0:TAPS-1];
    
    // 系数计算(Hamming窗带通)
    integer i;
    reg [31:0] phase_acc;
    reg [31:0] wc_low, wc_high;
    reg [31:0] hamming_val;
    reg [31:0] sinc_val;
    reg [31:0] coef_tmp;
    
    // 系数更新逻辑
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            for (i = 0; i < TAPS/2; i++) begin
                coef[i] <= 0;
            end
        end else if (cfg_update) begin
            wc_low  <= (cfg_fc - cfg_bw/2) * 2;  // 归一化角频率
            wc_high <= (cfg_fc + cfg_bw/2) * 2;
            
            for (i = 0; i < TAPS/2; i++) begin
                // 计算带通冲激响应 h[n] = (sin(wc2*n) - sin(wc1*n))/(pi*n)
                if (i == TAPS/2 - 1) begin
                    // 中心点(n=0)
                    coef_tmp = (wc_high - wc_low) / 3.14159;
                end else begin
                    phase_acc = (i - (TAPS/2 - 1)) * 2;
                    // 简化sinc计算(实际工程建议使用CORDIC或查找表)
                    sinc_val = ((wc_high * phase_acc) >> 16) - ((wc_low * phase_acc) >> 16);
                    coef_tmp = (sinc_val * 32768) / (phase_acc * 3.14159);
                end
                // Hamming窗
                hamming_val = 0.54 - 0.46 * 3.14159 * (i * 2) / (TAPS - 1);
                coef[i] <= coef_tmp * hamming_val;
            end
        end
    end
    
    // 滤波主逻辑(对称结构)
    reg signed [ACC_WIDTH-1:0] acc;
    reg [7:0] tap_idx;
    reg [1:0] state;
    
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            data_out <= 0;
            data_out_valid <= 0;
            acc <= 0;
            state <= 0;
        end else begin
            case (state)
                2'd0: begin
                    if (data_valid) begin
                        // 移位寄存器更新
                        for (i = TAPS-1; i > 0; i--) begin
                            shift_reg[i] <= shift_reg[i-1];
                        end
                        shift_reg[0] <= data_in;
                        acc <= 0;
                        tap_idx <= 0;
                        state <= 2'd1;
                    end
                end
                2'd1: begin
                    if (tap_idx < TAPS/2) begin
                        // 对称系数累加
                        acc <= acc + coef[tap_idx] * 
                              (shift_reg[tap_idx] + shift_reg[TAPS-1-tap_idx]);
                        tap_idx <= tap_idx + 1;
                    end else begin
                        data_out <= acc[ACC_WIDTH-1:DATA_WIDTH-1];
                        data_out_valid <= 1;
                        state <= 2'd0;
                    end
                end
            endcase
        end
    end

endmodule

仿真验证

创建testbench,输入两个不同频率的正弦波叠加信号,验证滤波器输出仅保留通带内频率分量。

module tb_fir_bandpass;
    reg clk = 0;
    reg rst_n = 0;
    reg signed [15:0] din = 0;
    reg din_valid = 0;
    reg [31:0] fc = 32'd536870912;   // fc/fs = 0.125 (归一化)
    reg [31:0] bw = 32'd107374182;   // bw/fs = 0.025
    reg update = 0;
    wire signed [15:0] dout;
    wire dout_valid;
    
    // 输入信号:1kHz + 5kHz混合(假设fs=40kHz)
    real t = 0;
    real freq1 = 1000.0, freq2 = 5000.0;
    real fs = 40000.0;
    
    fir_bandpass dut(
        .clk(clk), .rst_n(rst_n),
        .data_in(din), .data_valid(din_valid),
        .cfg_fc(fc), .cfg_bw(bw), .cfg_update(update),
        .data_out(dout), .data_out_valid(dout_valid)
    );
    
    always #12.5 clk = ~clk;  // 40kHz采样时钟
    
    initial begin
        #100 rst_n = 1;
        #50 update = 1;
        #25 update = 0;
        
        // 生成混合信号
        forever begin
            #25;
            t = t + 1.0/fs;
            din = $rtoi(10000 * (1.0 + $sin(2*3.14159*freq1*t) + 
                                 0.5*$sin(2*3.14159*freq2*t)));
            din_valid = 1;
        end
    end
    
    // 输出监测
    integer fd;
    initial begin
        fd = $fopen("filter_out.txt");
        #100000;
        $fclose(fd);
        $finish;
    end
    
    always @(posedge clk) begin
        if (dout_valid) begin
            $fwrite(fd, "%d\n", dout);
        end
    end
endmodule

运行与验证

  1. 仿真运行:使用Vivado或ModelSim运行上述testbench,仿真时间建议≥10ms(400个采样周期以上)
  2. 结果分析:将filter_out.txt导入MATLAB或Python,执行FFT分析输出频谱
  3. 通过标准:
    • 输出频谱中1kHz分量幅度衰减≤3dB
    • 5kHz分量幅度衰减≥20dB(带外抑制)
    • 滤波器输出无直流偏置,波形稳定

关键说明

  • 系数计算采用简化sinc近似,实际工程中建议使用CORDIC算法或预计算查找表提高精度
  • 中心频率配置范围:0.05~0.45(归一化频率),超出此范围需调整滤波器阶数
  • 输出位宽截取时采用高16位,可根据实际动态范围调整
  • 系数更新需在cfg_update高电平期间完成,更新期间输出无效