感謝
知乎龔大佬
打雜大佬
網上幾個nice的博客(忘了是哪個了。。。。)
前言
雖然FIFO都有IP可以使用,但理解原理還是自己寫一個來得透徹。
什么是FIFO?
Fist in first out。先入先出的數據緩存器,沒有外部讀寫地址線,可同時讀寫。
規則:永遠不要寫一個已經寫滿了的fifo。
永遠不要讀一個讀空了的fifo。
FIFO種類?
同步FIFO和異步FIFO。
同步FIFO只有一個時鍾,也就是說寫端和讀端的時鍾是一毛一樣的。
異步FIFO讀端和寫端兩個時鍾則是不一樣的。包括同頻異相,異頻異相。
FIFO用途?
- 數據緩沖器。比如你寫端burst一個數據,沒有fifo緩沖的話就炸了。Fifo會把寫端的突發數據吃到肚子里,讀端可以慢慢的一個個讀出來。
- 跨時鍾域。異步fifo主要使用在不同時鍾域的邊緣,用來同步數據到另一個時鍾域。
ALTERA FIFO IP 的缺點是什么?
雖然altera貼心的提供了FIFO的IP塊,但是對於可移植性與自定義位寬深度更好的話,還是自己寫的更佳。
FIFO深度如何計算?(避免溢出)
對於異步fifo,如果讀時鍾大於寫時鍾且每個周期讀寫,那么一定是會讀空的,反之一定會被寫滿。一般來說,不會設計這么無聊的東西。
假設寫端有突發的數據,而讀端是均勻的讀出,怎么保證fifo不溢出呢?
異步FIFO快轉慢的問題:可能采樣踩不到某些值。
同步FIFO:
當緩沖器使用,可以用ram資源搭。
原理圖:
信號定義:
clk:時鍾信號
rst_n:異步復位信號
wr:寫請求
rd:讀請求
data:數據輸入
q: 數據輸出
full:滿信號,表示fifo吃飽了
empty:空信號,表示fifo肚子已經空掉了
usedw:表示fifo中已有的數據個數
仿真:
沒有usedw款:
有usedw款:
資源使用量:
如何設計一個異步FIFO?
一般用作跨時鍾域,可用ram搭。
判斷讀空與寫滿,讀寫指針要跨時鍾域,所以采用格雷碼減少亞穩態。
什么是格雷碼?如何與二進制碼轉換?
格雷碼的基本特點是兩個相鄰的碼只有一位二進制數不同。
二進制轉格雷碼:
簡單來說就是把二進制碼右移一位再與二進制異或。
assign wr_poi_gray = wr_poi ^ (wr_poi>>1); //produce wr pointer gray code;
格雷碼轉二進制:
格雷碼轉二進制是從左邊第二位起,將每位與左邊一位二進制碼的值異或,作為該位二進制碼的值。
比如四位的碼:
bin[3] = gray[3];
bin[2] = gray[2]^bin[3];
bin[1] = gray[1]^bin[2];
bin[0] = gray[0]^bin[1];
原理圖:
信號定義:
wrclk:寫時鍾信號
rdclk: 讀時鍾信號
rst_n:異步復位信號
wr:寫請求
rd:讀請求
data:數據輸入
q: 數據輸出
full:滿信號,表示fifo吃飽了
empty:空信號,表示fifo肚子已經空掉了
仿真:寫時鍾是讀時鍾的兩倍。
寫讀測試:由於兩級同步器的存在,判定空滿滯后兩個周期。
同時讀寫測試:
資源使用量:
注意:
同步FIFO的地址擴展一位作為判斷空滿的狀態位,讀寫指針低位都相同的時候,最高位相同則表示讀空,不同則表示寫滿。
1 assign full = (wr_poi[clogb2(DEPTH)-1:0]== rd_poi[clogb2(DEPTH)-1:0]) && (wr_poi[clogb2(DEPTH)] ^ rd_poi[clogb2(DEPTH)] == 1); //highest bit is not same but rests bit is same; 2 assign empty = (wr_poi[clogb2(DEPTH)-1:0]== rd_poi[clogb2(DEPTH)-1:0]) && (wr_poi[clogb2(DEPTH)] ^ rd_poi[clogb2(DEPTH)] == 0); //every bit is same;
異步FIFO的地址位也擴展一位作為狀態位,通過格雷碼跨過時鍾域判斷空滿。
在寫時鍾域判斷full:如果本時鍾域的寫地址格雷碼和同步過來的讀地址格雷碼最高位和次高位均不同,其余位相同,則表示寫滿了。
在讀時鍾域判斷empty:如果本時鍾域的讀地址格雷碼和同步過來的寫地址格雷碼最高位和次高位都一樣,其余位也一樣,則表示讀空。
1 assign full = (wr_poi_gray == {~rd_poi_gray2[clogb2(DEPTH):clogb2(DEPTH)-1],rd_poi_gray2[clogb2(DEPTH)-2:0]}); 2 assign empty = (wr_poi_gray2 == rd_poi_gray);
貼一下同步FIFO的代碼:
已定制ramstyle,如果你的不同請刪掉或者更改。深度必須為2^n,否則更改函數clogb2中的depth為depth>0.
僅供學習交流,請勿用於商業用途,版權所有。異步代碼就不貼了,想研究請聯系我。
1 //************************************************ 2 // Filename : fifo_syn.v 3 // Author : kingstacker 4 // Company : School 5 // Email : kingstacker_work@163.com 6 // Device : Altera cyclone4 ep4ce6f17c8 7 // Description : synchronize fifo ;8*8 ;depth shuold be 2^n,otherwise change the clogb2 funtion; 8 //************************************************ 9 module fifo_syn #(parameter WIDTH = 8,DEPTH = 8)( 10 //input; 11 input wire clk, //only one clock; 12 input wire rst_n, 13 input wire wr, //wr request; 14 input wire rd, //rd request; 15 input wire [WIDTH-1:0] data, //data in; 16 //output; 17 output wire [WIDTH-1:0] q, //data out; 18 output wire full, //fifo is full; 19 output wire empty, //fifo is empty; 20 output wire [clogb2(DEPTH)-1:0] usedw //data number in fifo; 21 ); 22 function integer clogb2 (input integer depth); 23 begin 24 for (clogb2=0; depth>1; clogb2=clogb2+1) //depth>1 when you choose depth 2^n;otherwise change it to depth>0;for example depth is 7; 25 depth = depth >>1; 26 end 27 endfunction 28 (* ramstyle = "M9K" *) reg [WIDTH-1:0] memory [0:DEPTH-1]; 29 reg [clogb2(DEPTH):0] wr_poi; //wr pointer; 30 reg [clogb2(DEPTH):0] rd_poi; //rd pointer; 31 reg [WIDTH-1:0] q_r; //reg q; 32 reg [clogb2(DEPTH)-1:0] usedw_r; //reg usedw_r; 33 wire wr_flag; //real wr request; 34 wire rd_flag; //real rd request; 35 assign q = q_r; 36 assign usedw = usedw_r; 37 assign full = (wr_poi[clogb2(DEPTH)-1:0]== rd_poi[clogb2(DEPTH)-1:0]) && (wr_poi[clogb2(DEPTH)] ^ rd_poi[clogb2(DEPTH)] == 1); //highest bit is not same but rests bit is same; 38 assign empty = (wr_poi[clogb2(DEPTH)-1:0]== rd_poi[clogb2(DEPTH)-1:0]) && (wr_poi[clogb2(DEPTH)] ^ rd_poi[clogb2(DEPTH)] == 0); //every bit is same; 39 assign wr_flag = ((wr == 1'b1) && (full == 1'b0)); //wr enable; 40 assign rd_flag = ((rd == 1'b1) && (empty == 1'b0)); //rd enable; 41 always @(posedge clk or negedge rst_n) begin 42 if (~rst_n) begin 43 wr_poi <= 0; 44 end //if 45 else begin 46 wr_poi <= wr_flag ? wr_poi + 1'b1 : wr_poi; 47 memory[wr_poi[clogb2(DEPTH)-1:0]] <= wr_flag ? data : memory[wr_poi[clogb2(DEPTH)-1:0]]; 48 end //else 49 end //always 50 always @(posedge clk or negedge rst_n) begin 51 if (~rst_n) begin 52 rd_poi <= 0; 53 q_r <= 0; 54 end //if 55 else begin 56 rd_poi <= rd_flag ? rd_poi + 1'b1 : rd_poi; 57 q_r <= rd_flag ? memory[rd_poi[clogb2(DEPTH)-1:0]] : q_r; 58 end //else 59 end //always 60 always @(posedge clk or negedge rst_n) begin 61 if (~rst_n) begin 62 usedw_r <= 0; 63 end //if 64 else begin 65 case ({wr_flag,rd_flag}) 66 2'b00: begin 67 usedw_r <= usedw_r; 68 end 69 2'b10: begin 70 if (usedw_r == DEPTH-1) begin // full; 71 usedw_r <= usedw_r; 72 end 73 else begin 74 usedw_r <= usedw_r + 1'b1; 75 end 76 end 77 2'b01: begin 78 if (usedw_r == 0) begin //empty; 79 usedw_r <= usedw_r; 80 end 81 else begin 82 usedw_r <= usedw_r - 1'b1; 83 end 84 end 85 2'b11: begin 86 usedw_r <= usedw_r; 87 end 88 default: usedw_r <= 0; 89 endcase //case 90 end //else 91 end //always 92 93 endmodule
以上。