当前位置:   article > 正文

CIC插值滤波器与直接频率合成器DDS的FPGA实现

dds直接频率合成器

来源:FPGA学习之旅

CIC滤波器是无线通信中的常用模块,一般用于数字下变频(DDC)和数字上变频(DUC)系统。随着现代无线通信中数据速率的增加,它的应用变得尤为重要。CIC滤波器的结构简单,没有乘法器,只有加法器、积分器和寄存器,适合于工作在高采样率条件下,而且CIC滤波器是一种基于零点相消的FIR滤波器,已经被证明是在高速抽取或插值系统中非常有效的单元。

我们首先产生一个采样率Fs=0.78125Mhz,频率Fout=0.078125Mhz的样本信号,对其进行16倍插值。这就涉及到直接频率合成器DDS的知识:

e471c04ea1ab4fe1909da9d2f3a6e75a.png

     上图的RAM部分存放通过MATLAB生成一个周期完整正弦波的离散点。Fc采样时钟:本模块用一个50MHZ的采样时钟,即每隔20ns,从RAM中读出一个采样值。这里记住还有一种通过使能信号去读取ram的值,这个时候的Fc一定不能用时钟信号50M去算,而应该以使能信号频率去计算,下面会详细体现。

相位累加器:顾名思义,在这里完成相位累加的功能,相位累加器的溢出频率就是DDS输出的信号频率,位宽为N。

频率控制字M: 决定输出频率,相当于相位累加器地址addr步进值。

目标频率:Fout=Fs*M/2^N,对于这个公式我们可以这么理解,一秒内相位累加器的累加值为Fs*M,而寄存器最大值为2^(N-1),溢出次数为Fs*M/2^N,因为寄存器中的值从0增加到2^(N-1),刚好输出一个周期的正弦波信号。所以,寄存器溢出的次数就是输出正弦波的周期数。

110431c75a74322e6820f2914fe1415c.gif

接下来我们来实现它,首先用MATLAB产生一个完整周期的正弦波样本,将其存入ROM。具体实现方法参见这篇文章:如何用MATLAB产生FPGA设计中所需的coe\txt\mif文件

  1. clc;
  2. clear all;
  3. N=2^8;
  4. s_p=0:255;%正弦波一个周期的采样点数 ROM深度256,位宽8
  5. sin_data=sin(2*pi*s_p/N);
  6. fix_p_sin_data=fix(sin_data*127);
  7. for i=1:N
  8. if fix_p_sin_data(i)<0
  9. fix_p_sin_data(i)=N+fix_p_sin_data(i);%有符号数--得+
  10. else
  11. fix_p_sin_data(i)=fix_p_sin_data(i);
  12. end
  13. end
  14. fid = fopen('sin.coe','w');% 打开一个.coe文件
  15. % 存放在ROM中的.coe文件第一行必须是这个字符串,10表示10进制,可以改成其他进制
  16. fprintf(fid,'memory_initialization_radix = 10;\n');
  17. % 存放在ROM中的.coe文件第二行必须是这个字符串
  18. fprintf(fid,'memory_initialization_vector = \n');
  19. % 把前255个数据写入.coe文件中,并用逗号隔开,为了方便知道数据的个数,每行只写一个数据
  20. fprintf(fid,'%d,\n',fix_p_sin_data(1:end-1));
  21. % 把最后一个数据写入.coe文件中,并用分号结尾
  22. fprintf(fid,'%d;\n',fix_p_sin_data(end));
  23. fclose(fid);  % 关闭文件指针

dcf9fbd7f766b4f955248490f1f34133.png

cdeb33d4232bb6fedbd25d638919d0d7.png

在VIVADO中调用ROM IP核,存放一个周期正弦波的信息:

6e5acfc8885af45f61cae92f8fc75974.png

b6f1bbc51ff4216cdef45abacc674828.png

7c5d804d973d4fd91b755c75d48b5008.png

这一步加载我们用MATLAB生成的coe文件,如果加载的coe文件错误,箭头指的地方会报红。创建完rom后例化仿真:

  1. module dds(
  2. input wire clk,
  3. input wire rst_n,
  4. output wire [7:0] o_wave
  5. );
  6. reg [7:0] addr;
  7. always @(posedge clk)begin
  8. if(!rst_n)
  9. addr <= 8'd0;
  10. else
  11. addr <= addr + 1'b1;
  12. end
  13. sp_ram_256x8 sp_ram_256x8 (
  14. .clka(clk), // input wire clka
  15. .addra(addr ), // input wire [7 : 0] addra
  16. .douta(o_wave) // output wire [7 : 0] douta
  17. );

仿真图如下:

2d541c993b5e1ecb39b42cbfcdc59f3d.png

