一 傅里葉變換FFT
想必大家對傅里葉老人家都不陌生了,網(wǎng)上也有這方面的很多資料。通過FFT將時域信號轉(zhuǎn)換到頻域,從而對一些在時域上難以分析的信號在頻域上進(jìn)行處理。在這里,我們需要注意采樣頻率、FFT采樣點(diǎn)數(shù)這兩個參數(shù):
根據(jù)奈奎斯特采樣定理,采樣頻率需大于信號頻率的兩倍;
FFT采樣點(diǎn)數(shù),代表對信號在頻域的采樣數(shù);
采樣頻率Fs和采樣點(diǎn)數(shù)N決定了信號的頻域分辨力,即分辨力=Fs/N,即N越大,頻域分辨力越好,反之頻域分辨力越差。
二 Xilinx FFT v9.0
1.輸入輸出端口
如上圖所示,左側(cè)的端口均為輸入端口,右側(cè)端口均為輸出端口,其中,S_AXIS_DATA為輸入數(shù)據(jù)端口,我們要進(jìn)行FFT的數(shù)據(jù)需要通過這根線輸入給IP核;S_AXIS_CONFIG為輸入配置端口,這個信號包含了對數(shù)據(jù)進(jìn)行FFT還是IFFT、縮放因子、FFT變換點(diǎn)數(shù)等信息;FFT變換后的數(shù)據(jù)從M_AXIS_DATA端口輸出。這些端口的具體功能可以參見pg109手冊。
2.Vivado中IP核的配置
打開Vivado軟件,我的版本是2018.04
找到FFT IP核后,雙擊,彈出如下對話框:
第二頁implementation
第三頁
配置完成后,我們可以點(diǎn)擊左側(cè)的implementation detail選項卡,看到IP核的具體信息:
其中包含了S_AXIS_DATA_TDATA、S_AXIS_CONFIG_TDATA以及M_AXIS_DATA_TDATA的數(shù)據(jù)格式,我們需要加以關(guān)注:
S_AXIS_DATA_TDATA:共32位,其中低16位為輸入數(shù)據(jù)的實(shí)部,高16位為輸入數(shù)據(jù)的虛部(但在實(shí)際使用中,高16位才是實(shí)部,低16位是虛部,如果有大神明白是咋回事兒,歡迎留言)
S_AXIS_CONFIG_TDATA:最低位第0位,決定對數(shù)據(jù)進(jìn)行FFT還是IFFT,置1時FFT,清零時IIFT,由于要進(jìn)行補(bǔ)零操作,因此在最終寫入S_AXIS_CONFIG_TDATA時,除了最低位以外,還要再補(bǔ)七個零,補(bǔ)到8位
M_AXIS_DATA_TDATA:48位數(shù)據(jù)輸出,低24位為實(shí)部,高24位為虛部
3.軟件仿真
IP核配置完成后,下面開始編寫我們的TestBench文件。
我們通過matlab對F(t) = 200 + 100cos(2pi10t) + 100cos(2pi30t)這個信號以Fs = 100HZ進(jìn)行采樣,采樣點(diǎn)數(shù)N = 128,采樣完成后,將數(shù)據(jù)轉(zhuǎn)換為16位二進(jìn)制,并存入txt文件中。matlab程序如下:
clear Fs=100; %采樣率1ns一個點(diǎn) %t=0:1/Fs:63/Fs; %數(shù)據(jù)時長:64個采樣周期 N = 128; n = 1:N; t = n/Fs; % 生成測試信號 f1 = 10; % f2 = 30; % s1 = cos(2*pi*f1*t); s2 = cos(2*pi*f2*t); signalN = 2 + s1 + s2 ; data_before_fft = 100*signalN; %系數(shù)放大100倍 fp = fopen('D:ynq_Coredata_before_fft.txt','w'); for i = 1:N if(data_before_fft(i)>=0) temp= dec2bin(data_before_fft(i),16); else temp= dec2bin(data_before_fft(i)+2^16+1, 16); end for j=1:16 fprintf(fp,'%s',temp(j)); end fprintf(fp,' '); end fclose(fp); y = fft(data_before_fft,N); y = abs(y); f = n*Fs/N; plot(f,y);
程序執(zhí)行結(jié)束后,我們可以看到在指定目錄下新建了一個txt文件,內(nèi)容如下所示:
由于我們在配置IP核的時候配置了數(shù)據(jù)位寬為16位,因此我們存入的數(shù)據(jù)也要設(shè)置為16位的。采樣點(diǎn)數(shù)N=128,因此一共有128個這樣的數(shù)據(jù)。
得到采樣數(shù)據(jù)后,在vivado中新建一個sim文件:
TB文件代碼如下:
`timescale 1ns / 1ps module FFT_test2(); reg clk; reg rst_n; reg signed [15:0] Time_data_I[127:0]; reg data_finish_flag; wire fft_s_config_tready; reg signed [31:0] fft_s_data_tdata; reg fft_s_data_tvalid; wire fft_s_data_tready; reg fft_s_data_tlast; wire signed [47:0] fft_m_data_tdata; wire signed [7:0] fft_m_data_tuser; wire fft_m_data_tvalid; reg fft_m_data_tready; 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; reg [7:0] count; reg signed [23:0] fft_i_out; reg signed [23:0] fft_q_out; reg signed [47:0] fft_abs; initial begin clk = 1'b1; rst_n = 1'b0; fft_m_data_tready = 1'b1; $readmemb("D:/Zynq_Core/data_before_fft.txt",Time_data_I); end always #5 clk = ~clk; always @ (posedge clk or negedge rst_n) begin if(!rst_n) begin fft_s_data_tvalid <= 1'b0; fft_s_data_tdata <= 32'd0; fft_s_data_tlast <= 1'b0; data_finish_flag <= 1'b0; count <= 8'd0; rst_n = 1'b1; end else if (fft_s_data_tready) begin if(count == 8'd127) begin fft_s_data_tvalid <= 1'b1; fft_s_data_tlast <= 1'b1; fft_s_data_tdata <= {Time_data_I[count],16'd0}; count <= 8'd0; data_finish_flag <= 1'b1; end else begin fft_s_data_tvalid <= 1'b1; fft_s_data_tlast <= 1'b0; fft_s_data_tdata <= {Time_data_I[count],16'd0}; count <= count + 1'b1; end end else begin fft_s_data_tvalid <= 1'b0; fft_s_data_tlast <= 1'b0; fft_s_data_tdata <= fft_s_data_tdata; end end always @ (posedge clk) begin if(fft_m_data_tvalid) begin fft_i_out <= fft_m_data_tdata[23:0]; fft_q_out <= fft_m_data_tdata[47:24]; end end always @ (posedge clk) begin fft_abs <= $signed(fft_i_out)* $signed(fft_i_out)+ $signed(fft_q_out)* $signed(fft_q_out); end //fft ip核例化 xfft_0 u_fft( .aclk(clk), // 時鐘信號(input) .aresetn(rst_n), // 復(fù)位信號,低有效(input) .s_axis_config_tdata(8'd1), // ip核設(shè)置參數(shù)內(nèi)容,為1時做FFT運(yùn)算,為0時做IFFT運(yùn)算(input) .s_axis_config_tvalid(1'b1), // ip核配置輸入有效,可直接設(shè)置為1(input) .s_axis_config_tready(fft_s_config_tready), // output wire s_axis_config_tready //作為接收時域數(shù)據(jù)時是從設(shè)備 .s_axis_data_tdata(fft_s_data_tdata), // 把時域信號往FFT IP核傳輸?shù)臄?shù)據(jù)通道,[31:16]為虛部,[15:0]為實(shí)部(input,主->從) .s_axis_data_tvalid(fft_s_data_tvalid), // 表示主設(shè)備正在驅(qū)動一個有效的傳輸(input,主->從) .s_axis_data_tready(fft_s_data_tready), // 表示從設(shè)備已經(jīng)準(zhǔn)備好接收一次數(shù)據(jù)傳輸(output,從->主),當(dāng)tvalid和tready同時為高時,啟動數(shù)據(jù)傳輸 .s_axis_data_tlast(fft_s_data_tlast), // 主設(shè)備向從設(shè)備發(fā)送傳輸結(jié)束信號(input,主->從,拉高為結(jié)束) //作為發(fā)送頻譜數(shù)據(jù)時是主設(shè)備 .m_axis_data_tdata(fft_m_data_tdata), // FFT輸出的頻譜數(shù)據(jù),[47:24]對應(yīng)的是虛部數(shù)據(jù),[23:0]對應(yīng)的是實(shí)部數(shù)據(jù)(output,主->從)。 .m_axis_data_tuser(fft_m_data_tuser), // 輸出頻譜的索引(output,主->從),該值*fs/N即為對應(yīng)頻點(diǎn); .m_axis_data_tvalid(fft_m_data_tvalid), // 表示主設(shè)備正在驅(qū)動一個有效的傳輸(output,主->從) .m_axis_data_tready(fft_m_data_tready), // 表示從設(shè)備已經(jīng)準(zhǔn)備好接收一次數(shù)據(jù)傳輸(input,從->主),當(dāng)tvalid和tready同時為高時,啟動數(shù)據(jù)傳輸 .m_axis_data_tlast(fft_m_data_tlast), // 主設(shè)備向從設(shè)備發(fā)送傳輸結(jié)束信號(output,主->從,拉高為結(jié)束) //其他輸出數(shù)據(jù) .event_frame_started(fft_event_frame_started), // output wire event_frame_started .event_tlast_unexpected(fft_event_tlast_unexpected), // output wire event_tlast_unexpected .event_tlast_missing(fft_event_tlast_missing), // output wire event_tlast_missing .event_status_channel_halt(fft_event_status_channel_halt), // output wire event_status_channel_halt .event_data_in_channel_halt(fft_event_data_in_channel_halt), // output wire event_data_in_channel_halt .event_data_out_channel_halt(fft_event_data_out_channel_halt) // output wire event_data_out_channel_halt ); endmodule
由于我們設(shè)置程序一直保持正向FFT模式,因此將s_axis_config_tdata始終寫入1即可。
同時我們還要注意文件讀入函數(shù)readmemb(),這一函數(shù)是以二進(jìn)制格式讀入數(shù)據(jù),而readmemh()是以16進(jìn)制讀入數(shù)據(jù),大家不要搞混了。我就是用readmemh()弄了半天,結(jié)果數(shù)據(jù)一個也不對,找了半天才發(fā)現(xiàn)那是h不是b…[cry][cry][cry]
4.仿真分析
運(yùn)行仿真后,時序圖如下所示:
如圖所示,首先判斷fft_s_data_tready信號是否為高電平,即IP核是否準(zhǔn)備好了接收數(shù)據(jù),當(dāng)檢測到該信號有效后,將fft_s_data_tvalid信號拉高,準(zhǔn)備向IP核寫入數(shù)據(jù),并開啟count計數(shù)。在fft_s_data_tvalid有效期間內(nèi),讀出指定txt文件中的數(shù)據(jù),并在低16位進(jìn)行補(bǔ)零處理后,按順序?qū)懭氲絝ft_s_data_tdata信號線中。當(dāng)count計數(shù)到127,即最后一個數(shù)據(jù)時,將fft_s_data_tlast信號拉高,代表數(shù)據(jù)寫入完成。
可以看到,在數(shù)據(jù)寫入完成后(fft_s_data_tlast出現(xiàn)脈沖),fft_s_data_tready變?yōu)榈碗娖剑瑒t代表IP核此時變?yōu)槊顟B(tài),不能再繼續(xù)寫入數(shù)據(jù)。
延時一段時間后,fft_m_data_tvalid變?yōu)楦唠娖?,代表fft_m_data_tdata中將輸出有效數(shù)據(jù),即128點(diǎn)FFT的計算結(jié)果。結(jié)果的實(shí)部和虛部分別見上圖中的fft_q_out和fft_i_out。將IP核的計算結(jié)果與matlab的計算結(jié)果相對比,發(fā)現(xiàn)實(shí)部數(shù)據(jù)基本正確,虛部數(shù)據(jù)略有偏差。
通過對IP核的計算結(jié)果進(jìn)行分析,發(fā)現(xiàn)數(shù)據(jù)在第0個、第14個和34個數(shù)據(jù)的位置出現(xiàn)峰值,對應(yīng)0HZ、10HZ和30HZ,正代表著原始信號中的這三個頻率分量,因此FFT IP核計算結(jié)果正確無誤。
同時,從仿真中還可以看出,當(dāng)FFT計算結(jié)果輸出完成后,信號fft_m_data_tlast變?yōu)楦唠娖剑頂?shù)據(jù)輸出結(jié)束,并在延時一小段時間后,fft_s_data_tready重新變?yōu)榈碗娖剑鞩P核重新進(jìn)入到空閑狀態(tài)??梢赃M(jìn)行對IP核下一組數(shù)據(jù)的輸入。
原文鏈接:
https://gitcode.csdn.net/6628ae7c9c80ea0d22719dd0.html
-
Xilinx
+關(guān)注
關(guān)注
73文章
2183瀏覽量
124489 -
IP
+關(guān)注
關(guān)注
5文章
1790瀏覽量
151567 -
FFT
+關(guān)注
關(guān)注
15文章
444瀏覽量
60682 -
Vivado
+關(guān)注
關(guān)注
19文章
831瀏覽量
68328
原文標(biāo)題:Vivado Xilinx FFT IP核v9.0 使用詳解
文章出處:【微信號:gh_9d70b445f494,微信公眾號:FPGA設(shè)計論壇】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
關(guān)于altera FFT IP核 matlab 仿真問題請教
xilinx FPGA的FFT IP核的調(diào)用
基于FPGA的FFT和IFFT IP核應(yīng)用實(shí)例
玩轉(zhuǎn)Zynq連載48——[ex67] Vivado FFT和IFFT IP核應(yīng)用實(shí)例
fft核心v9.0的數(shù)據(jù)表如何實(shí)現(xiàn)FFT核心
基于Xilinx_FPGA_IP核的FFT算法的設(shè)計與實(shí)現(xiàn)
Xilinx Vivado的使用詳細(xì)介紹(3):使用IP核

FFT的分析和Xilinx FFT核的介紹
vivado調(diào)用IP核詳細(xì)介紹
ACDSee v9.0 Build 50

評論