ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Vivado中FFT IP核的使用

2020-05-05 11:39:51  阅读:762  来源: 互联网

标签:FFT IP fft tvalid Vivado wire data event


FFT(快速傅里叶变换)作为数字信号处理的核心算法具有重要的研究价值,可应用于傅里叶变换所能涉及的任何领域,如图像处理、音频编码、频谱分析、雷达信号脉冲压缩等数字信号处理领域。FFT的鲜明特征之一是计算离散傅里叶变换(DFT)的高效算法,把计算N点DFT的乘法运算量从N2次降低到N/2*log2N次。而采用FPGA实现FFT的缘由在于:FPGA具有并行处理、流水线处理、易编程、片上资源丰富等方面特点,用于实现高速、大点数的FFT优势明显。

本设计使用的软件编程环境是Xilinx公司的Vivado 2018.3,笔者将从FFT IP核的创建,模块文件的编写,波形仿真等方面来具体讲解FFT在Xilinx FPGA上的实现。

1.FFT IP核的创建

(1)在Vivado软件主界面,打开IP Catalog,在搜索框内输入FFT,然后找到Digital Signal Processing->Transforms->FFTs目录下的Fast Fourier Transform,双击进入配置界面。
在这里插入图片描述(2)进入到配置界面,左边是IP核的接口图、实现的一些细节信息和FFT的延迟,右边是Configuration、Implementation和Detailed Implementation三个标签卡。

Vivado的FFT IP核支持多通道输入(Number of Channels)和实时更改FFT的点数(Run Time Configurable Transform Length)。Configuration标签下可设置FFT的点数(Transform Length)和工作时钟(Target Clock Frequency),以及选择一种FFT结构。FFT的结构包括流水线Streaming、基4 Burst、基2 Burst和轻量级基2 Burst,它们的计算速度和消耗的资源依次减少,可根据工程实际进行选择。
在这里插入图片描述 Implementation标签卡下可设置FFT的数据格式为定点Fixed Point或浮点Float Point;输出截位方式选择:不截位(Unscaled),截位(Scaled),块浮点(Block Floating Point);设置输入数据的位宽和相位因子位宽。还有一些可选的附加信号,如时钟使能(ACLKEN),复位信号(ARESETn,低有效)等。“Output Ordering”用以选择FFT计算结果以自然顺序(Nature Order)或位倒序(Bit/Digit Reversed Order)输出。
在这里插入图片描述 Detailed Implementation里可设置优化方式、存储的类型。存储类型分为两种:Block RAM(块RAM)和Distributed RAM(分布式RAM);优化方式可选择资源最优或者速度最优。
在这里插入图片描述(3)配置完成后,可在Latency下看到计算fft所需的时间,可以以此衡量设计是否满足实时处理的要求。如不满足,可选择性能更好的FFT结构或选择可以提高运算速度的优化选项
在这里插入图片描述

2.模块文件的编写

(1)IP核工作必须要满足一定的时序要求,所以需要编写一个fft核的接口模块(FFT_interface.v),用于产生该IP核的时序控制信号并将输入的实部、虚部数据根据需要拼接成一个数据。和该IP核交互是用AXI-Stream接口,关于AXI-Stream接口的时序可自行查一些相关资料,这里不做详细介绍。

module FFT_interface
#(   parameter Data_width=16,
               Cnt_width=16,
               Frame_length=2048,
               L=1'b0,H=1'b1
)
(
	input                              clk,
	input                              rst_n,
	input                              tvalid_i,
	input       [Data_width-1:0]       dati_in,
	input       [Data_width-1:0]       datq_in,
	output reg  [(Data_width<<1)-1:0]  dat_out,
	output reg                         tlast_o,
	output reg                         tvalid_o,
	output wire                        local_rst
);

reg  [Cnt_width-1:0]  cnt;
reg                   tvalid_reg;
wire                  tvalid_cat;

always @(posedge clk) begin
  tvalid_reg <= tvalid_i;
end

