Xilinx FFT IP核 Verilog代码实现

本文介绍了如何使用Xilinx FFT IP核在FPGA上实现复数FFT,通过对比MATLAB中的1KHz采样率、1024点以及2.5GHz采样率、8192点的FFT,详细阐述了Verilog代码的实现过程,并展示了ILA调试结果。在1KHz采样率下,1024点FFT捕获了100Hz、200Hz和300Hz的频率成分;而在2.5GHz采样率下,8192点FFT则显示了100Hz、350Hz和500Hz的频率成分。
摘要由CSDN通过智能技术生成

说明:通过对比Matlab实践来运用Xilinx FFT IP核实现复数的FFT,Verilog代码实现,ila进行调试。


一、FPGA实现FFT

1、1KHz采样率、1024点FFT

Matlab产生原实信号 s,将1024点的数据写入FPGA的ROM
采样率为1000Hz,信号为100Hz、200Hz、300Hz叠加,FFT点数为1024。

clc
clear all;
row=3;
column=1;
N=1024;
fs=1000;                    %采样间隔为1/1000
t=(1:1024)/fs;               %时间轴
f0=100;                     %载频频率
f1=200;                     %载频频率
f2=300;                     %载频频率
y1=100*exp(1i*2*pi*f0*t);   
y2=100*exp(1i*2*pi*f1*t);
y3=100*exp(1i*2*pi*f2*t);
y=y1+y2+y3;                 %信号产生
real_y = int32(real(y));    %实部
imag_y = int32(imag(y));    %虚部
a=[real_y;imag_y];
s = int32(bitshift(imag_y,16)+real_y);%将实部和虚部拼接为32bit(实部16bit+虚部16bit)
s = s';                     %此数据输出到FPGA

%绘制原信号
subplot(row,column,1);
plot(t,y);
title("时域");
xlabel('t');
ylabel('y');
grid on;
%FFT
f=(0:N-1)*(fs/N);
fft_y=fft(y,N);             %FFT
abs_fft_y=abs(fft(y,N));    %FFT取绝对值

subplot(row,column,2);      %FFT波形
plot(f,fft_y);
title("FFT");
xlabel('t');
ylabel('y');
grid on;

subplot(row,column,3);      %FFT取绝对值波形
plot(f,abs_fft_y);
title("FFT取绝对值");
xlabel('t');
ylabel('y');
grid on;

在这里插入图片描述

数据通过coe文件写入ROM:
参考博文:Xilinx_ROM_IP核的使用
在这里插入图片描述

FPGA端IP核的配置:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

FPGA端代码:
从ROM中读取1024点,送入FFT IP核做快速傅里叶变换。

module top
#
(
	parameter	ROM_DEPTH = 2048,
	parameter	ROM_WIDTH = 32
)
(
	input		clk_100m

);
/********************************************************
*	时钟
*********************************************************/
wire		clk_25m;
wire		clk_locked;
clk_wiz_0 clk
(
    .clk_out1(clk_25m),
    .locked(clk_locked),  

    .clk_in1(clk_100m)
);     
/********************************************************
*	复位
*********************************************************/	
wire		RSTn;
assign		RSTn = clk_locked;

/********************************************************
*	ROM
*********************************************************/		
parameter 	IDLE  		= 2'b00,
			FFT_STATE   = 2'b01;
reg[1:0]	STATE		= 2'b00;

wire				rom_ena;
wire[9:0]			rom_addra;
(*dont_touch = "TRUE"*)wire[ROM_WIDTH-1:0]	rom_douta;

reg			rom_ena_r;
reg[9:0]	rom_addra_r;

assign		rom_ena 	= rom_ena_r;
assign		rom_addra   = rom_addra_r;

reg[15:0]	rom_cnt;
//ROM深度为1024、宽度为ROM_WIDTH
always@(posedge clk_100m or negedge RSTn)
begin
	if(!RSTn)
	begin
		rom_cnt		<= 16'd0;
		rom_ena_r 	<= 1'b0;
		rom_addra_r	<= 16'h00;
		STATE		<= 2'b00;
	end
	else
	begin
		case(STATE)
			IDLE:
			begin
				STATE		<= (s_axis_data_tready==1)?FFT_STATE:IDLE;
			end
			FFT_STATE:
			begin
				rom_ena_r	<= (rom_cnt<=(ROM_DEPTH/2))?1'b1:1'b0;//2048个时钟里读出1024个点,为了每1024个点分开 不连续
				rom_cnt		<= (rom_cnt==ROM_DEPTH-1)?16'd0:rom_cnt+16'd1;
				rom_addra_r	<= rom_cnt;
				STATE		<= (rom_cnt==ROM_DEPTH-1)?IDLE:FFT_STATE;
			end
			default:
			begin
				STATE		<= IDLE;
			end
		endcase	
	end
