工具:vivado2018.3,modelsim10.6d
场景:在进行数据进行频谱分析的时候,使用FPGA来完成FFT的计算可以加快数据的计算速度。
下面使用仿真完成DDS产生的数据的FFT以及IFFT。原始数据使用DDSIP产生,通过IP产生的波形数据直接输入到FFT进行傅里叶正变换。然后再使用FFT对数据进行IFFT傅里叶逆变换还原波形数据。过程中完成了fftshift(将零频分量搬移到频谱中心),以及使用cordic计算平方根的过程。
FFT端口说明
端口名称 | 方向 | 描述 |
aclk | I | 时钟 |
aresetn | I | 复位 |
s_axis_config_tvalid | I | 配置有效 |
s_axis_config_tready | O | 配置准备好 |
s_axis_config_tdata | I | 针对配置通道的TDATA。携带配置信息: CP_LEN、FWD/INV、NFFT和SCALE_SCH。关于FWD/INV为FFTIP的方式,1为FFT,0为IFFT。当选择FFT点数可配置时NFFT为点数。SCALE_SCH为缩放计划,以确保在计算过程中不溢出。对于不同的IO架构有不同的规则。 |
s_axis_data_tdata | I | 输入数据 |
s_axis_data_tvalid | I | 数据有效 |
s_axis_data_tready | O | 数据准备好 |
s_axis_data_tlast | I | 输入的一帧最后一个数据 |
m_axis_data_tvalid | O | 输出数据有效 |
m_axis_data_tready | I | 输出数据准备好 |
m_axis_data_tdata | O | 输出数据 |
m_axis_data_tuser | O | 输出数据状态参数。包含XK_INDEX, OVFLO, and BLK_EXP. XK_INDEX输出数据索引 OVFLO溢出标志 |
m_axis_data_tlast | O | 输出的最后一个数据。 |
m_axis_status_tvalid | O | 状态有效 |
m_axis_status_tready | I | 状态准备好 |
m_axis_status_tdata | O | 状态数据。包含每一帧的状态休息。 |
event_frame_started | O | 事件帧开始 |
event_tlast_unexpected | O | 当核心在不是帧中最后一个的数据样本上看到s_axis_data_tlast高时有效 |
event_tlast_missing | O | 当一帧的最后一个数据样本上的s_axis_data_tlast为低时断言。 |
event_fft_overflow | O | 当正在从数据输出通道卸载的数据样本中出现溢出时断言。仅在溢出是一个有效的选项时才会出现。 |
event_data_in_channel_halt | O | 当核心从数据输入通道请求数据而没有数据可用时断言。 |
event_data_out_channel_halt | O | 当核心从数据输入通道请求数据而没有数据可用时断言。 |
event_status_in_channel_halt | O | 当核心试图将数据写入状态通道而无法这样做时,将被断言。仅在非实时模式下出现。 |
FFT IP的配置界面
Channels:从1到12中选择通道数。三种Burst I/O架构均可使用多通道操作。对于浮点格式,通道必须为1。
Transform Length: 选择一次处理所需点的大小。
The Pipelined Streaming I/O:运行连续处理。
Radix-4,Radix-2,Radix-2 Lite Burst I/O。几种不同的实现方式,延迟依次增大,资源依次减少。
Run time configurable Transform Length:允许运行的过程中改变点数。勾选此后注意,s_axis_config_tdata的字段的意义。
DataFormat:选择输入和输出数据样本是否为定点格式,或在IEEE-754单精度(32位)浮点格式中。浮点格式不是当核心处于多通道配置时可用。
Scaling Options:
Scaling:用户自定义缩放格式。注意s_axis_config_tdata的字段需要配置每个阶段的缩放信息。
Block Floating-Point:由核心来判断所需的缩放程度,以实现对可用动态范围的最佳利用,并以块指数的形式报告缩放因子。注意m_axis_data_tuser字段的块指数报告。
Output ordering:自然顺序和反转顺序。这里使用自然顺序。
Optional Output Fields:XK_INDEX,输出数据索引。
资源消耗设置界面。
关于matlab仿真,产生1MHZ的正弦波余弦波信号,采样率为100MHZ。共计1024个点。对其进行FFT。
matlab仿真程序
% 生成正弦波
fc = 1e6; % 频率
fs = 100e6; % 采样率
t1 = 0:1/fs:1e-3; % 时间序列,1微秒
t = t1(1:1024); %RW需要取整数计算出的频率是真实
sin_wave = sin(2 * pi * fc * t);
cos_wave = cos(2 * pi * fc * t);
% 复数
plural_wave = cos_wave+ 1i*sin_wave;
% 加噪声
data =awgn(cos_wave,100) ;
% data =awgn(plural_wave,100) ;
% 绘制原始正弦波数据
figure;
subplot(1,1,1);
plot(t*1e9, imag(plural_wave));
title('原始正弦波');
xlabel('时间 (ns)');
ylabel('幅值');
%%做FT变化,算平均功率谱,并画谱输出
FFT_data = data; N = length(FFT_data); w = blackman(N);Fs =100e6;
% Sf_I_Q_wave=fftshift(fft((FFT_data).*w',N)*2.381);
% w = gausswin(N);%高斯窗函数,窗长度为N
gauss_data = fft((FFT_data).*w',N)*2.396;%%做FFT变换,加窗并还原窗系数
Sf_I_Q_wave=fftshift(gauss_data);
Sf_I_Q_wave_dBm =(abs(Sf_I_Q_wave));
% Sf_I_Q_wave_dBm =20*log10(1/N*abs(Sf_I_Q_wave));
X_freq=Fs.*(-N/2:N/2-1)/N;figure(2);plot(X_freq,Sf_I_Q_wave_dBm);title('plural_wave原始数据普');
对实信号余弦波进行傅里叶变换,频谱图如下。
对复信号进行进行傅里叶变换频谱。
在逻辑中我们使用DDSIP来产生我们需要的波形数据。
DDS IP配置界面
可以看到我需要控制DDS的控制字来生成不同频率的波形数据,输出数据的格式为高16位为正弦,低16位为余弦。
输出频率计算公式。
其中X为频率控制字,Fs采样率,即系统时钟。N为多少位的控制字。要输出1MHZ的波形信号的控制字为X=655。
输出波形正好为一个周期1000ns。即1MHZ。
1024点FFT实信号输入。
FFTSHIFT频谱搬移模块
对于FFTSHIFT模块我们打开matlab帮助后可以发现,搬移的效果就是以频谱中心左右两端谱线互换。
ABS求平方根模块
求复数的模,定义为
复数的
在逻辑中我们使用cordic来计算平方根。
cordicIP配置如下所示
模块仿真
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2024/08/12 14:14:51
// Design Name:
// Module Name: vtf_fft_test
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module vtf_fft_test;
//fft
//input
wire [31:0] s_axis_data_tdata ;
wire s_axis_data_tvalid ;
wire s_axis_data_tready ;
wire s_axis_data_tlast ;
//output
wire [31:0] m_axis_data_tdata ;
wire m_axis_data_tvalid ;
wire m_axis_data_tready ;
wire m_axis_data_tlast ;
wire [23:0] m_axis_data_tuser ;
//dds
wire m_dds_tvalid ;
wire [31:0] m_dds_tdata ;
//system signal
reg clk ;
reg rst_n ;
reg data_gen ;
reg data_flog ;
reg douta_vld ;
reg [15:0] cnt ;
//fftshift
wire [31:0] fftshift_data ; //
wire fftshift_data_valid ; //
wire [9:0] fftshift_cnt ; //
//abs
wire [15:0] abs_data ; // (output)
wire abs_data_valid ; // (output)
//system signal
//
assign s_axis_data_tdata = douta_vld ? m_dds_tdata : 32'h0;//给复信号
//assign s_axis_data_tdata = douta_vld ? {16'd0,m_dds_tdata[15:0]} : 32'h0;//实信号
assign s_axis_data_tvalid = douta_vld;
assign s_axis_data_tlast = (cnt == 16'd1023 );
assign m_axis_data_tready = 1'b1;
//FFT后数据
//取实部
wire [15:0] f_imag ;
wire [15:0] f_real ;
wire [31:0] f_abs ;
assign f_imag = m_axis_data_tdata[31:16];
assign f_real = m_axis_data_tdata[15:0];
assign f_abs = f_imag + f_real;
//=========================================================
dds_compiler_0 u_dds (
.aclk (clk ), // input wire aclk
.s_axis_config_tvalid (1'b1 ), // input wire s_axis_config_tvalid
.s_axis_config_tdata (16'd655 ), // input wire [15 : 0] s_axis_config_tdata
.m_axis_data_tvalid (m_dds_tvalid ), // output wire m_axis_data_tvalid
.m_axis_data_tdata (m_dds_tdata ), // output wire [31 : 0] m_axis_data_tdata
.m_axis_phase_tvalid ( ), // output wire m_axis_phase_tvalid
.m_axis_phase_tdata ( )// output wire [15 : 0] m_axis_phase_tdata
);
//=========================================================
xfft_0 u_fft (
.aclk ( clk ), // input wire aclk
.aresetn ( rst_n ), // input wire aresetn
.s_axis_config_tdata (8'd1 ), // input wire [7 : 0] s_axis_config_tdata
.s_axis_config_tvalid (1'b1 ), // input wire s_axis_config_tvalid
.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 [31 : 0] m_axis_data_tdata
.m_axis_data_tuser (m_axis_data_tuser ), // output wire [23 : 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
.m_axis_status_tdata ( ), // output wire [7 : 0] m_axis_status_tdata
.m_axis_status_tvalid ( ), // output wire m_axis_status_tvalid
.m_axis_status_tready (1'b1 ), // input wire m_axis_status_tready
.event_frame_started ( ), // output wire event_frame_started
.event_tlast_unexpected ( ), // output wire event_tlast_unexpected
.event_tlast_missing ( ), // output wire event_tlast_missing
.event_status_channel_halt ( ), // output wire event_status_channel_halt
.event_data_in_channel_halt ( ), // output wire event_data_in_channel_halt
.event_data_out_channel_halt ( )// output wire event_data_out_channel_halt
);
//==========================================================
fft_shift u_fft_shift(
//
.fft_data (m_axis_data_tdata ), // (input )
.fft_cnt (m_axis_data_tuser[9:0] ), // (input )
.fft_last (m_axis_data_tlast ), // (input )
.fft_data_valid (m_axis_data_tvalid ), // (input )
.fftshift_data (fftshift_data ), // (output)
.fftshift_data_valid (fftshift_data_valid ), // (output)
.fftshift_cnt (fftshift_cnt[9:0] ), // (output)
//system signal
.sys_clk (clk ), // (input )
.rst_n (rst_n )// (input )
);
//==========================================================
abs_top u_abs_top(
//
.plural_data (fftshift_data[31:0] ), // (input ) (input )
.plural_data_valid (fftshift_data_valid ), // (input ) (input )
.abs_data (abs_data[15:0] ), // (output) (output)
.abs_data_valid (abs_data_valid ), // (output) (output)
//system signal
.clk (clk ), // (input ) (input )
.rst_n (rst_n ) // (input ) (input )
);
//==========================================================
xfft_0 u_ifft (
.aclk ( clk ), // input wire aclk
.aresetn ( rst_n ), // input wire aresetn
.s_axis_config_tdata (8'd1 ), // input wire [7 : 0] s_axis_config_tdata
.s_axis_config_tvalid (1'b1 ), // input wire s_axis_config_tvalid
.s_axis_config_tready ( ), // output wire s_axis_config_tready
.s_axis_data_tdata (m_axis_data_tdata ), // input wire [31 : 0] s_axis_data_tdata
.s_axis_data_tvalid (m_axis_data_tvalid ), // input wire s_axis_data_tvalid
.s_axis_data_tready (m_axis_data_tready ), // output wire s_axis_data_tready
.s_axis_data_tlast (m_axis_data_tlast ), // input wire s_axis_data_tlast
.m_axis_data_tdata ( ), // output wire [31 : 0] m_axis_data_tdata
.m_axis_data_tuser ( ), // output wire [23 : 0] m_axis_data_tuser
.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 ( ), // output wire m_axis_data_tlast
.m_axis_status_tdata ( ), // output wire [7 : 0] m_axis_status_tdata
.m_axis_status_tvalid ( ), // output wire m_axis_status_tvalid
.m_axis_status_tready (1'b1 ), // input wire m_axis_status_tready
.event_frame_started ( ), // output wire event_frame_started
.event_tlast_unexpected ( ), // output wire event_tlast_unexpected
.event_tlast_missing ( ), // output wire event_tlast_missing
.event_status_channel_halt ( ), // output wire event_status_channel_halt
.event_data_in_channel_halt ( ), // output wire event_data_in_channel_halt
.event_data_out_channel_halt ( )// output wire event_data_out_channel_halt
);
//================================================================
//================================================================
initial
begin
clk = 0;
rst_n=0;
data_gen =0;
#100;
rst_n =1;
#1000;
data_gen =1;
#100;
data_gen =0;
end
always @ (posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)begin
data_flog <= 1'b0;
end
else if(data_gen == 1'b1)begin
data_flog <= 1'b1;
end
else if(cnt >= 10'd1022)begin
data_flog <= 1'b0;
end
end
always @ (posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)begin
douta_vld <= 1'b0;
end
else begin
douta_vld <= data_flog;
end
end
always @ (posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)begin
cnt <= 16'd0;
end
else if(s_axis_data_tvalid == 1'b1 && s_axis_data_tready == 1'b1)begin
cnt <= cnt + 1'b1;
end
else begin
cnt <= cnt;
end
end
//================================================================
//================================================================
always #5 clk = ~clk;
endmodule
复信号输入。
实信号输入。
逆傅里叶变换结果