1. 程式人生 > 其它 >同步FIFO 原理及verilog模擬實現(保姆級)

同步FIFO 原理及verilog模擬實現(保姆級)

轉載自: 行走的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_enrd_en分別為寫/讀使能端,就像上面隧道的例子,兩個使能端就好像是兩邊的門,只有門開啟的時候才允許車輛進出
  • wr_datard_data分別是要寫入FIFO的資料和要從FIFO中讀取的資料
  • fifo_fullfifo_empty分別為FIFO的滿/空標誌位

3 FIFO設計的重要原則

  1. 任何FIFO都不要向滿FIFO中寫入資料(寫溢位)
  2. 任何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];