DSP

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

2019-07-13 20:18发布

查过很多资料,对于IIR滤波器结构和原理介绍很多,但是,真正对于FPGA的快速设计介绍很少。我对IIR滤波器的MATLAB仿真和FPGA硬件仿真做了充分的对比,关于IIR滤波器设计和实现做一下总结: 说明:IIR滤波器最佳实现结构、IIR滤波器结果舍入处理已做过文档说明。 1.         FDAtool设计IIR滤波器参数——结构为直接I型
比较简单,记录一下几种常用滤波器区别: l  巴特沃斯滤波器:通带和阻带有最大平坦度,但是过渡带比较平缓,当需要比较陡峭的过渡带时,需要较大的阶数;巴特沃斯滤波器对于系数量化影响相对较弱。 l  Chebyshev滤波器:分为I型和II型,I型通带等纹波,阻带平坦;II型通带平坦,阻带等纹波,过渡带较巴特沃斯滤波器陡峭; l  椭圆滤波器:在通带和阻带都是等纹波,过渡带最陡峭,对于相同的过渡带指标,椭圆滤波器需要的阶数最少,但是对于系数量化最敏感。 2.         FDAtool系数量化 说明:FPGA计算都是基于二进制补码计算的,采用定点数,在实际设计时需要对FPGA硬件资源有一定了解。 量化分为两部分:增益量化和零点极点量化,一般为了节省硬件资源,增益量化和系数量化采用相同的位数,只是他们缩放倍数不一样,在实现时需要考虑。 设计例子如下:   关于滤波器系数量化位数选择:个人建议在保证滤波器稳定的前提下,结合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滤波器结构如下:
乘加运算包括:增益计算、零点计算、极点计算。增益计算:输入乘以增益(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