当前位置: 首页 > 教程 > Verilog >

7.2 Verilog 并行 FIR 滤波器设计

精华
小牛编辑
198浏览
2023-03-14

FIR(Finite Impulse Response)滤波器是一种有限长单位冲激响应滤波器,又称为非递归型滤波器。

FIR 滤波器具有严格的线性相频特性,同时其单位响应是有限长的,因而是稳定的系统,在数字通信、图像处理等领域都有着广泛的应用。

FIR 滤波器原理

FIR 滤波器是有限长单位冲击响应滤波器。直接型结构如下:

FIR 滤波器本质上就是输入信号与单位冲击响应函数的卷积,表达式如下:

FIR 滤波器有如下几个特性:

  • (1) 响应是有限长序列。
  • (2) 系统函数在 |z| > 0 处收敛,极点全部在 z=0 处,属于因果系统。
  • (3) 结构上是非递归的,没有输出到输入的反馈。
  • (4) 输入信号相位响应是线性的,因为响应函数 h(n) 系数是对称的。
  • (5) 输入信号的各频率之间,相对相位差也是固定不变的。
  • (6) 时域卷积等于频域相乘,因此该卷积相当于筛选频谱中各频率分量的增益倍数。某些频率分量保留,某些频率分量衰减,从而实现滤波的效果。

并行 FIR 滤波器设计

设计说明

输入频率为 7.5 MHz 和 250 KHz 的正弦波混合信号,经过 FIR 滤波器后,高频信号 7.5MHz 被滤除,只保留 250KHz 的信号。设计参数如下:

输入频率:    7.5MHz 和 250KHz
采样频率:    50MHz
阻带:           1MHz ~ 6MHz
阶数:           15(N-1=15)

由 FIR 滤波器结构可知,阶数为 15 时,FIR 的实现需要 16 个乘法器,15 个加法器和 15 组延时寄存器。为了稳定第一拍的数据,可以再多用一组延时寄存器,即共用 16 组延时寄存器。由于 FIR 滤波器系数的对称性,乘法器可以少用一半,即共使用 8 个乘法器。

并行设计,就是在一个时钟周期内对 16 个延时数据同时进行乘法、加法运算,然后在时钟驱动下输出滤波值。这种方法的优点是滤波延时短,但是对时序要求比较高。

并行设计

设计中使用到的乘法器模块代码,可参考之前流水线式设计的乘法器。

为方便快速仿真,也可以直接使用乘号 * 完成乘法运算,设计中加入宏定义 SAFE_DESIGN 来选择使用哪种乘法器。

FIR 滤波器系数可由 matlab 生成,具体见附录。

实例

/***********************************************************
>> V201001 : Fs:50Mhz, fstop:1Mhz-6Mhz, order: 15
************************************************************/

`define SAFE_DESIGN
 
module fir_guide     (
    input                rstn ,   //复位,低有效
    input                clk ,   //工作频率,即采样频率
    input                en ,     //输入数据有效信号
    input         [ 11 : 0 ]  xin ,   //输入混合频率的信号数据
    output               valid , //输出数据有效信号
    output       [ 28 : 0 ]  yout   //输出数据,低频信号,即250KHz
    ) ;
 
    //data en delay
    reg [ 3 : 0 ]            en_r ;
    always @ ( posedge clk or negedge rstn ) begin
        if ( !rstn ) begin
            en_r [ 3 : 0 ]       <= 'b0 ;
        end
        else begin
            en_r [ 3 : 0 ]       <= {en_r [ 2 : 0 ] , en } ;
        end
    end
 
    //(1) 16 组移位寄存器
    reg         [ 11 : 0 ]    xin_reg [ 15 : 0 ] ;
    reg [ 3 : 0 ]            i , j ;
    always @ ( posedge clk or negedge rstn ) begin
        if ( !rstn ) begin
            for (i = 0 ; i < 15 ; i =i + 1 ) begin
                xin_reg [i ]   <= 1 2'b0 ;
            end
        end
        else if (en ) begin
            xin_reg [ 0 ] <= xin ;
            for (j = 0 ; j < 15 ; j =j + 1 ) begin
                xin_reg [j + 1 ] <= xin_reg [j ] ; //周期性移位操作
            end
        end
    end
 
    //Only 8 multipliers needed because of the symmetry of FIR filter coefficient
    //(2) 系数对称,16个移位寄存器数据进行首位相加
    reg         [ 12 : 0 ]    add_reg [ 7 : 0 ] ;
    always @ ( posedge clk or negedge rstn ) begin
        if ( !rstn ) begin
            for (i = 0 ; i < 8 ; i =i + 1 ) begin
                add_reg [i ] <= 1 3'd0 ;
            end
        end
        else if (en_r [ 0 ] ) begin
            for (i = 0 ; i < 8 ; i =i + 1 ) begin
                add_reg [i ] <= xin_reg [i ] + xin_reg [ 15 -i ] ;
            end
        end
    end
 
    //(3) 8个乘法器
    // 滤波器系数,已经过一定倍数的放大
    wire         [ 11 : 0 ]   coe [ 7 : 0 ] ;
    assign coe [ 0 ]         = 1 2'd11 ;
    assign coe [ 1 ]         = 1 2'd31 ;
    assign coe [ 2 ]         = 1 2'd63 ;
    assign coe [ 3 ]         = 1 2'd104 ;
    assign coe [ 4 ]         = 1 2'd152 ;
    assign coe [ 5 ]         = 1 2'd198 ;
    assign coe [ 6 ]         = 1 2'd235 ;
    assign coe [ 7 ]         = 1 2'd255 ;
    reg         [ 24 : 0 ]   mout [ 7 : 0 ] ;
 
`ifdef SAFE_DESIGN
    //流水线式乘法器
    wire [ 7 : 0 ]          valid_mult ;
    genvar              k ;
    generate
        for (k = 0 ; k < 8 ; k =k + 1 ) begin
            mult_man # ( 13 , 12 )
            u_mult_paral           (
              .clk         (clk ) ,
              .rstn       (rstn ) ,
              .data_rdy   (en_r [ 1 ] ) ,
              .mult1       (add_reg [k ] ) ,
              .mult2       (coe [k ] ) ,
              .res_rdy     (valid_mult [k ] ) , //所有输出使能完全一致  
              .res         (mout [k ] )
            ) ;
        end
    endgenerate
    wire valid_mult7     = valid_mult [ 7 ] ;
 
