FIR滤波器代码及仿真设计

描述

上文 FPGA数字信号处理之滤波器2_使用dsp48e1的fir滤波器设计完成了结构设计:

仿真设计

根据这一结构,假定要设计一个满速率的fir滤波器,滤波器系数为:[3,13,27,58,62,204,47,546,233,1465,3260,3260,1465,233,546,47,204,62,58,27,13,3],滤波器总共22个系数,对称结构,所以有效系数11个。

以输入数据为自加数为例,根据结构可以得到数据摆放格式如下,clk0时刻的计算公式为:(500+521)*c0+(501+520)*c1+......+(510+511)*c10;clk1时刻的计算公式为:(501+522)*c0+(502+521)*c1+......+(511+512)*c10;....

仿真设计

据此,设计fir代码如下:

代码设置三个参数,数据位宽、系数个数、系数增益,其中系数个数决定代码中乘法器的个数及代码的处理时延,系数增益是系数给数据带来的增益,在数据输出的时候要通过截位截掉。

// ============================================================
// File Name: cm_fir_top
// VERSION  : V1.0
// DATA     : 2023/3/4
// Author   : FPGA干货分享
// ============================================================
// 功能:fir滤波器代码
// coef = 
// delay : 4+C_COEF_NUM*2
// ============================================================




`timescale 1ns/100ps
module cm_fir_top #(
    parameter                           C_DATA_WIDTH     = 16    ,
    parameter                           C_COEF_NUM       = 11    , ///有效系数个数
    parameter                           C_COEF_CUT_NUM   = 12    ) ///四舍五入使用的0.5大小
(
    input  wire                         I_sys_clk                , /// 工作时钟 
    input  wire                         I_rst_in                 , /// 复位 
    input  wire [C_DATA_WIDTH-1:0]      I_data_in                , /// 数据输入
    output reg  [C_DATA_WIDTH-1:0]      O_data_out                 /// 数据输出
);


// ============================================================
// 内部参数
// ============================================================
localparam  C_COEF_05 = 2**C_COEF_CUT_NUM ;


// ============================================================
// 变量
// ============================================================
reg     [C_DATA_WIDTH-1:0]      S_data_in[C_COEF_NUM*2-1:0] ;
wire    [17:0]                  S_coef[C_COEF_NUM-1:0]      ;
wire    [47:0]                  S_pcout[C_COEF_NUM-1:0]     ;
wire    [47:0]                  S_dsp_out[C_COEF_NUM-1:0]   ;

然后就是主代码,使用assign给系数赋值,然后根据系数个数缓存输入数据,用于fir滤波器的卷积操作。随后例化第一个dsp,U0_cm_dsp48e1,该滤波器作为级联滤波器组的开头乘法器,没有级联输入,但是使用C端口作为假四舍五入预加的输入,随后使用generate根据滤波器系数个数生成级联dsp组,最后将最后一级滤波器的输出进行截位,得到最终结果。

// ============================================================
// main code
// ============================================================
assign S_coef[0 ] =  18'd3     ;
assign S_coef[1 ] =  18'd13    ;
assign S_coef[2 ] = -18'd27   ;
assign S_coef[3 ] = -18'd58   ;
assign S_coef[4 ] =  18'd62    ;
assign S_coef[5 ] =  18'd204   ;
assign S_coef[6 ] = -18'd47   ;
assign S_coef[7 ] = -18'd546  ;
assign S_coef[8 ] = -18'd233  ;
assign S_coef[9 ] =  18'd1465  ;
assign S_coef[10] =  18'd3260  ;


always@(posedge I_sys_clk)
    S_data_in[0] <= I_data_in ;




genvar i;
generate for(i=1;i< C_COEF_NUM*2;i=i+1)
    begin
        always@(posedge I_sys_clk)
            S_data_in[i] <= S_data_in[i-1];
    end
endgenerate




cm_dsp48e1 #(
    .C_DATA_WITH_A      (C_DATA_WIDTH                   ),
    .C_DATA_WITH_B      (18                             ),
    .C_DATA_WITH_C      (32                             ),
    .C_DATA_WITH_D      (C_DATA_WIDTH                   )
)
U0_cm_dsp48e1(
    .I_CLK              (I_sys_clk                      ) , // clk
    .I_RST              (I_rst_in                       ) , // RST
    .I_A                (S_data_in[0]                   ) , // [29:0] 
    .I_B                (S_coef[0 ]                     ) , // [17:0] 
    .I_C                (C_COEF_05                      ) , // [47:0] 
    .I_D                (S_data_in[C_COEF_NUM*2-1]      ) , // [24:0] 
    .I_PCIN             (48'd0                          ) , // [47:0] 只能直连PCOUT
    .I_ALUMODE          (4'd0                           ) , // [3:0] 
    .I_INMODE           (5'b00101                       ) , // [4:0] 
    .I_OPMODE           (7'b0110101                     ) , // [6:0]  C + (A+D)*B
    .O_P                (                               ) , // [47:0]
    .O_PCOUT            (S_pcout[0]                     )   // [47:0] 只能直连PCIN
    );




genvar j;
generate for(j=1;j< C_COEF_NUM;j=j+1)
    begin


        cm_dsp48e1 #(
            .C_DATA_WITH_A      (C_DATA_WIDTH                   ),
            .C_DATA_WITH_B      (18                             ),
            .C_DATA_WITH_C      (32                             ),
            .C_DATA_WITH_D      (C_DATA_WIDTH                   )
        )
        U1_cm_dsp48e1(
            .I_CLK              (I_sys_clk                      ) , // clk
            .I_RST              (I_rst_in                       ) , // RST
            .I_A                (S_data_in[2*j]                 ) , // [29:0] 
            .I_B                (S_coef[j ]                     ) , // [17:0] 
            .I_C                (32'd0                          ) , // [47:0] 
            .I_D                (S_data_in[C_COEF_NUM*2-1]      ) , // [24:0] 
            .I_PCIN             (S_pcout[j-1]                   ) , // [47:0] 只能直连PCOUT
            .I_ALUMODE          (4'd0                           ) , // [3:0] 
            .I_INMODE           (5'b00101                       ) , // [4:0] 
            .I_OPMODE           (7'b0010101                     ) , // [6:0] PCin + (A+D)*B
            .O_P                (S_dsp_out[j]                   ) , // [47:0]
            .O_PCOUT            (S_pcout[j]                     )   // [47:0] 只能直连PCIN
            );


    end
endgenerate


always@(posedge I_sys_clk)
    O_data_out <= S_dsp_out[C_COEF_NUM-1][C_COEF_CUT_NUM+1+:C_DATA_WIDTH] ;




endmodule

仿真tb如下,可使用自加数或者单音作为输入:

// ============================================================
// File Name: cm_fir_top
// VERSION  : V1.0
// DATA     : 2023/3/4
// Author   : FPGA干货分享
// ============================================================
// 功能:fir滤波器代码
// delay : 20clk
// ============================================================




`timescale 1ns/100ps
module tb_cm_fir_top ;
    parameter                           C_DATA_WIDTH     = 16    ;
    parameter                           C_COEF_NUM       = 11    ; ///有效系数个数
    parameter                           C_COEF_CUT_NUM   = 12    ; ///四舍五入使用的0.5大小


    reg                                 I_sys_clk                ; /// 工作时钟 
    reg                                 I_rst_in                 ; /// 复位 
    reg         [C_DATA_WIDTH-1:0]      I_data_in                ; /// 数据输入
    wire        [C_DATA_WIDTH-1:0]      O_data_out               ; /// 数据输出,从start开始连续输出,位宽按照最大能力输出


    reg [31:0]      S_clk_cnt ;

