一、概述
在实际工程中,往往需要对ADC(模数转换)数据不间断的做FFT,或者在基于OFDM数字通信中,需要对连续输入的数据不间断地处理,因为这里采用了xilinx的RAM、FIFO和FFT等IP核,实现对连续输入的数据做256点FFT,并插入32点CP(循环前缀),最后将FFT的结果连续输出,符合实际应用需求。其中: 1、RAM用于存储256点正弦信号并且连续不间断的输出; 2、FIFO的读写采用异步时钟; 3、FFT采用了基于pipeline的架构,确保FFT连续输入输出,并且将FFT结果前添加CP; 整个设计框图如下:
二、各个模块配置介绍
1、PLL时钟模块
PLL时钟模块IP所处位置如下图所示。 这里的时钟模块选择的是MMCM,输入时钟为50M,分别用于产生50M和25M两个输出时钟源,具体配置如下图。
2、RAM模块
RAM模块IP所处位置如下图所示。 RAM选择的单口RAM,内部存储了256个正弦信号,具体配置如下图。
3、FIFO模块
FIFO模块IP所处位置如下图所示。 这里我们选择的是异步FIFO,写FIFO时钟为25M,读FIFO的时钟为50M,FFT点数为256,由于输出需要插入32点CP,为了保证输入数据连续,因此将FIFO深度配置为512,有点乒乓RAM的意思。具体配置如下图。 如下图,这里需要特别注意,Read Mode选择的是standard FIFO模式
standard FIFO模式与First word Fall through模式的区别见时序图: 上图为在standard FIFO模式下的时序图,FIFO读出数据有效信号要比读使能信号晚1拍。 上图为First word Fall through模式下的时序图,FIFO读出数据有效信号与读使能信号完全同步。
注意:在本项目中,我们没有选择First word Fall through模式,因为该模式下即使在读使能无效的情况下,读数据还是一直有效,影响到我们通过读使能信号判断输入数据个数,如下图,可能是在First word Fall through模式下,先将数据抛到FIFO输出数据总线一直保持,因此读出数据有效信号一直为高电平。 在使用FIFO时,还需要注意: 1、FIFO的读空信号empty要滞后零点几ns,如下图。 2、写FIFO反馈信号wr_ack信号要比写FIFO使能信号fifo_wr_en要晚1拍。 3、FIFO的复位信号为高电平有效
4、FFT模块
FFT配置界面如下图所示,为保证连续输入输出,这里选择pipelined Streaming IO模式。 本项目要自动完成插入CP,需要选择下图选项。
三、测试代码
下面是完整的测试代码,里面有注释,供参考。
`timescale 1ns / 1ns
module tb_fft;
//--------------------系统宏定义--------------------
parameter FFT_LEN=256;
parameter FFT_ADD_CP_LEN=288;
//--------------------FFT模块配置参数---------------
//基2的参数配置
//parameter FWD=1'b1;//选择是FFT还是IFFT
//parameter SCALE=16'b01_01_01_01_01_00_01_10;
//parameter PAD=7'b000_0000;
//基4的参数配置
//parameter FWD=1'b1;//选择是FFT还是IFFT
//parameter SCALE=8'b01_10_11_10;//每级右移位数
//parameter PAD=7'b000_0000;
//基4+CP的参数配置
parameter FWD=1'b1;//选择是FFT还是IFFT
parameter SCALE=8'b01_10_11_10;
parameter PAD=7'b000_0000;
parameter CP_LEN=8'b0010_0000;//32点CP
//基2的寄存器长度
//wire [23:0] s_axis_config_tdata;
//基4的寄存器长度
//wire [15:0] s_axis_config_tdata;
//基4+CP的寄存器长度
wire [23:0] s_axis_config_tdata;
wire s_axis_config_tready;
reg sclk;
reg rst_n;
wire fifo_wr_en;
reg Ram_rd_en;
wire fifo_rd_en;
wire locked;
reg fifo_w_clk_25M;
reg fifo_r_clk_50M;
reg [7:0] addra;
wire [7:0] sin_out;
wire s_axis_data_tready;
wire[15:0] s_axis_data_tdata;
wire s_axis_data_tvalid;
wire[15:0] m_axis_data_tdata;
wire[15:0] m_axis_data_tuser;
wire m_axis_data_tvalid;
wire m_axis_data_tlast;
reg s_axis_data_tlast;
reg [8:0] tlast_cnt;
wire[7:0] out_re;
wire[7:0] out_im;
wire m_axis_status_tdata;
wire m_axis_status_tvalid;
wire event_frame_started;
wire event_tlast_unexpected;
wire event_tlast_missing;
wire event_fft_overflow;
wire event_status_channel_halt;
wire event_data_in_channel_halt;
wire event_data_out_channel_halt;
wire [7:0] fifo_out;// output wire [7 : 0] dout
wire full; // output wire full
wire almost_full; // output wire almost_full
wire wr_ack; // output wire wr_ack
wire overflow; // output wire overflow
wire empty; // output wire empty
wire almost_empty; // output wire almost_empty
wire underflow; // output wire underflow
wire wr_rst_busy; // output wire wr_rst_busy
wire rd_rst_busy; // output wire rd_rst_busy
wire [8:0] rd_data_count;
wire [8:0] wr_data_count;
reg [8:0] empty_cnt;
initial
begin
#0 rst_n=0;
#0 fifo_r_clk_50M=0;fifo_w_clk_25M=0;sclk=0;
#0 sclk=0;
#90 rst_n=1;
end
initial
begin
#0 Ram_rd_en=0;
#705 Ram_rd_en=1;
wire_data();
end
always #10 fifo_r_clk_50M=~fifo_r_clk_50M;
always #20 fifo_w_clk_25M=~fifo_w_clk_25M;
always #10 sclk=~sclk;
//配置FFT参数
//基4的参数配置
//assign s_axis_config_tdata={PAD,SCALE,FWD};
//基4+CP的寄存器长度
assign s_axis_config_tdata={PAD,SCALE,FWD,CP_LEN};
//------------------------------------产生2个时钟模块----------------------------------------
clk_wiz_0 clk_wiz_inst
(
// Clock out ports
//.clk_out1(fifo_r_clk_50M), // output clk_out1
//.clk_out2(fifo_w_clk_25M), // output clk_out2
.clk_out1(), // output clk_out1
.clk_out2(), // output clk_out2
// Status and control signals
.resetn(rst_n), // input reset
.locked(locked), // output locked
// Clock in ports
.clk_in1(sclk) // input clk_in1
);
//------------------------------------产生输入信号模块----------------------------------------
//周期性产生256点正弦信号地址
always@(posedge fifo_w_clk_25M or negedge rst_n)
begin
if(rst_n==1'b0)
addra<=8'd0;
else if(Ram_rd_en==1'b1)
addra<=addra+1'b1;
end
//生成单口RAM的IP模块,读使能信号Ram_rd_en由外部测试信号给出
blk_mem_gen_0 blk_mem_gen_0_inst (
.clka(fifo_w_clk_25M), // input wire clka
.ena(Ram_rd_en), // input wire ena
.wea(1'b0), // input wire [0 : 0] wea
.addra(addra), // input wire [7 : 0] addra
.dina('d0), // input wire [7 : 0] dina
.douta(sin_out) // output wire [7 : 0] douta
);
//----------------------------------------FIFO模块--------------------------------------------
//产生FIFO写使能信号fifo_wr_en,即将读RAM信号Ram_rd_en延迟1拍,保证输出数据与FIFO写使能信号同步
reg [1:0] delay;
always@(posedge fifo_w_clk_25M or negedge rst_n)
begin
if(rst_n==1'b0)
delay<=2'd0;
else
delay[1:0]<={delay[0:0],Ram_rd_en};
end
assign fifo_wr_en=delay[1];
//对非空信号进行计数,做为FIFO读使能信号的判断条件
always@(posedge fifo_r_clk_50M or negedge rst_n )
begin
if(rst_n==1'b0)
empty_cnt<=9'd0;
else if((empty_cnt==FFT_ADD_CP_LEN-1) && (empty==1'b0))
empty_cnt<=9'd0;
else if(empty==1'b0)
empty_cnt<=empty_cnt+1'b1;
end
//产生FIFO读使能信号fifo_rd_en,使能信号要拉低32点,用于添加32点CP
assign fifo_rd_en = ((s_axis_data_tready==1'b1) && (empty==1'b0) && (empty_cnt<FFT_LEN))? 1'b1 : 1'b0;
//生成FIFO的IP模块
fifo_generator_0 fifo_generator_0_inst (
.rst(~rst_n), // input wire rst
.wr_clk(fifo_w_clk_25M), // input wire wr_clk
.rd_clk(fifo_r_clk_50M), // input wire rd_clk
.din(sin_out), // input wire [7 : 0] din
.wr_en(fifo_wr_en), // input wire wr_en
.rd_en(fifo_rd_en), // input wire rd_en
.dout(fifo_out), // output wire [7 : 0] dout
.full(full), // output wire full
.almost_full(almost_full), // output wire almost_full
.wr_ack(wr_ack), // output wire wr_ack
.overflow(overflow), // output wire overflow
.empty(empty), // output wire empty
.almost_empty(almost_empty), // output wire almost_empty
.valid(s_axis_data_tvalid), // output wire valid
.underflow(underflow), // output wire underflow
.rd_data_count(rd_data_count), // output wire [8 : 0] rd_data_count
.wr_data_count(wr_data_count), // output wire [8 : 0] wr_data_count
.wr_rst_busy(wr_rst_busy), // output wire wr_rst_busy
.rd_rst_busy(rd_rst_busy) // output wire rd_rst_busy
);
//-----------------------------------------FFT模块----------------------------------------------
//输入的正弦信号为实信号放在实部,虚部全部补零
assign s_axis_data_tdata={8'd0,fifo_out};
//用于产生FFT模块每帧输入最后(第256个数据)一个数据的控制信息s_axis_data_tlast
always@(posedge fifo_r_clk_50M or negedge rst_n)
begin
if(rst_n==1'b0)
tlast_cnt<=9'd0;
else if((tlast_cnt==FFT_LEN-1)&&(s_axis_data_tvalid==1'b1))
tlast_cnt<=9'd0;
else if(s_axis_data_tvalid==1'b1)
tlast_cnt<=tlast_cnt+1'b1;
end
always@(posedge fifo_r_clk_50M or negedge rst_n)
begin
if(rst_n==1'b0)
s_axis_data_tlast<=1'b0;
else if(tlast_cnt==FFT_LEN-1)
s_axis_data_tlast<=1'b1;
else
s_axis_data_tlast<=1'b0;
end
//生成FFT的IP模块
xfft_0 xfft_0_inst (
.aclk(fifo_r_clk_50M), // input wire aclk
.aclken(1'b1), // input wire aclken
.aresetn(rst_n), // input wire aresetn
.s_axis_config_tdata(s_axis_config_tdata), // input wire [23 : 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 [15 : 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(1'b0), // input wire s_axis_data_tlast
.s_axis_data_tlast(s_axis_data_tlast), // input wire s_axis_data_tlast
.m_axis_data_tdata(m_axis_data_tdata), // output wire [15 : 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(1'b1), // input wire m_axis_data_tready
.m_axis_data_tlast(m_axis_data_tlast), // output wire m_axis_data_tlast
.m_axis_status_tdata(m_axis_status_tdata), // output wire [7 : 0] m_axis_status_tdata
.m_axis_status_tvalid(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(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_fft_overflow(event_fft_overflow), // output wire event_fft_overflow
.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
);
//-----------------------------------------FFT输出结果存成文件----------------------------------------------
//查看FFT输出结果的实虚部
assign out_re=m_axis_data_tdata[7:0];
assign out_im=m_axis_data_tdata[15:8];
//将FFT输出结果存成文件
integer fp_w;
task wire_data();
integer i;
begin
fp_w= $fopen("fft_data_out.txt","w+");//以写的方式打开文件
i=0;
while(1)
begin
@(posedge fifo_r_clk_50M);
begin
if(m_axis_data_tvalid==1'b1)
begin
$fwrite(fp_w,"%d",out_im) ;//写入文件
i=i+1;
if((i%FFT_ADD_CP_LEN)==0)//256+32点CP=288点
begin
$fwrite(fp_w,"\n") ;//写入文件
end
end
end
end
$fclose(fp_w);//以写的方式打开文件
end
endtask
endmodule
|