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

7.3 Verilog 串行 FIR 滤波器设计

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

串行 FIR 滤波器设计

设计说明

设计参数不变,与并行 FIR 滤波器参数一致。即,输入频率为 7.5 MHz 和 250 KHz 的正弦波混合信号,经过 FIR 滤波器后,高频信号 7.5MHz 被滤除,只保留 250KMHz 的信号。

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

串行设计,就是在 16 个时钟周期内对 16 个延时数据分时依次进行乘法、加法运算,然后在时钟驱动下输出滤波值。考虑到 FIR 滤波器系数的对称性,计算一个滤波输出值的周期可以减少到 8 个。串行设计时每个周期只进行一次乘法运算,所以设计中只需一个乘法器即可。此时数据需要每 8 个时钟周期有效输入一次,但是为了保证输出信号频率的正确性,工作时钟需要为采样频率的 8 倍,即 400MHz。这种方法的优点是资源耗费少,但是工作频率要求高,数据不能持续输出。

串行设计

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

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

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

实例

/**********************************************************
>> Description : fir study with serial tech
>> V190403     : Fs:50Mhz, fstop:1-6Mhz, order:16, sys clk:400MHz
***********************************************************/

`define SAFE_DESIGN
 
module fir_serial_low (
    input                rstn ,
    input                clk ,   // 系统工作时钟,400MHz
    input                en ,   // 输入数据有效信号
    input         [ 11 : 0 ]  xin ,   // 输入混合频率的信号数据
    output               valid , // 输出数据有效信号
    output       [ 28 : 0 ]  yout   // 输出数据
    ) ;
 
    //delay of input data enable
    reg [ 11 : 0 ]            en_r ;
    always @ ( posedge clk or negedge rstn ) begin
        if ( !rstn ) begin
            en_r [ 11 : 0 ]       <= 'b0 ;
        end
        else begin
            en_r [ 11 : 0 ]       <= {en_r [ 10 : 0 ] , en } ;
        end
    end
 
    //fir coeficient
    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 ;
 
    //(1) 输入数据移位部分
    reg [ 2 : 0 ]            cnt ;
    integer              i , j ;
    always @ ( posedge clk or negedge rstn ) begin
        if ( !rstn ) begin
            cnt <= 3'b0 ;
        end
        else if (en || cnt != 0 ) begin
            cnt <= cnt + 1'b1 ;     //8个周期计数
        end
    end
 
    reg [ 11 : 0 ]           xin_reg [ 15 : 0 ] ;
    always @ ( posedge clk or negedge rstn ) begin
        if ( !rstn ) begin
            for (i = 0 ; i < 16 ; i =i + 1 ) begin
                xin_reg [i ]   <= 1 2'b0 ;
            end
        end
        else if (cnt == 3'd0 && en ) begin     //每8个周期读入一次有效数据
            xin_reg [ 0 ] <= xin ;
            for (j = 0 ; j < 15 ; j =j + 1 ) begin
                xin_reg [j + 1 ] <= xin_reg [j ] ; // 数据移位
            end
        end
    end
 
    //(2) 系数对称,16个移位寄存器数据进行首位相加
    reg   [ 11 : 0 ]          add_a , add_b ;
    reg   [ 11 : 0 ]          coe_s ;
    wire [ 12 : 0 ]          add_s ;
    wire [ 2 : 0 ]           xin_index = cnt >= 1 ? cnt - 1 : 3'd7 ;
    always @ ( posedge clk or negedge rstn ) begin
        if ( !rstn ) begin
            add_a   <= 1 3'b0 ;
            add_b   <= 1 3'b0 ;
            coe_s   <= 1 2'b0 ;
        end
        else if (en_r [xin_index ] ) begin //from en_r[1]
            add_a   <= xin_reg [xin_index ] ;
            add_b   <= xin_reg [ 15 -xin_index ] ;
            coe_s   <= coe [xin_index ] ;
        end
    end
    assign add_s = {add_a } + {add_b } ;  
 
    //(3) 乘法运算,只用一个乘法
    reg         [ 24 : 0 ]    mout ;
`ifdef SAFE_DESIGN
    wire                 en_mult ;
    wire [ 3 : 0 ]           index_mult = cnt >= 2 ? cnt - 1 : 4'd7 + cnt [ 0 ] ;
    mult_man # ( 13 , 12 )   u_mult_single     //例化自己设计的流水线乘法器
        (.clk         (clk ) ,
         .rstn       (rstn ) ,
         .data_rdy   (en_r [index_mult ] ) ,   //注意数据时序对应
         .mult1       (add_s ) ,
         .mult2       (coe_s ) ,
         .res_rdy     (en_mult ) ,  
         .res         (mout )
        ) ;
 