assign tvalid_cat = tvalid_reg | tvalid_i; //tvalid拉高一个时钟周期后tready才有效,故将valid信号多拉高一个时钟周期

always @(posedge  clk)  begin
  if (!rst_n) cnt<=0;
  else if(tvalid_i)  cnt<=cnt+16'd1;
  else if(cnt>=Frame_length&& cnt<=Frame_length+5) cnt<= cnt+16'd1;
  else if(cnt==Frame_length+6) cnt<=0;
 end
 
assign local_rst=(rst_n)&&(( tvalid_i&&(cnt==1))||(( tvalid_i&&(cnt==2)))?L:H);

always @(posedge  clk)begin
  if(!rst_n) tlast_o<=L;
  else if(cnt==Frame_length+4) tlast_o=H;
  else tlast_o=L;
end

reg   valid_temp1, valid_temp2;
reg   valid_temp3, valid_temp4;
reg   [(Data_width<<1)-1:0]  dat_temp1, dat_temp2;
reg   [(Data_width<<1)-1:0]  dat_temp3, dat_temp4;

always @(posedge  clk)begin
if(!rst_n) begin
  valid_temp1<=L; valid_temp2<=L;
  valid_temp3<=L; valid_temp4<=L; tvalid_o<=L;
  dat_temp1<=0; dat_temp2<=0;
  dat_temp3<=0; dat_temp4<=0; dat_out<=0;
end
else begin
  valid_temp1<= tvalid_cat; valid_temp2<= valid_temp1;
  valid_temp3<= valid_temp2; valid_temp4<= valid_temp3; tvalid_o<= valid_temp4;
  dat_temp1<={ datq_in , dati_in }; dat_temp2<= dat_temp1;
  dat_temp3<= dat_temp2; dat_temp4<= dat_temp3; dat_out <=dat_temp4;
end
end

endmodule

(2)接口模块编写完成后就可在顶层模块(FFT_TOP)进行调用,并将其输出端口连接到FFT IP核上,以实现对IP核的时序控制。

module FFT_TOP
#(   parameter Din_width=16,
               Dout_width=24,   
               L=1'b0,H=1'b1
)
(
  input                    clk,
  input                    rst_n,
  input                    tvalid_i,
  input  [Din_width-1:0]   dati_in,
  input  [Din_width-1:0]   datq_in,
  output [Dout_width-1:0]  dati_out,
  output [Dout_width-1:0]  datq_out,
  output                   tvalid_o
);

wire [Din_width*2-1:0]  fft_dat_in;
wire                    fft_last_i;
wire                    fft_valid_i;
wire                    fft_local_rst;

FFT_interface #(16,16,128,1'b0,1'b1) u_FFT_interfence(
	.clk(clk),
	.rst_n(rst_n),
	.tvalid_i(tvalid_i),
	.dati_in(dati_in),
	.datq_in(datq_in),
	.dat_out(fft_dat_in),
	.tlast_o(fft_last_i),
	.tvalid_o( fft_valid_i),
	.local_rst(fft_local_rst)
);
						  
wire                      fft_s_config_tready;
wire                      fft_s_data_tready;
wire  [Dout_width*2-1:0]  fft_m_data_tdata;
wire                      fft_m_data_tvalid;
wire                      fft_m_data_tlast;
wire                      fft_event_frame_started;
wire                      fft_event_tlast_unexpected;
wire                      fft_event_tlast_missing;
wire                      fft_event_status_channel_halt;
wire                      fft_event_data_in_channel_halt;
wire                      fft_event_data_out_channel_halt;

