最近在学习宽带数字接收机的一些东西,其中多相滤波是属于其中比较关键的一环,笔者在matlab上成功仿真了多相滤波这一环节后,便想着在FPGA上实现多相滤波,多相滤波的最后一个环节是IFFT运算,在vivado上实现IFFT还是比较简单的,使用FFT IP核的config通道配置为IFFT模式即可,但在具体运算上,还是有一些细节需要考虑,比如IFFT运算的实时性,吞吐量。
ADC采集的数据是流模式,不间断的,所以在进行IFFT运算时,便需要考虑使用FFT IP核的流水线结构,以实现数据的实时计算,如果使用非实时模式,且不采用流水线结构,因FFT计算的延迟过大,数据缓存存在极大压力,所以在本文中,笔者使用FFT的流水线架构,实时模式,实现IFFT计算。
FFT IP配置如下图所示,本文中采用32点FFT,也即经过配置后,32点IFFT。
在进行IFFT的使用时,笔者发现了一个问题,在实时模式下,FFT ip核的数据通道的s_axis_tdata_ready信号会出现拉低情况,并固定出现在数据开始传输之后的几个周期内,如下图所示,此时IFFT的配置也尚未完成
笔者一开始以为是没有勾选FFT IP核的复位配置,在添加了复位控制后其仍然出现这类情况。
那么如何解决这个问题,因为AXI_STREAM协议是在tready和tvalid信号同时为高时才缓存当前数据,如果不解决这个问题,必然会对IFFT运算结果产生影响,故可以在IFFT运算与数据输入之间,添加一级FIFO缓存,当tready信号为高时才进行数据的传输,这样就可以保证每一个数据成功缓存,而为了保证tvalid与当前时刻的tready信号同步为高,对于FIFO的读数据模式选择First Word Fall Through模式,即将最先写入的数据,直接放在输出端口上,此时的读延迟为0
同时,对于FIFO的读控制也使用组合逻辑,保证没有周期延迟,即当tready信号为高且fifo的empty信号不为1时进行FIFO的读使能,修改逻辑后的仿真如下。
可以看出,成功实现了数据的不丢失写入,接下来进行IFFT运算结果的查看
笔者是传输了1~32这32个数据进行第一次IFFT运算,同样在matlab中,进行这32点数据的IFFT运算,并且需要注意的是vivado的IFFT运算结果需要除以运算点数才是真实的运算结果,可以直接在代码中将运算结果进行移位即可,需要注意有符号数据的移位要考虑符号位的保持,在本文中,直接在MATLAB的运算结果上乘以32,进行数据查看。对于有符号数据,二进制补码的移位,笔者会在以后的文章中进行详细介绍。
还需要注意一点,在实际仿真中发现,如果FFT IP核添加了复位逻辑,在进行IFFT运算时,会出现运算结果的虚部与实际结果呈相反数的情况,目前只能将复位关闭,未能找到其他的解决办法。如果有同学知道如何解决这一问题,欢迎在评论区进行指正。
verilog代码如下
module ifft_test(
input i_clk ,
input i_rst ,
input [11:0] i_data ,
input i_valid ,
output o_active
);
reg [7 :0] s_axis_config_tdata ;
reg s_axis_config_tvalid ;
wire s_axis_config_tready ;
wire [47:0] m_axis_data_tdata ;
wire m_axis_data_tvalid ;
wire m_axis_data_tlast ;
wire s_axis_data_tready ;
wire event_frame_started ;
wire event_tlast_unexpected ;
wire event_tlast_missing ;
wire event_data_in_channel_halt ;
wire w_config_activate ;
wire signed[17:0] w_ifft_re ;
wire signed[17:0] w_ifft_im ;
wire w_fifo_rd_en ;
wire [11:0] w_fifo_dout ;
wire w_fifo_full ;
wire w_fifo_empty ;
wire s_axis_data_tvalid ;
FIFO_12X64 FIFO_12X64_U0 (
.clk (i_clk ),
.din (i_data ),
.wr_en (i_valid ),
.rd_en (w_fifo_rd_en ),
.dout (w_fifo_dout ),
.full (w_fifo_full ),
.empty (w_fifo_empty )
);
ifft_32 ifft_32_u0 (
.aclk (i_clk ),
// .aresetn (~i_rst ),
.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_tdata ({4'd0,12'd0,4'd0,w_fifo_dout}),
.s_axis_data_tvalid (s_axis_data_tvalid ),
.s_axis_data_tready (s_axis_data_tready ),
.s_axis_data_tlast (0),
.m_axis_data_tdata (m_axis_data_tdata ),
.m_axis_data_tvalid (m_axis_data_tvalid ),
.m_axis_data_tlast (m_axis_data_tlast ),
.event_frame_started (event_frame_started ),
.event_tlast_unexpected (event_tlast_unexpected ),
.event_tlast_missing (event_tlast_missing ),
.event_data_in_channel_halt (event_data_in_channel_halt )
);
assign w_ifft_re = m_axis_data_tdata[17 :0] ;
assign w_ifft_im = m_axis_data_tdata[41:24] ;
assign w_config_activate = s_axis_config_tvalid & s_axis_config_tready;
assign w_fifo_rd_en = s_axis_data_tready & !w_fifo_empty;
assign o_active = w_config_activate;
assign s_axis_data_tvalid = w_fifo_rd_en;
always@(posedge i_clk,posedge i_rst)
begin
if(i_rst)
s_axis_config_tdata <= 'd0;
else
s_axis_config_tdata <= 'd0;
end
always@(posedge i_clk,posedge i_rst)
begin
if(i_rst)
s_axis_config_tvalid <= 'b1;
else if(s_axis_config_tvalid & s_axis_config_tready)
s_axis_config_tvalid <= 'b0;
else
s_axis_config_tvalid <= s_axis_config_tvalid;
end
endmodule
整体来说,这个功能实现起来还是比较简单的,笔者也是个萌新,在学习过程中探索,如果有什么问题,欢迎大家在评论区批评指正。
如果觉得笔者的文章对您有些帮助,希望点个赞,激励下笔者的创作,不胜感激!
关于本文的完整matlab代码与vivado工程,可以私信博主免费领取!!!