基于FPGA的FFT算法实现(2)

基于FPGA的FFT IP的实时配置

  • 项目简述
  • FFT进行重配置
  • FPGA代码
    • FPGA逻辑代码
    • FPGA测试代码
  • MATLAB验证
  • 参考文献
  • 总结

项目简述

前面我们已经讲解过Xilinx中FFT IP的使用,但是使用的时候IP的配置接口我们没有进行相应的讲解,直接使用GUI配置好的接口,这在现实应用中很不方方便,会让人感觉到还不如自己手写一个FFT算法,当然博主也可完全手撕FFT、CORDIC代码,但是把IP用好了绝对比我们手写的代码要好用的多。这篇博客主要讲解FFT IP的重配置及其参数的意义,最后将给出Modelsim与MATLAB的两盒验证。

本次实验所使用的软硬件环境如下:
1、VIVADO 2019.1
2、Modelsim 10.7
3、MATLAB 2015b

这里再多说一句,从博主之前的文章中可以发现MATLAB在信号处理中的正确性,所以绝对不要说MATLAB不重要。而且一般Xilinx的IP核都可以生成相应的m文件,也就是说我们可以实现MATLAB与Modelsim完全一致的验证。 所以要想做信号处理或算法得FPGA实现一定要掌握MATLAB。

FFT进行重配置

我们在定制FFT IP核得时候就已经对FFT进行了配置,但是我们实际使用IP得时候经常可以碰见IP得重配置,这一块内容直至一年前我进行PLL得重配置得时候还是无从下手,但是这篇文章我们主要讲解FFT IP得重配置,可以让大家学习到Xilinx IP重配置得设计技巧。这里我们首先强调技术手册得重要性,因为市面上没有这方面得资料,所以我们要学习相应IP得重配置必须学习技术手册。我们首先看FFT IP得技术手册如下:
基于FPGA的FFT算法实现(2)_第1张图片
总共不到100页,是值得浏览一遍的,因为其他得IP基本上也是这几部分。C Model我们上篇博客已经进行了介绍,主要是为了我们在MATLAB中验证该模块得正确性来使用的。

首先我们来看FFT IP核的接口引脚:
基于FPGA的FFT算法实现(2)_第2张图片
其中FFT的接口主要可以分为6组如上图:
1、FFT的重配置接口
2、FFT的数据输入接口,遵循AXI-Stream协议
3、FFT的时钟、时钟使能、复位信号(注意复位信号要多给几个时钟)
4、FFT的数据输出接口,遵循AXI-Stream协议
5、可以输出FFT IP的当前的状态(一般不常使用)
6、可以输出一些FFT的错误信息,比如输入的last未知不正确或没有,数据溢出等等
基于FPGA的FFT算法实现(2)_第3张图片
基于FPGA的FFT算法实现(2)_第4张图片
上面是简要介绍了FFT IP的接口描述。具体的功能引脚的定义还是需要我们查找技术手册,我们这篇博客主要讲解IP的重配置,不会对AXI-Stream进行过多的介绍。

从FFT IP技术手册的首页我们可以发现,FFT可以完成的功能:
基于FPGA的FFT算法实现(2)_第5张图片
上面也是我们进行重配置的主要内容:
1、FFT最大变换的点数
2、FFT正变换还是逆变换
3、每级蝶形运算缩放因子的输入
4、CP_LEN的长度(这个具体的所用,我也不知道,知道的同学可以在评论里讨论一下)

要想配置上面的这些信息,我们就一定要进行配置数据的输入,配置数据的不同位数代表不同的功能,如下:
基于FPGA的FFT算法实现(2)_第6张图片
上面为什么会有PAD,主要是因为字节对齐,每个配置功能占整数个字节。其中除了SCALE_SCH上面的位宽都是确定的,如下:
基于FPGA的FFT算法实现(2)_第7张图片
上面每位的取值情况如下:
基于FPGA的FFT算法实现(2)_第8张图片
基于FPGA的FFT算法实现(2)_第9张图片
上面的功能需要大家仔细读,尤其是SCALE_SCH,这里我给大家稍微解读一下。
1、每两个比特位构成的数字作为一级蝶形运算的缩放比例。2位比特位构成了0,1,2,3,这三个数代表分别代表蝶形运算之后的结果移位的个数。
2、SCALE_SCH的位数对于基-4 FFT算法是 2 ∗ c e i l ( N F F T 2 ) 2*ceil(\frac{NFFT}{2}) 2ceil(2NFFT),其中ceil是指向上取整;对于基-2 FFT算法是 2 ∗ N F F T 2*NFFT 2NFFT,相信熟悉FFT蝶形运算的同学很容易明白,其实这就是蝶形运算的个数然后乘以2。
关于FFT重配置的理论我们就讲到这里,下面我们给出相应的代码供大家学习,并且将代码与上篇博客中的MATLAB生成的结果相互验证,从而使得MATLAB与VIVADO实现双重验证。

