当前位置:   article > 正文

FPGA:verilog实现直接型巴特沃斯高通IIR滤波器_fpga实现iir滤波器

fpga实现iir滤波器

目录

前言

1、matlab设计滤波器

1.1、通过FDATOOL设计滤波器

1.2、对滤波器系数进行量化

2、verilog设计IIR滤波器

2.1 零点模块

2.2 极点模块

2.3 顶层文件

3、vivado仿真

3.1 matlab生成测试数据

3.2vivado添加测试数据

3.3添加tb文件运行仿真

总结


前言

在matlab设计7阶(8级)高通IIR巴特沃斯滤波器,并实现verilog代码,并在vivado中进行仿真


1、matlab设计滤波器

1.1、通过FDATOOL设计滤波器

        打开matlab,在APP这一栏的搜索框中搜索 ’filter‘ 或直接搜索 ’滤波器‘ ,单击打开(matlab版本为2022a)

        我们要设计的滤波器的相应类型为高通,设计方法为IIR 巴特沃斯,采样频率Fs设置为1M(1000k),Fc设置为200k。在下面的界面填好这些参数,并点击设计滤波器。

         我们可以从当前滤波器信息窗口看到,我们设计的滤波器目前的结构是级联型,七阶(8)级的滤波器被设计为4个节,其中三个节是2阶(3级),一个节是1阶(2级),2*3+1 = 7,组成了我们的七阶滤波器。

        为了设计成直接型,我们对滤波器的结构进行转换

         打开编辑-转换结构

在这里插入图片描述

        选择Direct-Form Ⅰ

         转化为单节

在这里插入图片描述

       到这里就完成了到直接型的转化,下一步我们可以看看量化效应,对滤波器效果的影响,根据下图操作,可以看到幅值响应区域出现了量化前后的幅频响应曲线。

        可以看到16位量化后,滤波器的性能产生了恶化,但在可接受范围内,实际工程中可以通过级联型的滤波器来减小量化带来的误差。

        进行如下操作,将滤波器系数导出到matlab工作区

        Den Num这里分别是分母和分子

1.2、对滤波器系数进行量化

         在matlab中运行下面代码(来自杜勇老师的《数字滤波器的matlab与fpga实现》)

  1. Qcoe = 16
  2. b = Num;
  3. a = Den;
  4. %对滤波器系数进行量化,四舍五入截尾
  5. m=max(max(abs(a),abs(b)));
  6. Qm = floor(log2(m/a(1)))
  7. if Qm<log2(m/a(1))
  8. Qm = Qm + 1;
  9. end
  10. Qm = 2^Qm;
  11. Qb16=round(b/Qm*(2^(Qcoe-1)-1))
  12. Qa16=round(a/Qm*(2^(Qcoe-1)-1))

        运行结果如下:

  1. Qb16 =
  2. 15
  3. 742 -5197 15590 -25982 25982
  4. 68
  5. -15590 5197 -742
  6. Qa16 =
  7. 15
  8. 16384 -22761 27443 -17266 8332
  9. 68
  10. -2373 430 -33

        可以看到零点系数Qb16具有对称性

2、verilog设计IIR滤波器