end
//画出频域轴
reg	[9:0]	f_cnt;
wire[9:0]	f;
assign		f = (f_cnt*1000)/1024;
always@(posedge clk_100m or negedge RSTn)
begin
	if(!RSTn)
	begin
		f_cnt <= 10'd0;
	end
	else
	begin
		f_cnt <= (m_axis_data_tvalid==1'b1)?f_cnt + 1'd1:10'd0;
	end
end



//提取时域的实部和虚部
(*dont_touch = "TRUE"*)wire [15:0]	real_data;	
(*dont_touch = "TRUE"*)wire [15:0]	imag_data;	
assign	imag_data = (rom_douta[15]==1)?(rom_douta[31:16]+1):rom_douta[31:16];//消除移位时符号位的影响
assign	real_data = rom_douta[15:0];
blk_mem_gen_0 rom (
  .clka(clk_100m),    // input wire clka
  .ena(rom_ena),      // input wire ena
  .addra(rom_addra),  // input wire [8 : 0] addra
  .douta(rom_douta)  // output wire [31 : 0] douta
);	

/********************************************************
*	fft
*********************************************************/	
wire		s_axis_data_tvalid;	
wire		s_axis_data_tready;
wire		s_axis_data_tlast;
assign		s_axis_data_tlast = (rom_cnt==(ROM_DEPTH/2))?1'b1:1'b0;
assign		s_axis_data_tvalid = rom_ena;

(*dont_touch = "TRUE"*)wire[31:0]	m_axis_data_tdata;
(*dont_touch = "TRUE"*)wire			m_axis_data_tvalid;
(*dont_touch = "TRUE"*)wire			m_axis_data_tlast;

wire[31:0]	abs_m_axis_data_tdata;
wire[15:0]	abs_real; 
wire[15:0]	abs_imag; 
wire[31:0]	abs_fft;

//提取FFT的实部和虚部 并求模值
assign 	abs_imag = m_axis_data_tdata[31]?(-m_axis_data_tdata[31:16]):m_axis_data_tdata[31:16];
assign 	abs_real = m_axis_data_tdata[15]?(-m_axis_data_tdata[15:0]):m_axis_data_tdata[15:0];
assign	abs_fft  = abs_real + abs_imag;


xfft_0 fft (
  .aclk(clk_100m),                                              // input wire aclk
  .aresetn(RSTn),                                        // input wire aresetn
  .s_axis_config_tdata(16'd1),                				// input wire [15 : 0] s_axis_config_tdata
  .s_axis_config_tvalid(1),              					// input wire s_axis_config_tvalid
  .s_axis_config_tready(s_axis_config_tready),              // output wire s_axis_config_tready
  
  .s_axis_data_tdata(rom_douta),                    		// 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 [31 : 0] m_axis_data_tdata
  .m_axis_data_tvalid(m_axis_data_tvalid),                  // output wire m_axis_data_tvalid
  .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_data_in_channel_halt(event_data_in_channel_halt)  // output wire event_data_in_channel_halt
);
/********************************************************
*	ila
*********************************************************/		
ila_0 ila (
	.clk(clk_100m), // input wire clk


	.probe0(clk_25m), // input wire [0:0]  probe0  
	.probe1(rom_ena), // input wire [0:0]  probe1 
	.probe2(rom_addra), // input wire [9:0]  probe2 
	.probe3(rom_douta), // input wire [31:0]  probe3
	.probe4(real_data), // input wire [15:0]  probe4 
	.probe5(imag_data), // input wire [15:0]  probe5
	.probe6(m_axis_data_tdata), // input wire [31:0]  probe6 
	.probe7(m_axis_data_tvalid), // input wire [0:0]  probe7 
	.probe8(m_axis_data_tlast), // input wire [0:0]  probe8 
	.probe9(s_axis_data_tvalid), // input wire [0:0]  probe9 
	.probe10(s_axis_data_tready), // input wire [0:0]  probe10 
	.probe11(s_axis_data_tlast), // input wire [0:0]  probe11
	.probe12(abs_m_axis_data_tdata), // input wire [31:0]  probe12
	.probe13(f) // input wire [9:0]  probe13
);	
	
endmodule

FPGA FFT波形
在这里插入图片描述

采样率为1000Hz,1024个点,每个点对应的频率为1000/1024。

观察图中f变量(可以表示频率的横轴),三个峰值对应的频率为100Hz,200Hz,300Hz。
在这里插入图片描述

2、2.5GHz采样率、8192点FFT

Matlab产生原实信号 s,将8192点的数据写入FPGA的ROM

采样率为2.5GHz,信号为100Hz、350Hz、500Hz叠加,FFT点数为8192。

clc
clear all;
close all;
row=3;
column=1;
N=8192;
fs=2.5e9;                    %采样间隔为1/1000
t=(1:N)/fs;               %时间轴
f0=100e6;                     %载频频率
f1=350e6;                     %载频频率
f2=500e6;                     %载频频率
y1=20*exp(1i*2*pi*f0*t);   
y2=20*exp(1i*2*pi*f1*t);
y3=20*exp(1i*2*pi*f2*t);
y=y1+y2+y3;                 %信号产生
real_y = int32(real(y));    %实部
imag_y = int32(imag(y));    %虚部
a=[real_y;imag_y];
s = int32(bitshift(imag_y,16)+real_y);%将实部和虚部拼接为32bit(实部16bit+虚部16bit)
s = s';                     %此数据输出到FPGA

%绘制原信号
subplot(row,column,1);
plot(t,y);
title("时域");
xlabel('t');
ylabel('y');
grid on;
%FFT
f=(0:N-1)*(fs/N);
fft_y=fft(y,N);             %FFT
abs_fft_y=abs(fft(y,N));    %FFT取绝对值

subplot(row,column,2);      %FFT波形
plot(f,fft_y);
title("FFT");
xlabel('t');
ylabel('y');
grid on;

subplot(row,column,3);      %FFT取绝对值波形
plot(f,abs_fft_y);
title("FFT取绝对值");
xlabel('t');
ylabel('y');
grid on;

在这里插入图片描述

FPGA端verilog代码:

module top
#
(
	parameter	ROM_DEPTH = 16384,
	parameter	ROM_ADDR_WIDTH = 13,
	parameter	ROM_WIDTH = 32
)
(
	input		clk_100m

);
/********************************************************
*	时钟
*********************************************************/
wire		clk_25m;
wire		clk_locked;
clk_wiz_0 clk
(
    .clk_out1(clk_25m),
    .locked(clk_locked),  

    .clk_in1(clk_100m)
);     
/********************************************************
*	复位
*********************************************************/	
wire		RSTn;
assign		RSTn = clk_locked;

/********************************************************
*	ROM
*********************************************************/		
parameter 	IDLE  		= 2'b00,
			FFT_STATE   = 2'b01;
reg[1:0]	STATE		= 2'b00;

wire								rom_ena;
wire[ROM_ADDR_WIDTH-1:0]			rom_addra;
(*dont_touch = "TRUE"*)wire[ROM_WIDTH-1:0]	rom_douta;

reg			rom_ena_r;
reg[ROM_ADDR_WIDTH-1:0]				rom_addra_r;

assign		rom_ena 	= rom_ena_r;
assign		rom_addra   = rom_addra_r;

reg[15:0]	rom_cnt;
//ROM深度为8192、宽度为ROM_WIDTH
always@(posedge clk_100m or negedge RSTn)
begin
	if(!RSTn)
	begin
		rom_cnt		<= 16'd0;
		rom_ena_r 	<= 1'b0;
		rom_addra_r	<= 16'h00;
		STATE		<= 2'b00;
	end
	else
	begin
		case(STATE)
			IDLE:
			begin
				STATE		<= (s_axis_data_tready==1)?FFT_STATE:IDLE;
			end
			FFT_STATE:
			begin
				rom_ena_r	<= (rom_cnt<=(ROM_DEPTH/2))?1'b1:1'b0;//16384个时钟里读出8192个点
				rom_cnt		<= (rom_cnt==ROM_DEPTH-1)?16'd0:rom_cnt+16'd1;
				rom_addra_r	<= rom_cnt;
				STATE		<= (rom_cnt==ROM_DEPTH-1)?IDLE:FFT_STATE;
			end
			default:
			begin
				STATE		<= IDLE;
			end
		endcase	
	end
end
//画出频域轴
reg	[47:0]	f_cnt;
wire[47:0]	f;
assign		f = (f_cnt*32'd25_00_000_000)/8192;
always@(posedge clk_100m or negedge RSTn)
begin
	if(!RSTn)
	begin
		f_cnt <= 10'd0;
	end
	else
	begin
		f_cnt <= (m_axis_data_tvalid==1'b1)?f_cnt + 1'd1:10'd0;
	end
end



//提取时域数据的实部和虚部
(*dont_touch = "TRUE"*)wire [15:0]	real_data;	
(*dont_touch = "TRUE"*)wire [15:0]	imag_data;	
assign	imag_data = (rom_douta[15]==1)?(rom_douta[31:16]+1):rom_douta[31:16];//消除移位时符号位的影响
assign	real_data = rom_douta[15:0];
blk_mem_gen_0 rom (
  .clka(clk_100m),    // input wire clka
  .ena(rom_ena),      // input wire ena
  .addra(rom_addra),  // input wire [8 : 0] addra
  .douta(rom_douta)  // output wire [31 : 0] douta
);	

/********************************************************
*	fft
*********************************************************/	
wire		s_axis_data_tvalid;	
wire		s_axis_data_tready;
wire		s_axis_data_tlast;
assign		s_axis_data_tlast = (rom_cnt==(ROM_DEPTH/2))?1'b1:1'b0;
assign		s_axis_data_tvalid = rom_ena;

(*dont_touch = "TRUE"*)wire[31:0]	m_axis_data_tdata;
(*dont_touch = "TRUE"*)wire			m_axis_data_tvalid;
(*dont_touch = "TRUE"*)wire			m_axis_data_tlast;

wire[31:0]	abs_m_axis_data_tdata;
wire[15:0]	abs_real; 
wire[15:0]	abs_imag; 
wire[31:0]	abs_fft;
//提取FFT的实部和虚部
assign 	abs_imag = m_axis_data_tdata[31]?(-m_axis_data_tdata[31:16]):m_axis_data_tdata[31:16];
assign 	abs_real = m_axis_data_tdata[15]?(-m_axis_data_tdata[15:0]):m_axis_data_tdata[15:0];
assign	abs_fft  = abs_real + abs_imag;


xfft_0 fft (
  .aclk(clk_100m),                                              // input wire aclk
  .aresetn(RSTn),                                        // input wire aresetn
  .s_axis_config_tdata(16'd1),                				// input wire [15 : 0] s_axis_config_tdata
  .s_axis_config_tvalid(1),              					// input wire s_axis_config_tvalid
  .s_axis_config_tready(s_axis_config_tready),              // output wire s_axis_config_tready
  
  .s_axis_data_tdata(rom_douta),                    		// 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 [31 : 0] m_axis_data_tdata
  .m_axis_data_tvalid(m_axis_data_tvalid),                  // output wire m_axis_data_tvalid
  .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_data_in_channel_halt(event_data_in_channel_halt)  // output wire event_data_in_channel_halt
);
/********************************************************
*	ila
*********************************************************/		
ila_0 ila (
	.clk(clk_100m), // input wire clk


	.probe0(clk_25m), // input wire [0:0]  probe0  
	.probe1(rom_ena), // input wire [0:0]  probe1 
	.probe2(rom_addra), // input wire [9:0]  probe2 
	.probe3(rom_douta), // input wire [31:0]  probe3
	.probe4(real_data), // input wire [15:0]  probe4 
	.probe5(imag_data), // input wire [15:0]  probe5
	.probe6(m_axis_data_tdata), // input wire [31:0]  probe6 
	.probe7(m_axis_data_tvalid), // input wire [0:0]  probe7 
	.probe8(m_axis_data_tlast), // input wire [0:0]  probe8 
	.probe9(s_axis_data_tvalid), // input wire [0:0]  probe9 
	.probe10(s_axis_data_tready), // input wire [0:0]  probe10 
	.probe11(s_axis_data_tlast), // input wire [0:0]  probe11
	.probe12(abs_fft), // input wire [31:0]  probe12
	.probe13(f) // input wire [9:0]  probe13
);	
	
endmodule


在这里插入图片描述

观察图中f变量(可以表示频率的横轴),三个峰值对应的频率为100Hz,350Hz,500Hz。
在这里插入图片描述

★★★如有错误欢迎指导!!!

  • 2
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 16
    评论
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

数字硬鉴

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

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

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

打赏作者

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

抵扣说明:

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

余额充值