同步FIFO 原理及verilog模擬實現(保姆級)
阿新 • • 發佈:2022-04-12
轉載自: 行走的bug永動機,感謝老鐵!
https://baijiahao.baidu.com/s?id=1723287516895080443
0 寫在前面
FIFO可根據讀寫時鐘是否為同一時鐘域可分為同步FIFO和非同步FIFO,本文主要介紹同步FIFO,非同步FIFO將在下篇介紹
1 什麼是FIFO
FIFO全稱 First In First Out
,即先進先出。
FIFO主要用於以下幾個方面:
- 跨時鐘域資料傳輸
- 將資料傳送到晶片外之前進行緩衝,如傳送到DRAM或SRAM
- 儲存資料以備後用
FIFO是非同步資料傳輸時常用的儲存器,多bit資料非同步傳輸時,無論是從快時鐘域到慢時鐘域,還是從慢時鐘域到快時鐘域,都可以使用FIFO處理。
2 重要引數
FIFO中重要的引數有深度、寬度、空標誌、滿標誌、讀時鐘、讀時針、寫時鐘和寫時針
我看到過一個很形象的比喻:
把FIFO比作汽車進入一個單向行駛的隧道,隧道兩端都有一個門進行控制,
FIFO寬度
就是這個隧道單向有幾個車道,FIFO的深度
就是一個車道能容納多少輛車,當隧道內停滿車輛時,這就是FIFO的滿標誌
,當隧道內沒有一輛車時,這便是空標誌
讀時鐘: 讀操作所遵循的時鐘,時鐘沿到來時讀取資料
寫時鐘: 寫操作所遵循的時鐘,時鐘沿到來時寫入資料
讀指標: 指向下一個要讀出的地址,讀完自動加1
寫指標: 指向下一個要寫入地址,寫完自動加1
下面從FIFO介面開始說起,下圖適用於任何FIFO的基本介面框圖
FIFO基本介面
FIFO可分為讀資料一端和寫資料一端
-
wr_en
和rd_en
分別為寫/讀使能端,就像上面隧道的例子,兩個使能端就好像是兩邊的門,只有門開啟的時候才允許車輛進出 -
wr_data
和rd_data
分別是要寫入FIFO的資料和要從FIFO中讀取的資料 -
fifo_full
和fifo_empty
分別為FIFO的滿/空標誌位
3 FIFO設計的重要原則
- 任何FIFO都不要向滿FIFO中寫入資料(寫溢位)
- 任何FIFO都不要從空FIFO中讀取資料(讀溢位)
FIFO設計的核心便是空滿的判斷,如何判斷FIFO是否寫滿(或讀空),這裡我們可以利用地址指標,如下圖:
每寫入一次資料,寫地址指標會加1,每讀取一次資料,讀地址指標會加1
就像上圖所示,當讀地址指標追上寫地址指標,FIFO便是讀空狀態
同理,當寫地址指標再次追上讀地址指標,FIFO便是寫滿狀態,就像下圖
4 同步FIFO設計
先直接給出Verilog程式碼
module syn_fifo(clk, rstn, wr_en, rd_en, wr_data, rd_data, fifo_full, fifo_empty);
//引數定義
parameter width = 8;
parameter depth = 8;
parameter addr = 3;
//輸入訊號
input clk; //時鐘訊號
input rstn; //下降沿復位
input wr_en; //寫入使能
input rd_en; //讀取使能
//資料訊號
input [width - 1 : 0] wr_data; //寫資料
output [width - 1 : 0] rd_data; //讀資料
reg [width - 1 : 0] rd_data;
//空滿判斷訊號
output fifo_full;
output fifo_empty;
//定義一個計數器,用於判斷空滿
reg [$clog2(depth): 0] cnt;
//定義讀寫地址
reg [depth - 1 : 0] wr_ptr;
reg [depth - 1 : 0] rd_ptr;
//定義一個寬度為為width,深度為depth的fifo
reg [width - 1 : 0] fifo [depth - 1 : 0];
//寫地址操作
always @ (posedge clk or negedge rstn) begin
if(!rstn)
wr_ptr <= 0;
else if(wr_en && !fifo_full) //寫使能,且fifo未寫滿
wr_ptr <= wr_ptr + 1;
else
wr_ptr <= wr_ptr;
end
//讀地址操作
always @ (posedge clk or negedge rstn) begin
if(!rstn)
rd_ptr <= 0;
else if(rd_en && !fifo_empty) //讀使能,且fifo不為空
rd_ptr <= rd_ptr + 1;
else
rd_ptr <= rd_ptr;
end
//寫資料
integer i;
always @ (posedge clk or negedge rstn) begin
if(!rstn) begin //復位清空fifo
for(i = 0; i < depth; i = i + 1)
fifo[i] <= 0;
end
else if(wr_en) //寫使能時將資料寫入fifo
fifo[wr_ptr] <= wr_data;
else //否則保持
fifo[wr_ptr] <= fifo[wr_ptr];
end
//讀資料
always @ (posedge clk or negedge rstn) begin
if(!rstn)
rd_data <= 0;
else if (rd_en)
rd_data <= fifo[rd_ptr];