当前位置:   article > 正文

FIR数字滤波器的FPGA实现_fpga实现fir滤波器

fpga实现fir滤波器

目录

1.FIR数字滤波器实现原理

2.FPGA实现

2.1全串行FIR滤波器

2.1.1 原理图 

2.1.2 Verilog代码

2.1.3 仿真测试代码

2.1.4 仿真结果图


1.FIR数字滤波器实现原理

一个 N 阶的 FIR 滤波器输出公式 y(n) 如下:

式1中 h(k)为滤波器的系数,x(n-k)为x(n)延时k个周期。系统的传输函数H(z)可表示成公式2:

 从式1看出:滤波过程主要是一组特定的系数与信号完成卷积的过程。从式2看出,在有限的Z平面内它有N-1个零点,同时其 N-1个极点全部位于 z=0 中,因此 FIR 滤波器也被称为全零点滤波器,是一个单位脉冲响应有限长的稳定系统
FIR滤波器在系数满足一定条件的情况下,它的相频特性是线性的,可以有效的保留信号的相位信息,因此线性相位的 FIR 滤波器在实际工程中有着较为广泛的应用。

2.FPGA实现

Fir滤波器的结构形式分为直接型、级联型、频率取样型、快速卷积型四种,其中最常用,最简单的是直接型。FPGA实现直接型Fir滤波器时,可有采用串行结构、并行结构、分布式结构,以及直接使用器件提供的ip核,下面对每种结构的实现方法进行介绍,代码实现以及仿真测试。 

2.1全串行FIR滤波器

2.1.1 原理图 

