天天看点

Verilog学习 | 基于vivado平台的DDS、FIR、FFT核的综合学习使用一、自我介绍二、学习任务及内容三、工程实现 四、结语

目录

一、自我介绍

二、学习任务及内容

三、工程实现

         3.1 创建工程文件以及fir顶层文件

        3.2 生成正弦波模块的文件编写与DDS核的例化

        (1)编写生成1MHz正弦波模块文件如下:

         (2)编写生成10MHz正弦波模块文件如下:

        3.3 FIR核例化

        (1)利用MATLAB设计FIR滤波器

         (2)设计FIR核

         3.4 FFT核例化

         (1)编写对叠加信号xin进行频响分析的文件

          (2)编写对滤波后信号fir_out进行频响分析的文件

         3.5 Testbench文件编写与方针实现

 四、结语

一、自我介绍

        第一篇文章,先做一个简短的自我介绍。

        笔者目前就读于西电本科三年级,攻读电子信息工程方向。之前一直苦于学过的、做过的东西总会随着时间大浪淘沙,因此想在CSDN这个平台上记录下这些东西,方便日后温故知新,同时也希望自己记录的东西能帮到同行者,与诸君共勉!

二、学习任务及内容

        学习FPGA一个月有余,最近在着手老师布置的最后一个学习任务:

利用FPGA的DDS核产生两个不同频率的点频信号叠加后进行频谱分析(使用FFT核),然后设计FIR滤波器滤除其中的一个信号分量,将滤波后的信号再次进行频谱分析,以确定滤波器的结果是否符合设计要求。

         任务较为简单,主要考核对DDS、FIR、FFT等IP核的学习使用,为日后的信号处理工程打基础。根据要求,我打算采用思路如下:

1.利用DDS分别生成1MHz和10MHz的正弦波进行相加生成叠加信号。

2.利用MATLAB的fdatool工具设计低通滤波器,将生成的滤波器系数.coe文件载入vivado平台的FIR核中,从而生成FIR滤波器。

3.将叠加信号载入FIR滤波器滤除掉高频10MHz信号。

4.利用不同输入位宽的FFT核对叠加信号以及滤除后信号做频谱分析,从而验证滤波效果。 

三、工程实现

         3.1 创建工程文件以及fir顶层文件

        工程文件的创建不再赘述。工程文件创建好后,首先创建fir顶层文件,考虑到模块功能,需要例化两个DDS核分别生成1MHz以及10MHz的正弦波并进行叠加;之后送入FIR核例化的滤波器中进行滤波;之后将叠加信号与滤波信号输送入FFT核中进行频谱分析。因此给出代码如下(代码详解见注释):

//顶层模块:FIR滤波器设计
//功能:滤除掉xin(1MHz&10MHz)中的高频分量,并做频谱分析
//2021.8.14——8.18
module fir(
    input  wire                sclk              ,//系统时钟
    input  wire                rst_n             ,//复位按钮(低有效)
    output wire                m_axis_data_tvalid,//fir输出有效标志
    output wire signed [34:0]  fir_dout          ,//滤波后输出数据
    output wire signed [47:0]  fft_out_xin       ,//对复合输入信号做频谱分析
    output wire signed [63:0]  fft_out_dout       //对滤波后输出数据做频谱分析
    );

wire [15:0] sin_1MHz  ; //1MHz正弦波
wire [15:0] sin_10MHz ; //10MHz正弦波
wire signed [16:0] xin; //复合输入正弦波
reg  [34:0] fir_out_reg;//保存滤波输出数据的寄存器
//保存滤波输出数据至寄存器
always @ (posedge sclk) begin
    fir_out_reg <= fir_dout;
