[TOC]
> # 簡介
**FIFO**(First input First output)簡單說就是指先進先出。
**FIFO的作用:**
* 增加數據傳輸率
* 處理大量數據流
* 匹配不同傳輸率的系統
* 對連續的[數據流](https://baike.baidu.com/item/%E6%95%B0%E6%8D%AE%E6%B5%81)進行緩存,防止在進機和存儲操作時丟失數據
* 數據集中起來進行進機和存儲,可避免頻繁的總線操作,減輕CPU的負擔
* 允許系統進行DMA操作,提高數據的傳輸速度
## 一些關鍵點
* FIFO的本質是RAM,先進先出
* FIFO深度:簡單來說就是需要存多少個數據
* FIFO位寬:每個數據的寬度
* FIFO有同步和異步兩種,**同步即讀寫時鐘相同,異步即讀寫時鐘不相同**
* 同步FIFO用的少,可以作為數據緩存
* 異步FIFO可以解決跨時鐘域的問題,在應用時需根據實際情況考慮好fifo深度即可
># 同步FIFO的實現
fifo中的ram一般是雙端口ram,所以有獨立的讀寫地址。因此可以一種是設置**讀,寫指針**,寫指針指向下一個要寫入數據的地址,讀指針指向下一個要讀的地址,最后通過比較讀指針和寫指針的大小來確定空滿狀態。</br>
設置一個計數器,當寫使能有效的時候計數器加一;當讀使能有效的時候,計數器減一,將計數器與ram的size進行比較來判斷fifo的空滿狀態。這種方法設計比較簡單,但是需要的額外的計數器,就會產生額外的資源,而且當fifo比較大時,會降低fifo最終可以達到的速度。
># 異步FIFO的設計
## 讀空信號如何產生?寫滿信號如何產生?
**讀空信號**:復位的時候,讀指針和寫指針相等,讀空信號有效(這里所說的指針其實就是讀地址、寫地址)當讀指針趕上寫指針的時候,寫指針等于讀指針意味著最后一個數據被讀完,此時讀空信號有效</br>
**寫滿信號**:當寫指針比讀指針多一圈時,寫指針等于讀指針意味著寫滿了,此時寫滿信號有效。我們會發現讀空的條件是寫指針等于讀指針,寫滿的條件也是寫指針等于讀指針,到底如何區分呢?</br>
**解決方法**:將指針的位寬多定義一位</br>
舉個例子說明:假設要設計深度為8的異步FIFO,此時定義讀寫指針只需要3位(2^3=8)就夠用了,但是我們在設計時將指針的位寬設計成4位,最高位的作用就是區分是讀空還是寫滿,具體**理論1**如下
>**當最高位相同,其余位相同認為是讀空**
>**當最高位不同,其余位相同認為是寫滿**
注意:理論1試用的是二進制數之間的空滿比較判斷。
使用格雷碼時:
用格雷碼判斷是否為讀空或寫滿時應使用**理論2**,看最高位和次高位是否相等,具體如下:
>**當最高位和次高位相同,其余位相同認為是讀空**
>**當最高位和次高位不同,其余位相同認為是寫滿**
補:理論2這個判斷方法適用于用格雷碼判斷比較空滿</br>
在實際設計中如果不想用格雷碼比較,就可以利用格雷碼將讀寫地址同步到一個時鐘域后再將格雷碼再次轉化成二進制數再用理論1進行比較就好了。
## 由于是異步FIFO的設計,讀寫時鐘不一樣,在產生讀空信號和寫滿信號時,會涉及到跨時鐘域的問題,如何解決?
**跨時鐘域的問題:** 上面我們已經提到要通過比較讀寫指針來判斷產生讀空和寫滿信號,但是讀指針是屬于讀時鐘域的,寫指針是屬于寫時鐘域的,而異步FIFO的讀寫時鐘域不同,是異步的,要是將讀時鐘域的讀指針與寫時鐘域的寫指針不做任何處理直接比較肯定是錯誤的,因此我們需要進行同步處理以后仔進行比較
**解決方法**:**兩級寄存器同步****+格雷碼**</br>
同步的過程有兩個:
**(1)將寫時鐘域的寫指針同步到讀時鐘域,將同步后的寫指針與讀時鐘域的讀指針進行比較產生讀空信號**
**(2)將讀時鐘域的讀指針同步到寫時鐘域,將同步后的讀指針與寫時鐘域的寫指針進行比較產生寫滿信號**</br>
同步的思想就是用**兩級寄存器同步**,簡單說就是打兩拍,相信有點基礎的早都爛熟于心,就不再多做解釋,不懂的可以看看代碼結合理解。</br>
只是這樣簡單的同步就可以了嗎?no no no,可怕的亞穩態還在等著你。</br>
我們如果直接用二進制編碼的讀寫指針去完成上述的兩種同步是不行的,使用格雷碼更合適,為什么呢?</br>
因為二進制編碼的指針在跳變的時候有可能是多位數據一起變化,如二進制的7-->8即0111--> 1000 ,在跳變的過程中4位全部發生了改變,這樣很容易產生毛刺,例如異步FIFO的寫指針和讀指針分屬不同時鐘域,這樣指針在進行同步過程中很容易出錯,比如寫指針在從0111到1000跳變時4位同時改變,這樣讀時鐘在進行寫指針同步后得到的寫指針可能是0000-1111的某個值,一共有2^4個可能的情況,而這些都是不可控制的,你并不能確定會出現哪個值,那出錯的概率非常大,怎么辦呢?到了格雷碼發揮作用的時候了,而格雷碼的編碼特點是相鄰位每次只有1位發生變化,這樣在進行指針同步的時候,只有兩種可能出現的情況:1.指針同步正確,正是我們所要的;2.指針同步出錯,舉例假設格雷碼寫指針從000->001,將寫指針同步到讀時鐘域同步出錯,出錯的結果只可能是000->000,因為相鄰位的格雷碼每次只有一位變化,這個出錯結果實際上也就是寫指針沒有跳變保持不變,我們所關心的就是這個錯誤會不會導致讀空判斷出錯?答案是不會,最多是讓空標志在FIFO不是真正空的時候產生,而不會出現空讀的情形。所以gray碼保證的是同步后的讀寫指針即使在出錯的情形下依然能夠保證FIFO功能的正確性。在同步過程中的亞穩態不可能消除,但是我們只要保證它不會影響我們的正常工作即可。
## 由于設計的時候讀寫指針用了至少兩級寄存器同步,同步會消耗至少兩個時鐘周期,勢必會使得判斷空或滿有所延遲,這會不會導致設計出錯呢?
異步FIFO通過比較讀寫指針進行滿空判斷,但是讀寫指針屬于不同的時鐘域,所以在比較之前需要先將讀寫指針進行同步處理,將寫指針同步到讀時鐘域再和讀指針比較進行FIFO空狀態判斷,因為在同步寫指針時需要時間,而在這個同步的時間內有可能還會寫入新的數據,因此同步后的寫指針一定是小于或者等于當前實際的寫指針,所以此時判斷FIFO為空不一定是真空,這樣更加保守,一共不會出現空讀的情況,雖然會影響FIFO的性能,但是并不會出錯,同理將讀指針同步到寫時鐘域再和寫指針比較進行FIFO滿狀態判斷,同步后的讀指針一定是小于或者等于當前的讀指針,所以此時判斷FIFO為滿不一定是真滿,這樣更保守,這樣可以保證FIFO的特性:FIFO空之后不能繼續讀取,FIFO滿之后不能繼續寫入。**總結來說異步邏輯轉到同步邏輯不可避免需要額外的時鐘開銷,這會導致滿空趨于保守,但是保守并不等于錯誤,這么寫會稍微有性能損失,但是不會出錯。**</br>
**舉個例子:**大多數情形下,異步FIFO兩端的時鐘不是同頻的,或者讀快寫慢,或者讀慢寫快,慢的時鐘域同步到快的時鐘域不會出現漏掉指針的情況,但是將指針從快的時鐘域同步到慢的時鐘域時可能會有指針遺漏,**舉個例子**以讀慢寫快為例,進行滿標志判斷的時候需要將讀指針同步到寫時鐘域,因為讀慢寫快,所以不會有讀指針遺漏,同步消耗時鐘周期,所以同步后的讀指針滯后(小于等于)當前讀地址,所以可能滿標志會提前產生,滿并非真滿。進行空標志判斷的時候需要將寫指針同步到讀指針,因為讀慢寫快,所以當讀時鐘同步寫指針的時候,必然會漏掉一部分寫指針,我們不用關心那到底會漏掉哪些寫指針,我們在乎的是漏掉的指針會對FIFO的空標志產生影響嗎?比如寫指針從0寫到10,期間讀時鐘域只同步捕捉到了3、5、8這三個寫指針而漏掉了其他指針。當同步到8這個寫指針時,真實的寫指針可能已經寫到10,相當于在讀時鐘域還沒來得及覺察的情況下,寫時鐘域可能偷偷寫了數據到FIFO去,這樣在判斷它是不是空的時候會出現不是真正空的情況,漏掉的指針也沒有對FIFO的邏輯操作產生影響。
> # 異步FIFO深度計算
異步FIFO可以解決跨時終域的問題,但是需要注意深度。
## 異步FIFO最小深度計算
計算FIFO深度是FIFO設計中常遇到的問題。當異步FIFO讀寫端口的throught-put(吞吐量)不同時,會遇到數據丟失的問題,這時就需要考慮FIFO的Deepth問題了,即**為滿足讀寫流暢不卡頓(數據不丟失)時,FIFO的Deepth的最小值**。</br>
計算異步FIFO的最小深度,首先必定是要了解清楚應用場景的,這關乎到FIFO的最小深度的計算。**FIFO主要是用于數據的緩存,用在讀慢寫快的場景下**。異步FIFO讀寫不同頻,我們選用的FIFO要能夠在極端的情況下仍然能夠保證數據的不溢出。因此,**考慮的前提一般都是讀慢寫快的情景(寫時鐘大于讀時鐘)**,但需要注意的是,這里的寫操作是**猝發傳輸**,而不能使連續操作。倘若寫快讀慢的場景下,寫數據流是連續的,那再大的FIFO都會有寫滿的時候,因此無法避免數據的溢出(下面有一個蓄水的例子)。</br>
當**寫快讀慢**時,FIFO便可被用作系統中元件或隊列。因此**FIFO的大小**其實也就暗示了所需**緩存數據的容量**,該**容量**取決于**讀寫數據的速率**。據統計,**系統的數據速率**取決于**系統的負載能力**。因此為了保證FIFO的大小,需要考慮FIFO傳輸的最壞情況。
> **所謂最壞情況,就是使得寫速率最大,讀速率最小;通常是考慮猝發傳輸。**
宏觀看,整個時間域上,"寫數據=讀數據",這個是異步FIFO正常工作最基本的要求,是大前提。由于**寫快讀慢**,在發送方"**突發傳輸**"的發送數據的T內,是很有可能發送方寫數據量>接收方讀取的數據量,那么剩下未讀取的數據必定需要存儲共接收方繼續讀取并不能丟棄,因此FIFO的深度要能夠保證,在這段時間T內,如果接收方未能將發送方發送的數據接收完畢的話,剩下的數據都是可以存儲在FIFO內部而且不會溢出的,那么在發送方停止發送數據的"空閑時隙"內,接收方可以從容地接收剩下來的數據。
### 異步FIFO最小深度計算原理
**速率的概念**
>想象一個場景,有一個水龍頭在不斷向下流水,水龍頭下放著一口缸在接水,而缸上有一個出水口也在源源不斷的出水。假如,水龍頭的單位時間進水量大于出水口的出水量,那么缸內就會開始不斷的積水。如果水龍頭按照這樣持續不斷的進水,那積水必將越來越多,結果就是無論缸多大,早晚都會盛滿溢出的。因此這種供水需要? ?間歇性? ?的地往缸里注水(數據的**猝發傳輸**)。
**全速讀寫,引起的溢出問題:**

同樣的道理,如果數據流是在**連續不斷寫**,則FIFO無論多大,只要是讀寫時鐘**不同源同頻**就都會**丟數(****類比可能不太恰當,水缸空了,讀空;水缸滿了,寫數據會覆蓋****)**。這涉及到一個數據的最大連續寫長度(一個cycle寫一個數據)以保證數據的正確傳輸即FIFO能夠完整傳輸數據。</br>
?那到底如何利用異步FIFO呢?一般數據的傳輸會以一定格式的數據包,且以一定頻率進行傳輸,而不是永久的連續傳輸下去。這樣的話,就算是寫快,讀慢,只要保證在寫滿FIFO之前能把一個數據包發送完畢.</br>
很多場景比較簡單,沒有考慮性能和資源的問題,只要Deepth合理就行,比如有時候常取Deepth_value=? ?WR_Burst_len*(Wr_clk/Rd_clk),會選取大于Deepth_vlaue最接近的2^N數值,或是直接對Wr_clk/Rclk向上取整。</br>
FIFO常用于緩沖塊數據,一般用在寫快讀慢的情況下,遵循如下規則:

本質上就是:

**例**: A/D采樣速率50Mhz,dsp讀A/D的速率40Mhz,要不丟失地將將10萬個采樣數據送入DSP ,在A/D和DSP之間至少加多大容量的(深度)FIFO才行??(來源網絡)

**答**: Deepth/(50000000-40000000)>(100000/50000000)
即Deepth>10\_0000/5=20000。那么最小深度為20k。
- 序
- 第1章 Linux下開發FPGA
- 1.1 Linux下安裝diamond
- 1.2 使用輕量級linux仿真工具iverilog
- 1.3 使用linux shell來讀寫串口
- 1.4 嵌入式上的linux
- 設備數教程
- linux C 標準庫文檔
- linux 網絡編程
- 開機啟動流程
- 1.5 linux上實現與樹莓派,FPGA等通信的串口腳本
- 第2章 Intel FPGA的使用
- 2.1 特別注意
- 2.2 高級應用開發流程
- 2.2.1 生成二進制bit流rbf
- 2.2.2 制作Preloader Image
- 2.2.2.1 生成BSP文件
- 2.2.2.2 編譯preloader和uboot
- 2.2.2.3 更新SD的preloader和uboot
- 2.3 HPS使用
- 2.3.1 通過JTAG下載代碼
- 2.3.2 HPS軟件部分開發
- 2.3 quartus中IP核的使用
- 2.3.1 Intel中RS232串口IP的使用
- 2.4 一些問題的解決方法
- 2.4.1 關于引腳的復用的綜合出錯
- 第3章 關于C/C++的一些語法
- 3.1 C中數組作為形參不傳長度
- 3.2 匯編中JUMP和CALL的區別
- 3.3 c++中map的使用
- 3.4 鏈表的一些應用
- 3.5 vector的使用
- 3.6 使用C實現一個簡單的FIFO
- 3.6.1 循環隊列
- 3.7 C語言不定長參數
- 3.8 AD采樣計算同頻信號的相位差
- 3.9 使用C實現棧
- 3.10 增量式PID
- 第4章 Xilinx的FPGA使用
- 4.1 Alinx使用中的一些問題及解決方法
- 4.1.1 在Genarate Bitstream時提示沒有name.tcl
- 4.1.2 利用verilog求位寬
- 4.1.3 vivado中AXI寫DDR說明
- 4.1.4 zynq中AXI GPIO中斷問題
- 4.1.5 關于時序約束
- 4.1.6 zynq的PS端利用串口接收電腦的數據
- 4.1.7 SDK啟動出錯的解決方法
- 4.1.8 讓工具綜合是不優化某一模塊的方法
- 4.1.9 固化程序(雙核)
- 4.1.10 分配引腳時的問題
- 4.1.11 vivado仿真時相對文件路徑的問題
- 4.2 GCC使用Attribute分配空間給變量
- 4.3 關于Zynq的DDR寫入byte和word的方法
- 4.4 常用模塊
- 4.4.1 I2S接收串轉并
- 4.5 時鐘約束
- 4.5.1 時鐘約束
- 4.6 VIVADO使用
- 4.6.1 使用vivado進行仿真
- 4.7 關于PicoBlaze軟核的使用
- 4.8 vivado一些IP的使用
- 4.8.1 float-point浮點單元的使用
- 4.10 zynq的雙核中斷
- 第5章 FPGA的那些好用的工具
- 5.1 iverilog
- 5.2 Arduino串口繪圖器工具
- 5.3 LabVIEW
- 5.4 FPGA開發實用小工具
- 5.5 Linux下繪制時序圖軟件
- 5.6 verilog和VHDL相互轉換工具
- 5.7 linux下搭建輕量易用的verilog仿真環境
- 5.8 VCS仿真verilog并查看波形
- 5.9 Verilog開源的綜合工具-Yosys
- 5.10 sublim text3編輯器配置verilog編輯環境
- 5.11 在線工具
- 真值表 -> 邏輯表達式
- 5.12 Modelsim使用命令仿真
- 5.13 使用TCL實現的個人仿真腳本
- 5.14 在cygwin下使用命令行下載arduino代碼到開發板
- 5.15 STM32開發
- 5.15.1 安裝Atollic TrueSTUDIO for STM32
- 5.15.2 LED閃爍吧
- 5.15.3 模擬U盤
- 第6章 底層實現
- 6.1 硬件實現加法的流程
- 6.2 硬件實現乘法器
- 6.3 UART實現
- 6.3.1 通用串口發送模塊
- 6.4 二進制數轉BCD碼
- 6.5 基本開源資源
- 6.5.1 深度資源
- 6.5.2 FreeCore資源集合
- 第7章 常用模塊
- 7.1 溫濕度傳感器DHT11的verilog驅動
- 7.2 DAC7631驅動(verilog)
- 7.3 按鍵消抖
- 7.4 小腳丫數碼管顯示
- 7.5 verilog實現任意人數表決器
- 7.6 基本模塊head.v
- 7.7 四相八拍步進電機驅動
- 7.8 單片機部分
- 7.8.1 I2C OLED驅動
- 第8章 verilog 掃盲區
- 8.1 時序電路中數據的讀寫
- 8.2 從RTL角度來看verilog中=和<=的區別
- 8.3 case和casez的區別
- 8.4 關于參數的傳遞與讀取(paramter)
- 8.5 關于符號優先級
- 第9章 verilog中的一些語法使用
- 9.1 可綜合的repeat
- 第10章 system verilog
- 10.1 簡介
- 10.2 推薦demo學習網址
- 10.3 VCS在linux上環境的搭建
- 10.4 deepin15.11(linux)下搭建system verilog的vcs仿真環境
- 10.5 linux上使用vcs寫的腳本仿真管理
- 10.6 system verilog基本語法
- 10.6.1 數據類型
- 10.6.2 枚舉與字符串
- 第11章 tcl/tk的使用
- 11.1 使用Tcl/Tk
- 11.2 tcl基本語法教程
- 11.3 Tk的基本語法
- 11.3.1 建立按鈕
- 11.3.2 復選框
- 11.3.3 單選框
- 11.3.4 標簽
- 11.3.5 建立信息
- 11.3.6 建立輸入框
- 11.3.7 旋轉框
- 11.3.8 框架
- 11.3.9 標簽框架
- 11.3.10 將窗口小部件分配到框架/標簽框架
- 11.3.11 建立新的上層窗口
- 11.3.12 建立菜單
- 11.3.13 上層窗口建立菜單
- 11.3.14 建立滾動條
- 11.4 窗口管理器
- 11.5 一些學習的腳本
- 11.6 一些常用的操作語法實現
- 11.6.1 刪除同一后綴的文件
- 11.7 在Lattice的Diamond中使用tcl
- 第12章 FPGA的重要知識
- 12.1 面積與速度的平衡與互換
- 12.2 硬件原則
- 12.3 系統原則
- 12.4 同步設計原則
- 12.5 乒乓操作
- 12.6 串并轉換設計技巧
- 12.7 流水線操作設計思想
- 12.8 數據接口的同步方法
- 第13章 小項目
- 13.1 數字濾波器
- 13.2 FIFO
- 13.3 一個精簡的CPU( mini-mcu )
- 13.3.1 基本功能實現
- 13.3.2 中斷添加
- 13.3.3 使用中斷實現流水燈(實際硬件驗證)
- 13.3.4 綜合一點的應用示例
- 13.4.5 使用flex開發匯編編譯器
- 13.4.5 linux--Flex and Bison
- 13.4 有符號數轉單精度浮點數
- 13.5 串口調試FPGA模板