` else
    always @ ( posedge clk or negedge rstn ) begin
        if ( !rstn ) begin
            mout   <= 2 5'b0 ;
        end
        else if ( |en_r [ 8 : 1 ] ) begin
            mout   <= coe_s * add_s ;   //直接乘
        end
    end
    wire                 en_mult = en_r [ 2 ] ;
`endif
 
    //(4) 积分累加,8组25bit数据 -> 1组 29bit 数据
    reg         [ 28 : 0 ]    sum ;
    reg                  valid_r ;
    //mult output en counter
    reg [ 4 : 0 ]            cnt_acc_r ;
    always @ ( posedge clk or negedge rstn ) begin
        if ( !rstn ) begin
            cnt_acc_r <= 'b0 ;
        end
        else if (cnt_acc_r == 5'd7 ) begin   //计时8个周期
            cnt_acc_r <= 'b0 ;
        end
        else if (en_mult || cnt_acc_r != 0 ) begin //只要en有效,计时不停
            cnt_acc_r <= cnt_acc_r + 1'b1 ;
        end
    end
 
    always @ ( posedge clk or negedge rstn ) begin
        if ( !rstn ) begin
            sum       <= 2 9'd0 ;
            valid_r   <= 1'b0 ;
        end
        else if (cnt_acc_r == 5'd7 ) begin //在第8个累加周期输出滤波值
            sum       <= sum + mout ;
            valid_r   <= 1'b1 ;
        end
        else if (en_mult && cnt_acc_r == 0 ) begin //初始化
            sum       <= mout ;
            valid_r   <= 1'b0 ;
        end
        else if (cnt_acc_r != 0 ) begin //acculating between cycles
            sum       <= sum + mout ;
            valid_r   <= 1'b0 ;
        end
    end
 
    //时钟锁存有效的输出数据,为了让输出信号不是那么频繁的变化
    reg [ 28 : 0 ]           yout_r ;
    always @ ( posedge clk or negedge rstn ) begin
        if ( !rstn ) begin
            yout_r <= 'b0 ;
        end
        else if (valid_r ) begin
            yout_r <= sum ;
        end
    end
    assign yout = yout_r ;
 
    //(5) 输出数据有效延迟,即滤波数据丢掉前15个滤波值
    reg [ 4 : 0 ]    cnt_valid ;
    always @ ( posedge clk or negedge rstn ) begin
        if ( !rstn ) begin
            cnt_valid       <= 'b0 ;
        end
        else if (valid_r && cnt_valid != 5'd16 ) begin
            cnt_valid       <= cnt_valid + 1'b1 ;
        end
    end
    assign valid = (cnt_valid == 5'd16 ) & valid_r ;

endmodule

testbench

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

其中,工作频率为 400MHz,但输入数据和输入数据有效信号应当都保持 50MHz 的频率输入。

实例

module test ;
    //input
    reg          clk ;
    reg          rst_n ;
    reg          en ;
    reg   [ 11 : 0 ]  xin ;
    //output
    wire [ 28 : 0 ]  yout ;
    wire         valid ;

    parameter    SIMU_CYCLE   = 6 4'd1000 ;
    parameter    SIN_DATA_NUM = 200 ;

//=====================================
// 8*50MHz clk generating
    localparam   TCLK_HALF     = ( 10_000 >> 3 ) ;
    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 * 8   * SIMU_CYCLE ) ;
        $finish ;
    end
 
//=======================================
// read cos data into register
    reg           [ 11 : 0 ] stimulus [ 0 : SIN_DATA_NUM - 1 ] ;
    integer      i ;
    initial begin
        $readmemh ( "../tb/cosx0p25m7p5m12bit.txt" , stimulus ) ;
        en = 0 ;
        i = 0 ;
        xin = 0 ;
        # 200 ;
        forever begin
            repeat ( 7 )   @ ( negedge clk ) ; //空置7个周期,第8个周期给数据
            en           = 1 ;
            xin         = stimulus [i ] ;
            @ ( negedge clk ) ;
            en           = 0 ;         //输入数据有效信号只持续一个周期即可
            if (i == SIN_DATA_NUM - 1 )  i = 0 ;
            else  i = i + 1 ;
        end
    end
 
    fir_serial_low       u_fir_serial (
        .clk         (clk ) ,
        .rstn         (rst_n ) ,
        .en           (en ) ,
        .xin         (xin ) ,
        .valid       (valid ) ,
        .yout         (yout ) ) ;

endmodule

仿真结果

由下图仿真结果可知,经过 FIR 滤波器后的信号只有一种低频率信号(250KHz),高频信号(7.5MHz)被滤除了。为了波形更加的美观,取 16 个之后的滤波数据作为有效输出。

波形局部放大后如下图所示,此时输入数据有效信号 en 与输出数据有效信号 valid 是周期(50MHz)相同的脉冲信号,不是持续有效的。但工作时钟为 400MHz,所以输出也会呈现为 50MHz 采样频率下的 250KHz 频率的正弦波信号。

附录:matlab 使用(与《并行 FIR 滤波器设计》一致)

生成 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