FPGA代码

FPGA逻辑代码

tx_ifft_op模块:

`timescale 1ns / 1ps
// *********************************************************************************
// Project Name : OSXXXX
// Author       : zhangningning
// Email        : [email protected]
// Website      : 
// Module Name  : tx_ifft_op.v
// Create Time  : 2020-06-04 16:33:48
// Editor       : sublime text3, tab size (4)
// CopyRight(c) : All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date             By              Version                 Change Description
// -----------------------------------------------------------------------
// XXXX       zhangningning          1.0                        Original
//  
// *********************************************************************************

module tx_ifft_op(
	input             		sclk 					,
	input             		rst_n					,
	input             		cfg_vld					,
	output	reg       		p1_start 				,
	input             		s_config_tvalid			,
	input 		[29:0]	  	s_config_tdata			,
	input 			  		s_data_tvalid 			,
	input 		[23:0]      s_data_tdata			,	
	input             		s_data_tlast			,		
	output reg        		s_axis_config_tready	,
	output reg        		s_axis_data_tready		,
	output reg 	[31:0] 		m_axis_data_tdata  		,
	output     	[23:0] 		m_axis_data_tuser  		,
	output reg        		m_axis_data_tvalid		,
	input             		m_axis_data_tready		,	
	output reg        		m_axis_data_tlast		,
	output  	[ 7:0]   	m_axis_status_tdata		,
	output            		m_axis_status_tvalid	,
	input             		m_axis_status_tready	,
	output            		event_frame_started		,
	output            		event_tlast_unexpected	,
	output            		event_tlast_missing		,
	output            		event_fft_overflow		,
	output            		event_status_channel_halt	,
	output            		event_data_in_channel_halt	,
	output            		event_data_out_channel_halt	,
	output  	[ 9:0]     	NOFDM
);

//========================================================================================\
//**************Define Parameter and  Internal Signals**********************************
//========================================================================================/            
reg 			[ 9:0]		NOFDM_CNT				;
wire 			[39:0] 		s_axis_config_tdata 	;
wire 						s_axis_config_tvalid	;
wire 			[31:0]   	s_axis_data_tdata		;
wire            			s_axis_data_tvalid		;
wire            			s_axis_data_tlast		;   
wire 			[ 4:0]      NFFT					;
wire 			[13:0]     	SCALE_SCH				;
wire 			[11:0]     	RE_DATA					;
wire 			[11:0]     	IM_DATA					;
wire            			DATA_LAST				;
wire            			DATA_EN					;
wire            			FWD_INV					;
wire            			fft_config_en			;
wire 			[31:0]   	m_axis_data_tdata_store	;
wire            			m_axis_data_tvalid_store;
wire            			m_axis_data_tlast_store	;
reg             			p1_start_D				;
wire 			[12:0]     	CP_LEN					;
reg 						last_delay				;
wire 						Neg_ifft_tlast			;
reg 			[ 4:0]		rstn_cnt				;
wire 						s_axis_data_tready1 	;
 
//========================================================================================\
//**************     Main      Code        **********************************
//========================================================================================/
assign 	Neg_ifft_tlast		=		last_delay&&(~m_axis_data_tlast_store);

always@(posedge sclk)
	last_delay 			<=   		m_axis_data_tlast_store;

always @(posedge sclk)
	if(rst_n == 1'b0)
		NOFDM_CNT 		<=   		10'd0;
	else if(NOFDM_CNT == NOFDM+1'b1 && NOFDM != 10'd0)
		NOFDM_CNT 		<=   		10'd0;
	else if(Neg_ifft_tlast == 1'b1)
		NOFDM_CNT 		<=   		NOFDM_CNT + 1'b1;		

always @(posedge sclk)
    if(rst_n == 1'b0)
        rstn_cnt 		<=   		5'h1f;  
	else if(cfg_vld == 1'b1)
		rstn_cnt 		<=   		5'd0;
	else if(rstn_cnt == 5'h1f)
		rstn_cnt 		<=    		rstn_cnt;
	else 
		rstn_cnt  		<=   		rstn_cnt + 1'b1;

always @(posedge sclk)
	if(rst_n == 1'b0)
		p1_start 		<=   		1'b0;
	else if(rstn_cnt == 5'h1e)
		p1_start 		<=    		1'b1;
	else if(NOFDM_CNT == NOFDM+1'b1 && NOFDM != 10'd0)
		p1_start 		<=   		1'b1;
	else 
		p1_start 		<=   		1'b0;


always @(posedge sclk)
	if(rst_n == 1'b0)
		s_axis_data_tready 	<=   	1'b1;
	else if(Neg_ifft_tlast == 1'b1)
		s_axis_data_tready 	<=   	1'b1;    
	else if(s_data_tlast == 1'b1)
		s_axis_data_tready 	<=   	1'b0;
		

always @(posedge sclk)
	p1_start_D 			<=   		p1_start;

always @(posedge sclk)
	if(rst_n == 1'b0)
		s_axis_config_tready 	<=   1'b0;
	else if(p1_start_D == 1'b1)
		s_axis_config_tready 	<=   1'b0;
	else if(Neg_ifft_tlast == 1'b1)
		s_axis_config_tready 	<=   1'b1;

always @(posedge sclk)
begin
	m_axis_data_tdata  	<=   		m_axis_data_tdata_store;
	m_axis_data_tvalid 	<=   		m_axis_data_tvalid_store && m_axis_data_tready;
	m_axis_data_tlast  	<=   		m_axis_data_tlast_store;
end 


ifft_op_map ifft_op_map
(
	.sclk 						(sclk 						),
	.rst_n						(rst_n						),
	.s_config_tvalid			(s_config_tvalid			),
	.s_config_tdata 			(s_config_tdata 			),
	.s_data_tvalid  			(s_data_tvalid  			),
	.s_data_tdata   			(s_data_tdata   			),
	.s_data_tlast   			(s_data_tlast   			),
	.RE_DATA        			(RE_DATA        			),
	.IM_DATA        			(IM_DATA        			),
	.DATA_EN        			(DATA_EN        			),
	.DATA_LAST      			(DATA_LAST      			),
	.fft_config_en  			(fft_config_en  			),
	.NFFT						(NFFT						),
	.CP_LEN						(CP_LEN						),
	.SCALE_SCH					(SCALE_SCH					),
	.FWD_INV					(FWD_INV					),
	.NOFDM						(NOFDM						) 
	
);
/*===================================================================
====================================================================*/	
fft_sig_comp fft_sig_comp
(
	.sclk	         		  	(sclk	         		  	),
	.rst_n         		      	(rst_n         		      	),
	.P1_EN        		      	(DATA_EN                    ),
	.RE_P1_DATA   		      	(RE_DATA   	              	),
	.IM_P1_DATA   		      	(IM_DATA   	              	),
	.data_LAST    		      	(DATA_LAST    	          	),
	.fft_config_en		      	(fft_config_en		      	),
	.NFFT         		      	(NFFT         		      	),
	.CP_LEN		 		      	(CP_LEN		 		      	),
	.SCALE_SCH	 		      	(SCALE_SCH	 		      	),
	.FWD_INV		 		  	(FWD_INV		 		  	),
	.s_axis_config_tdata      	(s_axis_config_tdata        ),
	.s_axis_config_tvalid     	(s_axis_config_tvalid  		),
	.s_axis_data_tdata        	(s_axis_data_tdata          ),
	.s_axis_data_tvalid       	(s_axis_data_tvalid         ),
	.s_axis_data_tlast        	(s_axis_data_tlast          ) 
	                                   
);


tx_xfft_0 tx_xfft_0 (
  .aclk							(sclk						),   
  .aresetn						(rst_n						),   
  .s_axis_config_tdata			(s_axis_config_tdata		),   
  .s_axis_config_tvalid			(s_axis_config_tvalid		),   
  .s_axis_config_tready			(sim_config_tready			),   
  .s_axis_data_tdata			(s_axis_data_tdata			),   
  .s_axis_data_tvalid			(s_axis_data_tvalid			),   
  .s_axis_data_tready			(s_axis_data_tready1 		),   
  .s_axis_data_tlast 			(s_axis_data_tlast			),   
  
  .m_axis_data_tdata			(m_axis_data_tdata_store	),   
  .m_axis_data_tuser			(m_axis_data_tuser			),   
  .m_axis_data_tvalid			(m_axis_data_tvalid_store	),   
  .m_axis_data_tready			(m_axis_data_tready			),   
  .m_axis_data_tlast			(m_axis_data_tlast_store	),   
  .m_axis_status_tdata			(m_axis_status_tdata		),   
  
  .m_axis_status_tvalid		  	(m_axis_status_tvalid		),
  .m_axis_status_tready		  	(m_axis_status_tready		),
  .event_frame_started		  	(event_frame_started		),
  .event_tlast_unexpected	  	(event_tlast_unexpected		),
  .event_tlast_missing		  	(event_tlast_missing		),
  .event_fft_overflow		  	(event_fft_overflow			),
  .event_status_channel_halt  	(event_status_channel_halt	),
  .event_data_in_channel_halt 	(event_data_in_channel_halt	),
  .event_data_out_channel_halt	(event_data_out_channel_halt) 
);



endmodule 

ifft_op_map模块:

`timescale 1ns / 1ps
// *********************************************************************************
// Project Name : OSXXXX
// Author       : zhangningning
// Email        : [email protected]
// Website      : 
// Module Name  : ifft_op_map.v
// Create Time  : 2020-06-04 16:49:13
// Editor       : sublime text3, tab size (4)
// CopyRight(c) : All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date             By              Version                 Change Description
// -----------------------------------------------------------------------
// XXXX       zhangningning          1.0                        Original
//  
// *********************************************************************************

