NCO ip核生成正弦波并用FIR滤波器输出滤波波形

一、认识ip核

实验任务:使用NCO ip核分别生成1Mhz和10Mhz正弦波,叠加两个列波作为输入数据,通过FIR滤波器处理得到输出波形,分析FIR输入输出两列波形,查看FIR滤波器效果。

1、数字振荡器(NCO)

  • 英文:numerically controlled oscillator;
  • 软件无线电、直接数据频率合成器(DDS,Direct digital synthesizer)、快速傅立叶变换(FFT,Fast Fourier Transform) 等的重要组成部分;
  • 同时也是决定其性能的主要因素之一,用于产生可控的正弦波或余弦波。随着芯片集成度的提高、在信号处理、数字通信领域、调制解调、变频调速、制导控制、电力电子等方面得到越来越广泛的应用。

基本原理
数控振荡器的作用是产生正交的正弦和余弦样本。传统方法是采用查表法(LUT),即事先根据各个正余弦波相位计算好相位的正余弦值,并按相位角度作为地址存储该相位的正余弦值,构成一个幅度P相位转换电路(即波形存储器)。在系统时钟的控制下,由相位累加器对输入频率字不断累加,得到以该频率字为步进的数字相位,再通过相位相加模块进行初始相位偏移,得到要输出的当前相位,将该值作为取样地址值送入幅度P相位转换电路,查表获得正余弦信号样本。
FPGA实现
频率控制字寄存器将接收到的的频率控制字送入相位累加器,相位累加器对系统时钟进行计数,每到达输入频率控制字的值即对相位进行累加,随后将累加值送入相位相加器,与相位控制字寄存器接收到的初始相位进行相加,得到当前的相位值。
在这里插入图片描述

有关更多的NCO认识,可参考链接:
NCO (数字振荡器)

2、FIR滤波器

  • 广泛应用于数字信号处理中,主要功能就是将不感兴趣的信号滤除,留下有用信号;
  • FIR滤波器是全零点结构,系统永远稳定;
  • 具有线性相位的特征,在有效频率范围内所有信号相位上不失真;
  • 在无线通信收发机中的DDC/DUC模块,抽取和内插都需要加入滤波器以防止信号在频谱上混叠,最典型的是采用FIR滤波器实现半带滤波器。

FIR滤波处理如下式所示,其中x(n)为输入信号,h(n)为FIR滤波系数,y(n)为经过滤波后的信号;N表示FIR滤波器的抽头数,滤波器阶数为N-1
在这里插入图片描述
FIR滤波器在FPGA中的实现结构
在这里插入图片描述

主要由延迟单元Z-1、乘法器和累加器组成。此结构为直接型FIR滤波器结构,也称横向结构(transverse)

二、quartus调用ip核

1、NCO产生正弦波

调用ip核
在这里插入图片描述
进入配置界面,修改ip名称以及保存路径
在这里插入图片描述
界面认识
在这里插入图片描述
Generation ALgorithm

  • Small ROM(消耗存储空间少,精准度低);
  • Large ROM(消耗存储空间大,精准度高)
  • CORDIC(不消耗存储空间,但占用大量逻辑块,精准度最低)
  • Multiplier-based(使用更少的存储空间,同时使用逻辑块和DSP块)

Outputs

  • Single Output:只产生sin wave
  • Dual Output: 产生sin wave and cos wave

界面认识
在这里插入图片描述
(1)32bits 是累加器的精度。因为FPGA产生的是数字信号,故精度越高,产生的波形肉眼看上去越平滑;
(2)18bits是输出数据的位宽;
(3)Dither Level,用于提高输出信号质量,默认4即可;
(4)Clock Rate 是时钟频率,根据板子上的频率自由选择;
(5)Desired Output Frequency:理想输出信号的频率;
(6)Phase Increment Value:是后续计数器需要计数的值,在后续编写testbench时需要把这个数据在initial语句中赋值给[31:0]phi_inc_i

频畴图
在这里插入图片描述
时域图
在这里插入图片描述
界面认识
在这里插入图片描述
相关信号以及位宽
在这里插入图片描述
.sip文件和.qip文件添加到工程中
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2、FIR ip核调用

调用IP核
在这里插入图片描述
进入配置界面
在这里插入图片描述
界面认识
在这里插入图片描述
界面认识
在这里插入图片描述
界面认识
在这里插入图片描述
界面认识
在这里插入图片描述
界面认识(保持默认)
在这里插入图片描述
界面认识 保持默认
在这里插入图片描述

3、乘法器 ip核调用

调用ip核
在这里插入图片描述
界面认识
在这里插入图片描述
界面认识
在这里插入图片描述
时钟与时钟使能
在这里插入图片描述点击NEXT,勾选方框文件,点击Finish
在这里插入图片描述
ip核查看
在这里插入图片描述

三、项目源码

例化NCO数字振荡器以及FIR滤波器模块,并进行正弦信号叠加
fir.v