2.1.2 Verilog代码

  1. //这是FirFullSerial.v文件的程序清单
  2. module fir_filter(
  3. rst,clk,Xin,
  4. Yout);
  5. input rst; //复位信号,高电平有效
  6. input clk; //FPGA系统时钟,频率为16kHz
  7. input signed [11:0] Xin; //数据输入频率为2khZ
  8. output signed [28:0] Yout; //滤波后的输出数据
  9. //实例化有符号数乘法器IP核mult
  10. reg signed [11:0] coe; //滤波器为12比特量化数据
  11. wire signed [12:0] add_s; //输入为12比特量化数据,两个对称系数相加需要13比特存储
  12. wire signed [24:0] Mout;
  13. mult_ip Umult (
  14. .clock (clk),
  15. .dataa (coe),
  16. .datab (add_s),
  17. .result (Mout));
  18. //实例化有符号数加法器IP核,对输入数据进行1位符号位扩展,输出结果为13比特数据
  19. reg signed [12:0] add_a;
  20. reg signed [12:0] add_b;
  21. add_ip Uadder (
  22. .dataa (add_a),
  23. .datab (add_b),
  24. .result (add_s));
  25. //3位计数器,计数周期为8,为输入数据速率
  26. reg [2:0] count;
  27. always @(posedge clk or posedge rst)
  28. if (rst)
  29. count = 3'd0;
  30. else
  31. count = count + 1;
  32. //将数据存入移位寄存器Xin_Reg中
  33. reg [11:0] Xin_Reg[15:0];
  34. reg [3:0] i,j;
  35. always @(posedge clk or posedge rst)
  36. if (rst)
  37. //初始化寄存器值为0
  38. begin
  39. for (i=0; i<15; i=i+1)
  40. Xin_Reg[i]=12'd0;
  41. end
  42. else
  43. begin
  44. if (count==7)
  45. begin
  46. for (j=0; j<15; j=j+1)
  47. Xin_Reg[j+1] <= Xin_Reg[j];
  48. Xin_Reg[0] <= Xin;
  49. end
  50. end
  51. //将对称系数的输入数据相加,同时将对应的滤波器系数送入乘法器
  52. //需要注意的是,下面程序只使用了一个加法器及一个乘法器资源
  53. //以8倍数据速率调用乘法器IP核,由于滤波器长度为16,系数具有对称性,故可在一个数据
  54. //周期内完成所有8个滤波器系数与数据的乘法运算
  55. //为了保证加法运算不溢出,输入输出数据均扩展为13比特。
  56. always @(posedge clk or posedge rst)
  57. if (rst)
  58. begin
  59. add_a <= 13'd0;
  60. add_b <= 13'd0;
  61. coe <= 12'd0;
  62. end
  63. else
  64. begin
  65. if (count==3'd0)
  66. begin
  67. add_a <= {Xin_Reg[0][11],Xin_Reg[0]};
  68. add_b <= {Xin_Reg[15][11],Xin_Reg[15]};
  69. coe <= 12'h000;//c0
  70. end
  71. else if (count==3'd1)
  72. begin
  73. add_a <= {Xin_Reg[1][11],Xin_Reg[1]};
  74. add_b <= {Xin_Reg[14][11],Xin_Reg[14]};
  75. coe <= 12'hffd; //c1
  76. end
  77. else if (count==3'd2)
  78. begin
  79. add_a <= {Xin_Reg[2][11],Xin_Reg[2]};
  80. add_b <= {Xin_Reg[13][11],Xin_Reg[13]};
  81. coe <= 12'h00f; //c2
  82. end
  83. else if (count==3'd3)
  84. begin
  85. add_a <= {Xin_Reg[3][11],Xin_Reg[3]};
  86. add_b <= {Xin_Reg[12][11],Xin_Reg[12]};
  87. coe <= 12'h02e; //c3
  88. end
  89. else if (count==3'd4)
  90. begin
  91. add_a <= {Xin_Reg[4][11],Xin_Reg[4]};
  92. add_b <= {Xin_Reg[11][11],Xin_Reg[11]};
  93. coe <= 12'hf8b; //c4
  94. end
  95. else if (count==3'd5)
  96. begin
  97. add_a <= {Xin_Reg[5][11],Xin_Reg[5]};
  98. add_b <= {Xin_Reg[10][11],Xin_Reg[10]};
  99. coe <= 12'hef9; //c5
  100. end
  101. else if (count==3'd6)
  102. begin
  103. add_a <= {Xin_Reg[6][11],Xin_Reg[6]};
  104. add_b <= {Xin_Reg[9][11],Xin_Reg[9]};
  105. coe <= 12'h24e; //c6
  106. end
  107. else
  108. begin
  109. add_a <= {Xin_Reg[7][11],Xin_Reg[7]};
  110. add_b <= {Xin_Reg[8][11],Xin_Reg[8]};
  111. coe <= 12'h7ff; //c7
  112. end
  113. end
  114. //对滤波器系数与输入数据的乘法结果进行累加,并输出滤波后的数据
  115. //考虑到乘法器及累加器的延时,需要计数器为2时对累加器清零,同时输出滤波器结果数据。
  116. //类似的时延长度一方面可通过精确计算获取,但更好的方法是通过行为仿真查看
  117. reg signed [28:0] sum;
  118. reg signed [28:0] yout;
  119. always @(posedge clk or posedge rst)
  120. if (rst)
  121. begin
  122. sum = 29'd0;
  123. yout <= 29'd0;
  124. end
  125. else
  126. begin
  127. if (count==2)
  128. begin
  129. yout <= sum;
  130. sum = 29'd0;
  131. sum =sum + Mout;
  132. end
  133. else
  134. sum = sum + Mout;
  135. end
  136. assign Yout = yout;
  137. endmodule

2.1.3 仿真测试代码

  1. `timescale 1 ns/ 1 ns //设置仿真时间单位:ns
  2. module fir_filter_tb();
  3. // constants
  4. // general purpose registers
  5. reg eachvec;
  6. // test vector input registers
  7. reg [11:0] Xin;
  8. reg clk;
  9. reg rst;
  10. wire clk_data; //数据时钟,速率为系统时钟clk的1/8;
  11. // wires
  12. wire [28:0] Yout;
  13. // assign statements (if any)
  14. fir_filter i1 (
  15. // port map - connection between master ports and signals/registers
  16. .Xin(Xin),
  17. .Yout(Yout),
  18. .clk(clk),
  19. .rst(rst)
  20. );
  21. parameter clk_period=62500; //设置时钟信号周期(频率):16kHz
  22. parameter clk_period_data=clk_period*8;
  23. parameter clk_half_period=clk_period/2;
  24. parameter clk_half_period_data=clk_half_period*8;
  25. parameter data_num=2000; //仿真数据长度
  26. parameter time_sim=data_num*clk_period; //仿真时间
  27. initial
  28. begin
  29. //设置时钟信号初值
  30. clk=1;
  31. //clk_data=1;
  32. //设置复位信号
  33. rst=1;
  34. #200000 rst=0;
  35. //设置仿真时间
  36. #time_sim $finish;
  37. //设置输入信号初值
  38. Xin=12'd10;
  39. end
  40. //产生时钟信号
  41. always
  42. #clk_half_period clk=~clk;
  43. reg [2:0] cn_clk=3'd0;
  44. always @(posedge clk) cn_clk <= cn_clk + 3'd1;
  45. assign clk_data = cn_clk[2];
  46. //从外部TX文件(SinIn.txt)读入数据作为测试激励
  47. integer Pattern;
  48. reg [11:0] stimulus[1:data_num];
  49. initial
  50. begin
  51. //文件必须放置在"工程目录\simulation\modelsim"路径下
  52. //$readmemb("E4_7_Bin_noise.txt",stimulus);
  53. $readmemb("E4_7_Bin_s.txt",stimulus);
  54. Pattern=0;
  55. repeat(data_num)
  56. begin
  57. @(posedge clk_data);
  58. Pattern=Pattern+1;
  59. Xin=stimulus[Pattern];
  60. end
  61. end
  62. //将仿真数据dout写入外部TXT文件中(out.txt)
  63. integer file_out;
  64. initial
  65. begin
  66. //文件放置在"工程目录\simulation\modelsim"路径下
  67. //file_out = $fopen("E4_7_Noiseout.txt");
  68. file_out = $fopen("E4_7_Sout.txt");
  69. if(!file_out)
  70. begin
  71. $display("could not open file!");
  72. $finish;
  73. end
  74. end
  75. wire rst_write;
  76. wire signed [28:0] dout_s;
  77. assign dout_s = Yout; //将dout转换成有符号数据
  78. assign rst_write = clk_data & (!rst);//产生写入时钟信号,复位状态时不写入数据
  79. always @(posedge rst_write )
  80. $fdisplay(file_out,"%d",dout_s);
  81. endmodule

2.1.4 仿真结果图

可见,输出结果dout_a,与输入数据的时钟上升沿clk_data差两个时钟周期(输入数据的时钟上升沿输入数据),正如代码中所写,考虑到加法器和乘法器的延时,所以延迟两个时钟周期读输出数据,及cn_clk=2时输出。

 持续更新中

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

闽ICP备14008679号