IIR滤波器设计——个人感悟

查过很多资料,对于IIR滤波器结构和原理介绍很多,但是,真正对于FPGA的快速设计介绍很少。我对IIR滤波器的MATLAB仿真和FPGA硬件仿真做了充分的对比,关于IIR滤波器设计和实现做一下总结:

说明:IIR滤波器最佳实现结构、IIR滤波器结果舍入处理已做过文档说明。

1.         FDAtool设计IIR滤波器参数——结构为直接I型

IIR滤波器设计——个人感悟

比较简单,记录一下几种常用滤波器区别:

l  巴特沃斯滤波器:通带和阻带有最大平坦度,但是过渡带比较平缓,当需要比较陡峭的过渡带时,需要较大的阶数;巴特沃斯滤波器对于系数量化影响相对较弱。

l  Chebyshev滤波器:分为I型和II型,I型通带等纹波,阻带平坦;II型通带平坦,阻带等纹波,过渡带较巴特沃斯滤波器陡峭;

l  椭圆滤波器:在通带和阻带都是等纹波,过渡带最陡峭,对于相同的过渡带指标,椭圆滤波器需要的阶数最少,但是对于系数量化最敏感。

2.         FDAtool系数量化

说明:FPGA计算都是基于二进制补码计算的,采用定点数,在实际设计时需要对FPGA硬件资源有一定了解。

量化分为两部分:增益量化和零点极点量化,一般为了节省硬件资源,增益量化和系数量化采用相同的位数,只是他们缩放倍数不一样,在实现时需要考虑。

设计例子如下:

 IIR滤波器设计——个人感悟

关于滤波器系数量化位数选择:个人建议在保证滤波器稳定的前提下,结合FPGA硬件结构来选择。比如:xilinx dsp48e IP core可以实现25x18位的硬件乘法器,实际设计时,16位量化位数滤波器就达到稳定,最终还是选择18位,不用白不用,同时,输出结果也更加准确,增加滤波器系数量化位数,还可以允许更低位数的数据输入。

上图界面中还需要确定Input/output参数量化位数,这两个参数我们可以不管,到此为止,fdatool的工作已经完成。

那么对于任意位数的输入数据滤波器都是有意义的?当然不是。通过实际MATLAB仿真,滤波器有输入最小位数限制,否则输出全为0,这也不难理解,因为在FPGA实现时需要进行两次截尾或舍入处理,数据过小,就会输出为零。通常对于小的输入数据可以进行左移位。

IIR滤波器的最小数据输入位数可以通过MATLAB定点仿真来确定,这个是很有必要的,后面会说明。

 

3.         滤波器输入数据位宽确定——乘法器类型确定

一般的硬件乘法器IP core都可以达到100M以上,所以一般的几十兆以下采样率的滤波器都采用串行结构实现,即所有运算复用一个IP core。结合xilinx dsp48e特点,可以设计成乘加器,这样只用耗费一个dsp资源就可以实现所有滤波器计算。

共用一个dsp,就要求每次数据的输入和输出位宽相同,方便处理,同时保证不会溢出。

简单直接I型IIR滤波器结构如下:

IIR滤波器设计——个人感悟

乘加运算包括:增益计算、零点计算、极点计算。增益计算:输入乘以增益(18位);极点计算:输出乘以极点系数(18位)。X,y具有相同的数据位宽,由于y后面有除法操作,所以y的表示位宽直接影响到滤波器的数据输出,y的位宽需要通过MATLAB量化仿真来确定。例如:在实际设计时,使用16位输入量化,不能得到正确的输出结果,输入要到18位以上才能得到相对正确的结果,最终取20位数据输入,小数据可以通过左移位来满足要求。

 

4.         滤波器参数获取——fdatool强大功能

Targets ->generate HDL选择需要的hdl语言和保存位置即可获得IIR滤波器程序,如下:我只用到它的量化参数,其他由于IP和具体器件有关,代码自己编写:

parameter signed [17:0] scaleconst1 = 18'b011000001001011000; //sfix18_En21

parameter signed [17:0] coeff_b1_section1 = 18'b010000000000000000; //sfix18_En16

