说明:通过对比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。
★★★如有错误欢迎指导!!!