原創(chuàng)聲明:
本原創(chuàng)教程由芯驛電子科技(上海)有限公司(ALINX)創(chuàng)作,版權(quán)歸本公司所有,如需轉(zhuǎn)載,需授權(quán)并注明出處。
適用于板卡型號:
AXU2CGA/AXU2CGB/AXU3EG/AXU4EV-E/AXU4EV-P/AXU5EV-E/AXU5EV-P /AXU9EG/AXU15EG
FIFO是FPGA應(yīng)用當(dāng)中非常重要的模塊,廣泛用于數(shù)據(jù)的緩存,跨時鐘域數(shù)據(jù)處理等。學(xué)好FIFO是FPGA的關(guān)鍵,靈活運用好FIFO是一個FPGA工程師必備的技能。本章主要介紹利用XILINX提供的FIFO IP進行讀寫測試。
1.實驗原理
FIFO: First in, First out代表先進的數(shù)據(jù)先出,后進的數(shù)據(jù)后出。Xilinx在VIVADO里為我們已經(jīng)提供了FIFO的IP核, 我們只需通過IP核例化一個FIFO,根據(jù)FIFO的讀寫時序來寫入和讀取FIFO中存儲的數(shù)據(jù)。
其實FIFO是也是在RAM的基礎(chǔ)上增加了許多功能,F(xiàn)IFO的典型結(jié)構(gòu)如下,主要分為讀和寫兩部分,另外就是狀態(tài)信號,空和滿信號,同時還有數(shù)據(jù)的數(shù)量狀態(tài)信號,與RAM最大的不同是FIFO沒有地址線,不能進行隨機地址讀取數(shù)據(jù),什么是隨機讀取數(shù)據(jù)呢,也就是可以任意讀取某個地址的數(shù)據(jù)。而FIFO則不同,不能進行隨機讀取,這樣的好處是不用頻繁地控制地址線。
![o4YBAGAKLjSAZDBTAABLhrdLeUk478.jpg](https://file.elecfans.com//web1/M00/DB/AE/o4YBAGAKLjSAZDBTAABLhrdLeUk478.jpg)
雖然用戶看不到地址線,但是在FIFO內(nèi)部還是有地址的操作的,用來控制RAM的讀寫接口。其地址在讀寫操作時如下圖所示,其中深度值也就是一個FIFO里最大可以存放多少個數(shù)據(jù)。初始狀態(tài)下,讀寫地址都為0,在向FIFO中寫入一個數(shù)據(jù)后,寫地址加1,從FIFO中讀出一個數(shù)據(jù)后,讀地址加1。此時FIFO的狀態(tài)即為空,因為寫了一個數(shù)據(jù),又讀出了一個數(shù)據(jù)。
![pIYBAGAKLjWAAEloAABLKPOvKBU642.jpg](https://file.elecfans.com//web1/M00/DC/2D/pIYBAGAKLjWAAEloAABLKPOvKBU642.jpg)
可以把FIFO想象成一個水池,寫通道即為加水,讀通道即為放水,假如不間斷的加水和放水,如果加水速度比放水速度快,那么FIFO就會有滿的時候,如果滿了還繼續(xù)加水就會溢出overflow,如果放水速度比加水速度快,那么FIFO就會有空的時候,所以把握好加水與放水的時機和速度,保證水池一直有水是一項很艱巨的任務(wù)。也就是判斷空與滿的狀態(tài),擇機寫數(shù)據(jù)或讀數(shù)據(jù)。
根據(jù)讀寫時鐘,可以分為同步FIFO(讀寫時鐘相同)和異步FIFO(讀寫時鐘不同)。同步FIFO控制比較簡單,不再介紹,本節(jié)實驗主要介紹異步FIFO的控制,其中讀時鐘為75MHz,寫時鐘為100MHz。實驗中會通過VIVADO集成的在想邏輯分析儀ila,我們可以觀察FIFO的讀寫時序和從FIFO中讀取的數(shù)據(jù)。
2. 創(chuàng)建Vivado工程
2.1 添加FIFO IP核
在添加FIFO IP之前先新建一個fifo_test的工程, 然后在工程中添加FIFO IP,方法如下:
2.1.1點擊下圖中IP Catalog,在右側(cè)彈出的界面中搜索fifo,找到FIFO Generator,雙擊打開。
![o4YBAGAKLjWAW1INAACGE_muVv0644.jpg](https://file.elecfans.com//web1/M00/DB/AE/o4YBAGAKLjWAW1INAACGE_muVv0644.jpg)
2.1.2 彈出的配置頁面中,這里可以選擇讀寫時鐘分開還是用同一個,一般來講我們使用FIFO為了緩存數(shù)據(jù),通常兩邊的時鐘速度是不一樣的。所以獨立時鐘是最常用的,我們這里選擇“Independent Clocks Block RAM”,然后點擊“Next”到下一個配置頁面。
![pIYBAGAKLjaARCIAAACb92CE6CA859.jpg](https://file.elecfans.com//web1/M00/DC/2D/pIYBAGAKLjaARCIAAACb92CE6CA859.jpg)
2.1.3 切換到Native Ports欄目下,選擇數(shù)據(jù)位寬16;FIFO深選擇512,實際使用大家根據(jù)需要自行設(shè)置就可以。Read Mode有兩種方式,一個Standard FIFO,也就是平時常見的FIFO,數(shù)據(jù)滯后于讀信號一個周期,還有一種方式為First Word Fall Through,數(shù)據(jù)預(yù)取模式,簡稱FWFT模式。也就是FIFO會預(yù)先取出一個數(shù)據(jù),當(dāng)讀信號有效時,相應(yīng)的數(shù)據(jù)也有效。我們首先做標(biāo)準(zhǔn)FIFO的實驗。
![o4YBAGAKLjaAJ8VzAABRcVxPElY348.jpg](https://file.elecfans.com//web1/M00/DB/AE/o4YBAGAKLjaAJ8VzAABRcVxPElY348.jpg)
2.1.4 切換到Data Counts欄目下,使能Write Data Count(已經(jīng)FIFO寫入多少數(shù)據(jù))和Read Data Count(FIFO中有多少數(shù)據(jù)可以讀),這樣我們可以通過這兩個值來看FIFO內(nèi)部的數(shù)據(jù)多少。點擊OK,Generate生成FIFO IP。
![pIYBAGAKLjeAFWzLAAB-Ay6_q90942.jpg](https://file.elecfans.com//web1/M00/DC/2D/pIYBAGAKLjeAFWzLAAB-Ay6_q90942.jpg)
2.2 FIFO的端口定義與時序
信號名稱 | 方向 | 說明 |
rst | in | 復(fù)位信號,高有效 |
wr_clk | in | 寫時鐘輸入 |
rd_clk | in | 讀時鐘輸入 |
din | in | 寫數(shù)據(jù) |
wr_en | in | 寫使能,高有效 |
rd_en | in | 讀使能,高有效 |
dout | out | 讀數(shù)據(jù) |
full | out | 滿信號 |
empty | out | 空信號 |
rd_data_count | out | 可讀數(shù)據(jù)數(shù)量 |
wr_data_count | out | 已寫入的數(shù)據(jù)數(shù)量 |
FIFO的數(shù)據(jù)寫入和讀出都是按時鐘的上升沿操作的,當(dāng)wr_en信號為高時寫入FIFO數(shù)據(jù),當(dāng)almost_full信號有效時,表示FIFO只能再寫入一個數(shù)據(jù),一旦寫入一個數(shù)據(jù)了,full信號就會拉高,如果在full的情況下wr_en仍然有效,也就是繼續(xù)向FIFO寫數(shù)據(jù),則FIFO的overflow就會有效,表示溢出。
![o4YBAGAKLjeAAXVAAACCecptl1A817.jpg](https://file.elecfans.com//web1/M00/DB/AE/o4YBAGAKLjeAAXVAAACCecptl1A817.jpg)
當(dāng)rd_en信號為高時讀FIFO數(shù)據(jù),數(shù)據(jù)在下個周期有效。valid為數(shù)據(jù)有效信號,almost_empty表示還有一個數(shù)據(jù)讀,當(dāng)再讀一個數(shù)據(jù),empty信號有效,如果繼續(xù)讀,則underflow有效,表示下溢,此時讀出的數(shù)據(jù)無效。
![pIYBAGAKLjiAKGTVAABxkw-Snmw397.jpg](https://file.elecfans.com//web1/M00/DC/2D/pIYBAGAKLjiAKGTVAABxkw-Snmw397.jpg)
而從FWFT模式讀數(shù)據(jù)時序圖可以看出,rd_en信號有效時,有效數(shù)據(jù)D0已經(jīng)在數(shù)據(jù)線上準(zhǔn)備好有效了,不會再延后一個周期。這就是與標(biāo)準(zhǔn)FIFO的不同之處。
![o4YBAGAKLjiAQm9TAABt-HnXDtA296.jpg](https://file.elecfans.com//web1/M00/DB/AE/o4YBAGAKLjiAQm9TAABt-HnXDtA296.jpg)
關(guān)于FIFO的詳細(xì)內(nèi)容可參考pg057文檔,可在xilinx官網(wǎng)下載。
3. FIFO測試程序編寫
我們按照異步FIFO進行設(shè)計,用PLL產(chǎn)生出兩路時鐘,分別是100MHz和75MHz,用于寫時鐘和讀時鐘,也就是寫時鐘頻率高于讀時鐘頻率。
`timescale1ns/1ps//////////////////////////////////////////////////////////////////////////////////modulefifo_test ( input clk, //25MHz時鐘 input rst_n //復(fù)位信號,低電平有效 );reg [15:0] w_data ; //FIFO寫數(shù)據(jù)wire wr_en ; //FIFO寫使能wire rd_en ; //FIFO讀使能wire[15:0] r_data ; //FIFO讀數(shù)據(jù)wire full ; //FIFO滿信號wire empty ; //FIFO空信號wire[8:0] rd_data_count ; //可讀數(shù)據(jù)數(shù)量 wire[8:0] wr_data_count ; //已寫入數(shù)據(jù)數(shù)量 wire clk_100M ; //PLL產(chǎn)生100MHz時鐘wire clk_75M ; //PLL產(chǎn)生100MHz時鐘wire locked ; //PLLlock信號,可作為系統(tǒng)復(fù)位信號,高電平表示lock住wire fifo_rst_n ; //fifo復(fù)位信號,低電平有效wire wr_clk ; //寫FIFO時鐘wire rd_clk ; //讀FIFO時鐘reg [7:0] wcnt ; //寫FIFO復(fù)位后等待計數(shù)器reg [7:0] rcnt ; //讀FIFO復(fù)位后等待計數(shù)器wireclkbuf; BUFGBUFG_inst(.O(clkbuf),//1-bitoutput:Clockoutput..I(clk)//1-bitinput:Clockinput.);//例化PLL,產(chǎn)生100MHz和75MHz時鐘clk_wiz_0fifo_pll(//Clockoutports.clk_out1(clk_100M), //outputclk_out1.clk_out2(clk_75M), //outputclk_out2//Statusandcontrolsignals.reset(~rst_n), //inputreset.locked(locked), //outputlocked//Clockinports.clk_in1(clkbuf) //inputclk_in1); assignfifo_rst_n =locked ; //將PLL的LOCK信號賦值給fifo的復(fù)位信號assignwr_clk =clk_100M ; //將100MHz時鐘賦值給寫時鐘assignrd_clk =clk_75M ; //將75MHz時鐘賦值給讀時鐘/*寫FIFO狀態(tài)機*/localparamW_IDLE=1 ;localparamW_FIFO =2 ;reg[2:0]write_state;reg[2:0]next_write_state;always@(posedgewr_clkornegedgefifo_rst_n)begin if(!fifo_rst_n) write_state<=?W_IDLE; else write_state?<=?next_write_state;endalways@(*)begin case(write_state) W_IDLE: begin if(wcnt?==8'd79)//復(fù)位后等待一定時間,safety?circuit模式下的最慢時鐘60個周期 next_write_state?<=?W_FIFO; else next_write_state?<=?W_IDLE; end W_FIFO: next_write_state?<=?W_FIFO; //一直在寫FIFO狀態(tài) default: next_write_state?<=?W_IDLE; endcaseend//在IDLE狀態(tài)下,也就是復(fù)位之后,計數(shù)器計數(shù)always@(posedge?wr_clk?ornegedge?fifo_rst_n)begin if(!fifo_rst_n) wcnt?<=8'd0; elseif(write_state?==?W_IDLE) wcnt?<=?wcnt?+1'b1; else wcnt?<=8'd0;end//在寫FIFO狀態(tài)下,如果不滿就向FIFO中寫數(shù)據(jù)assign?wr_en?=(write_state?==?W_FIFO)?~full?:1'b0;//在寫使能有效情況下,寫數(shù)據(jù)值加1always@(posedge?wr_clk?ornegedge?fifo_rst_n)begin if(!fifo_rst_n) w_data?<=16'd1; elseif(wr_en) w_data?<=?w_data?+1'b1;end/*?讀FIFO狀態(tài)機?*/localparam??????R_IDLE??????=1 ;localparam??????R_FIFO????? =2 ;reg[2:0]??read_state;reg[2:0]??next_read_state;///產(chǎn)生FIFO讀的數(shù)據(jù)always@(posedge?rd_clk?ornegedge?fifo_rst_n)begin if(!fifo_rst_n) read_state?<=?R_IDLE; else read_state?<=?next_read_state;endalways@(*)begin case(read_state) R_IDLE: begin if(rcnt?==8'd59) //復(fù)位后等待一定時間,safety?circuit模式下的最慢時鐘60個周期 next_read_state?<=?R_FIFO; else next_read_state?<=?R_IDLE; end R_FIFO: next_read_state?<=?R_FIFO?; //一直在讀FIFO狀態(tài) default: next_read_state?<=?R_IDLE; endcaseend//在IDLE狀態(tài)下,也就是復(fù)位之后,計數(shù)器計數(shù)always@(posedge?rd_clk?ornegedge?fifo_rst_n)begin if(!fifo_rst_n) rcnt?<=8'd0; elseif(write_state?==?W_IDLE) rcnt?<=?rcnt?+1'b1; else rcnt?<=8'd0;end//在讀FIFO狀態(tài)下,如果不空就從FIFO中讀數(shù)據(jù)assign?rd_en?=(read_state?==?R_FIFO)?~empty?:1'b0;//-----------------------------------------------------------//實例化FIFOfifo_ip?fifo_ip_inst?(.rst????????????(~fifo_rst_n???? ),//?input?rst.wr_clk?????????(wr_clk?????????? ),//?input?wr_clk.rd_clk?????????(rd_clk?????????? ),//?input?rd_clk.din????????????(w_data??????? ),//?input?[15?:?0]?din.wr_en??????????(wr_en???????? ),//?input?wr_en.rd_en??????????(rd_en???????? ),//?input?rd_en.dout???????????(r_data??????? ),//?output?[15?:?0]?dout.full???????????(full????????? ),//?output?full.empty??????????(empty???????? ),//?output?empty.rd_data_count??(rd_data_count ),//?output?[8?:?0]?rd_data_count.wr_data_count??(wr_data_count )//?output?[8?:?0]?wr_data_count);//寫通道邏輯分析儀ila_m0?ila_wfifo?( .clk (wr_clk ), .probe0 (w_data ), .probe1 (wr_en ), .probe2 (full ), .probe3 (wr_data_count ));//讀通道邏輯分析儀ila_m0?ila_rfifo?( .clk (rd_clk ), .probe0 (r_data ), .probe1 (rd_en ), .probe2 (empty ), .probe3 (rd_data_count )); endmodule
在程序中采用PLL的lock信號作為fifo的復(fù)位,同時將100MHz時鐘賦值給寫時鐘,75MHz時鐘賦值給讀時鐘。
![pIYBAGAKLjiAAwvGAAA_nneiMx8957.jpg](https://file.elecfans.com//web1/M00/DC/2D/pIYBAGAKLjiAAwvGAAA_nneiMx8957.jpg)
有一點需要注意的是,F(xiàn)IFO設(shè)置默認(rèn)為采用safety circuit,此功能是保證到達內(nèi)部RAM的輸入信號是同步的,在這種情況下,如果異步復(fù)位后,則需要等待60個最慢時鐘周期,在本實驗中也就是75MHz的60個周期,那么100MHz時鐘大概需要(100/75)x60=80個周期。
![o4YBAGAKLjmABmUwAAEv4rMJnkE142.jpg](https://file.elecfans.com//web1/M00/DB/AE/o4YBAGAKLjmABmUwAAEv4rMJnkE142.jpg)
因此在寫狀態(tài)機中,等待80個周期進入寫FIFO狀態(tài)
![pIYBAGAKLjmAb9MdAAA7cx7i5Dk736.jpg](https://file.elecfans.com//web1/M00/DC/2D/pIYBAGAKLjmAb9MdAAA7cx7i5Dk736.jpg)
在讀狀態(tài)機中,等待60個周期進入讀狀態(tài)
![o4YBAGAKLjqABJMBAABxRY3H-0o324.jpg](https://file.elecfans.com//web1/M00/DB/AE/o4YBAGAKLjqABJMBAABxRY3H-0o324.jpg)
如果FIFO不滿,就一直向FIFO寫數(shù)據(jù)
![o4YBAGAKLjuAalN_AAAj13yUJsg023.jpg](https://file.elecfans.com//web1/M00/DB/AE/o4YBAGAKLjuAalN_AAAj13yUJsg023.jpg)
如果FIFO不空,就一直從FIFO讀數(shù)據(jù)
![pIYBAGAKLjuAfV3fAAAU083a6wA674.jpg](https://file.elecfans.com//web1/M00/DC/2D/pIYBAGAKLjuAfV3fAAAU083a6wA674.jpg)
例化兩個邏輯分析儀,分別連接寫通道和讀通道的信號
![o4YBAGAKLjyAJ9YRAAAlOySHdx8062.jpg](https://file.elecfans.com//web1/M00/DB/AE/o4YBAGAKLjyAJ9YRAAAlOySHdx8062.jpg)
4. 仿真
以下為仿真結(jié)果,可以看到寫使能wr_en有效后開始寫數(shù)據(jù),初始值為0001,從開始寫到empty不空,是需要一定周期的,因為內(nèi)部還要做同步處理。在不空后,開始讀數(shù)據(jù),讀出的數(shù)據(jù)相對于rd_en滯后一個周期。
![pIYBAGAKLjyAWBVoAAB5OapzUlA894.jpg](https://file.elecfans.com//web1/M00/DC/2D/pIYBAGAKLjyAWBVoAAB5OapzUlA894.jpg)
在后面可以看到如果FIFO滿了,根據(jù)程序的設(shè)計,滿了就不向FIFO寫數(shù)據(jù)了,wr_en也就拉低了。為什么會滿呢,就是因為寫時鐘比讀時鐘快。如果將寫時鐘與讀時鐘調(diào)換,也就是讀時鐘快,就會出現(xiàn)讀空的情況,大家可以試一下。
![o4YBAGAKLj2AS_PBAACJdMUQXZI456.jpg](https://file.elecfans.com//web1/M00/DB/AE/o4YBAGAKLj2AS_PBAACJdMUQXZI456.jpg)
如果將FIFO的Read Mode改成First Word Fall Through
![pIYBAGAKLj6ANj9JAABq3rplyZI241.jpg](https://file.elecfans.com//web1/M00/DC/2D/pIYBAGAKLj6ANj9JAABq3rplyZI241.jpg)
仿真結(jié)果如下,可以看到rd_en有效的時候數(shù)據(jù)也有效,沒有相差一個周期
![o4YBAGAKLj6ALf_7AAB92agic-o492.jpg](https://file.elecfans.com//web1/M00/DB/AE/o4YBAGAKLj6ALf_7AAB92agic-o492.jpg)
5. 板上驗證
生成好bit文件,下載bit文件,會出現(xiàn)兩個ila,先來看寫通道的,可以看到full信號為高電平時,wr_en為低電平,不再向里面寫數(shù)據(jù)。
![pIYBAGAKLj-AalwOAACH8Z5EqA0818.jpg](https://file.elecfans.com//web1/M00/DC/2D/pIYBAGAKLj-AalwOAACH8Z5EqA0818.jpg)
而讀通道也與仿真一致
![o4YBAGAKLj-AW9wkAAB-pRU40VM994.jpg](https://file.elecfans.com//web1/M00/DB/AE/o4YBAGAKLj-AW9wkAAB-pRU40VM994.jpg)
如果以rd_en上升沿作為觸發(fā)條件,點擊運行,然后按下復(fù)位,也就是我們綁定的PL KEY1,會出現(xiàn)下面的結(jié)果,與仿真一致,標(biāo)準(zhǔn)FIFO模式下,數(shù)據(jù)滯后rd_en一個周期。
![pIYBAGAKLkCAd7uQAACF3AzQlpA206.jpg](https://file.elecfans.com//web1/M00/DC/2D/pIYBAGAKLkCAd7uQAACF3AzQlpA206.jpg)
-
FPGA
+關(guān)注
關(guān)注
1630文章
21803瀏覽量
606469 -
Xilinx
+關(guān)注
關(guān)注
71文章
2172瀏覽量
122269 -
fifo
+關(guān)注
關(guān)注
3文章
390瀏覽量
43903 -
Zynq
+關(guān)注
關(guān)注
10文章
610瀏覽量
47329 -
MPSoC
+關(guān)注
關(guān)注
0文章
199瀏覽量
24336
發(fā)布評論請先 登錄
相關(guān)推薦
【ZYNQ Ultrascale+ MPSOC FPGA教程】第六章FPGA片內(nèi)RAM讀寫測試實驗
![【<b class='flag-5'>ZYNQ</b> <b class='flag-5'>Ultrascale+</b> <b class='flag-5'>MPSOC</b> <b class='flag-5'>FPGA</b>教程】第六<b class='flag-5'>章</b><b class='flag-5'>FPGA</b><b class='flag-5'>片</b><b class='flag-5'>內(nèi)</b>RAM<b class='flag-5'>讀寫</b><b class='flag-5'>測試</b><b class='flag-5'>實驗</b>](https://file.elecfans.com//web1/M00/DB/AE/o4YBAGAKLa-ABoycAACNMZ5WWzQ972.jpg)
如何調(diào)試Zynq UltraScale+ MPSoC VCU DDR控制器
如何調(diào)試Zynq UltraScale+ MPSoC VCU DDR控制器
51單片機第八章素材
Zynq UltraScale+ MPSoC的發(fā)售消息
【ZYNQ Ultrascale+ MPSOC FPGA教程】第八章FPGA片內(nèi)FIFO讀寫測試實驗
![【<b class='flag-5'>ZYNQ</b> <b class='flag-5'>Ultrascale+</b> <b class='flag-5'>MPSOC</b> <b class='flag-5'>FPGA</b>教程】<b class='flag-5'>第八章</b><b class='flag-5'>FPGA</b><b class='flag-5'>片</b><b class='flag-5'>內(nèi)</b><b class='flag-5'>FIFO</b><b class='flag-5'>讀寫</b><b class='flag-5'>測試</b><b class='flag-5'>實驗</b>](https://file.elecfans.com/web1/M00/DB/9A/o4YBAGAJi7qAAp71AABTmac2MoU816.jpg)
【ZYNQ Ultrascale+ MPSOC FPGA教程】第七章 FPGA片內(nèi)ROM測試實驗
![【<b class='flag-5'>ZYNQ</b> <b class='flag-5'>Ultrascale+</b> <b class='flag-5'>MPSOC</b> <b class='flag-5'>FPGA</b>教程】第七<b class='flag-5'>章</b> <b class='flag-5'>FPGA</b><b class='flag-5'>片</b><b class='flag-5'>內(nèi)</b>ROM<b class='flag-5'>測試</b><b class='flag-5'>實驗</b>](https://file.elecfans.com/web1/M00/DC/1B/pIYBAGAJlkSADj6fAAAXhmyP0T4901.png)
【ZYNQ Ultrascale+ MPSOC FPGA教程】第六章 FPGA片內(nèi)RAM讀寫測試實驗
![【<b class='flag-5'>ZYNQ</b> <b class='flag-5'>Ultrascale+</b> <b class='flag-5'>MPSOC</b> <b class='flag-5'>FPGA</b>教程】第六<b class='flag-5'>章</b> <b class='flag-5'>FPGA</b><b class='flag-5'>片</b><b class='flag-5'>內(nèi)</b>RAM<b class='flag-5'>讀寫</b><b class='flag-5'>測試</b><b class='flag-5'>實驗</b>](https://file.elecfans.com/web1/M00/DC/1C/pIYBAGAJnVSAeGXsAACSoVXg6Dw420.jpg)
評論