parameter signed [17:0] coeff_b2_section1 = 18'b100000000100011110; //sfix18_En16

parameter signed [17:0] coeff_b3_section1 = 18'b010000000000000000; //sfix18_En16

parameter signed [17:0] coeff_a2_section1 = 18'b100000001001000011; //sfix18_En16

parameter signed [17:0] coeff_a3_section1 = 18'b001111110111001010; //sfix18_En16

parameter signed [17:0] scaleconst2 = 18'b001001011100101101; //sfix18_En21

parameter signed [17:0] coeff_b1_section2 = 18'b010000000000000000; //sfix18_En16

parameter signed [17:0] coeff_b2_section2 = 18'b100000001011101011; //sfix18_En16

parameter signed [17:0] coeff_b3_section2 = 18'b010000000000000000; //sfix18_En16

parameter signed [17:0] coeff_a2_section2 = 18'b100000010111111001; //sfix18_En16

parameter signed [17:0] coeff_a3_section2 = 18'b001111101000010101; //sfix18_En16

parameter signed [17:0] scaleconst3 = 18'b000011101111100011; //sfix18_En21

parameter signed [17:0] coeff_b1_section3 = 18'b010000000000000000; //sfix18_En16

parameter signed [17:0] coeff_b2_section3 = 18'b010000000000000000; //sfix18_En16

parameter signed [17:0] coeff_b3_section3 = 18'b000000000000000000; //sfix18_En16

parameter signed [17:0] coeff_a2_section3 = 18'b110000001110111110; //sfix18_En16

parameter signed [17:0] coeff_a3_section3 = 18'b000000000000000000; //sfix18_En16

       这些参数自动生成,直接调用。

5.         代码编写——直接I型IIR滤波器级联实现

直接I型二阶IIR滤波器verilog代码:IP例化为20X18位乘加器,一级需要至少7个时钟周期,以上设计为8个周期:

module IIROrder2FormI

    #(

    parameter DataWidth = 20,

    parameter CoeffientWidth = 18,

    parameter GainScaleWidth = 20,

    parameter signed [CoeffientWidth - 1 :0] Gain = 18'h4_6862,

    parameter signed [0 : CoeffientWidth * 3 - 1] NumCoeffient = {18'h4_0000,18'h8_002A,18'h4_0000},

    parameter signed [0 : CoeffientWidth * 2 - 1] DenCoeffient = {18'h8_01DE,18'h3_FE2E}

    )

    (

    input Clk,

    input Reset,

    input DataInValid,

    input [DataWidth-1:0] DataIn,

    output DataOutValid,

    output [DataWidth-1:0] DataOut

    );

    parameter STATENUM = 5'd8;

    reg signed [DataWidth-1:0] DataOutTemp;

    wire signed [DataWidth - 1:0] DataRoundOut;

    wire signed [DataWidth - 1:0] DataRoundOut2;

     (* dont_touch="true" *)   

    //////////multAdder signal////////////

    reg signed [DataWidth - 1:0] DataA;

     (* dont_touch="true" *)

    reg signed [CoeffientWidth - 1:0] DataB;

    reg signed [DataWidth + CoeffientWidth + 1:0] DataC;   

    wire signed [DataWidth + CoeffientWidth + 1:0] DataP;

    reg SUBTRACT;

    wire signed [47:0] PCOUT;

    //numeri////////////////////////

    reg signed [DataWidth - 1:0] DataNumIn;

    reg signed [DataWidth - 1:0] DataNumRegDelay1;

    reg signed [DataWidth - 1:0] DataNumRegDelay2;

    /////*********** DenCoeffient ***************//////////////

    reg signed [DataWidth - 1 :0] DataDenRegDelay1;

    reg signed [DataWidth - 1 :0] DataDenRegDelay2;

    reg [4:0] StateCounter;

    reg ConvState;

    reg DataOutValidTemp;

    assign DataOutValid = DataOutValidTemp;

    always @(posedge Clk)

    begin

       if(Reset)

           ConvState <= 1'b0;

       else

           case(ConvState)

               1'b0 :   begin

                            if(DataInValid == 1'b1)

                                ConvState <= 1'b1;

                            else

                                ConvState <= 1'b0;

                        end

               1'b1 :   begin

                            if(StateCounter == (STATENUM - 5'd2))

                                ConvState <= 1'b0;

                            else

                                ConvState <= 1'b1;

                        end

               default: ConvState <= 1'b0;

           endcase

    end

   

    always @(posedge Clk)

    begin

        if(Reset)

            StateCounter <= 0;

        else if(StateCounter == (STATENUM - 5'd2))

            StateCounter <= 0;

        else if(ConvState == 1'b1)

            StateCounter <= StateCounter + 1'd1;   

        else

            StateCounter <= StateCounter;

    end

   

    always @(posedge Clk)

    begin

        if (Reset)

        begin

            DataNumRegDelay1 <= 0;

            DataNumRegDelay2 <= 0;

            DataDenRegDelay1 <= 0;

            DataDenRegDelay2 <= 0;

            SUBTRACT <= 1'b0;

            DataOutTemp <= 0;

            DataNumIn <= 0;

            DataOutValidTemp <= 1'b0;

        end

        else

        begin

            case(StateCounter)

                5'd0:   begin

                            DataOutValidTemp <= 1'b0;

                            DataA <= DataIn;

                            DataB <= Gain;

                            DataC <= $signed(0);

                            SUBTRACT <= 1'b0;

                        end

                5'd1:   begin

                            DataA <= DataRoundOut;

                            DataB <= NumCoeffient[0 : CoeffientWidth - 1];

                            DataC <= $signed(0);

                            SUBTRACT <= 1'b0;

                            DataNumIn <= DataRoundOut;

                            DataNumRegDelay1 <= DataNumIn;

                            DataNumRegDelay2 <= DataNumRegDelay1;

                        end

                5'd2:   begin

                            DataA <= DataNumRegDelay1;

                            DataB <= NumCoeffient[CoeffientWidth : CoeffientWidth * 2 - 1];

                            DataC <= DataP;

                            SUBTRACT <= 1'b0;

                        end

                5'd3:   begin

                            DataA <= DataNumRegDelay2;

                            DataB <= NumCoeffient[CoeffientWidth * 2 : CoeffientWidth * 3 - 1];

                            DataC <= DataP;

                            SUBTRACT <= 1'b0;

                            DataDenRegDelay1 <= DataOutTemp;

                            DataDenRegDelay2 <= DataDenRegDelay1;

                        end

                5'd4:   begin

                            DataA <= DataDenRegDelay1;

                            DataB <= DenCoeffient[0 : CoeffientWidth - 1];

                            DataC <= DataP;

                            SUBTRACT <= 1'b1;

                        end

                5'd5:   begin

                            DataA <= DataDenRegDelay2;

                            DataB <= DenCoeffient[CoeffientWidth : CoeffientWidth * 2 - 1];

                            DataC <= DataP;

                            SUBTRACT <= 1'b1;

                        end

                5'd6:   begin

                            DataOutTemp <= DataRoundOut2;

                            DataOutValidTemp <= 1'b1;

                        end

//              5'd7:   begin

//                            DataOutTemp <= DataRoundOut2;

//                        end

                default :   begin

                                DataA <= DataA;

                                DataB <= DataB;

                                DataC <= DataC;

                                SUBTRACT <= SUBTRACT;

                                DataNumRegDelay1 <= DataNumRegDelay1;

                                DataNumRegDelay2 <= DataNumRegDelay2;

                                DataDenRegDelay1 <= DataDenRegDelay1;

                                DataDenRegDelay2 <= DataDenRegDelay2;

                                DataOutTemp <= DataOutTemp;

                                DataNumIn <= DataNumIn;

                                DataOutValidTemp <= DataOutValidTemp;

                            end

            endcase

        end

    end

 

    assign DataOut = DataOutTemp;

   RoundData #(

    .DataInWidth(DataWidth + GainScaleWidth),

    .DataOutWidth(DataWidth)

    )U_FilterDataIn

    (

    .DataIn({{(GainScaleWidth - CoeffientWidth){DataP[DataWidth + CoeffientWidth - 1 ]}} ,DataP[DataWidth + CoeffientWidth - 1 :0 ]}),

    .OverFlow(DataP[DataWidth + CoeffientWidth - 1]),

    .DataOut(DataRoundOut)

    );

   

    RoundData #(

    .DataInWidth(DataWidth + CoeffientWidth -2 ),

    .DataOutWidth(DataWidth)

    )U_DENround

    (

    .DataIn(DataP[DataWidth + CoeffientWidth - 3 :0 ]),

    .OverFlow(DataP[DataWidth + CoeffientWidth - 2]),

    .DataOut(DataRoundOut2)

    );

          

    MultAdder20x18x40 U_MultSub(

//    .CLK(Clk),

//    .CE(1'b1),

//    .SCLR(1'b0),

    .A(DataA),

    .B(DataB),

    .C(DataC),

    .SUBTRACT(SUBTRACT),

    .P(DataP),

    .PCOUT(PCOUT));   

endmodule

顶层文件:

module IIRLowPass12500K2Sections(

    input Clk,

    input Reset,

    input DataInValid,

    input [19:0] DataIn,

    output DataOutValid,

    output [19:0] DataOut

    );

    parameter signed [17:0] scaleconst1 = 18'b010010101111011011; //sfix18_En18

    parameter signed [17:0] coeff_b1_section1 = 18'b010000000000000000; //sfix18_En16

    parameter signed [17:0] coeff_b2_section1 = 18'b100000001100000111; //sfix18_En16

    parameter signed [17:0] coeff_b3_section1 = 18'b010000000000000000; //sfix18_En16

    parameter signed [17:0] coeff_a2_section1 = 18'b100000101001110100; //sfix18_En16

    parameter signed [17:0] coeff_a3_section1 = 18'b001111011001101111; //sfix18_En16

    parameter signed [17:0] scaleconst2 = 18'b000100001010000011; //sfix18_En18

    parameter signed [17:0] coeff_b1_section2 = 18'b010000000000000000; //sfix18_En16

    parameter signed [17:0] coeff_b2_section2 = 18'b100001000101100101; //sfix18_En16

    parameter signed [17:0] coeff_b3_section2 = 18'b010000000000000000; //sfix18_En16

    parameter signed [17:0] coeff_a2_section2 = 18'b100001111010010010; //sfix18_En16

    parameter signed [17:0] coeff_a3_section2 = 18'b001110001010010000; //sfix18_En16

    parameter DataWidth = 20;

    parameter CoeffientWidth = 18;

    parameter GainScaleWidth = 18;

   

    wire signed [DataWidth - 1 :0] SectionTemp;

    wire VaildTemp;

 

    IIROrder2FormI

        #(

        .DataWidth(DataWidth),

        .CoeffientWidth(CoeffientWidth),

        .GainScaleWidth(GainScaleWidth),

        .Gain(scaleconst1),

        .NumCoeffient({coeff_b1_section1,coeff_b2_section1,coeff_b3_section1}),

        .DenCoeffient({coeff_a2_section1,coeff_a3_section1})

        )

        Section1

        (

        .Clk(Clk),

        .Reset(Reset),

        .DataInValid(DataInValid),

        .DataIn(DataIn),

        .DataOutValid(VaildTemp),

        .DataOut(SectionTemp)

        );

       

    IIROrder2FormI

        #(

        .DataWidth(DataWidth),

        .CoeffientWidth(CoeffientWidth),

        .GainScaleWidth(GainScaleWidth),

        .Gain(scaleconst2),

        .NumCoeffient({coeff_b1_section2,coeff_b2_section2,coeff_b3_section2}),

        .DenCoeffient({coeff_a2_section2,coeff_a3_section2})

        )

        Section2

        (

        .Clk(Clk),

        .Reset(Reset),

        .DataInValid(VaildTemp),

        .DataIn(SectionTemp),

        .DataOutValid(DataOutValid),

        .DataOut(DataOut)

        );

endmodule