` else
    //如果对时序要求不高,可以直接用乘号
    always @ ( posedge clk or negedge rstn ) begin
        if ( !rstn ) begin
            for (i = 0 ; i < 8 ; i =i + 1 ) begin
                mout [i ]     <= 2 5'b0 ;
            end
        end
        else if (en_r [ 1 ] ) begin
            for (i = 0 ; i < 8 ; i =i + 1 ) begin
                mout [i ]     <= coe [i ] * add_reg [i ] ;
            end
        end
    end
    wire valid_mult7 = en_r [ 2 ] ;
`endif
 
    //(4) 积分累加,8组25bit数据 -> 1组 29bit 数据
    //数据有效延时
    reg [ 3 : 0 ]            valid_mult_r ;
    always @ ( posedge clk or negedge rstn ) begin
        if ( !rstn ) begin
            valid_mult_r [ 3 : 0 ]   <= 'b0 ;
        end
        else begin
            valid_mult_r [ 3 : 0 ]   <= {valid_mult_r [ 2 : 0 ] , valid_mult7 } ;
        end
    end

`ifdef SAFE_DESIGN
    //加法运算时,分多个周期进行流水,优化时序
    reg         [ 28 : 0 ]    sum1 ;
    reg         [ 28 : 0 ]    sum2 ;
    reg         [ 28 : 0 ]    yout_t ;
    always @ ( posedge clk or negedge rstn ) begin
        if ( !rstn ) begin
            sum1   <= 2 9'd0 ;
            sum2   <= 2 9'd0 ;
            yout_t <= 2 9'd0 ;
        end
        else if (valid_mult7 ) begin
            sum1   <= mout [ 0 ] + mout [ 1 ] + mout [ 2 ] + mout [ 3 ] ;
            sum2   <= mout [ 4 ] + mout [ 5 ] + mout [ 6 ] + mout [ 7 ] ;
            yout_t <= sum1 + sum2 ;
        end
    end
 
` else
    //一步计算累加结果,但是实际中时序非常危险
    reg signed [ 28 : 0 ]    sum ;
    reg signed [ 28 : 0 ]    yout_t ;
    always @ ( posedge clk or negedge rstn ) begin
        if ( !rstn ) begin
            sum     <= 2 9'd0 ;
            yout_t <= 2 9'd0 ;
        end
        else if (valid_mult7 ) begin
            sum     <= mout [ 0 ] + mout [ 1 ] + mout [ 2 ] + mout [ 3 ] + mout [ 4 ] + mout [ 5 ] + mout [ 6 ] + mout [ 7 ] ;
            yout_t <= sum ;
        end
    end
`endif
    assign yout   = yout_t ;
    assign valid = valid_mult_r [ 0 ] ;

endmodule

testbench

testbench 编写如下,主要功能就是不间断连续的输入 250KHz 与 7.5MHz 的正弦波混合信号数据。输入的混合信号数据也可由 matlab 生成,具体见附录。

实例

`timescale 1ps / 1ps
 
module test ;
    //input
    reg          clk ;
    reg          rst_n ;
    reg          en ;
    reg [ 11 : 0 ]   xin ;
    //output
    wire         valid ;
    wire [ 28 : 0 ]  yout ;
 
    parameter    SIMU_CYCLE   = 6 4'd2000 ;   //50MHz 采样频率
    parameter    SIN_DATA_NUM = 200 ;       //仿真周期