fft_16 u_fft_16(
	.aclk(clk),
	.aresetn(fft_local_rst),
	.s_axis_config_tdata(8'd1),
	.s_axis_config_tvalid(H),
	.s_axis_config_tready(fft_s_config_tready),
	.s_axis_data_tdata(fft_dat_in),
	.s_axis_data_tvalid(fft_valid_i),
	.s_axis_data_tready(fft_s_data_tready),
	.s_axis_data_tlast(fft_last_i),
	.m_axis_data_tdata(fft_m_data_tdata),
	.m_axis_data_tvalid(fft_m_data_tvalid),
	.m_axis_data_tready(H),
	.m_axis_data_tlast(fft_m_data_tlast),
	.event_frame_started(fft_event_frame_started),
	.event_tlast_unexpected(fft_event_tlast_unexpected),
	.event_tlast_missing(fft_event_tlast_missing),
	.event_status_channel_halt(fft_event_status_channel_halt),
	.event_data_in_channel_halt(fft_event_data_in_channel_halt),
	.event_data_out_channel_halt(fft_event_data_out_channel_halt)
);

assign   dati_out=fft_m_data_tdata[Dout_width-1:0];
assign   datq_out=fft_m_data_tdata[Dout_width*2-1: Dout_width];
assign   tvalid_o=fft_m_data_tvalid;

endmodule

(3)此工程的RTL视图:该视图下,可清楚直观的显示各模块间的连线关系以及输入输出端口信息。
在这里插入图片描述

3.功能仿真

模块编写完成后,需要通过功能仿真来验证我们设计逻辑的正确性。进行仿真之前,我们需要编写仿真测试文件(testbench)。

module testbench;
reg          clk;
reg          rst_n;
reg          tvalid_i;
reg [15:0]   dati_in;
reg [15:0]   datq_in;
wire [23:0]  dati_out;
wire [23:0]  datq_out;
wire         tvalid_o;
reg [15:0]   dataI [127:0];

initial  begin
clk=0;
rst_n=0;
tvalid_i=0;
dati_in=0;
datq_in=0;

$readmemb("C:/Users/radar/Desktop/y1.txt",dataI);
#100
rst_n=1;
end

always #5  clk=~clk;

reg  [7:0]  count=0;
always @(posedge  clk)  begin
   if (rst_n) begin
     if(count==128) begin
       tvalid_i=1'b0;
       count<=count;
     end
     else begin
       dati_in<= dataI[count];
       datq_in<=16'd0;
       tvalid_i=1'b1;
       count<=count+1;
     end
   end
end

FFT_TOP  U_FFT_TOP(
.clk(clk),
.rst_n(rst_n),
.tvalid_i(tvalid_i),
.dati_in(dati_in),
.datq_in(datq_in),
.dati_out(dati_out),
.datq_out(datq_out),
.tvalid_o(tvalid_o)
);

wire  [47:0]  fft_abs;
assign fft_abs=$signed(dati_out)* $signed(dati_out)+ $signed(datq_out)* $signed(datq_out);

endmodule

testbench中输入的时域波形数据是我们通过matlab生成的,在matlab中我们仿真的是采样率为2kHz情况下,频率分别为50Hz和200Hz的两正弦波叠加后的信号。

N=128;
n=1:N;
f0=50;
f1=200;
fs=2e3;
y=sin(2*pi*f0.*n/fs)+2*sin(2*pi*f1.*n/fs);
figure;
plot(y);
Y=fft(y);
figure;
plot(abs(Y));
y1=y';
q=quantizer([16 12]);
y2=num2bin(q,y1);
fid1=fopen('C:/Users/radar/Desktop/y1.txt','wt');
for i=1:N
    fwrite(fid1,y2(i,:));
    fprintf(fid1,'\n');
end
fclose(fid1);

进行功能仿真时我们将仿真时长设置为11us。为了直观验证fft是否正确,可将输入的时域数据和做完fft后信号功率值的数据格式均设置为anolog(模拟),如下图,可以看到fft后的功率谱为两根独立的谱线,分别代表50Hz和200Hz两个频率点,和matlab仿真结果一致。
在这里插入图片描述在这里插入图片描述

对于该IP核更复杂的应用,大家可以阅读Xilinx官方提供的文档,根据自己的实际需要进行设计。限于笔者水平有限,文章内难免会有错误,希望大家不吝赐教,批评指正。

标签:FFT,IP,fft,tvalid,Vivado,wire,data,event
来源: https://blog.csdn.net/qq_43622265/article/details/105905927

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有