module fir(

    input                         sys_clk   ,//系统时钟
    input                         sys_rst_n ,//系统复位 低电平有效
    output    signed[11:0]        sin1      ,//1Mhz正弦波
    output                        sin1_vld  ,//数据输出有效
    output    signed[11:0]        sin10     ,//10Mhz正弦波
    output                        sin10_vld ,//输出数据有效
    output    signed[12:0]        add_sin   ,//正弦波叠加输出
    output                        add_vld   ,//叠加输出有效
    output    signed[34:0]        fir_out    //滤波输出
);

   //信号定义
   reg        signed[12:0]        sum_sin    ;//叠加结果寄存
   wire       signed[11:0]        sin1_do    ;
   wire       signed[11:0]        sin10_do   ;
   wire                           out_en     ;
   wire                           s_val      ;
   wire                           s_err      ;

   //结果赋值
   assign sin1 = sin1_do;
   assign sin10 = sin10_do;
   assign add_sin = sum_sin;
   assign add_vld = sin1_vld & sin10_vld;

   //波形叠加
   always @(posedge sys_clk or negedge sys_rst_n) begin
       if(!sys_rst_n)begin
         sum_sin <= 13'd0;
       end
       else begin
         if(sin1_vld & sin10_vld)
         sum_sin <= sin1 + sin10;
         else
         sum_sin <= sum_sin;
       end
   end

   //模块例化
   //1Mhz
   sin1     u_sin1
   (
       .clk                         ( sys_clk    ),
	     .reset_n                     ( sys_rst_n  ),
	     .clken                       ( 1'b1       ),
	  	 .phi_inc_i                   ( 11'd41     ),
	 	   .fsin_o                      ( sin1_do    ),
	     .out_valid                   ( sin1_vld   )
    );

    //10Mhz
    sin10     u_sin10
    (
    .clk                            ( sys_clk    ),
	  .reset_n                        ( sys_rst_n  ),
	  .clken                          ( 1'b1       ),
	  .phi_inc_i                      ( 11'd410    ),
	  .fsin_o                         ( sin10_do   ),
	  .out_valid                      ( sin10_vld  )
    );

    //fir
    fir_do        u_fir_do
    (
    .clk                             ( sys_clk   ),              
    .reset_n                         ( sys_rst_n ),          
    .ast_sink_data                   ( add_sin   ),    
    .ast_sink_valid                  ( add_vld   ),   
    .ast_sink_error                  ( 2'b0      ),   
    .ast_source_data                 ( fir_out   ),  
    .ast_source_valid                ( s_val     ), 
    .ast_source_error                ( s_err     )
    );
   
endmodule

通过FIR滤波器,进行滤波处理
fir_lb.v

module fir_lb(

    input                            sys_clk         ,//系统时钟
    input                            sys_rst_n       ,//系统复位  低电平有效
    input              [12:0]        fir_sin         ,//输入叠加的正弦波信号
    input                            fir_vld         ,//输入数据有效
    output        reg [33:0]         fir_out         ,//滤波输出
    output        reg                out_vld          //输出有效
);

   //信号定义
   reg              [20:0]          vld_r  ;//对数据有效进行打拍
   reg        [12:0]  fir_data_in  [22:0] ;//输入数据寄存
   reg        [13:0]  add_data     [11:0]  ;//加法寄存器
   reg        [29:0]  mult_data    [11:0]  ;//乘法寄存器
   reg        [30:0]  sum          [5:0]   ;//第一次求和
   reg        [31:0]  s6,s7,s8             ;//第二次求和
   reg        [32:0]  s9,s10               ;//第三次求和

   wire                            mult_en ;//乘法器使能

   assign mult_en = (vld_r[1] | vld_r[2] |vld_r[3]);

   //抽头系数定义
   wire  [15:0]h[11:0];
   assign h[ 0] = 16'd623   ;  
   assign h[ 1] = 16'd1370  ;
   assign h[ 2] = 16'd3469  ;
   assign h[ 3] = 16'd6495  ;
   assign h[ 4] = 16'd9481  ;
   assign h[ 5] = 16'd11332 ;
   assign h[ 6] = 16'd11332 ;
   assign h[ 7] = 16'd9481  ; 
   assign h[ 8] = 16'd6495  ;
   assign h[ 9] = 16'd3469  ;
   assign h[10] = 16'd1370  ;
   assign h[11] = 16'd623   ;

   //对输入有效数据打拍
   always @(posedge sys_clk or negedge sys_rst_n) begin
       if(!sys_rst_n)begin
         vld_r <= 6'b0;
       end
       else begin
         vld_r <= {vld_r[19:0],fir_vld};
       end
   end

   //循环变量定义
   integer i,j;
   //输入数据 移位寄存
   always @(posedge sys_clk or negedge sys_rst_n) begin
       if(!sys_rst_n)begin
         for(i=0;i<23;i=i+1)begin
           fir_data_in[i] <= 8'd0;
         end
       end
       else begin
         if(fir_vld)begin
             fir_data_in[i] <= fir_sin;
             for(j=1;j<23;j=j+1)begin
                 fir_data_in[j] <= fir_data_in[j-1];//移位寄存
                 end
         end
       end
   end

   //对称相加
   always @(posedge sys_clk or negedge sys_rst_n) begin
       if(!sys_rst_n)begin
         for(i=0;i<12;i=i+1)begin
             add_data[i] <= 9'd0;
       end
   end
   else begin
     add_data[11] <= fir_data_in[11];
     if(vld_r[0])begin
         for(j=0;j<10;j=j+1)begin
             add_data[j] <= fir_data_in[j] + fir_data_in[22-j];
         end
     end
   end
   end

   //乘法器
   genvar k;
   generate
       for(k=0;k<12;k=k+1)begin:bit
       mult_do    u_mult_do
       (
           .clock        ( sys_clk        ),
           .clken        ( 1'b1           ),
           .dataa        ( h[k]           ),
           .datab        ( add_data[k]    ),
           .result       ( mult_data[k]   )
       );
       end
   endgenerate

   //s0~s5
   always @(posedge sys_clk or negedge sys_rst_n) begin
       if(!sys_rst_n)begin
         for(i=0;i<6;i=i+1)begin
             sum[i] <= 26'd0;
       end
   end
   else begin
     if(vld_r[16])begin
         for(j=0;j<6;j=j+1)begin
             sum[j] <= mult_data[j*2] + mult_data[j*2+1];
         end
     end
   end
   end

   //s6~s8
   always @(posedge sys_clk or negedge sys_rst_n) begin
       if(!sys_rst_n)begin
         s6 <= 27'd0;
         s7 <= 27'd0;
         s8 <= 27'd0;
       end
       else begin
         if(vld_r[17])begin
         s6 <= sum[0] + sum[1];
         s7 <= sum[2] + sum[3];
         s8 <= sum[4] + sum[5];
       end
       end
   end

   //s9~s10
   always @(posedge sys_clk or negedge sys_rst_n) begin
       if(!sys_rst_n)begin
         s9 <= 26'd0;
         s10 <= 26'd0;
       end
       else begin
         if(vld_r[18])begin
             s9 <= s6 + s7 ;
             s10 <= s8;
         end
       end
   end

   //结果输出
   always @(posedge sys_clk or negedge sys_rst_n) begin
       if(!sys_rst_n)begin
         fir_out <= 30'd0;
       end
       else begin
         if(vld_r[19])begin
             fir_out <= s9 + s10;
             out_vld <= 1'b1;
       end
       else
       out_vld <= 1'b0;
       end
   end

endmodule

仿真文件
fir_tb.v

//时间尺度
`timescale 1ns/1ns

module fir_tb();

//激励信号
reg                             sys_clk    ;
reg                             sys_rst_n  ;

//输出信号
wire              [11:0]        sin1       ;
wire                            sin1_vld   ;
wire              [11:0]        sin10      ;
wire                            sin10_vld  ;
wire              [12:0]        add_sin    ;
wire                            add_vld    ;
wire              [34:0]        fir_out    ;

 //模块例化
 fir     u_fir_nco
 (
     .sys_clk       ( sys_clk   ),
     .sys_rst_n     ( sys_rst_n ),
     .sin1          ( sin1      ),
     .sin1_vld      ( sin1_vld  ),
     .sin10         ( sin10     ),
     .sin10_vld     ( sin10_vld ),
     .add_sin       ( add_sin   ),
     .add_vld       ( add_vld   ),
     .fir_out       ( fir_out   )
 );

 //产生时钟
 parameter CLK_PRE = 20;

 //时钟周期
 always #(CLK_PRE/2) sys_clk = ~sys_clk;

 integer i;//循环变量
 //产生激励
 initial begin
     sys_clk = 1'b1;
     sys_rst_n = 1'b0;
     i=0;
     #(CLK_PRE*10);
     sys_rst_n = 1'b1;
     forever begin
         #(CLK_PRE);
         i=i+1;
         if(i==4000)
         $stop;
     end
 end
endmodule

编译工程
在这里插入图片描述

四、modelsim仿真

1、启动仿真

添加仿真文件
在这里插入图片描述
启动仿真
在这里插入图片描述
添加波形
在这里插入图片描述

2、效果查看

显示正弦波
在这里插入图片描述
sin 1Mhz波形
在这里插入图片描述
sin 10Mhz波形(采样点太少)
在这里插入图片描述
叠加波形
在这里插入图片描述
滤除后的波形
在这里插入图片描述

五、参考资料

quartus FIR II IP核使用 【FIR II】

FPGA十二步产生正弦波:基于quartus CON ip核

猜你喜欢

转载自blog.csdn.net/QWERTYzxw/article/details/121455096