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频率步进精度计算公式:

\Delta f = \tfrac{f_{sclk}}{2^{N}}

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

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

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

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

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

         (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可观察设计滤波器的幅频响应:

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

 

 导出.coe文件并保存:

         (2)设计FIR核

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

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

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

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

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

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

         注意到上图中箭头标出,输入数据位宽实际为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算法:

         

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

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

         之后建立模块文件,并对该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中找到:

        其具体的数据配置说明可参此链接: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位):

         因此还需要对输入数据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进行行为级仿真即可得到结果图如下:

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

 四、结语

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

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

 

  

 

 

  • 30
    点赞
  • 183
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论
FPGA DDS (数字信号处理器直接数字合成)是一种可调波形的技术。DDS是一种通过数字方式实现信号合成的技术,它使用数字技术来产生不同频率的信号波形。 FPGA (可编程逻辑门阵列)是一种可重构硬件设备,可以使用Verilog或VHDL等硬件描述语言来编程实现不同的功能。在FPGA上实现DDS功能可以实现高灵活性和高性能的可调波形。 FPGA DDS可调波形使用相位累加器和查找表来生成一个精确的、连续的信号波形。相位累加器根据设定的频率和相位来控制输出波形的精度,而查找表则存储了预先计算好的采样值。通过不断修改相位累加器的值,DDS可以精确地生成不同频率和相位的信号。 FPGA DDS可调波形具有以下优点: 1. 高灵活性:DDS可以通过修改相位累加器的值来实现任意频率和相位的合成信号波形,使得其在各种应用中具有广泛的适应性。 2. 高精度:DDS使用数字方式进行信号合成,可以实现非常精确的波形合成,具有较低的相位和频率误差,使其在需要高精度的应用中得到广泛应用。 3. 高性能:FPGA作为硬件设备,具有高速的计算和处理能力,可以实现实时的、高性能的DDS波形合成,满足对实时性能要求较高的应用场景。 总之,FPGA DDS可调波形技术是一种利用FPGA实现的数字信号合成技术,在灵活性、精度和性能方面具有明显优势,广泛应用于通信、雷达、医疗等领域。
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

XD_MaoHai

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值