前言

fpga初学者,在vivado软件上调用fft IP核时,一头雾水。参考了部分博文,总结出FFT调用的流程,并更新了部分端口的说明 (https://www.cnblogs.com/lgy-gdeu/p/11590626.html 中有两个端口说明可能有误解)。

一、调用IP核

1、点击Flow Navigator中的IP Catalog,搜索FFT,选择xfft9.1.

2、双击后,有三个界面需要配置

第2页:

主要配置通道数,点数,时钟,吞吐量,结构,以及是否可以运行时配置,需要注意的是结构的配置会影响调整因子。如下图所示:

          number  of  channels :变换通道,可以选择多通道,实现多帧数据同时进行FFT运算;

        transform  lenfgth   : FFT变换长度,如果选择了最下面的‘run time configurable transdorm legth’,则该参数是FFT变化的最大长度,一般不选。

     architecure  choice : 这个标签里主要是用来实现,FFT变换所选用的架构:

             Automatically  selected :   自动选择所需要的,FFT变化架构。

                pipelined  streaming     :    并行流水线结构

                radix-4, burst i/o            :   基4 I/O突发结构 

                                               radix-2, burst i/o            :    基2 I/O突发结构

                                               radix-2 life, burst i/o       :   基2 I/O突发结构

          其中流水线的结构变换处理时间最短,资源消耗最大。

     run time configurable transdorm legth  : 该选项可以在FFT变换中通过设置s_axis_config_tdata中NFFT字段的长度来改变FFT变化的长度。

第2页:

配置 数据宽度,格式,控制信号,输出方式,和可选的控制信号。

         需要注意的是输据的输入是自然方式(Natural  Order),输出可以是自然方式也可以是倒序方式(Reversed Order),如果选用倒序方式输出,在后面处理中就要注意这一特性。

         其中在data format;下拉标签中,对应着FFT IP核支持两种数据类型: 
                                       1. 定点全精度 
                                       2. 定点缩减位宽 
         scaling optios :缩放选项 :

                                        1、 block floating point :不管输入的格式如何,FFT变化内部都采用浮点,会根据每一级的的数据情况自动缩放。 这个模式的输入输出位宽一致,便于调用。

                                        2、scaled :在m_axis_data_tuser中会有5BIT表示每一级的缩放情况,在s_axis_config_data中会有相应的字段配置配置缩放因子.每一级别包含2个stage ,2个bit 表示一级缩放,一般0-3可选,如果log(NFFT)不是2的倍数,则最高一级的缩放只能在0-1之间选取。

                                        3、unscaled :不用担心变化过程中会出现溢出,但是输入是32bit的话,输出是64bit。

          Aresten : 复位信号要勾选,至少保持两个时钟的低电平。

           output odering options: 输出顺序选项。

                                        1、nature order:就是FFT变化后的输出已经调整了顺序,按照xk_index自然顺序列出变化结果,

                                        2、bit/digital reserved oder就是按照变化后的顺序直接输出,是倒序输出,需要自己后续处理,

                                        3、cyclic perfix insertion :循环前缀插入,一般添加,在进行IFFT后可以根据s_axis_config_data中的CP长度配置自动添加CP。

           optional output fileds :选项输出字段,

                                        1、xk_index:FFT 变幻的结果索引,在m_axis_data_user中有相应的字段。

                                        2、OVFLO是变换中溢出的指示信号,对应event_fft_overflow.

第3页:没有变化

详细的FFT IP核说明可参考  Documentation 中的 Product Guide的pdf文件。

点击OK,即可。

 

二、编写一个.v 文件来驱动FFT单元

1、例化fft IP核。 点击.IP Source中 Instantiation Template下的 .veo文件,双击。将以下代码复制到.v文件中。

该代码定义了端口名称,输入输出模式,位宽等。

2. 利用以上代码。 看起来很头疼,但研究之后回过头来发现,例化代码中真正对数据输入和FFT输出有关系的端口,只有s_axis_config_XXX,s_axis_data_XXX,和m_axis_data_XXX,其中前2个是输入配置,第3个是输出配置。

最终编写的.v文件如下

`timescale 1ns/1ps

module fft64(
	input           i_clk,	//100MHz, 10ns
	input           aresetn,
	input[7:0]     s_axis_config_tdata,
	input           s_axis_config_tvalid,
	output          s_axis_config_tready,
	output          s_axis_data_tready, 
    input[31:0]     s_axis_data_tdata,  //输入的16位数据 16*2
    input           s_axis_data_tvalid, //拉高N个周期,输入N个数据进行fft
    input           s_axis_data_tlast,  //输入N个数据后拉高,停止数据输入
    output[47:0]    m_axis_data_tdata,
    output[7:0]     m_axis_data_tuser,
    output          m_axis_data_tvalid,
    input           m_axis_data_tready,
    output          m_axis_data_tlast
    );

    wire            event_frame_started;
    wire            event_tlast_unexpected;
    wire            event_tlast_missing;
    wire            event_status_channel_halt;
    wire            event_data_in_channel_halt;
    wire            event_data_out_channel_halt;

fft_f fft64 (
  .aclk(i_clk),                                                // input wire aclk
  .aresetn(aresetn),                                          // input wire aresetn
  .s_axis_config_tdata(s_axis_config_tdata),                  // input wire [7 : 0] s_axis_config_tdata
  .s_axis_config_tvalid(s_axis_config_tvalid),                // 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 [31 : 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(m_axis_data_tready),                    // 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

3.  端口说明

Input[N:0]:  s_axis_config_tdata:控制输入模式,进行fft/ifft以及衰减因子的设置,FWD_INV = 1做 fft,FWD_INV = 0做ifft。

Input:          s_axis_config_tvalid:拉高两个时钟周期之后,将端口s_axis_data_tvalid和s_axis_data_tready拉高。

Output:       s_axis_config_tready:s_axis_config_tvalid拉高两个时钟周期后,该口给1输出;若干个时钟周期后,自动归零。

Output:       s_axis_data_tready:aresetn拉高两个时钟周期后,该口给1输出;此时ip核初始化完成,可进行数据输入。

Input:          s_axis_data_tvalid:当s_axis_data_tready高电平后,将s_axis_data_tvalid拉高L个周期,输入L个数据进行fft;L是FFT的点数。

Input[M:0]:  s_axis_data_tdata:将数据输入进行FFT运算。

Input:          s_axis_data_tlast:输入L个数据后拉高,停止数据输入。

做fft需要耗费的时钟周期计算如下s_axis_data_tlast- s_axis_data_tvalid

Output[M1:0]:m_axis_data_tdata:高位为虚部,低位为实部。(经过测试发现的,implementation也有定义)

Output:          m_axis_data_tvalid:当fft结果输出时拉高,输出2048个点的数据后拉低。

Output[M2:0]:m_axis_data_tuser:输出fft的地址值,输出值*fs/L为对应频点。

Input:             m_axis_data_tready:需保持高电平,保证FFT单元处在计算模式,并且能够输出结算结果;

Output:          m_axis_data_tlast:当fft结果输出到最后一个结果时拉高,紧接着下一个时钟就拉低。

说明

(a)FWD_INV的字段从这里得到

(b)当输入数据包含实部和虚部时,输入端口s_axis_data_tdata的高位存放虚部,低位存放实部。

(c)输出数据的实部和虚部的字段分布

 

三、编写一个testbench_top.v 文件对FFT单元进行测试。

1、需要在testbench_top文件中对“二、编写一个.v 文件来驱动FFT单元” 中编写的.v文件进行例化。 代码如下:

module testbench_top();
	

//参数定义

`define CLK_PERIORD		10		//时钟周期设置为10ns(100MHz)	


//接口申明
	
reg 		  clk;
reg 		  rst_n;
reg 		  aresetn;
reg[7:0]	  s_axis_config_tdata;
reg           s_axis_config_tvalid;
wire          s_axis_config_tready;

wire          s_axis_data_tready; 
reg[31:0]     s_axis_data_tdata;  //输入的16位数据 16*2
reg           s_axis_data_tvalid; //拉高4096个周期,输入4096个数据进行fft
reg           s_axis_data_tlast;  //输入4096个数据后拉高,停止数据输入

wire[47:0]    m_axis_data_tdata;
wire[7:0]     m_axis_data_tuser;
wire          m_axis_data_tvalid;
reg           m_axis_data_tready;
wire          m_axis_data_tlast;	

reg    [15:0] mem0_re[1:64]; 
wire[23:0]    o_data_real;
wire[23:0]    o_data_imag; 
assign o_data_real = m_axis_data_tdata[23:0];
assign o_data_imag = m_axis_data_tdata[47:24];
	
//对被测试的设计进行例化

fft64		uut_fft64(
	.i_clk				 (clk),  //100MHz, 10ns
	.aresetn			 (aresetn),
	.s_axis_config_tdata (s_axis_config_tdata),
	.s_axis_config_tvalid(s_axis_config_tvalid),
	.s_axis_config_tready(s_axis_config_tready),
	.s_axis_data_tready	 (s_axis_data_tready),  
	.s_axis_data_tdata	 (s_axis_data_tdata),
	.s_axis_data_tvalid	 (s_axis_data_tvalid),
	.s_axis_data_tlast	 (s_axis_data_tlast),
	.m_axis_data_tdata	 (m_axis_data_tdata),
	.m_axis_data_tuser	 (m_axis_data_tuser),
	.m_axis_data_tvalid	 (m_axis_data_tvalid),
	.m_axis_data_tready	 (m_axis_data_tready),
	.m_axis_data_tlast	 (m_axis_data_tlast)
    );	
	

//复位和时钟产生
	//时钟和复位初始化、复位产生
initial begin
	clk <= 0;
	rst_n <= 0;
	#1000;
	rst_n <= 1;
end
	//时钟产生
always #(`CLK_PERIORD/2) clk = ~clk;	


//测试激励产生

initial $readmemb("D:/FPGA_Code/TeQuan_Study/sim_115_FFT2/0Matlab/data_before_fft.txt", mem0_re);
integer i;
	
initial begin
	// Initialize Inputs
	clk = 0;
	aresetn = 0;
	s_axis_config_tdata = 0;
	s_axis_config_tvalid = 0;
	
	s_axis_data_tvalid = 0;
	s_axis_data_tdata = 0;
	s_axis_data_tlast = 0;
	
	m_axis_data_tready = 0;
	i = 0;
	// Wait 200 ns for global reset to finish
	#200;
	aresetn = 1;
	m_axis_data_tready = 1;
	s_axis_config_tvalid = 1;
	s_axis_config_tdata = {7'b0, 1'b1}; 


	#8000
	s_axis_config_tvalid = 0;
	aresetn = 0;
	#300;
	
	$stop;
	
end


always @(posedge clk)
begin
  if(s_axis_data_tready == 1)
  begin
    i = i + 1;
    s_axis_data_tvalid <= 1;
    s_axis_data_tdata <= mem0_re[i];
    $display("mem_a[%d] = %d", i, mem0_re[i]);
	
	if(i == 68)
	begin 
	  s_axis_data_tlast <= 1;
	  s_axis_data_tvalid <= 0;
	end

  end
end


endmodule

说明:

(a)输入数据在data_before_fft.txt中,测试使用的是从1到64的整数(间隔1),一共64个数字。

(b)输出的端口一定要记得定义wire 输出,否则没有输出数据。

四、Vivado仿真结果

运行结果如下:

可以看出,可完成1~64的数据输入。

经过研究发现:

(a)aresetn信号存在时,是控制FFT是否运行,以及是否输出运算结果的使能信号。此外,aresetn信号必须在FFT输入之前,到FFT结果计算结束之间这段时间,都必须保持高电平。

(b)s_axis_data_tvalid也是控制输入和输出的信号端口。使用的时候给fft喂数据就enable(1),数据喂完了就disable(0)。但是在aresetn信号存在,s_axis_data_tvalid失去作用。

 

五、与Matlab仿真结果对比

Matlab运算结果:

IP核运算结果

1、运算结果实部和虚部,数字的值趋势相同,但是存在误差;

2、实部数值正负号相同,数字值差别也不大,正确;

3、虚部数值正负号完全相反,数字值本身差别不大,有问题。 如何解决?

将config_tdata信号修改为s_axis_config_tdata = 8'b0000_0000。可得到FFT正确结果。

六、目前还有以下问题:

然而,8b0的配置明明是IFFT的配置,为什么在这里却能得到FFT的结果?

当输入是浮点数时,FFT如何配置?

应该在IP核界面中配置:

算术类型:

  • 无标度(全精度)定点
  • 定标定点
  • 浮点数

定点或浮点接口

蝴蝶后舍入或截断

 

补一补Verilog中signed,原码,反码,补码基础 https://blog.csdn.net/maxwell2ic/article/details/80596210

这篇博文同时实现了FFT/IFFT 在VIVADO中实现FFT/IFFT https://blog.csdn.net/luobluesky/article/details/90516652?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.channel_param&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.channel_param 值得参考!

这篇博文利用的FFT模式是Fixed Point,Block Floating Point,Convergent Rounding。这样得到的结果,FFT模值趋势上与Matlab相同,但是具体的实部、虚部数值也存在相反现象。

ModelSim结果

Matlab结果(上面是实部,下面是虚部)

 

 

参考博文

https://blog.csdn.net/u013215852/article/details/105345952/

https://blog.csdn.net/qq_36375505/article/details/81742680

https://blog.csdn.net/id_yqduan/article/details/108882080?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param

https://www.cnblogs.com/lgy-gdeu/p/11590626.html

 

 

 

 

 

 

 

 

 

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