2.1 零点模块

  1. module Zero(
  2. input rst,
  3. input clk,
  4. input signed [11:0] Xin,
  5. output signed [30:0] Xout
  6. );
  7. //-------------------------------------------------------
  8. // 将数据移位寄存
  9. //-------------------------------------------------------
  10. reg signed [11:0] Xin_Reg[6:0];
  11. reg [3:0] i,j;
  12. always @(posedge clk or posedge rst)
  13. if (rst)
  14. begin
  15. for (i=0; i<7; i=i+1)
  16. Xin_Reg[i]=12'd0;
  17. end
  18. else
  19. begin
  20. for (j=0; j<6; j=j+1)
  21. Xin_Reg[j+1] <= Xin_Reg[j];
  22. Xin_Reg[0] <= Xin;
  23. end
  24. //-------------------------------------------------------
  25. // 将对称数据相加,结果存储在数组中 2个12bit数据相加,结果为13bit
  26. //-------------------------------------------------------
  27. wire signed [12:0] Add_Reg[3:0];
  28. assign Add_Reg[0] = {Xin[11],Xin} - {Xin_Reg[6][11],Xin_Reg[6]};
  29. assign Add_Reg[1] = {Xin_Reg[0][11],Xin_Reg[0]} - {Xin_Reg[5][11],Xin_Reg[5]};
  30. assign Add_Reg[2] = {Xin_Reg[1][11],Xin_Reg[1]} - {Xin_Reg[4][11],Xin_Reg[4]};
  31. assign Add_Reg[3] = {Xin_Reg[2][11],Xin_Reg[2]} - {Xin_Reg[3][11],Xin_Reg[3]};
  32. //-------------------------------------------------------
  33. // 将数据与系数相乘,结果存储在数组中 13bit与16bit相乘,结果为28bit
  34. //-------------------------------------------------------
  35. wire signed [27:0] Mult_Reg[3:0];
  36. wire signed [15:0] coe [3:0];
  37. assign coe[0] = 16'd742;
  38. assign coe[1] = -16'd5196;
  39. assign coe[2] = 16'd15589;
  40. assign coe[3] = -16'd25982;
  41. //-------------------------------------------------------
  42. // 调用4个乘法器,设置为13bit有符号数和16bit有符号数相乘
  43. //-------------------------------------------------------
  44. mult_gen_0 Umultz0 (
  45. .A (Add_Reg[0]),
  46. .B (coe[0]),
  47. .P (Mult_Reg[0])
  48. );
  49. mult_gen_0 Umultz1 (
  50. .A (Add_Reg[1]),
  51. .B (coe[1]),
  52. .P (Mult_Reg[1])
  53. );
  54. mult_gen_0 Umultz2 (
  55. .A (Add_Reg[2]),
  56. .B (coe[2]),
  57. .P (Mult_Reg[2])
  58. );
  59. mult_gen_0 Umultz3 (
  60. .A (Add_Reg[3]),
  61. .B (coe[3]),
  62. .P (Mult_Reg[3])
  63. );
  64. assign Xout = {{3{Mult_Reg[0][27]}},Mult_Reg[0]}+
  65. {{3{Mult_Reg[1][27]}},Mult_Reg[1]}+
  66. {{3{Mult_Reg[2][27]}},Mult_Reg[2]}+
  67. {{3{Mult_Reg[3][27]}},Mult_Reg[3]};
  68. endmodule

          这里的乘法器调用了vivado的乘法IP,设置为13bit有符号数和16bit有符号数相乘,输出结果设置为28bit,因为输入数据不可能同时出现最大负值。流水线级数设置为0。

        这种做法的缺点是比较浪费资源,在资源比较宝贵的项目中不要采取这种方法。可以采取移位相加法来节省资源,也可以对不同的大小的系数,用不同输入位宽的乘法器,比方说乘以742可以设置IP核的输入为11位,相比于16位,可以节省资源。

        但这样做的好处是,移植起来很方便,调用的时候把参数填进coe[]数组就好了,可以节省很多时间    

        可以按照下图进行设置,如果仿真结果有问题,很有可能就是乘法IP设置出错了。

