2. FIR高通滤波器设计

任务目标

在FPGA上实现一个32阶(33个系数)FIR高通滤波器,采用分布式算法(DA)将乘累加运算转化为查找表操作以节省DSP48资源,并通过仿真验证其频率响应符合高通特性。

设计规格

  • 采样率:Fs = 50 MHz
  • 通带:≥ 10 MHz(归一化0.4π)
  • 阻带:≤ 5 MHz(归一化0.2π)
  • 输入/输出位宽:16 bit有符号
  • 滤波器阶数:32阶(33个对称系数)
  • 系数位宽:16 bit有符号,经量化后使用

滤波器系数生成(MATLAB/Octave)

% 高通FIR设计,归一化截止频率0.3(即7.5MHz@Fs=50MHz)
fir_coeff = firpm(32, [0 0.2 0.4 1], [0 0 1 1]);  % 等波纹设计
coeff_16bit = round(fir_coeff * 2^15);            % 量化到16bit
fprintf('%d,\n', coeff_16bit);

由于系数对称(h[n] = h[32-n]),实际只需存储17个独立系数,利用对称性减半查找表规模。

分布式算法原理

将每个输入样本x[n]按位拆分(16 bit),滤波输出y = Σ h[k]·x[n-k]可重写为:

y = Σ_{b=0}^{15} 2^b · Σ_{k=0}^{32} h[k]·x_b[k]

其中x_b[k]是第k个输入的第b位(0/1)。内层求和通过预先计算的查找表实现——将33个输入的同一位组合成33位地址,查表得到部分和。因系数对称,可将输入对(x[k], x[32-k])先相加再查表,地址降至17位,查找表深度从2³³降至2¹⁷(配合两个半表拆分实现)。

Verilog实现(关键完整代码)

module fir_hp_da #(
    parameter DATA_W = 16,
    parameter TAPS   = 33,
    parameter COEF_W = 16
)(
    input  wire clk,
    input  wire rst_n,
    input  wire signed [DATA_W-1:0] din,
    output reg  signed [DATA_W+COEF_W-1:0] dout
);

    // 对称系数(仅存17个,由MATLAB生成后填入)
    // 示例前3个系数(实际需完整填入17个)
    localparam signed [COEF_W-1:0] COEFS [0:16] = '{
        16'd0,    // h[0]=h[32]
        -16'd12,  // h[1]=h[31]
        -16'd20,  // h[2]=h[30]
        // ... 其余14个系数
        16'd3276  // h[16]中心系数
    };

    // 输入移位寄存器(33级)
    reg signed [DATA_W-1:0] shift_reg [0:TAPS-1];
    integer i;
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            for (i = 0; i < TAPS; i = i + 1)
                shift_reg[i] <= 16'sd0;
        end else begin
            shift_reg[0] <= din;
            for (i = 1; i < TAPS; i = i + 1)
                shift_reg[i] <= shift_reg[i-1];
        end
    end

    // 对称对相加(17个和)
    reg signed [DATA_W:0] pair_sum [0:16];  // 17bit防溢出
    always @(*) begin
        for (i = 0; i < 17; i = i + 1)
            pair_sum[i] = shift_reg[i] + shift_reg[TAPS-1-i];
    end

    // DA查找表:对每个bit位置,用17位地址查表
    // 实际工程中将LUT拆分为两个8输入LUT+加法,此处展示核心结构
    reg signed [COEF_W+4:0] lut_out [0:15];  // 16个bit位置的部分和
    integer b;
    always @(*) begin
        for (b = 0; b < DATA_W; b = b + 1) begin
            lut_out[b] = 0;
            for (i = 0; i < 17; i = i + 1) begin
                if (pair_sum[i][b])
                    lut_out[b] = lut_out[b] + COEFS[i];
            end
        end
    end

    // 累加各bit加权和
    reg signed [DATA_W+COEF_W+3:0] acc;
    integer j;
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            acc <= 0;
            dout <= 0;
        end else begin
            acc <= 0;
            for (j = 0; j < DATA_W; j = j + 1)
                acc <= acc + (lut_out[j] <<< j);
            dout <= acc[DATA_W+COEF_W-1:0];
        end
    end

endmodule

说明:上述代码展示了DA核心结构——对称对相加、按位查表、移位累加。实际FPGA实现时,lut_out应替换为分布式RAM或LUT原语以发挥DA优势;此处用组合逻辑等效描述算法逻辑,便于仿真验证。系数数组需用MATLAB生成的实际值完整填入。

测试平台与频率响应验证

// testbench:输入扫频信号,观察输出幅度
module tb_fir_hp;
    reg clk = 0, rst_n = 0;
    reg signed [15:0] din;
    wire signed [31:0] dout;
    real freq, t, step;

    fir_hp_da uut (.clk(clk), .rst_n(rst_n), .din(din), .dout(dout));

    always #10 clk = ~clk;  // 50MHz

    initial begin
        rst_n = 0; #100; rst_n = 1;
        // 低频测试:2MHz正弦(应被衰减)
        for (t = 0; t < 5000; t = t + 1) begin
            @(posedge clk);
            din = $rtoi(1000 * $sin(2 * 3.14159 * 2e6 * t / 50e6));
        end
        // 高频测试:15MHz正弦(应通过)
        for (t = 0; t < 5000; t = t + 1) begin
            @(posedge clk);
            din = $rtoi(1000 * $sin(2 * 3.14159 * 15e6 * t / 50e6));
        end
        $stop;
    end
endmodule

验证方法:在Vivado/ModelSim中运行仿真,观察dout波形。2MHz输入时输出幅度应明显小于输入(衰减≥20dB);15MHz输入时输出幅度应接近输入(增益≈0dB)。若需精确频率响应,可导入MATLAB生成的扫频激励,将输出保存后与理想响应对比。

综合资源预估

相比直接使用33个乘法器(需33个DSP48),DA方案仅需LUT资源:17输入查找表拆分为两个8输入LUT+加法器,约需16bit × 2 × 256×16bit的分布式RAM,在Artix-7级别FPGA上资源占用极低,适合高通道数或低成本应用。

常见问题

  • 查找表规模爆炸:利用系数对称性将地址从33位降至17位,再拆分为高低两个8位LUT,避免直接实现2¹⁷深度表。
  • 符号位处理:输入为有符号数时,最高位(符号位)需单独处理——符号位为1表示负数,查表结果应取反加一,或采用偏移二进制编码避免。
  • 输出位宽:33个16bit系数乘16bit输入,理论最大位宽为16+16+ceil(log2(33))=37bit,本设计取32bit输出,需确认无溢出。