//=====================================
// 50MHz clk generating
    localparam   TCLK_HALF     = 10_000 ;
    initial begin
        clk = 1'b0 ;
        forever begin
            # TCLK_HALF ;
            clk = ~clk ;
        end
    end
 
//============================
//  reset and finish
    initial begin
        rst_n = 1'b0 ;
        # 30   rst_n = 1'b1 ;
        # (TCLK_HALF * 2 * SIMU_CYCLE ) ;
        $finish ;
    end
 
//=======================================
// read signal data into register
    reg           [ 11 : 0 ] stimulus [ 0 : SIN_DATA_NUM - 1 ] ;
    integer      i ;
    initial begin
        $readmemh ( "../tb/cosx0p25m7p5m12bit.txt" , stimulus ) ;
        i = 0 ;
        en = 0 ;
        xin = 0 ;
        # 200 ;
        forever begin
            @ ( negedge clk ) begin
                en           = 1'b1 ;
                xin         = stimulus [i ] ;
                if (i == SIN_DATA_NUM - 1 ) begin   //周期送入数据控制
                    i = 0 ;
                end
                else begin
                    i = i + 1 ;
                end
            end
        end
    end
 
    fir_guide u_fir_paral (
      .xin         (xin ) ,
      .clk         (clk ) ,
      .en           (en ) ,
      .rstn         (rst_n ) ,
      .valid       (valid ) ,
      .yout         (yout ) ) ;
 
endmodule

仿真结果

由下图仿真结果可知,经过 FIR 滤波器后的信号只有一种低频率信号(250KHz),高频信号(7.5MHz)被滤除了。而且输出波形是连续的,能够持续输出。

但是,如红圈所示,波形起始部分呈不规则状态,对此进行放大。

波形起始端放大后如下图所示,可见不规则波形的时间段,即两根竖线之间的时间间隔是 16 个时钟周期。

因为数据是串行输入,设计中使用了 16 组延时寄存器,所以滤波后的第一个正常点应该较第一个滤波数据输出时刻延迟 16 个时钟周期。即数据输出有效信号 valid 应该再延迟 16 个时钟周期,则会使输出波形更加完美。

附录:matlab 使用

生成 FIR 滤波器系数

打开 matlab,在命令窗口输入命令: fdatool。

然后会打开如下窗口,按照 FIR 滤波器参数进行设置。

这里选择的 FIR 实现方法是最小二乘法(Least-squares),不同的实现方式滤波效果也不同。

点击 File -> Export

将滤波器参数输出,存到变量 coef 中,如下图所示。

此时 coef 变量应该是浮点型数据。对其进行一定倍数的相乘扩大,然后取其近似的定点型数据作为设计中的 FIR 滤波器参数。这里取扩大倍数为 2048,结果如下所示。

生成输入的混合信号

利用 matlab 生成混合的输入信号参考代码如下。

信号为无符号定点型数据,位宽宽度为 12bit,存于文件 cosx0p25m7p5m12bit.txt

实例

clear all ;close all ;clc ;
%=======================================================
% generating a cos wave data with txt hex format
%=======================================================

fc           = 0.25e6 ;       % 中心频率
fn           = 7.5e6 ;       % 杂波频率
Fs           = 50e6 ;         % 采样频率
T           = 1 /fc ;         % 信号周期
Num         = Fs * T ;       % 周期内信号采样点数
t           = ( 0 :Num - 1 ) /Fs ;       % 离散时间
cosx         = cos ( 2 *pi *fc *t ) ;     % 中心频率正弦信号
cosn         = cos ( 2 *pi *fn *t ) ;     % 杂波信号
cosy         = mapminmax (cosx + cosn ) ;     %幅值扩展到( - 1 , 1) 之间
cosy_dig     = floor ( ( 2 ^ 11 - 1 ) * cosy + 2 ^ 11 ) ;     %幅值扩展到 0 ~ 4095
fid         = fopen ('cosx0p25m7p5m12bit.txt' , 'wt' ) ;   %写数据文件
fprintf (fid , ' %x\n' , cosy_dig ) ;
fclose (fid ) ;
 
%时域波形
figure ( 1 ) ;
subplot ( 121 ) ;plot (t ,cosx ) ;hold on ;
plot (t ,cosn ) ;
subplot ( 122 ) ;plot (t ,cosy_dig ) ;
 
%频域波形
fft_cosy     = fftshift (fft (cosy , Num ) ) ;
f_axis       = ( -Num / 2 : Num / 2 - 1 ) * (Fs /Num ) ;
figure ( 5 ) ;
plot (f_axis , abs (fft_cosy ) ) ;

源码下载

Download