2.2 极点模块

  1. module Pole(
  2. input rst ,
  3. input clk ,
  4. input signed [11:0] Yin ,
  5. output signed [30:0] Yout
  6. );
  7. reg signed[11:0] Yin_Reg[6:0];
  8. reg [3:0] i,j;
  9. //-------------------------------------------------------
  10. // 将数据移位寄存
  11. //-------------------------------------------------------
  12. always @(posedge clk or posedge rst)
  13. if (rst)
  14. begin
  15. for (i=0; i<7; i=i+1)
  16. Yin_Reg[i]=12'd0;
  17. end
  18. else
  19. begin
  20. for (j=0; j<6; j=j+1)
  21. Yin_Reg[j+1] <= Yin_Reg[j];
  22. Yin_Reg[0] <= Yin;
  23. end
  24. //-------------------------------------------------------
  25. // 将数据与系数相乘,结果存储在数组中 12bit与16bit相乘,结果为27bit
  26. //-------------------------------------------------------
  27. wire signed [15:0] coe[6:0] ;
  28. wire signed [26:0] Mult_Reg[7:0];
  29. assign coe[0]= -16'd22761 ;
  30. assign coe[1]= 16'd27443 ;
  31. assign coe[2]= -16'd17266 ;
  32. assign coe[3]= 16'd8332 ;
  33. assign coe[4]= -16'd2373 ;
  34. assign coe[5]= 16'd430 ;
  35. assign coe[6]= -16'd33 ;
  36. //-------------------------------------------------------
  37. // 调用七个乘法器,设置为12bit有符号数和16bit有符号数相乘
  38. //-------------------------------------------------------
  39. mult_gen_1 Umult1 (
  40. .A (Yin_Reg[0]),
  41. .B (coe[0]),
  42. .P (Mult_Reg[0])
  43. );
  44. mult_gen_1 Umult2 (
  45. .A (Yin_Reg[1]),
  46. .B (coe[1]),
  47. .P (Mult_Reg[1])
  48. );
  49. mult_gen_1 Umult3 (
  50. .A (Yin_Reg[2]),
  51. .B (coe[2]),
  52. .P (Mult_Reg[2])
  53. );
  54. mult_gen_1 Umult4 (
  55. .A (Yin_Reg[3]),
  56. .B (coe[3]),
  57. .P (Mult_Reg[3])
  58. );
  59. mult_gen_1 Umult5 (
  60. .A (Yin_Reg[4]),
  61. .B (coe[4]),
  62. .P (Mult_Reg[4])
  63. );
  64. mult_gen_1 Umult6 (
  65. .A (Yin_Reg[5]),
  66. .B (coe[5]),
  67. .P (Mult_Reg[5])
  68. );
  69. mult_gen_1 Umult7 (
  70. .A (Yin_Reg[6]),
  71. .B (coe[6]),
  72. .P (Mult_Reg[6])
  73. );
  74. assign Yout = {{4{Mult_Reg[0][26]}},Mult_Reg[0]}+
  75. {{4{Mult_Reg[1][26]}},Mult_Reg[1]}+
  76. {{4{Mult_Reg[2][26]}},Mult_Reg[2]}+
  77. {{4{Mult_Reg[3][26]}},Mult_Reg[3]}+
  78. {{4{Mult_Reg[4][26]}},Mult_Reg[4]}+
  79. {{4{Mult_Reg[5][26]}},Mult_Reg[5]}+
  80. {{4{Mult_Reg[6][26]}},Mult_Reg[6]};
  81. endmodule

        这里的乘法IP设计跟零点模块基本相同,就不重复了