end
//fir滤波器引脚声明
wire          s_axis_data_tready;//FIR准备接受数据
wire [39 : 0] m_axis_data_tdata ;//原FIR输出数据  
//根据FIR信息栏中显示输出数据只有35位,因此对原输出数据进行截取    
assign  fir_dout = m_axis_data_tdata[34:0];
//DDS例化1——生成1MHz正弦波
dds_1MHz sin1MHz(
    .sclk(sclk),
    .sin_1MHz(sin_1MHz)
);
//DDS例化2--生成10MHz正弦波
dds_10MHz sin10MHz(
    .sclk(sclk),
    .sin_10MHz(sin_10MHz)
);
//两信号想加得到复合信号
assign xin = {sin_1MHz[15],sin_1MHz} + {sin_10MHz[15],sin_10MHz};//注意这里添加了符号位,因为两边均要求是有符号数的相加
//FIR例化
fir_compiler_0 inst_fir(
  .aclk(sclk),                              // input wire aclk
  .s_axis_data_tvalid(1'b1),  // input wire s_axis_data_tvalid
  .s_axis_data_tready(s_axis_data_tready),  // output wire s_axis_data_tready
  .s_axis_data_tdata({{7{xin[16]}},xin}),    // input wire [23 : 0] s_axis_data_tdata(!注意这里要用讲17位的xin符号扩展至要求的24位)
  .m_axis_data_tvalid(m_axis_data_tvalid),  // output wire m_axis_data_tvalid
  .m_axis_data_tdata(m_axis_data_tdata)    // output wire [39 : 0] m_axis_data_tdata
);
//FFT例化1——对复合信号进行频谱分析
fft_xin inst_fft_xin(
    .sclk(sclk)  ,
    .rst_n(rst_n),
    .xin(xin)    , 
    .fft_out(fft_out_xin)       
    );
//FFT例化2——对FIR输出信号进行频谱分析    
fft_dout inst_fft_dout(
    .sclk(sclk) ,
    .rst_n(rst_n),
    .fir_out(fir_out_reg)    , 
    .fft_out(fft_out_dout)  
    );
endmodule
           

其中需要注意的几点罗列如下:

1.我个人喜欢自顶而下的编程方式,因此先创建好顶层文件,再在之后的步骤中例化IP核(上面展示的代码也是后来在IP核的一步步例化中完善的,部分要点也会在文章之后的部分说明)。也可以先创建需要的例化文件,再编写顶层文件。

2.部分变量(引脚)数据位以及模块的具体管脚需要根据具体生成的IP核的管脚数据显示而定,自顶而下时可先将数据位省略,而只列管脚名。

3.fir_dout、fir_dout、fft_out_dout以及xin均为signed(有符号数)变量。

4.xin为有符号数且数据位比右侧数据多一位,因此等号右侧两变量需扩展一位数据位。具体Verilog有符号数的运算推荐此链接:https://www.cnblogs.com/LJWJL/p/3481807.html

5.对滤波后信号fir_dout做FFT变换时,刚开始我将之直接连到了FFT核的输入管脚,但vivado一直报错。因此我在这两者之间加了一级寄存器fir_out_reg保存滤波输出数据就可以了。此处问题还请看官指点。

        顶层文件有了基本的雏形后,剩下的就是编写例化文件以及IP核了。

        3.2 生成正弦波模块的文件编写与DDS核的例化

        其实此处不需要单独编写生成正弦波模块的文件,只需要直接在顶层文件例化DDS核即可。但考虑到工程需要两个DDS核,每个DDS核引脚相同,若全部例化在顶层文件需要对其引脚做大量且不同的声明,会使顶层文件变得冗长;此外我们所需的生成正弦波模块只需两个引脚即可(即输入时钟及输出正弦信号),而无需多余引脚。综合以上两点,我选择先编写两个生成正弦波模块的文件,再在此文件中例化DDS核。之后FFT核的情况与之一致。

        (1)编写生成1MHz正弦波模块文件如下:

//生成1MHz正弦波
module dds_1MHz(
    input   wire        sclk,
    output  wire [15:0] sin_1MHz
    );
//DDS核引脚声明
wire        m_axis_data_tvalid ;
wire        m_axis_phase_tvalid;
wire [15:0] m_axis_phase_tdata ;
//DDS核例化    
dds_compiler_0 inst_dds_1MHz (
  .aclk(sclk),                                // input wire aclk
  .m_axis_data_tvalid(m_axis_data_tvalid),    // output wire m_axis_data_tvalid
  .m_axis_data_tdata(sin_1MHz),      // output wire [15 : 0] m_axis_data_tdata
  .m_axis_phase_tvalid(m_axis_phase_tvalid),  // output wire m_axis_phase_tvalid
  .m_axis_phase_tdata(m_axis_phase_tdata)    // output wire [15 : 0] m_axis_phase_tdata
);
endmodule
           

        本工程中DDS核采用100MHz系统时钟,输出正弦信号数据位宽为16,因此Spurious Free Dynamic Range填入16*6=96;相位累加器位宽为16位,因此根据DDS频率步进精度计算公式:

Verilog学习 | 基于vivado平台的DDS、FIR、FFT核的综合学习使用一、自我介绍二、学习任务及内容三、工程实现 四、结语

可得Frequency Resolution项为100M/2^16=1525.87890625(Hz)。因此Configuration配置如下:

Verilog学习 | 基于vivado平台的DDS、FIR、FFT核的综合学习使用一、自我介绍二、学习任务及内容三、工程实现 四、结语

        在 Implementation设置输出波形为正弦波:

Verilog学习 | 基于vivado平台的DDS、FIR、FFT核的综合学习使用一、自我介绍二、学习任务及内容三、工程实现 四、结语

        在Output Frequencies中设置输出频率为1MHz:

Verilog学习 | 基于vivado平台的DDS、FIR、FFT核的综合学习使用一、自我介绍二、学习任务及内容三、工程实现 四、结语

        其它选项选择缺省值。此时可以在Summary中看到DDS核的具体信息以确定是否设计正确:

Verilog学习 | 基于vivado平台的DDS、FIR、FFT核的综合学习使用一、自我介绍二、学习任务及内容三、工程实现 四、结语

        生成IP核后可在IP界面的.veo文件中找到例化模板,同时可确定响应管脚名及数据位宽:

Verilog学习 | 基于vivado平台的DDS、FIR、FFT核的综合学习使用一、自我介绍二、学习任务及内容三、工程实现 四、结语

         (2)编写生成10MHz正弦波模块文件如下:

module dds_10MHz(
    input   wire        sclk,
    output  wire [15:0] sin_10MHz
    );
//DDS核引脚声明
wire        m_axis_data_tvalid ;
wire        m_axis_phase_tvalid;
wire [15:0] m_axis_phase_tdata ;
//DDS核例化   
dds_compiler_1 inst_dds_1MHz (
  .aclk(sclk),                                // input wire aclk
  .m_axis_data_tvalid(m_axis_data_tvalid),    // output wire m_axis_data_tvalid
  .m_axis_data_tdata(sin_10MHz),      // output wire [15 : 0] m_axis_data_tdata
  .m_axis_phase_tvalid(m_axis_phase_tvalid),  // output wire m_axis_phase_tvalid
  .m_axis_phase_tdata(m_axis_phase_tdata)    // output wire [15 : 0] m_axis_phase_tdata
);
endmodule
           

        其DDS核的例化与1MHz的DDS核类似,仅将输出频率改为10MHz即可,这里不在赘述具体过程。

        3.3 FIR核例化

        Vivado平台的FIR核不支持生成滤波器系数,因此需要借助MATLAB的fdatool工具箱设计FIR滤波器,将其产生的滤波器系数.coe文件载入FIR核中,从而完成设计。

        (1)利用MATLAB设计FIR滤波器

        打开MATLAB软件,在命令行键入fdatool打开滤波器设计工具箱。考虑到滤波要求,这里选择低通滤波器,利用等波纹法设计法(Equiripple)设计滤波器,选择滤波器阶数为16,采样频率为100MHz(与DDS输出频率保持一致),通带截止频率为2MHz,阻带截止频率为10MHz,其它采用缺省值(当然进一步优化滤波器也可以深入学习下此工具箱的应用),点击Design Filter可观察设计滤波器的幅频响应:

Verilog学习 | 基于vivado平台的DDS、FIR、FFT核的综合学习使用一、自我介绍二、学习任务及内容三、工程实现 四、结语

 将滤波器系数导出为.coe文件需要将Filter arithmetic改为Fixed-point,另外选择数据位宽为16位。

Verilog学习 | 基于vivado平台的DDS、FIR、FFT核的综合学习使用一、自我介绍二、学习任务及内容三、工程实现 四、结语

 导出.coe文件并保存:

Verilog学习 | 基于vivado平台的DDS、FIR、FFT核的综合学习使用一、自我介绍二、学习任务及内容三、工程实现 四、结语

         (2)设计FIR核

        打开FIR的IP核,在Filter Options界面导入刚刚生成的.coe文件:

Verilog学习 | 基于vivado平台的DDS、FIR、FFT核的综合学习使用一、自我介绍二、学习任务及内容三、工程实现 四、结语

        可以看到滤波器系数有17个,正好比阶数多1。 

        在Channel Specification界面设置输入数据的采样频率与系统时钟(均为100MHz):

Verilog学习 | 基于vivado平台的DDS、FIR、FFT核的综合学习使用一、自我介绍二、学习任务及内容三、工程实现 四、结语

         考虑到FIR是对叠加信号进行滤波,而叠加信号xin是两个16位DDS核的输出的加和,因此此处选择输入数据位宽为17位,数据类型为有符号数。注意系数也要选择有符号数,位宽为16。

Verilog学习 | 基于vivado平台的DDS、FIR、FFT核的综合学习使用一、自我介绍二、学习任务及内容三、工程实现 四、结语

         由于FIR本质是两个数列的卷积和运算,因此注意到输出数据扩展至35位。

        其它选项选择缺省值,可在Summary界面看到FIR相关系数如下:

Verilog学习 | 基于vivado平台的DDS、FIR、FFT核的综合学习使用一、自我介绍二、学习任务及内容三、工程实现 四、结语

         注意到上图中箭头标出,输入数据位宽实际为24位,因此在顶层文件的FIR核例化中需要对17位的xin扩展7位符号位:

.s_axis_data_tdata({{7{xin[16]}},xin}),    // input wire [23 : 0] s_axis_data_tdata(!注意这里要用讲17位的xin符号扩展至要求的24位)
           

         同时注意到输出数据位宽实际为40位,因此为了不引起理解上的偏差,此处再声明一个变量fir_dout 用来盛装输出数据m_axis_data_tdata的低35位:

assign  fir_dout = m_axis_data_tdata[34:0];
           

         此外为了方便接受数据,此处将准备接受数据标志位s_axis_data_tvalid一直置为1:

.s_axis_data_tvalid(1'b1),  // input wire s_axis_data_tvalid
           

        配置完FIR核后,即可同上DDS核一样通过.veo文件得到例化模板在顶层文件例化,此处不再赘述。

         3.4 FFT核例化

        如上所述,为了优化文件结构,这里对FFT核进行单独的模块文件编写 。

         (1)编写对叠加信号xin进行频响分析的文件

        首先进行FFT的IP核例化,在Configuration界面选择做256点FFT,系统时钟为100MHz,算法选择基2,BurstI/O算法:

Verilog学习 | 基于vivado平台的DDS、FIR、FFT核的综合学习使用一、自我介绍二、学习任务及内容三、工程实现 四、结语

         在Implementation界面中选择输入数据位宽为17,输出位序为自然位序,若想要观察算法过程中是否会出现数据溢出的现象还可勾选OVFLO引脚。

Verilog学习 | 基于vivado平台的DDS、FIR、FFT核的综合学习使用一、自我介绍二、学习任务及内容三、工程实现 四、结语

         其它采取缺省项,生成IP核,找到其对应的.veo文件,可以看到例化模型以及各个管脚的功能:

Verilog学习 | 基于vivado平台的DDS、FIR、FFT核的综合学习使用一、自我介绍二、学习任务及内容三、工程实现 四、结语

         之后建立模块文件,并对该IP核进行例化,先给出代码如下:

//对复合信号做频谱分析
module fft_xin(
    input   wire                sclk   ,//系统时钟
    input   wire                rst_n  ,//复位按钮
    input   wire  signed [16:0] xin    ,//复合信号
    output  reg   signed [47:0] fft_out //幅频响应      
    );
//FFT核的引脚声明
wire s_axis_config_tready;
reg  [47:0] s_axis_data_tdata;
reg  s_axis_data_tvalid  ;
wire s_axis_data_tready  ;
reg  s_axis_data_tlast   ;
wire [47:0] m_axis_data_tdata;
wire [7:0] m_axis_data_tuser;
wire m_axis_data_tvalid;
wire m_axis_data_tlast;
wire [7:0] m_axis_status_tdata;
wire m_axis_status_tvalid;
wire m_axis_status_tready;
wire event_frame_started ;
wire event_tlast_unexpected;
wire event_tlast_missing;
wire event_fft_overflow;
wire event_status_channel_halt;
wire event_data_in_channel_halt;
wire event_data_out_channel_halt;
//声明count变量用以计数输入数据个数
reg [8:0]     count;
//FFT输出
reg signed [23:0] fft_i_out;//输出虚部
reg signed [23:0] fft_q_out;//输出实部
//FFT过程
always @ (posedge sclk or negedge rst_n) begin
    if(!rst_n) begin
        s_axis_data_tvalid <= 1'b0;
        s_axis_data_tdata  <= 48'd0;
        s_axis_data_tlast  <= 1'b0;
        count <= 9'd0;
    end
    //当s_axis_data_tready准备就绪时开始输入数据,并接受256个数据
    else if (s_axis_data_tready) begin 
        if(count == 10'd255) begin
            s_axis_data_tvalid <= 1'b1;
            s_axis_data_tlast  <= 1'b1;
            s_axis_data_tdata  <= {24'd0,{{7{xin[16]}},xin}};//注意要符号扩展(下同)
            count <= 9'd0;
        end
        else begin
            s_axis_data_tvalid <= 1'b1;
            s_axis_data_tlast  <= 1'b0;
            s_axis_data_tdata  <= {24'd0,{{7{xin[16]}},xin}};
            count <= count + 1'b1;
        end
    end
    else begin
        s_axis_data_tvalid <= 1'b0;
        s_axis_data_tlast  <= 1'b0;
        s_axis_data_tdata <= s_axis_data_tdata;
    end
end
//FFT输出赋值
always @ (posedge sclk) begin
    if(m_axis_data_tvalid) begin
        fft_i_out <= m_axis_data_tdata[23:0] ;
        fft_q_out <= m_axis_data_tdata[47:24];
    end
end
//计算幅频响应
always @ (posedge sclk) begin
    fft_out <= $signed(fft_i_out)* $signed(fft_i_out)+ $signed(fft_q_out)* $signed(fft_q_out);//注意是有符号数的乘法
end
//FFT核例化
xfft_0 your_instance_name (
  .aclk(sclk),                                                // input wire aclk
  .aresetn(rst_n),                                          // input wire aresetn
  //由于fft变换数据位数会不断增加,可能会出现溢出的情况,因此这里config通道在其scale部分设置各级放缩倍数;因为是256个数据(2^8)因此这里scale占2*8=16位
  .s_axis_config_tdata(24'b0000000_0101010101010101_1),                  // input wire [23 : 0] s_axis_config_tdata
  .s_axis_config_tvalid(1'b1),                // input wire s_axis_config_tvalid
  .s_axis_config_tready(s_axis_config_tready),                // output wire s_axis_config_tready
  .s_axis_data_tdata(s_axis_data_tdata),                      // input wire [47 : 0] s_axis_data_tdata  !!!!!!!!!
  .s_axis_data_tvalid(s_axis_data_tvalid),                    // input wire s_axis_data_tvalid
  .s_axis_data_tready(s_axis_data_tready),                    // output wire s_axis_data_tready
  .s_axis_data_tlast(s_axis_data_tlast),                      // input wire s_axis_data_tlast
  .m_axis_data_tdata(m_axis_data_tdata),                      // output wire [47 : 0] m_axis_data_tdata!!!!!!!!!!!
  .m_axis_data_tuser(m_axis_data_tuser),                      // output wire [7 : 0] m_axis_data_tuser
  .m_axis_data_tvalid(m_axis_data_tvalid),                    // output wire m_axis_data_tvalid
  .m_axis_data_tready(1'b1),                    // input wire m_axis_data_tready
  .m_axis_data_tlast(m_axis_data_tlast),                      // output wire m_axis_data_tlast
  .m_axis_status_tdata(m_axis_status_tdata),                  // output wire [7 : 0] m_axis_status_tdata
  .m_axis_status_tvalid(m_axis_status_tvalid),                // output wire m_axis_status_tvalid
  .m_axis_status_tready(m_axis_status_tready),                // input wire m_axis_status_tready
  .event_frame_started(event_frame_started),                  // output wire event_frame_started
  .event_tlast_unexpected(event_tlast_unexpected),            // output wire event_tlast_unexpected
  .event_tlast_missing(event_tlast_missing),                  // output wire event_tlast_missing
  .event_fft_overflow(event_fft_overflow),                    // output wire event_fft_overflow
  .event_status_channel_halt(event_status_channel_halt),      // output wire event_status_channel_halt
  .event_data_in_channel_halt(event_data_in_channel_halt),    // output wire event_data_in_channel_halt
  .event_data_out_channel_halt(event_data_out_channel_halt)  // output wire event_data_out_channel_halt
);
endmodule
           

        代码详细说明可见注释,相应管脚说明可参此链接:https://www.cnblogs.com/lgy-gdeu/p/11590626.html

        其中需要注意的几点罗列如下:     

 1.在.veo例化模型中,注意到FFT输入位宽为48位,即虚部和实部各24位,因此需要对输入的17位数据xin进行7位的符号扩展并赋值给低24位,高24位接地(赋零)即可。

2.输出的48位数据m_axis_data_tdata中高24和第24位分别为结果的虚部fft_i_out和实部fft_q_out,此两部分均为有符号数,因此计算幅频响应时需利用系统函数$signed进行修饰。该函数具体说明可参此链接:https://blog.csdn.net/wordwarwordwar/article/details/108039574

3.由于FFT算法中存在较多加法与乘法运算,因此会存在数据位增大而导致的数据溢出的现象,因此FFT核提供了数据缩放设置(SCALE_SCH),该系数可在s_axis_config_tdata通道中进行设置,对应数据位可在IP界面的Implementation Details中找到:

Verilog学习 | 基于vivado平台的DDS、FIR、FFT核的综合学习使用一、自我介绍二、学习任务及内容三、工程实现 四、结语

        其具体的数据配置说明可参此链接:https://www.cnblogs.com/xiaoxuesheng993/p/8064156.html,此处笔者就不班门弄斧了。

        本工程中我选择进行的是256(2^8)点FFT,因此对应8级压缩,每级对应2bits数据进行1,2,4,8倍的选择压缩,因此SCALE_SCH对应[16:1]位,选择对每级进行2倍的压缩,并通过配置s_axis_config_tdata[0]为1设置为正向FFT变换(0为逆向),得到s_axis_config_tdata配置数据为24'b0000000_0101010101010101_1。        

          (2)编写对滤波后信号fir_out进行频响分析的文件

         该部分与对xin进行频响分析的文件大同小异,仿照上述IP核设置过程与代码可编程如下:

//对FIR输出信号做频谱分析
module fft_dout(
    input   wire                sclk   ,//系统时钟
    input   wire                rst_n  ,//复位按钮
    input   wire  signed [34:0] fir_out,//FIR输出信号 
    output  reg   signed [63:0] fft_out //幅频响应 
    );
//FFT核的引脚声明    
wire s_axis_config_tready;
reg  [79:0] s_axis_data_tdata;
reg  s_axis_data_tvalid  ;
wire s_axis_data_tready  ;
reg  s_axis_data_tlast   ;
wire [79:0] m_axis_data_tdata;
wire [7:0] m_axis_data_tuser;
wire m_axis_data_tvalid;
wire m_axis_data_tlast;
wire [7:0] m_axis_status_tdata;
wire m_axis_status_tvalid;
wire m_axis_status_tready;
wire event_frame_started ;
wire event_tlast_unexpected;
wire event_tlast_missing;
wire event_fft_overflow;
wire event_status_channel_halt;
wire event_data_in_channel_halt;
wire event_data_out_channel_halt;
//声明count变量用以计数输入数据个数
reg [8:0]     count;
//FFT输出
reg signed [39:0] fft_i_out;
reg signed [39:0] fft_q_out;
//FFT过程
always @ (posedge sclk or negedge rst_n) begin
    if(!rst_n) begin
        s_axis_data_tvalid <= 1'b0;
        s_axis_data_tdata  <= 80'd0;
        s_axis_data_tlast  <= 1'b0;
        count <= 9'd0;
    end
    //当s_axis_data_tready准备就绪时开始输入数据,并接受256个数据
    else if (s_axis_data_tready) begin 
        if(count == 10'd255) begin
            s_axis_data_tvalid <= 1'b1;
            s_axis_data_tlast  <= 1'b1;
            s_axis_data_tdata  <= {40'd0,{{5{fir_out[34]}},fir_out}};//注意要符号扩展(下同)
            count <= 9'd0;
        end
        else begin
            s_axis_data_tvalid <= 1'b1;
            s_axis_data_tlast  <= 1'b0;
            s_axis_data_tdata  <= {40'd0,{{5{fir_out[34]}},fir_out}};
            count <= count + 1'b1;
        end
    end
    else begin
        s_axis_data_tvalid <= 1'b0;
        s_axis_data_tlast  <= 1'b0;
        s_axis_data_tdata <= s_axis_data_tdata;
    end
end
//FFT输出赋值
always @ (posedge sclk) begin
    if(m_axis_data_tvalid) begin
        fft_i_out <= m_axis_data_tdata[39:0] ;
        fft_q_out <= m_axis_data_tdata[79:40];
    end
end
//计算幅频响应
always @ (posedge sclk) begin
    fft_out <= $signed(fft_i_out)* $signed(fft_i_out)+ $signed(fft_q_out)* $signed(fft_q_out);
end
//FFT核例化
xfft_1 your_instance_name (
  .aclk(sclk),                                                // input wire aclk
  .aresetn(rst_n),                                          // input wire aresetn
  //注意这里config通道需要在scale相应位配置放缩因子,不然一方面会出现数据溢出现象;另外一方面会出现因为输出结果太大而fft_out位数大于64位而无法呈现analog波形的情况
  .s_axis_config_tdata(24'b0000000_0101101010101010_1),                  // input wire [23 : 0] s_axis_config_tdata
  .s_axis_config_tvalid(1'b1),                // input wire s_axis_config_tvalid
  .s_axis_config_tready(s_axis_config_tready),                // output wire s_axis_config_tready
  .s_axis_data_tdata(s_axis_data_tdata),                      // input wire [79 : 0] s_axis_data_tdata
  .s_axis_data_tvalid(s_axis_data_tvalid),                    // input wire s_axis_data_tvalid
  .s_axis_data_tready(s_axis_data_tready),                    // output wire s_axis_data_tready
  .s_axis_data_tlast(s_axis_data_tlast),                      // input wire s_axis_data_tlast
  .m_axis_data_tdata(m_axis_data_tdata),                      // output wire [79 : 0] m_axis_data_tdata
  .m_axis_data_tvalid(m_axis_data_tvalid),                    // output wire m_axis_data_tvalid
  .m_axis_data_tready(1'b1),                    // input wire m_axis_data_tready
  .m_axis_data_tlast(m_axis_data_tlast),                      // output wire m_axis_data_tlast
  .event_frame_started(event_frame_started),                  // output wire event_frame_started
  .event_tlast_unexpected(event_tlast_unexpected),            // output wire event_tlast_unexpected
  .event_tlast_missing(event_tlast_missing),                  // output wire event_tlast_missing
  .event_status_channel_halt(event_status_channel_halt),      // output wire event_status_channel_halt
  .event_data_in_channel_halt(event_data_in_channel_halt),    // output wire event_data_in_channel_halt
  .event_data_out_channel_halt(event_data_out_channel_halt)  // output wire event_data_out_channel_halt
);
endmodule
           

         IP核设置只需将输入数据位宽改为fir_out的位宽35即可,但发现此处选项最高可选为34,但选择34后,系统显示的输入位宽为80(虚实部各40位):

Verilog学习 | 基于vivado平台的DDS、FIR、FFT核的综合学习使用一、自我介绍二、学习任务及内容三、工程实现 四、结语

         因此还需要对输入数据fir_out进行5位的符号扩展:

s_axis_data_tdata  <= {40'd0,{{5{fir_out[34]}},fir_out}};//注意要符号扩展
           

         此外还需注意,因为Vivado平台不支持对64位以上的数据进行Analog显示,因此一方面需要将输出数据fft_out改为64位,另一方面需要为SCALE_SCH配置更高的压缩值,此处选择s_axis_config_tdata通道值为24'b00000_010101101010101010_1。

         编写好两FFT文件后,在顶层文件fir中进行例化即可。

         3.5 Testbench文件编写与方针实现

        因为我已经在顶层文件中将很多模块进行了连接,因此Testbench文件的编写就比较简单了,仅仅给出时钟信号、复位信号以及顶层文件的例化即可:

//Testbench仿真文件
module tb_fir;
reg                 sclk        ;//系统时钟         
reg                 rst_n       ;//复位按钮(低有效)    
wire                m_axis_data_tvalid;//fir输出有效标志    
wire signed [34:0]  fir_dout    ;//滤波后输出数据      
wire signed [47:0]  fft_out_xin ;//对复合输入信号做频谱分析 
wire signed [63:0]  fft_out_dout;//对滤波后输出数据做频谱分析
//初始化
initial begin
    rst_n =0 ;
    sclk  = 0;
    #100
    rst_n = 1;
end
//生成100MHz时钟
always #5 sclk <= ~sclk;
//顶层模块例化
fir inst_fir(
    .sclk(sclk),
    .rst_n(rst_n),
    .m_axis_data_tvalid(m_axis_data_tvalid),
    .fir_dout(fir_dout),
    .fft_out_xin(fft_out_xin),
    .fft_out_dout(fft_out_dout)
    );
endmodule
           

         点击Run Simulation进行行为级仿真即可得到结果图如下:

Verilog学习 | 基于vivado平台的DDS、FIR、FFT核的综合学习使用一、自我介绍二、学习任务及内容三、工程实现 四、结语

        可见叠加信号、滤波后信号以及两信号的幅频响应均达到预期,滤波效果也不错,较好的完成了此项工程任务。 

 四、结语

         断断续续学习FPGA一月有余,第一次自己着手做这么大的仿真任务,着实花费了笔者诸多心力,但也使我收获颇多。另外鉴于笔者乍学识浅,文中部分代码以及陈述说明均有再提升或是不当之处,还望诸君海涵斧正。

        最后感谢F学长和实验室几个学长的悉心指导,以及由于篇幅文中所未提及但对笔者学习有帮助的各个书籍、网站与作者!再次感谢!

继续阅读