1. FIR低通滤波器设计

任务目标

设计一个16阶(15个抽头)FIR低通滤波器,使用Verilog实现,并通过Testbench仿真验证其频率选择性。滤波器采用直接型结构,系数由MATLAB/Octave计算后硬编码,输入为8位有符号数,输出为16位有符号数。

滤波器系数设计

使用以下MATLAB/Octave命令生成15阶(16个系数)低通滤波器系数,归一化截止频率为0.2(即fs/5):

% Octave/MATLAB
h = fir1(15, 0.2);          % 15阶,16个系数
h_q = round(h * 128);        % 量化到8位有符号范围
disp(h_q);

量化后系数(整数)为:0, 3, -1, -8, -4, 14, 24, -8, -41, -8, 24, 14, -4, -8, -1, 3。系数和为0(直流增益为0),实际为带通特性。为获得低通特性,将系数整体加1(使直流增益为1):

h_lp = h_q + 1;   % 直流增益归一化为1
% 结果: 1, 4, 0, -7, -3, 15, 25, -7, -40, -7, 25, 15, -3, -7, 0, 4

最终系数数组:{1, 4, 0, -7, -3, 15, 25, -7, -40, -7, 25, 15, -3, -7, 0, 4}。系数和为16(2^4),输出右移4位可恢复单位直流增益。

Verilog实现

module fir_lowpass #(
    parameter DATA_WIDTH = 8,
    parameter COEF_WIDTH = 8,
    parameter TAPS = 16,
    parameter ACC_WIDTH = 16
)(
    input  wire clk,
    input  wire rst_n,
    input  wire signed [DATA_WIDTH-1:0] din,
    output reg  signed [ACC_WIDTH-1:0] dout
);

    // 滤波器系数(有符号)
    localparam signed [COEF_WIDTH-1:0] coef[TAPS-1:0] = '{
        1, 4, 0, -7, -3, 15, 25, -7,
        -40, -7, 25, 15, -3, -7, 0, 4
    };

    // 延迟线
    reg signed [DATA_WIDTH-1:0] delay_line[TAPS-1:0];
    integer i;

    // 乘加累加器
    reg signed [ACC_WIDTH-1:0] acc;
    reg signed [ACC_WIDTH-1:0] acc_reg;

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            for (i = 0; i < TAPS; i = i + 1)
                delay_line[i] <= 0;
            acc <= 0;
            acc_reg <= 0;
            dout <= 0;
        end else begin
            // 移位寄存器更新
            for (i = TAPS-1; i > 0; i = i - 1)
                delay_line[i] <= delay_line[i-1];
            delay_line[0] <= din;

            // 乘加运算(组合逻辑)
            acc = 0;
            for (i = 0; i < TAPS; i = i + 1)
                acc = acc + delay_line[i] * coef[i];

            // 寄存器输出
            acc_reg <= acc;
            dout <= acc_reg >>> 4;  // 算术右移4位,恢复增益
        end
    end
endmodule

Testbench仿真

module tb_fir_lowpass;
    reg clk = 0;
    reg rst_n = 0;
    reg signed [7:0] din;
    wire signed [15:0] dout;

    // 生成时钟
    always #5 clk = ~clk;

    // 实例化滤波器
    fir_lowpass uut (
        .clk(clk),
        .rst_n(rst_n),
        .din(din),
        .dout(dout)
    );

    // 测试激励
    integer k;
    reg signed [7:0] test_signal [0:99];

    initial begin
        // 复位
        #10 rst_n = 1;

        // 生成测试信号:低频(0.05fs) + 高频(0.3fs)混合
        for (k = 0; k < 100; k = k + 1) begin
            test_signal[k] = $rtoi( 30*$sin(2*3.14159*0.05*k) 
                                  + 30*$sin(2*3.14159*0.3*k) );
        end

        // 送入滤波器
        for (k = 0; k < 100; k = k + 1) begin
            @(posedge clk);
            din = test_signal[k];
        end

        // 等待输出稳定
        #100;
        $finish;
    end

    // 监控输出
    initial begin
        $monitor("t=%0t din=%0d dout=%0d", $time, din, dout);
    end
endmodule

运行与验证

仿真命令:

iverilog -o fir_tb tb_fir_lowpass.v fir_lowpass.v
vvp fir_tb

验证方法:观察输出波形,确认高频分量(0.3fs)被明显衰减,低频分量(0.05fs)基本保留。具体判定标准:

  1. 输入混合信号幅度约±60,输出稳态幅度应显著小于输入(高频被滤除)
  2. 输出信号周期应与低频分量(0.05fs)一致,即每20个时钟周期完成一个完整正弦波
  3. 输出幅度约为输入低频分量的1倍(增益已归一化),即约±30

预期输出特征:由于滤波器通带截止频率为0.2fs,0.05fs分量在通带内(增益≈1),0.3fs分量在阻带(增益≈0)。因此输出应为幅度约±30、周期20clk的近似正弦波,叠加少量纹波。

常见问题

  • 输出全零:检查复位时序,确保rst_n释放后至少等待1个时钟周期再输入数据
  • 输出幅度异常大:检查累加器位宽是否足够,16个8位×8位乘积累加最大需要16位(本设计已用16位,若溢出需扩展至20位)
  • 高频未被滤除:确认系数是否加载正确,特别是负数的二进制补码表示