module ifft_op_map
(
	input 				sclk			,
	input 				rst_n			,
	input 				s_config_tvalid	,
	input 		[29:0]	s_config_tdata	,	
	input 				s_data_tvalid	,
	input 		[23:0]	s_data_tdata	,
	input 				s_data_tlast	,	
	output	reg [11:0]	RE_DATA			,
	output	reg [11:0]	IM_DATA			,
	output	reg 		DATA_EN			,
	output	reg 		DATA_LAST 		,	
	output	reg 		fft_config_en 	,
	output	reg [ 4:0] 	NFFT 			,
	output	reg [12:0] 	CP_LEN 			,
	output	reg [13:0] 	SCALE_SCH 		,
	output	reg 		FWD_INV 		,
	output	reg [ 9:0]	NOFDM
	
);
/*===================================================================
====================================================================*/
wire 			[ 1:0]	fft_mode 		;
wire 			[ 9:0]	nofdm 			;
wire 			[ 2:0]	cp_pro 			;
wire 			[13:0]	scale 			;
wire      				inv 			;

reg  			[ 2:0]  PRO 			; 
reg  			[ 1:0]  MODE			;
reg  			[ 1:0]  MODE1			;
reg  			[ 9:0]  NOFDM1			;
reg  			[ 2:0]  PRO1			; 
reg  			[13:0]  SCALE_SCH1		;
reg  			        FWD_INV1		;
reg  			        fft_config_en1	;		

