参考文档:pg109
一、FFT IP核的配置
参考:P59~P62
1.1 Configuration
Number of channels:FFT通道数目,可以选择多通道,实现多帧数据同时进行FFT运算
Transform length:FFT变换长度
Target clock frequency:采样时钟
Target data throughput:数据时钟
Architecture choice:FFT架构选择,根据需求选择
- Automatically select:自动选择所需要的FFT架构
- Pipelined streaming IO:并行流水线结构,处理时间最短,资源消耗最大,多通道不支持pipeline架构
- Radix-4 burst i/o:基4 IO突发结构
Run Time Configurable Transdorm Legth:选中后可以通过设置s_axis_config_tdata中NFFT字段的长度来改变FFT变换的长度
1.2 Implementation
Data format:数据类型
- 定点数
- 浮点数
Scaling options:缩放选项
- Block floating point:不管输入的格式如何,FFT变化内部都采用浮点,会根据每一级的数据情况自动缩放。这个模式的输入输出位宽一致,便于调用。
- Scaled:在m_axis_data_tuser中会有5bit表示每一级的缩放情况,在s_axis_config_data中会有相应的字段配置来配置缩放因子。
- Unscaled:不用担心变化过程中出现溢出,但是输入是32bit的话,输出是64bit。
Aresetn:复位信号
Rounding modes:截断方式
Output ordering:输出顺序
- Nature order:就是FFT变化后的输出已经调整了的顺序,按照xk_index自然顺序列出变化结果。
- Bit/digital reserved order:按照变化后的顺序直接输出,是倒序输出,需要自己后续处理。
Cylic prefix insertion:循环前缀插入,在进行IFFT后可以根据s_axis_config_data中的CP长度配置自动添加CP。
Optional output fileds:选项输出字段
- Xk_index:FFT变化的结果索引,在m_axis_data_user中有相应的字段。
- OVFLO:是变换中溢出的指示信号,对应event_fft_overflow。
1.3 接口
参考:P10~P28
端口 | 意义 |
---|---|
s_axis_config_tdata | 用于配置IP核,置1时做FFT运算,置0时做IFFT运算。 |
s_axis_config_tvalid | IP核配置输入有效,一般直接置1。 |
s_axis_config_tready | 表示已准备好接受数据 |
s_axis_data_tready | 表示从设备已经准备好接收一次数据传输。 |
s_axis_data_tdata | 输入数据,[31:16]是虚部,[15:0]是实部。 |
s_axis_data_tvalid | 输入数据有效。 |
s_axis_data_tlast | 传输结束信号,一般发送完N点数据后置1,s_valid置1到s_last置1所用时间即为做FFT所耗费的时间。 |
m_axis_data_tdata | 输出的频谱数据,[47:24]对应的是虚部数据,[23:0]对应的是实部数据,高位补符号位。 |
m_axis_data_tvalid | 输出数据有效 |
m_axis_data_tuser | 输出FFT的索引值,该值*fs/N即为对应频点,N为FFT点数。 |
m_axis_data_tready | 表示从设备已经准备好接收一次数据传输。 |
event_frame_started | 当核开始处理一个新的帧时声明,输入数据时拉高一个时钟周期。 |
event_tlast_unexpected | 当内核在非帧中最后一个数据采样的s_axis_data_tlast为高电平时触发。 |
event_tlast_missing | 在帧的最后一个数据样本上s_axis_data_tlast为低时声明。 |
event_fft_overflow | 当在m_axis_data_tdata上传输的数据样本中看到溢出时在每个时钟周期声明,只有当溢出是一个有效的选项时才出现。 |
event_data_in_channel_halt | 内核从数据输入通道请求数据,并且没有数据可用时声明。 |
event_data_out_channel_halt | 内核尝试将数据写入数据输出通道,并且无法执行时声明。 |
event_status_channel_halt | 内核尝试将数据写入状态通道,并且无法执行时声明。 |
二、生成测试波形
参考代码:
clc;
clear;
close all;
f=2e6; %信号频率为2MHz
fs= 50e6 ;
N=1024;
t =(0:N-1)/fs;
x=cos(2*pi*f*t)*2^10 ; %范围为-1024~1024
%时域波形
figure(1);
plot(t,x);
%频域波形
f = (-N/2: N/2-1) * (fs/N) ;
mag=abs(fft(x));
figure(2) ;
plot(f, fftshift(mag)) ;
for i=1:N
if(x(i)>0)
x(i)=round(x(i));
elseif(x(i)==0)
x(i)=0;
else
x(i)=round(x(i))+2^12;%加2^12是求负数补码,12是因为-1024需要12位二进制表示
end
end
fid = fopen('wave.txt', 'wt') ;
fprintf(fid, '%x\n', x) ;%以16进制写入文件中
fclose(fid) ;
📄 对于负的输入,需要求它的补码,再送入IP核,FFT IP核会将输入数据视为有符号数来处理。
- 对于一个N位的负数,其最高位是符号位,求它的补码的方法是加 2 N 2^N 2N。
- 例如,-5(1101),加 2 4 = 16 2^4=16 24=16后为11(1011),1011正是-5的补码。
三、仿真
`timescale 1ns / 1ps
module tb();
//IP核接口信号
reg aclk;
reg aresetn;
wire s_axis_config_tready;
reg signed [31:0] s_axis_data_tdata;
reg s_axis_data_tvalid;
wire s_axis_data_tready;
reg s_axis_data_tlast;
wire signed [47 : 0] m_axis_data_tdata;
wire [15 : 0] m_axis_data_tuser;
wire m_axis_data_tvalid ;
reg m_axis_data_tready;
wire 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;
reg [11:0] Data[1023:0];
reg [10:0] cnt;
reg signed [23:0] fft_data_real;
reg signed [23:0] fft_data_imag;
wire signed [48:0] fft_power;
wire signed [15:0] data_display;
initial begin
aclk=1'b0;
aresetn=1'b0;
$readmemh("你的输入数据存放路径",Data);
m_axis_data_tready=1'b1;
#15;
aresetn=1'b1;
end
always #10 aclk=~aclk;
//求模平方
assign fft_power=fft_data_real*fft_data_real+fft_data_imag*fft_data_imag;
assign data_display=s_axis_data_tdata[15:0];//取输入的低16位观察波形
//送入数据,每1024个数据产生一个last
always@(posedge aclk or negedge aresetn)begin
if(!aresetn)begin
cnt<='b0;
s_axis_data_tdata<='b0;
s_axis_data_tvalid<='b0;
s_axis_data_tlast<='b0;
end
else if(s_axis_data_tready)begin
if(cnt<1023)begin
s_axis_data_tlast<=1'b0;
s_axis_data_tvalid<=1'b1;
s_axis_data_tdata<={16'b0,{4{Data[cnt][11]}},Data[cnt]};
cnt<=cnt+1'b1;
end
else begin
s_axis_data_tlast<=1'b1;
s_axis_data_tvalid<=1'b1;
cnt<=11'b0;
s_axis_data_tdata<={16'b0,{4{Data[cnt][11]}},Data[cnt]};
end
end
else begin
s_axis_data_tvalid<=1'b0;
s_axis_data_tlast<=1'b0;
s_axis_data_tdata<=s_axis_data_tdata;
end
end
always@(posedge aclk or negedge aresetn)begin
if(!aresetn)begin
fft_data_real<='b0;
fft_data_imag<='b0;
end
else if(m_axis_data_tvalid)begin
//取输出频谱的实部和虚部
fft_data_real<=m_axis_data_tdata[23:0];
fft_data_imag<=m_axis_data_tdata[47:24];
end
else begin
fft_data_real<=fft_data_real;
fft_data_imag<=fft_data_imag;
end
end
//例化IP核
xfft_0 xfft_u (
.aclk(aclk), // input wire aclk
.aresetn(aresetn), // input wire aresetn
.s_axis_config_tdata(1'b1), // input wire [7 : 0] s_axis_config_tdata
.s_axis_config_tvalid(1'b1), // 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 [15 : 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
📄输出频谱有两个谱峰,一个大概对应于tuser=42,一个大概对应于tuser=983。这里解释一下,FFT IP核的输出结果其实是它的真实谱的正频部分搬移至负频左侧并对应于0~1023点的结果。这其实有些类似于我们使用MATLAB中的fft函数做N点FFT后,得到的结果不经过fftshift函数转换而直接绘制全部频谱的情况。可以认为,真实谱的零点对应于第1023点,所以负频部分大概对应于1023-42=981的位置。这其中难免有微小的误差,所以983可以认为是正确的负频。
📄而正如在1.3中提到的tuser*fs/N就是真实的频点,这里可以计算验证一下,即
42
∗
50
M
/
1024
≈
2
M
42*50M/1024\approx2M
42∗50M/1024≈2M,和在MATLAB中设置的信号频率相符。