本次設計源碼下載地址:http://download.csdn.net/detail/noticeable/9915523
課程目標:學習調用quartus II 軟件的FIFO(先進先出)IP核,並通過仿真,了解其時序。
實驗現象:通過quartus II 調用FIFO IP核,並進行不同形式的配置,通過仿真驗證其接口時序。
知識點:FIFO IP核的使用。
FIFO是什么?有什么用?
FIFO 即先進先出存儲器,是一個在FPGA中使用到的具有先進先出特性的一個存儲器,其常被用來作為數據的緩存或者高速異步數據的交互。
FIFO可分為兩種結構:單時鍾FIFO(SCFIFO)和雙時鍾FIFO(DCFIFO),其中雙時鍾FIFO 又可分為普通時鍾(DCFIFO)和混合寬度時鍾FIFO(DCFIFO_MIXED_WIDTHS).。
兩種結構的時鍾的符號圖如下所示:


關於各接口的引腳說明可以在上傳到壓縮包中查看官方文檔,這里不再綴述 。
從結構圖可以看出
單時鍾FIFO具有一個獨立的時鍾端口clock,因此所有輸入信號的讀取都是在clock的上升沿進行的,所有輸出信號的變化也是在clock信號的上升沿的控制下進行的1,即單時鍾FIFO的所有輸入輸出信號都是同步於clock信號的。
在雙時鍾FIFO結構中,寫端口和讀端口分別有獨立的時鍾,所有與寫相關的信號都是同步於寫時鍾wrclk的,所有與讀相關的信號都是同步於讀時鍾rdclk的。在雙時鍾FIFO的符號圖中,位於上部分的為與寫相關的所有信號,位於中間部分的為與讀相關的所有信號,位於下部的為異步清零信號。
其中根據兩種FIFO 各種的用處不同,使用的地點也不同。‘
單時鍾FIFO:
單時鍾FIFO常用於片內數據交互,例如,在FPGA的控制下從外部傳感器讀取到的一連串傳感器數據,首先被寫入FIFO中,然后再以UART串口的數據發送速率將數據依次發送出去。由於傳感器的單次讀取數據可能很快,但並不是時刻都需要采集數據,例如某傳感器使用SPI接口的協議,FPGA以2M的SPI數據速率從該傳感器中讀取20個數據,然后以9600的波特率通過串口發送出去。此過程每秒鍾執行一次。因為2M的數據速率遠高於串口9600的波特率,因此需要將從傳感器中采集到的數據首先用FIFO緩存起來,然后再以串口的數據速率緩慢發送出去。這里,由於傳感器數據的讀取和串口數據的發送都是可以同步於同一個時鍾的,因此可以使用單時鍾結構的FIFO來實現此功能。
雙時鍾FIFO:
雙時鍾FIFO的一個典型應用就是異步數據的收發。
所謂異步數據是指數據的發送端和接收端分別同步與不同的時鍾域,使用雙時鍾FIFO的獨立的讀寫時鍾結構,能夠將不同時鍾域中的數據同步到所需的時鍾域系統中。例如,在一個高速數據采集系統中,實現將高速ADC采集的數據通過千兆以太網發送到PC機。ADC的采樣時鍾(CLK1)由外部專用鎖相環芯片產生,則高速ADC采樣得到的數據就是同步於該時鍾信號的,在FPGA內部,如果FPGA工作時鍾(CLK2)是由獨立的時鍾芯片加片上鎖相環產生的,則CLK1和CLK2就是兩個不同域的時鍾,他們的頻率和相位沒有必然的聯系,假如CLK1為65M,CLK2為125M,那么就不能使用125M的數據來直接采集65M速率的數據,因為兩者數據速率不匹配,在采集過程中會出現包括亞穩態問題在內的一系列問題,所以這里就可以使用一個具備雙時鍾結構的FIFO來進行異步數據的收發。
下圖為使用FIFO進行異步數據收發的簡易系統框圖:

基於千兆以太網傳輸的高速數據采集(8bit)系統
在此系統中,由於ADC的數據位寬為8位,基於UDP協議的以太網發送模塊所需的數據也是8位,因此使用的是非混合寬度的雙時鍾FIFO結構。假如CLK1的頻率為20M,ADC的數據位寬為16位,則可以使用混合寬度的雙時鍾FIFO,在實現異步時鍾域數據收發的同時,實現數據位寬的轉換。通過設置雙時鍾FIFO的寫入位寬為16位,讀取位寬為8位,則可以實現將16位的ADC數據轉換為以太網支持的8位發送數據,然后通過以太網發送到PC機。
總而言之:FIFO在系統中是作為緩沖器存在的一種狀態,其根據緩沖的需求不同選擇不同機構的fifo,從而使兩個或多個不同采樣率的數據可以進行數據的交互。
FIFO的設計方法:
在Altera FPGA中使用FIFO實現用戶功能設計主要有三種實現方式,第一種為用戶根據需求自己編寫FIFO邏輯,當用戶對於FIFO的功能有特殊需求時,可以使用此種方式實現,但此種方式要求用戶有較高的RTL設計能力。第二種方式為使用第三方提供的開源IP核,此種IP核以源碼的形式提供,能夠快速的應用到用戶系統中,當用戶對FIFO功能有特殊需求時,可以在此源碼的基礎上進行修改,以適應自己的系統需求。第三種方式為使用Quartus II軟件提供的免費FIFO IP核,此種方式下,Quartus II軟件為用戶提供了友好的圖形化界面方便用戶對FIFO的各種參數和結構進行配置,生成的FIFO IP核針對Altera不同系列的器件,還可以實現結構上的優化。該FIFO IP核也是通過Verilog語言進行描述的,在Quartus II13.0軟件中,該IP核源碼存放於Quartus II軟件安裝目錄quartus\eda\sim_lib下的altera_mf.v文件中的第48189行(scfifo)(dcfifo結構較多,因此代碼內容很多,與之相關的代碼有幾千行,大家可以在文件中搜索dcfifo即可找到)。由於該FIFO IP核已經提供了幾乎我們設計所需的所有功能,因此在系統設計中,推薦使用該FIFO IP核進行系統設計。
設計步驟:
新建porject ,打開megawizard plug-in manager,選擇FIFO IP核




之后直接next到finish即可。
下面對於IP核的時序接口進行仿真驗證:
編寫testbench文件
`timescale 1ns/1ps `define clock_peride 20 module fifo_tb; reg clk;//時鍾信號接口 reg [15:0] data;//輸入數據接口 reg rdreq;//讀請求 reg sclr;//同步清零 reg wrreq;//寫請求 wire almost_empty;//將空信號 wire almost_full;//將滿信號 wire empty;//空信號 wire full;//滿信號 wire [15:0] q;//輸出接口 wire [7:0] usedw;//可用數據 fifo fifo1 ( .clock(clk), .data(data), .rdreq(rdreq), .sclr(sclr), .wrreq(wrreq), .almost_empty(almost_empty), .almost_full(almost_full), .empty(empty), .full(full), .q(q), .usedw(usedw) ); initial clk=1; always#(`clock_peride/2) clk=~clk; integer i; initial begin wrreq=0; rdreq=0; data=0; #(`clock_peride*20+1); for(i=0;i<=255;i=i+1) //寫操作 begin wrreq=1; data=i; #(`clock_peride); end wrreq=0; #(`clock_peride*20); for(i=0;i<=255;i=i+1) //讀操作 begin rdreq=1; #(`clock_peride); end $stop; end endmodule
設置仿真文件路徑,點擊仿真進行前仿,仿真結果如下,可以通過仿真了解各接口的作用及FIFO的讀寫操作。


下面繼續創建一個雙時鍾FIFO,並進行仿真




之后next 到finish
編寫dc_fifo_tb文件對IP核文件進行仿真
`timescale 1ns/1ps `define wrclock_peride 20 `define rdclock_peride 10 module dc_fifo_tb; reg [15:0] data; //輸入數據 reg rdclk; //讀時鍾 reg rdreq; //讀請求 reg wrclk; //寫時鍾 reg wrreq; //寫請求 wire [7:0] q; //輸出開口 wire rdempty; //讀空 wire wrfull; //寫滿 dc_fifo dc_fifo1 ( .data(data), .rdclk(rdclk), .rdreq(rdreq), .wrclk(wrclk), .wrreq(wrreq), .q(q), .rdempty(rdempty), .wrfull(wrfull) ); initial wrclk = 1; always #(`wrclock_peride/2) wrclk = ~wrclk; initial rdclk = 1; always #(`rdclock_peride/2) rdclk = ~rdclk; integer i; initial begin data=0; rdreq=0; wrreq=0; #(`wrclock_peride*20+1) for (i=0;i <= 255 ;i = i + 1)begin wrreq = 1; data = i + 1024; #`wrclock_peride; end wrreq = 0; #(`rdclock_peride*20); for (i=0;i <= 511 ;i = i + 1)begin rdreq = 1; #(`rdclock_peride); end rdreq = 0; #(`rdclock_peride*20); $stop; end endmodule
設置仿真路徑,並進行仿真,仿真結果如下圖

還可以自己觀察仿真波形中的接口的變化規律,這里就不再綴述了。