assign  	fft_mode	 	=  		s_config_tdata[1:0];
assign  	nofdm   	 	=  		s_config_tdata[11:2];
assign  	cp_pro  	 	=  		s_config_tdata[14:12];
assign  	scale   	 	=  		s_config_tdata[28:15];
assign  	inv     	 	=  		s_config_tdata[29];

always @(posedge sclk)
begin
	if(rst_n == 1'b0)begin
	    MODE1		   		<=   	2'b00;
		NOFDM1		   		<=   	10'd0;
		PRO1		   		<=   	3'b000; 
		SCALE_SCH1      	<=   	14'd0;
		FWD_INV1        	<=   	1'b0;

	end else if(s_config_tvalid)begin		
	    MODE1		   		<=   	fft_mode;
		NOFDM1         		<=   	nofdm;
		PRO1		   		<=   	cp_pro; 
		SCALE_SCH1     		<=   	scale;
		FWD_INV1       		<=   	inv;
	end 
end 

always@(posedge sclk)begin
        MODE		   		<=   	MODE1;
	    NOFDM          		<=   	NOFDM1;
		PRO		       		<=   	PRO1; 
		SCALE_SCH      		<=  	SCALE_SCH1;
		FWD_INV        		<=  	FWD_INV1;
end
 
always @(posedge sclk)begin
		RE_DATA 	   		<=   	s_data_tdata[23:12];
		IM_DATA 	   		<=   	s_data_tdata[11:0]; 
		DATA_EN 	   		<=   	s_data_tvalid;
		DATA_LAST 	   		<=   	s_data_tlast;
end 

always@(posedge sclk)
begin   
      fft_config_en1  		<=   	s_config_tvalid;
	  fft_config_en   		<=   	fft_config_en1;
end	  

always @(posedge sclk)
	if(PRO1 == 3'd0)
		CP_LEN 				<=  	13'd0;
	else if(PRO1 == 3'd1)//1/32	
		CP_LEN				<=  	13'd32;
	else if(PRO1 == 3'd2)//1/16	
		CP_LEN				<=  	13'd64;
	else if(PRO1 == 3'd3)//1/8;	
		CP_LEN				<=  	13'd128;
	else if(PRO1 == 3'd4)//1/4;	
		CP_LEN				<=  	13'd256;
	else 	
		CP_LEN				<=  	13'd0;	

always @(posedge sclk)
	case(MODE1)
		2'b00:	NFFT		<=  	5'b01010;//1k
		2'b01:	NFFT		<=  	5'b01011;//2k
		2'b10:	NFFT		<=  	5'b01100;//4k
		2'b11:	NFFT		<=  	5'b01101;//8k
		default:NFFT 		<=  	5'b00000;
	endcase 

endmodule

fft_sig_comp模块:

`timescale 1ns / 1ps
// *********************************************************************************
// Project Name : OSXXXX
// Author       : zhangningning
// Email        : [email protected]
// Website      : 
// Module Name  : fft_sig_comp.v
// Create Time  : 2020-06-04 16:55:01
// Editor       : sublime text3, tab size (4)
// CopyRight(c) : All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date             By              Version                 Change Description
// -----------------------------------------------------------------------
// XXXX       zhangningning          1.0                        Original
//  
// *********************************************************************************

module fft_sig_comp(
	input 				sclk 				,
	input 				rst_n				,
	input 				P1_EN				,
	input 		[11:0]	RE_P1_DATA			,
	input 		[11:0]	IM_P1_DATA			,
	input 				data_LAST			,
	input 				fft_config_en		,
	input 		[ 4:0] 	NFFT 				,
	input 		[12:0] 	CP_LEN 				,
	input 		[13:0] 	SCALE_SCH 			,
	input 				FWD_INV 			,
	output  	[39:0] 	s_axis_config_tdata	,
	output  			s_axis_config_tvalid,
	output  	[31:0] 	s_axis_data_tdata  	,
	output  			s_axis_data_tvalid  ,
	output  			s_axis_data_tlast           
	                                   
);
 
//========================================================================================\
//**************Define Parameter and  Internal Signals**********************************
//========================================================================================/

reg 			[39:0] 	s_axis_config_tdata_reg	;
reg 					s_axis_config_tvalid_reg;                            
reg 			[31:0] 	s_axis_data_tdata_reg  	;
reg 					s_axis_data_tvalid_reg  ;
reg 					s_axis_data_tlast_reg   ;
 
//========================================================================================\
//**************     Main      Code        **********************************
//========================================================================================/
assign 	s_axis_config_tdata  	= 	s_axis_config_tdata_reg;
assign 	s_axis_config_tvalid 	= 	s_axis_config_tvalid_reg;
assign 	s_axis_data_tdata    	= 	s_axis_data_tdata_reg;
assign 	s_axis_data_tvalid   	= 	s_axis_data_tvalid_reg;
assign 	s_axis_data_tlast    	= 	s_axis_data_tlast_reg;


always @(posedge sclk)
	s_axis_config_tdata_reg 	<=  {1'b0,SCALE_SCH,FWD_INV,3'b000,CP_LEN,3'b000,NFFT};

always @(posedge sclk)
	s_axis_config_tvalid_reg 	<=  fft_config_en;

always @(posedge sclk)
	s_axis_data_tdata_reg 		<=  {4'b0000,IM_P1_DATA,4'b0000,RE_P1_DATA};

always @(posedge sclk)
	s_axis_data_tvalid_reg 		<=  P1_EN;

always @(posedge sclk)
	s_axis_data_tlast_reg 		<=  data_LAST;


endmodule

上面的代码联系之前的理论部分,便可以学会FFT的重配置,包括经过这篇博客的学习要学会其他Xilinx常用IP的重配置,比如:DDS、FIR等等。

FPGA测试代码

tb_tx_ifft_op模块:

`timescale 1ns / 1ps


module tb_tx_ifft_op;

reg         sclk                    ;
reg         rst_n                   ;
    
reg         cfg_vld                 ;
        
reg         s_config_tvalid         ;
reg [29:0]  s_config_tdata          ;
reg         s_data_tvalid;
reg         s_data_tvalid_delay,s_data_tvalid_delay1;
reg [23:0]  s_data_tdata; 

reg         s_data_tlast;
reg         m_axis_data_tready         ;
reg         m_axis_status_tready       ;

wire       s_axis_config_tready        ;
wire       s_axis_data_tready          ;
wire [31:0]m_axis_data_tdata           ;
wire [23: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       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 ; 
wire [9:0] NOFDM                       ;
wire       p1_start                    ;

reg  [1:0] fft_mode                    ;
reg  [9:0] nofdm                       ;
reg  [2:0] cp_pro                      ;
reg  [13:0]scale                       ;
reg        inv                         ;

reg  [11:0]re_data                     ;
reg  [11:0]im_data                     ;
    


always @(posedge sclk)
begin
    s_data_tvalid_delay  <=  s_data_tvalid;
    s_data_tvalid_delay1 <=  s_data_tvalid_delay;
end

initial
begin
    #0;
    sclk =1'b0;
    rst_n=1'b0;
    cfg_vld = 1'b0;
    m_axis_data_tready = 1'b1;
    m_axis_status_tready = 1'b1;
    s_config_tvalid = 1'b0;
    s_config_tdata = 30'd0;


    fft_mode=2'b01;
    nofdm = 10'd1;
    cp_pro = 3'b000;
    scale = 14'b01_0110_1010_1010;
    //scale = 14'd0;
    inv = 1'b0; 

    s_data_tvalid = 1'b0;
    s_data_tlast = 1'b0;

    repeat(1) @(posedge sclk)#1;
    cfg_vld = 1'b1;
    repeat(1) @(posedge sclk)#1;
    cfg_vld = 1'b0;
    repeat(30) @(posedge sclk)#1;
    rst_n =1'b1;
    repeat(30) @(posedge sclk)#1;
    s_config_tvalid = 1'b1;
    s_config_tdata = {inv,scale,cp_pro,nofdm,fft_mode};
    repeat(1) @(posedge sclk)#1;
    s_config_tvalid = 1'b0;
    repeat(300) @(posedge sclk)#1;
    s_data_tvalid = 1'b1;
    s_data_tlast = 1'b0;
    repeat(1) @(posedge sclk)#1;
    s_config_tvalid = 1'b0;
    repeat(2047) @(posedge sclk)#1;
    s_data_tlast = 1'b0;
    s_data_tvalid = 1'b0;//mark for test
    repeat(1) @(posedge sclk)#1;
    s_data_tlast = 1'b1;
    repeat(1) @(posedge sclk)#1;
    s_data_tlast = 1'b0;
end
always #10 sclk = ~sclk;

//=========================================================================
//   input
//========================================================================= 
    integer fid1;
    integer fid2;
    initial  
    begin
        fid2 = $fopen("IM_DATA.txt","r");
        fid1 = $fopen("RE_DATA.txt","r");
    end

    always@(posedge sclk)
    begin
        if(s_data_tvalid)begin
            $fscanf(fid1,"%d",re_data);
            $fscanf(fid2,"%d",im_data); 
        end    
    end
 
    always @(posedge sclk)
    begin
        s_data_tdata <=  {re_data,im_data}; 
    end 
//=========================================================================
// output
//========================================================================= 
wire signed [11:0] re_out;
wire signed [11:0] im_out;

assign re_out = {m_axis_data_tdata[15],m_axis_data_tdata[10:0]};    
assign im_out = {m_axis_data_tdata[31],m_axis_data_tdata[26:16]};   
    
    integer fid3;
    initial 
    begin
        fid3 = $fopen("re_fft_data.txt","w");
    end
    
    always@(posedge sclk)
    begin
        if(m_axis_data_tvalid)
            $fwrite(fid3,"%d\n",re_out);
    end

    integer fid4;
    initial 
    begin
        fid4 = $fopen("im_fft_data.txt","w");
    end
    
    always@(posedge sclk)
    begin
        if(m_axis_data_tvalid)
            $fwrite(fid4,"%d\n",im_out);
    end

tx_ifft_op uut
(
    .sclk                       (sclk                       ),
    .rst_n                      (rst_n                      ),
    .cfg_vld                    (cfg_vld                    ),
    .p1_start                   (p1_start                   ),
    .s_config_tvalid            (s_config_tvalid            ),
    .s_config_tdata             (s_config_tdata             ),
    .s_data_tvalid              (s_data_tvalid_delay1       ),
    .s_data_tdata               (s_data_tdata               ),  
    .s_data_tlast               (s_data_tlast               ),
    .m_axis_data_tready         (m_axis_data_tready         ),
    .m_axis_status_tready       (m_axis_status_tready       ),
    .s_axis_config_tready       (s_axis_config_tready       ), 
    .s_axis_data_tready         (s_axis_data_tready         ), 
    .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_tlast          (m_axis_data_tlast          ),
    .m_axis_status_tdata        (m_axis_status_tdata        ),
    .m_axis_status_tvalid       (m_axis_status_tvalid       ),
    .event_frame_started        (event_frame_started        ),
    .event_tlast_unexpected     (event_tlast_unexpected     ),
    .event_tlast_missing        (event_tlast_missing        ),
    .event_fft_overflow         (event_fft_overflow         ),
    .event_status_channel_halt  (event_status_channel_halt  ),
    .event_data_in_channel_halt (event_data_in_channel_halt ),
    .event_data_out_channel_halt(event_data_out_channel_halt),
    .NOFDM                      (NOFDM                      )   
);

endmodule

MATLAB验证

有关MATLAB对FFT IP核调用的部分,我们上篇博客已经进行了详细的讲解,在这里我们将给出MATLAB与Modelsim生成的数据相对比的代码:

clc;
clear all;
load fft_data_com.mat
sim_options = struct(...
    'MODE',        '2k'   ...         %fft模式  1k 2k 4k 8k
    );
tx_nFrame=1;
%------------------------------------------------------------------------------
% Parameters Definition
%------------------------------------------------------------------------------
switch sim_options.MODE
    case '1k'
        NFFT   = 1024;       % FFT number of points
    case '2k'
        NFFT   = 2048;       % FFT number of points
    case '4k'
        NFFT   = 4096;       % FFT number of points
    case '8k'
        NFFT   = 8192;       % FFT number of points
    otherwise, error('sim_options UNKNOWN MODE');
end
fid1 = fopen('re_fft_data.txt','r');
real_data_sim = fscanf(fid1,'%d');

fid1 = fopen('im_fft_data.txt','r');
imag_data_sim = fscanf(fid1,'%d');

start_Idx = NFFT*(tx_nFrame - 1);
if(isempty(real_data_sim))
    real_data_result = 0;
    imag_data_result = 0;
else
    real_data_result = sum(abs(real(fft_data_com(start_Idx+1:start_Idx+NFFT)) - real_data_sim(start_Idx+1:start_Idx+NFFT)));
    imag_data_result = sum(abs(imag(fft_data_com(start_Idx+1:start_Idx+NFFT)) - imag_data_sim(start_Idx+1:start_Idx+NFFT)));
end
a = real_data_result + imag_data_result

运行结果如下:
基于FPGA的FFT算法实现(2)_第10张图片
从上面结果可以验证我们实验的正确性,从而说明MATLAB与VIVADO输出的数据完全相同,两者相互验证了实验的正确性。

参考文献

[1]、电子发烧友学院

总结

创作不易,认为文章有帮助的同学们可以关注、点赞、转发支持。为行业贡献及其微小的一部分。或者对文章有什么看法或者需要更近一步交流的同学,可以加入下面的群:
在这里插入图片描述

你可能感兴趣的:(FPGA,fpga)