2.3 顶层文件

  1. module IIR_Direct (
  2. input rst,
  3. input clk,
  4. input signed [11:0] din,
  5. output signed [11:0] dout
  6. );
  7. wire signed [30:0] Xout;
  8. Zero U0 (
  9. .rst (rst),
  10. .clk (clk),
  11. .Xin (din),
  12. .Xout (Xout)
  13. );
  14. wire signed [11:0] Yin;
  15. wire signed [30:0] Yout;
  16. Pole U1 (
  17. .rst (rst),
  18. .clk (clk),
  19. .Yin (Yin),
  20. .Yout (Yout)
  21. );
  22. wire signed [30:0] Ysum;
  23. assign Ysum = Xout - Yout;
  24. //将Ydiv右移14位,相当于除了16384
  25. wire signed [30:0] Ydiv;
  26. assign Ydiv = {{14{Ysum[30]}},Ysum[30:14]};
  27. //直接对结果进行截尾
  28. assign Yin = (rst ? 12'd0 : Ydiv[11:0]);
  29. assign dout = Yin;
  30. endmodule

3、vivado仿真

        vivado仿真需要测试数据,以及仿真tb文件,给出代码如下:

3.1 matlab生成测试数据

        为了验证高通滤波器,我们生成的测试信号由50k和250k的正弦信号叠加而成。生成的二进制文件为Bin_s.txt

  1. f1=50000; %信号1频率为50KHz
  2. f2=250000; %信号2频率为250KHz
  3. Fs=1000000; %采样频率为1MHz
  4. N=12; %量化位数
  5. %产生信号
  6. t=0:(1/Fs):0.0012;
  7. c1=2*pi*f1*t;
  8. c2=2*pi*f2*t;
  9. s1=sin(c1);%产生正弦波
  10. s2=sin(c2);%产生正弦波
  11. s=0.5*s1+0.5*s2;
  12. plot(t,s1,t,s);
  13. Q_s=round(s*(2^(N-1)-1));
  14. %% 生成txt文件们
  15. %将生成的数据以十进制数据格式写入txt文件中
  16. fid=fopen('Int_s.txt','w');
  17. fprintf(fid,'%8d\r\n',Q_s);
  18. fprintf(fid,';');
  19. fclose(fid);
  20. %将生成的数据以二进制数据格式写入txt文件中
  21. fid=fopen('Bin_s.txt','w');
  22. for i=1:length(Q_s)
  23. B_s=dec2bin(Q_s(i)+(Q_s(i)<0)*2^N,N)
  24. for j=1:N
  25. if B_s(j)=='1'
  26. tb=1;
  27. else
  28. tb=0;
  29. end
  30. fprintf(fid,'%d',tb);
  31. end
  32. fprintf(fid,'\r\n');
  33. end
  34. fprintf(fid,';');
  35. fclose(fid);

3.2vivado添加测试数据

        需要将测试数据添加到vivado环境中,仿真才能正常运行,我的做法如下

3.3添加tb文件运行仿真

        tb文件如下

  1. `timescale 1ns / 1ps
  2. module IIR_tb();
  3. reg clk;
  4. reg [11:0] din;
  5. reg rst;
  6. wire [11:0] dout;
  7. IIR_Direct i1 (
  8. .clk(clk),
  9. .din(din),
  10. .dout(dout),
  11. .rst(rst)
  12. );
  13. parameter clk_period=626;
  14. parameter clk_half_period=clk_period/2;
  15. parameter data_num=2000;
  16. parameter time_sim=data_num*clk_period/2;
  17. initial
  18. begin
  19. clk=1;
  20. rst=1;
  21. #10000 rst=0;
  22. #time_sim $finish;
  23. din=12'd10;
  24. end
  25. always
  26. #clk_half_period clk=~clk;
  27. integer Pattern;
  28. reg [11:0] stimulus[1:data_num];
  29. initial
  30. begin
  31. $readmemb("Bin_s.txt",stimulus);
  32. Pattern=0;
  33. repeat(data_num)
  34. begin
  35. Pattern=Pattern+1;
  36. din=stimulus[Pattern];
  37. #clk_period;
  38. end
  39. end
  40. integer file_out;
  41. initial
  42. begin
  43. file_out = $fopen("Bin_s_out.txt");
  44. if(!file_out)
  45. begin
  46. $display("could not open file!");
  47. $finish;
  48. end
  49. end
  50. wire rst_write;
  51. wire signed [11:0] dout_s;
  52. assign dout_s = dout;
  53. assign rst_write = clk& (!rst);
  54. always @(posedge rst_write )
  55. $fdisplay(file_out,"%d",dout_s);
  56. endmodule

 局部放大如下:

总结

        本篇文章在matlab设计7阶(8级)高通IIR巴特沃斯滤波器,并实现verilog代码,并在vivado中进行仿真,结果符合预期。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/401448
推荐阅读
相关标签
  

闽ICP备14008679号