You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

低通FIR滤波器Verilog实现与MATLAB结果不符问题求助

FIR滤波器实现问题排查

背景信息

通过MATLAB命令h = fir1(99, 0.2, 'low')生成低通FIR滤波器系数,随后生成三个正弦波信号:$f(t)=\sin(2 \cdot \pi \cdot f \cdot t)$,频率分别取950Hz、1100Hz、2000Hz,以10000Hz采样率采集2000个样本。将样本与滤波器系数转换为Q(2,14)定点格式后,MATLAB输出结果中$y[0] = 0x0000$(十六进制),该结果符合预期,因为三种频率正弦波的首个输入样本均为0。

遇到的问题

  • 三种Verilog实现均未输出$y[0] = 0x0000$:按设计逻辑,第7个时钟上升沿应输出$yout = y[0] = 0$,实际却得到$yout = y[1] = FFFE$(十六进制),$y[0]$始终未生成。
  • 三种Verilog滤波器的输出结果不一致。

直接型FIR滤波器Verilog代码

模块代码

module direct_fir #(parameter integer N = 100)
(
    input  wire                 clk,
    input  wire                 rst,    // 同步高有效复位
    input  wire                 vin,
    input  wire signed  [15:0]  xin,    // Q_2_14
    output reg                  vout,
    output reg signed  [15:0]   yout     // Q_2_14
);

integer i;

    reg signed [15:0] xdel [0:N-1];
    reg signed [15:0] h    [0:N-1];

    reg signed [63:0] acc;
    reg signed [31:0] prod;
    reg vin_d;

    reg signed [63:0] acc_abs;
    reg signed [63:0] acc_scaled;

    localparam signed [63:0] HALF_LSB = 64'sd8192;  // 2^13
    localparam integer       SHIFT    = 14;

    initial begin
        $readmemh("coef_q214_hex.txt", h);
    end

    function automatic signed [15:0] sat16(input signed [63:0] x);
        begin
            if (x > 64'sd32767)        sat16 = 16'sd32767;
            else if (x < -64'sd32768)  sat16 = -16'sd32768;
            else                       sat16 = x[15:0];
        end
    endfunction

    always @(posedge clk) begin
        if (rst) begin
            for (i=0; i<N; i=i+1) xdel[i] <= 16'sd0;
            yout  <= 16'sd0;
            vout  <= 1'b0;
            vin_d <= 1'b0;
        end else begin
            if (vin) begin
                for (i=N-1; i>0; i=i-1)
                    xdel[i] <= xdel[i-1];
                xdel[0] <= xin;
            end

            vin_d <= vin;
            vout  <= 1'b0;

            if (vin_d) begin
                acc        = 64'sd0;
                acc_abs    = 64'sd0;
                acc_scaled = 64'sd0;
                for (i=0; i<N; i=i+1) begin
                    prod = xdel[i] * h[i];                 // Q_4_28
                    acc  = acc + {{32{prod[31]}}, prod};   // 符号扩展到64位
                end

                if (acc >= 0) begin
                    acc_scaled = (acc + HALF_LSB) >>> SHIFT;
                end else begin
                    acc_abs    = -acc;
                    acc_scaled = -((acc_abs + HALF_LSB) >>> SHIFT);
                end

                yout <= sat16(acc_scaled);
                vout <= 1'b1;
            end
        end
    end
endmodule

测试平台代码

`timescale 1ns/1ps

module tb_direct_fir;

  // 参数定义

  parameter integer N_TAPS   = 100;
  parameter integer NSAMPLES = 2000;

  parameter integer TCLK = 10;

  // DUT信号

  reg clk;
  reg rst;
  reg vin;
  reg signed [15:0] xin;

  wire vout;
  wire signed [15:0] yout;

 
  // 输入存储器

  reg [15:0] xmem [0:NSAMPLES-1];

  integer n;
  integer fout;


  // 例化DUT

  direct_fir #(.N(N_TAPS)) dut (
    .clk (clk),
    .rst (rst),
    .vin (vin),
    .xin (xin),
    .vout(vout),
    .yout(yout)
  );

  // 时钟生成

  initial begin
    clk = 1'b0;
    forever #(TCLK/2) clk = ~clk;
  end

//__________________________________________________________

initial begin
  $dumpfile("direct_fir.vcd");  
  $dumpvars(0, tb_direct_fir);   
end

//______________________________________________________________

  // 同步复位任务

  task apply_reset;
    begin
      rst = 1'b1;
      vin = 1'b0;
      xin = 16'sd0;

      @(posedge clk);
      @(posedge clk);
      @(posedge clk);

      rst = 1'b0;

      @(posedge clk);
    end
  endtask


  // 输出日志记录

  always @(negedge clk) begin
  if (!rst && vout) begin
    $fwrite(fout, "%04h\n", yout);
  end
end

  // 运行测试任务
 
    task run_file;
    input [8*64-1:0] in_filename;
    input [8*64-1:0] out_filename;
    begin
     
      $readmemh(in_filename, xmem);  // 加载输入样本到xmem

      fout = $fopen(out_filename, "w");  // 打开输出文件
      if (fout == 0) begin
        $display("ERROR: 无法打开输出文件 %s", out_filename);
        $finish;
      end

     
      apply_reset;  // 复位DUT

// 每时钟喂入一个样本:在时钟下降沿驱动,上升沿采样
vin = 1'b1;
for (n = 0; n < NSAMPLES; n = n + 1) begin
  @(negedge clk);
  xin = $signed(xmem[n]);   // 确保下一个上升沿时信号稳定
end

// 额外一个上升沿,确保最后一个样本被捕获
@(posedge clk);
      
    
      // 停止喂入样本
      @(negedge clk);
      xin = 16'sd0;
      vin = 1'b0;
 

      
      for (n = 0; n < 10; n = n + 1) begin // 额外等待10个周期确保输出完全刷新
        @(posedge clk);
      end
      
      @(negedge clk);
      $fclose(fout);
    end
  endtask   

  // 主测试流程

  initial begin

    rst = 1'b1;
    vin = 1'b0;
    xin = 16'sd0;

    run_file("x950_q214_hex.txt",  "y950_direct_hex.txt");
    run_file("x1100_q214_hex.txt", "y1100_direct_hex.txt");
    run_file("x2000_q214_hex.txt", "y2000_direct_hex.txt");

    $display("DONE: 输出已写入文件。");
    $finish;
  end

endmodule

内容的提问来源于stack exchange,提问作者Shashank Ranjan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.11 17:14:53