接下来我们首先产生一个采样率Fs=0.78125Mhz,频率Fout=0.078125Mhz的样本信号:

  1. module gen_sin(
  2. input wire sclk,
  3. input wire rst_n,
  4. output wire [7:0] data_o,
  5. output reg data_v
  6. );
  7. parameter DIV_NUM=6'd63;//50M/64=0.78125M采样率
  8. //FC = 0.078125Mhz信号频率
  9. //FS = 0.78125Mhz采样频率
  10. parameter FRQ_W_1M=32'd429496730;
  11. //2^32*(0.078125)/0.78125//fc=M*fs/2^32
  12. reg [31:0] phase_sum_1m;
  13. wire [7:0] addr_1m;
  14. wire [7:0] o_wave_1m;
  15. reg [5:0] div_cnt;
  16. reg s_flag;
  17. //分频产生0.78125M采样率
  18. always @(posedge sclk or negedge rst_n)
  19. if(rst_n == 1'b0)
  20. div_cnt <= 'd0;
  21. else if(div_cnt == DIV_NUM)
  22. div_cnt <= 'd0;
  23. else
  24. div_cnt <= div_cnt + 1'b1;
  25. //梳状滤波器驱动脉冲
  26. always @(posedge sclk or negedge rst_n)
  27. if(rst_n == 1'b0)
  28. s_flag <= 1'b0;
  29. else if(div_cnt == 6'd0)
  30. s_flag <= 1'b1;
  31. else s_flag <= 1'b0;
  32. always @(posedge sclk or negedge rst_n)
  33. if(rst_n == 1'b0)
  34. data_v <= 1'b0;
  35. else
  36. data_v <= 1'b1;
  37. assign data_o = o_wave_1m;//0.78125
  38. //
  39. //相位累加器
  40. always @(posedge sclk or negedge rst_n)
  41. if(rst_n == 1'b0)
  42. phase_sum_1m<='d0;
  43. else if(s_flag == 1'b1)
  44. phase_sum_1m <= phase_sum_1m + FRQ_W_1M ;
  45. //相位
  46. assign addr_1m=phase_sum_1m[31:24];
  47. sp_ram_256x8 sp_ram_256x8 (
  48. .clka(sclk), // input wire clka
  49. .addra(addr_1m), // input wire [7 : 0] addra
  50. .douta(o_wave_1m) // output wire [7 : 0] douta
  51. );
  52. endmodule

       数字信号的频谱是周期性的,且周期等于数据的采样频率。整数倍零值内插当然不能简单地等同于提高了数据采样频率,在插值过程中,频谱会以采样频率进行压缩,可能会引起频谱混叠,通过内插、低通滤波处理后,即可得到正确的经高速采样后的数字信号。如下图所示:

faf05652cc590e78f1ddef3be652830b.png

        所以多速率信号处理过程的关键是设计满足要求的抗混叠滤波器。即抽取或内插处理后,在有用信号频段内不产生频谱混叠。同时要求滤波器占用硬件资源少且运算速度快,一般只要有效增加滤波器的阶数,可以设计出满足指标要求的各种抗混叠滤波器。CIC(积分梳状)滤波器及半带滤波器因为具有运算速度快、占用资源少等特点,在多速率信号处理中得到了广泛的应用。其冲激响应为:

239be0f02b273351ccd7b0493219ae40.png

它的抽头系数只有1或者0。

直接用MATLAB仿真观察,分析一下 CIC 滤波器的频谱特性,可得到其幅频特性。

e441417ef8bc65105cfff4ba2c8558b0.png

根据上述公式在MATLAB中编写如下代码观察滤波器抑制情况:

  1. clc;
  2. clear all;
  3. R=16;
  4. M=1;
  5. N=3;
  6. n=0:1:500-1;
  7. w=pi*n/(length(n));
  8. num=sin(R*M*w/2);
  9. den=sin(w/2);
  10. H=(num./den).^N;
  11. ABS_H=abs(H);
  12. mag=20*log10(ABS_H/max(ABS_H));
  13. plot(w/pi,mag);
  14. grid on;
  15. axis([0,1,-200,0]);

163a79266a4e35617f404911aab4606d.png

由上图可知,对带外信号有很好的抑制效果。

在进行Verilog实现时,首先要把具体电路结构映射出来,CIC滤波器具体实现框图如下,系数为1:

ccbff1e6c69f625564b074280e281cfa.png

c95cfba5e6e0e27d301c4f57c5e4f552.png

按照上述框图进行编码,对上述生成的样本信号(采样率Fs=0.78125Mhz,频率Fout=0.078125Mhz的样本信号)进行16倍插值滤波处理(FS采样率提高,信号频率Fout不变),涉及代码均参考V3学社尤老师,读者可访问文后提供的网址学习参考:

  1. module cic_interpolate(
  2. input wire sclk,//50M
  3. input wire rst_n,
  4. input wire [7:0] data_in,
  5. input wire data_v,
  6. output wire [19:0] data_out
  7. );
  8. parameter W=20;
  9. parameter DIV_NUM=6'd63;//50M/64=0.78125M
  10. parameter DIV_NUM_I=3'd3;
  11. //BIN + N*log2(R)=8+3*4=20;
  12. reg [W-1:0] comb1;
  13. reg [W-1:0] comb2;
  14. reg [W-1:0] comb3;
  15. reg [W-1:0] integ1;
  16. reg [W-1:0] integ2;
  17. reg [W-1:0] integ3;
  18. wire [W-1:0] comb1_w;
  19. wire [W-1:0] comb2_w;
  20. wire [W-1:0] comb3_w;
  21. wire [W-1:0] integ1_w;
  22. wire [W-1:0] integ2_w;
  23. wire [W-1:0] integ3_w;
  24. reg [5:0] div_cnt;
  25. reg s_flag;
  26. reg s_flag_i;
  27. //此计数器用于控制分频,为梳状滤波器和积分滤波器提供驱动脉冲
  28. always @(posedge sclk or negedge rst_n)
  29. if(rst_n == 1'b0)
  30. div_cnt <= 'd0;
  31. else if(div_cnt == DIV_NUM)
  32. div_cnt <= 'd0;
  33. else
  34. div_cnt <= div_cnt + 1'b1;
  35. //梳状滤波器驱动脉冲
  36. always @(posedge sclk or negedge rst_n)
  37. if(rst_n == 1'b0)
  38. s_flag <= 1'b0;
  39. else if(div_cnt == 6'd0)
  40. s_flag <= 1'b1;
  41. else s_flag <= 1'b0;
  42. //积分滤波器驱动脉冲
  43. always @(posedge sclk or negedge rst_n)
  44. if(rst_n == 1'b0)
  45. s_flag_i <= 1'b0;
  46. else if(div_cnt[1:0] == 2'd0)
  47. s_flag_i <= 1'b1;
  48. else s_flag_i <= 1'b0;
  49. //梳状滤波器的寄存器
  50. always @(posedge sclk or negedge rst_n)
  51. if(rst_n == 1'b0)
  52. comb1<='d0;
  53. else if(data_v == 1'b1 && s_flag == 1'b1)
  54. comb1<= {{12{data_in[7]}},data_in};
  55. else if(data_v == 1'b0)
  56. comb1<='d0;
  57. always @(posedge sclk or negedge rst_n)
  58. if(rst_n == 1'b0)
  59. comb2<='d0;
  60. else if(data_v == 1'b1 && s_flag == 1'b1)
  61. comb2<= comb1_w;
  62. else if(data_v == 1'b0)
  63. comb2<='d0;
  64. always @(posedge sclk or negedge rst_n)
  65. if(rst_n == 1'b0)
  66. comb3<='d0;
  67. else if(data_v == 1'b1 && s_flag == 1'b1)
  68. comb3<= comb2_w;
  69. else if(data_v == 1'b0)
  70. comb3<='d0;
  71. //寄存器间的组合逻辑
  72. assign comb1_w= {{12{data_in[7]}},data_in}-comb1;
  73. assign comb2_w= comb1_w-comb2;
  74. assign comb3_w= comb2_w-comb3;
  75. //积分滤波器间的组合逻辑
  76. assign integ1_w=comb3_w + integ1;
  77. assign integ2_w=integ1_w + integ2;
  78. assign integ3_w=integ2_w + integ3;
  79. //插值后的结果输出
  80. assign data_out = integ3_w;
  81. //积分滤波器的寄存器
  82. always @(posedge sclk or negedge rst_n)
  83. if(rst_n == 1'b0) begin
  84. integ1<='d0;
  85. integ2<='d0;
  86. integ3<='d0;
  87. end
  88. else if(data_v == 1'b1 && s_flag_i == 1'b1)begin
  89. integ1<=integ1_w;
  90. integ2<=integ2_w;
  91. integ3<=integ3_w;
  92.   end
  93. endmodule

对上述模块进行仿真:红框为插值前,绿框为插值后效果,直观的看到输出信号更加平滑细腻:

816943123dcfda9b60777a510252741b.png

0f40e0e4cc0ceebfc9c73fce296a43d9.png

参考文献:

[1]杜勇.数字滤波器的MATLAB与FPGA实现

[2]如何用MATLAB产生FPGA设计中所需的coe\txt\mif文件

[3]V3学社 http://www.v3edu.org/

[4]https://blog.csdn.net/qq_34769608?type=blog

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

闽ICP备14008679号