Vivado FFT IP核配置:从入门到熟练
参考博文https://blog.csdn.net/qq_36375505/article/details/81742680https://blog.csdn.net/u013215852/article/details/105345952/https://blog.csdn.net/id_yqduan/article/details/108882080?utm_medium=distribute
前言
fpga初学者,在vivado软件上调用fft IP核时,一头雾水。参考了部分博文,总结出FFT调用的流程,并更新了部分端口的说明 (https://www.cnblogs.com/lgy-gdeu/p/11590626.html 中有两个端口说明可能有误解)。
一、调用IP核
1、点击Flow Navigator中的IP Catalog,搜索FFT,选择xfft9.1.
2、双击后,有三个界面需要配置
第2页:
主要配置通道数,点数,时钟,吞吐量,结构,以及是否可以运行时配置,需要注意的是结构的配置会影响调整因子。如下图所示:
number of channels :变换通道,可以选择多通道,实现多帧数据同时进行FFT运算;
transform lenfgth : FFT变换长度,如果选择了最下面的‘run time configurable transdorm legth’,则该参数是FFT变化的最大长度,一般不选。
architecure choice : 这个标签里主要是用来实现,FFT变换所选用的架构:
Automatically selected : 自动选择所需要的,FFT变化架构。
pipelined streaming : 并行流水线结构
radix-4, burst i/o : 基4 I/O突发结构
radix-2, burst i/o : 基2 I/O突发结构
radix-2 life, burst i/o : 基2 I/O突发结构
其中流水线的结构变换处理时间最短,资源消耗最大。
run time configurable transdorm legth : 该选项可以在FFT变换中通过设置s_axis_config_tdata中NFFT字段的长度来改变FFT变化的长度。
第2页:
配置 数据宽度,格式,控制信号,输出方式,和可选的控制信号。
需要注意的是输据的输入是自然方式(Natural Order),输出可以是自然方式也可以是倒序方式(Reversed Order),如果选用倒序方式输出,在后面处理中就要注意这一特性。
其中在data format;下拉标签中,对应着FFT IP核支持两种数据类型:
1. 定点全精度
2. 定点缩减位宽
scaling optios :缩放选项 :
1、 block floating point :不管输入的格式如何,FFT变化内部都采用浮点,会根据每一级的的数据情况自动缩放。 这个模式的输入输出位宽一致,便于调用。
2、scaled :在m_axis_data_tuser中会有5BIT表示每一级的缩放情况,在s_axis_config_data中会有相应的字段配置配置缩放因子.每一级别包含2个stage ,2个bit 表示一级缩放,一般0-3可选,如果log(NFFT)不是2的倍数,则最高一级的缩放只能在0-1之间选取。
3、unscaled :不用担心变化过程中会出现溢出,但是输入是32bit的话,输出是64bit。
Aresten : 复位信号要勾选,至少保持两个时钟的低电平。
output odering options: 输出顺序选项。
1、nature order:就是FFT变化后的输出已经调整了顺序,按照xk_index自然顺序列出变化结果,
2、bit/digital reserved oder就是按照变化后的顺序直接输出,是倒序输出,需要自己后续处理,
3、cyclic perfix insertion :循环前缀插入,一般添加,在进行IFFT后可以根据s_axis_config_data中的CP长度配置自动添加CP。
optional output fileds :选项输出字段,
1、xk_index:FFT 变幻的结果索引,在m_axis_data_user中有相应的字段。
2、OVFLO是变换中溢出的指示信号,对应event_fft_overflow.
第3页:没有变化
详细的FFT IP核说明可参考 Documentation 中的 Product Guide的pdf文件。
点击OK,即可。
二、编写一个.v 文件来驱动FFT单元
1、例化fft IP核。 点击.IP Source中 Instantiation Template下的 .veo文件,双击。将以下代码复制到.v文件中。
该代码定义了端口名称,输入输出模式,位宽等。
2. 利用以上代码。 看起来很头疼,但研究之后回过头来发现,例化代码中真正对数据输入和FFT输出有关系的端口,只有s_axis_config_XXX,s_axis_data_XXX,和m_axis_data_XXX,其中前2个是输入配置,第3个是输出配置。
最终编写的.v文件如下
`timescale 1ns/1ps
module fft64(
input i_clk, //100MHz, 10ns
input aresetn,
input[7:0] s_axis_config_tdata,
input s_axis_config_tvalid,
output s_axis_config_tready,
output s_axis_data_tready,
input[31:0] s_axis_data_tdata, //输入的16位数据 16*2
input s_axis_data_tvalid, //拉高N个周期,输入N个数据进行fft
input s_axis_data_tlast, //输入N个数据后拉高,停止数据输入
output[47:0] m_axis_data_tdata,
output[7:0] m_axis_data_tuser,
output m_axis_data_tvalid,
input m_axis_data_tready,
output 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;
fft_f fft64 (
.aclk(i_clk), // input wire aclk
.aresetn(aresetn), // input wire aresetn
.s_axis_config_tdata(s_axis_config_tdata), // input wire [7 : 0] s_axis_config_tdata
.s_axis_config_tvalid(s_axis_config_tvalid), // 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 [7 : 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
3. 端口说明
Input[N:0]: s_axis_config_tdata:控制输入模式,进行fft/ifft以及衰减因子的设置,FWD_INV = 1做 fft,FWD_INV = 0做ifft。
Input: s_axis_config_tvalid:拉高两个时钟周期之后,将端口s_axis_data_tvalid和s_axis_data_tready拉高。
Output: s_axis_config_tready:s_axis_config_tvalid拉高两个时钟周期后,该口给1输出;若干个时钟周期后,自动归零。
Output: s_axis_data_tready:aresetn拉高两个时钟周期后,该口给1输出;此时ip核初始化完成,可进行数据输入。
Input: s_axis_data_tvalid:当s_axis_data_tready高电平后,将s_axis_data_tvalid拉高L个周期,输入L个数据进行fft;L是FFT的点数。
Input[M:0]: s_axis_data_tdata:将数据输入进行FFT运算。
Input: s_axis_data_tlast:输入L个数据后拉高,停止数据输入。
做fft需要耗费的时钟周期计算如下s_axis_data_tlast- s_axis_data_tvalid
Output[M1:0]:m_axis_data_tdata:高位为虚部,低位为实部。(经过测试发现的,implementation也有定义)
Output: m_axis_data_tvalid:当fft结果输出时拉高,输出2048个点的数据后拉低。
Output[M2:0]:m_axis_data_tuser:输出fft的地址值,输出值*fs/L为对应频点。
Input: m_axis_data_tready:需保持高电平,保证FFT单元处在计算模式,并且能够输出结算结果;
Output: m_axis_data_tlast:当fft结果输出到最后一个结果时拉高,紧接着下一个时钟就拉低。
说明
(a)FWD_INV的字段从这里得到
(b)当输入数据包含实部和虚部时,输入端口s_axis_data_tdata的高位存放虚部,低位存放实部。
(c)输出数据的实部和虚部的字段分布
三、编写一个testbench_top.v 文件对FFT单元进行测试。
1、需要在testbench_top文件中对“二、编写一个.v 文件来驱动FFT单元” 中编写的.v文件进行例化。 代码如下:
module testbench_top();
//参数定义
`define CLK_PERIORD 10 //时钟周期设置为10ns(100MHz)
//接口申明
reg clk;
reg rst_n;
reg aresetn;
reg[7:0] s_axis_config_tdata;
reg s_axis_config_tvalid;
wire s_axis_config_tready;
wire s_axis_data_tready;
reg[31:0] s_axis_data_tdata; //输入的16位数据 16*2
reg s_axis_data_tvalid; //拉高4096个周期,输入4096个数据进行fft
reg s_axis_data_tlast; //输入4096个数据后拉高,停止数据输入
wire[47:0] m_axis_data_tdata;
wire[7:0] m_axis_data_tuser;
wire m_axis_data_tvalid;
reg m_axis_data_tready;
wire m_axis_data_tlast;
reg [15:0] mem0_re[1:64];
wire[23:0] o_data_real;
wire[23:0] o_data_imag;
assign o_data_real = m_axis_data_tdata[23:0];
assign o_data_imag = m_axis_data_tdata[47:24];
//对被测试的设计进行例化
fft64 uut_fft64(
.i_clk (clk), //100MHz, 10ns
.aresetn (aresetn),
.s_axis_config_tdata (s_axis_config_tdata),
.s_axis_config_tvalid(s_axis_config_tvalid),
.s_axis_config_tready(s_axis_config_tready),
.s_axis_data_tready (s_axis_data_tready),
.s_axis_data_tdata (s_axis_data_tdata),
.s_axis_data_tvalid (s_axis_data_tvalid),
.s_axis_data_tlast (s_axis_data_tlast),
.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_tready (m_axis_data_tready),
.m_axis_data_tlast (m_axis_data_tlast)
);
//复位和时钟产生
//时钟和复位初始化、复位产生
initial begin
clk <= 0;
rst_n <= 0;
#1000;
rst_n <= 1;
end
//时钟产生
always #(`CLK_PERIORD/2) clk = ~clk;
//测试激励产生
initial $readmemb("D:/FPGA_Code/TeQuan_Study/sim_115_FFT2/0Matlab/data_before_fft.txt", mem0_re);
integer i;
initial begin
// Initialize Inputs
clk = 0;
aresetn = 0;
s_axis_config_tdata = 0;
s_axis_config_tvalid = 0;
s_axis_data_tvalid = 0;
s_axis_data_tdata = 0;
s_axis_data_tlast = 0;
m_axis_data_tready = 0;
i = 0;
// Wait 200 ns for global reset to finish
#200;
aresetn = 1;
m_axis_data_tready = 1;
s_axis_config_tvalid = 1;
s_axis_config_tdata = {7'b0, 1'b1};
#8000
s_axis_config_tvalid = 0;
aresetn = 0;
#300;
$stop;
end
always @(posedge clk)
begin
if(s_axis_data_tready == 1)
begin
i = i + 1;
s_axis_data_tvalid <= 1;
s_axis_data_tdata <= mem0_re[i];
$display("mem_a[%d] = %d", i, mem0_re[i]);
if(i == 68)
begin
s_axis_data_tlast <= 1;
s_axis_data_tvalid <= 0;
end
end
end
endmodule
说明:
(a)输入数据在data_before_fft.txt中,测试使用的是从1到64的整数(间隔1),一共64个数字。
(b)输出的端口一定要记得定义wire 输出,否则没有输出数据。
四、Vivado仿真结果
运行结果如下:
可以看出,可完成1~64的数据输入。
经过研究发现:
(a)aresetn信号存在时,是控制FFT是否运行,以及是否输出运算结果的使能信号。此外,aresetn信号必须在FFT输入之前,到FFT结果计算结束之间这段时间,都必须保持高电平。
(b)s_axis_data_tvalid也是控制输入和输出的信号端口。使用的时候给fft喂数据就enable(1),数据喂完了就disable(0)。但是在aresetn信号存在,s_axis_data_tvalid失去作用。
五、与Matlab仿真结果对比
Matlab运算结果:
IP核运算结果
1、运算结果实部和虚部,数字的值趋势相同,但是存在误差;
2、实部数值正负号相同,数字值差别也不大,正确;
3、虚部数值正负号完全相反,数字值本身差别不大,有问题。 如何解决?
将config_tdata信号修改为s_axis_config_tdata = 8'b0000_0000。可得到FFT正确结果。
六、目前还有以下问题:
(然而,8b0的配置明明是IFFT的配置,为什么在这里却能得到FFT的结果?)
当输入是浮点数时,FFT如何配置?
应该在IP核界面中配置:
算术类型:
- 无标度(全精度)定点
- 定标定点
- 浮点数
定点或浮点接口
蝴蝶后舍入或截断
补一补Verilog中signed,原码,反码,补码基础 https://blog.csdn.net/maxwell2ic/article/details/80596210
这篇博文同时实现了FFT/IFFT 在VIVADO中实现FFT/IFFT https://blog.csdn.net/luobluesky/article/details/90516652?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.channel_param&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.channel_param 值得参考!
这篇博文利用的FFT模式是Fixed Point,Block Floating Point,Convergent Rounding。这样得到的结果,FFT模值趋势上与Matlab相同,但是具体的实部、虚部数值也存在相反现象。
ModelSim结果
Matlab结果(上面是实部,下面是虚部)
参考博文
https://blog.csdn.net/u013215852/article/details/105345952/
https://blog.csdn.net/qq_36375505/article/details/81742680
https://www.cnblogs.com/lgy-gdeu/p/11590626.html
更多推荐
所有评论(0)