initial
    begin
        I_sys_clk    = 'd1;
        I_rst_in     = 'd1;
        I_data_in    = 'd0;
        S_clk_cnt    = 'd0;

        #1000;
        I_rst_in     = 'd0;


    end

always #1 I_sys_clk = ~I_sys_clk;





always @(posedge I_sys_clk) 
    S_clk_cnt <= S_clk_cnt + 'd1;


/// 自加数
always @(posedge I_sys_clk)
    I_data_in <= I_data_in + 'd1;

///单音
// always @(posedge I_sys_clk)
    // I_data_in <= $sin(2*3.14*S_clk_cnt/512)*8192;


cm_fir_top #(
    .C_DATA_WIDTH               ( C_DATA_WIDTH          )  ,
    .C_COEF_NUM                 ( C_COEF_NUM            )  , ///有效系数个数
    .C_COEF_CUT_NUM             ( C_COEF_CUT_NUM        )  ) ///四舍五入使用的0.5大小
cm_fir_top(
    .I_sys_clk                  ( I_sys_clk             )  , /// 工作时钟 
    .I_rst_in                   ( I_rst_in              )  , /// 复位 
    .I_data_in                  ( I_data_in             )  , /// 数据输入
    .O_data_out                 ( O_data_out            )    /// 数据输出,从start开始连续输出,位宽按照最大能力输出
);






endmodule

使用自加数仿真对数如下:

输入输出:

仿真设计

dsp输入数据摆放:

仿真设计

python或者Excel计算的结果:

仿真设计

使用单音仿真结果如下:

仿真设计

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分