上節課我們介紹了,同步fifo,感覺就是在雙口異步RAM中進行了一些簡單的外圍操作,加了一些空滿標志,內部用指針來進行尋址,從而取消了外部的地址接口。FIFO的一側是讀。一側是寫。所以具有了''wr_en"和"rd_en",一邊是寫數據,一邊是讀數據,所以就有了“wr_data”和“rd_data”,寫會寫滿,讀會讀空所以具有了“empty”和“full”標志位。同步的fifo就是這么點東西。那么對於異步fifo 又該怎么樣呢?
一、分析異步FIFO
由於是異步FIFO的設計,讀寫時鍾不一樣,在產生讀空信號和寫滿信號時,會涉及到跨時鍾域的問題。此處的跨時鍾主要是讀指針屬於讀時終域的,寫指針屬於寫時鍾域的。而異步FIFO的讀寫時鍾是不同的。這種異步情況就會導致我們在處理空滿標志的時候出現錯誤的判斷。所以必須對其進行同步化處理后在進行空滿標志的判斷。同步化解決的方案是:加兩級寄存器同步 + 格雷碼(目的都是消除亞穩態)
1、加寄存器法
使用異步信號進行使用的時候,好的設計都會對異步信號進行同步處理,同步一般采用多級D觸發器級聯處理,如上圖。這種模型大部分資料都說的是第一級寄存器產生亞穩態后,第二級寄存器穩定輸出概率為90%,第三極寄存器穩定輸出的概率為99%,如果亞穩態跟隨電路一直傳遞下去,那就會另自我修護能力較弱的系統直接潰。轉化為代碼:
1 // 加兩級寄存器同步 2 always @( sclk_rd )begin 3 if(!rd_rst)begin 4 wr_ptr_gray_d1 <= 0; 5 wr_ptr_gray_d2 <= 0; 6 end 7 else begin 8 wr_ptr_gray_d1 <= wr_ptr_gray ; 9 wr_ptr_gray_d2 <= wr_ptr_gray_d1 ; 10 end 11 end 12 13 always @( sclk_wr )begin 14 if(!wr_rst)begin 15 rd_ptr_gray_d1 <= 0 ; 16 rd_ptr_gray_d2 <= 0 ; 17 end 18 else begin 19 rd_ptr_gray_d1 <= rd_ptr_gray ; 20 rd_ptr_gray_d2 <= rd_ptr_gray_d1; 21 end 22 end
2、將一個二進制的計數值從一個時鍾域同步到另一個時鍾域的時候很容易出現問題,因為采用二進制計數器時所有位都可能同時變化,在同一個時鍾沿同步多個信號的變化會產生亞穩態問題。而使用格雷碼只有一位變化,因此在兩個時鍾域間同步多個位不會產生問題。所以需要一個二進制到gray碼的轉換電路,將地址值轉換為相應的gray碼,然后將該gray碼同步到另一個時鍾域進行對比,作為空滿狀態的檢測。
1 assign wr_ptr_gray = wr_pointer ^(wr_pointer>>>1); 2 assign rd_ptr_gray = rd_pointer ^(rd_pointer>>>1); 二進制轉換為格雷碼。
3、在格雷碼的域進行空滿標志的判斷
同步FIFO可以使用計數方式來判斷空滿,但是異步FIFO不能,因為寫指針和讀指針根本不在同一個時鍾域,計數器無法處理這樣的計數。那么如何對獨處的數據和寫入的數據進行判斷。並給出空滿標志呢?解決方案:對讀寫指針的位寬多添1位,這樣可以在讀寫指針相等時,表示FIFO空,而在寫指針和讀指針最高位不同,而其他位相等時,也即寫指針大於讀指針一個FIFO深度的數值,表示FIFO滿,這不就是意味着寫指針繞了一圈,又追上了讀指針了嗎?恰是如此,用來解決不用計數而具體判斷FIFO空滿的問題。
1 //full 2 always @(posedge sclk_wr)begin 3 if(!wr_rst)begin 4 full <= 1'b0 ; 5 end 6 else if((rd_ptr_gray_d2[$clog2(DATA_DEPTH)-1:0]==wr_ptr_gray[$clog2(DATA_DEPTH)-1:0]) 7 && (rd_ptr_gray_d2[$clog2(DATA_DEPTH)]!=wr_ptr_gray[$clog2(DATA_DEPTH)]))begin 8 full <= 1'b1 ; 9 end 10 else 11 full <= 1'b0 ; 12 end 13 //empty 14 always @(posedge sclk_rd)begin 15 if(!rd_rst)begin 16 empty <= 1'b0 ; 17 end 18 else if(rd_ptr_gray==wr_ptr_gray_d2)begin 19 empty <= 1'b1 ; 20 end 21 else 22 empty <= 1'b0 ; 23 end
二、Verilog實現如下
1 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2 // Project Name : 3 // Website : https://home.cnblogs.com/lgy-gdeu/ 4 // Author : LGY GUET Uiversity 5 // Weixin : li15226499835 6 // Email : 15277385992@163.com 7 // File : 8 // Create : 2020 9 // Revise : 10 // Editor : sublime text{SUBLIME_VERSION}, tab size ({TABS}) 11 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 12 // Modification History: 13 // Date By Version Change Description 14 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 15 // {DATE} {TIME} LGY 1.0 ++++++++++++++++ 16 // ********************************************************************************* 17 `timescale 1ns/1ns 18 module asyn_fifo #( 19 parameter DATA_WIDTH = 8, 20 parameter DATA_DEPTH = 32 21 ) 22 23 ( 24 input wire sclk_wr , 25 input wire wr_rst , 26 input wire wr_en , 27 input wire[DATA_WIDTH-1:0] wr_data , 28 output reg full , 29 input wire sclk_rd , 30 input wire rd_en , 31 input wire rd_rst , 32 output reg [DATA_WIDTH-1:0] rd_data , 33 output reg empty 34 ); 35 36 //========================================================================\ 37 // ################ Define Parameter and Internal signals ################ 38 //========================================================================/ 39 reg [DATA_DEPTH-1:0] fifo_buffer [0:DATA_WIDTH-1] ;//space 40 reg [$clog2(DATA_DEPTH):0] wr_pointer ;//zhi zheng 41 reg [$clog2(DATA_DEPTH):0] rd_pointer ; 42 wire [$clog2(DATA_DEPTH):0] wr_ptr_gray ;//geleijishu 43 wire [$clog2(DATA_DEPTH):0] rd_ptr_gray ; 44 reg [$clog2(DATA_DEPTH):0] wr_ptr_gray_d1 ; 45 reg [$clog2(DATA_DEPTH):0] wr_ptr_gray_d2 ; 46 reg [$clog2(DATA_DEPTH):0] rd_ptr_gray_d1 ; 47 reg [$clog2(DATA_DEPTH):0] rd_ptr_gray_d2 ; 48 //============================================================================= 49 //+++++++++++++++++++++++++ Main Code +++++++++++++++++++++++++++++++ 50 //============================================================================= 51 //wr_pointer 52 always @(posedge sclk_wr)begin 53 if(!wr_rst)begin 54 wr_pointer <= 0 ; 55 end 56 else if(wr_en)begin 57 wr_pointer <= wr_pointer + 1'b1 ; 58 fifo_buffer[wr_pointer] <= wr_data ; 59 end 60 end 61 62 //rd_pointer 63 always @(posedge sclk_rd )begin 64 if(!rd_rst)begin 65 rd_pointer <= 0 ; 66 end 67 else if(rd_en)begin 68 rd_pointer <= rd_pointer+ 1'b1; 69 rd_data <= fifo_buffer[rd_pointer]; 70 end 71 72 end 73 //wr_ptr_gray,rd_ptr_gray 74 assign wr_ptr_gray = wr_pointer ^(wr_pointer>>>1); 75 assign rd_ptr_gray = rd_pointer ^(rd_pointer>>>1); 76 77 // 加兩級寄存器同步 78 always @( sclk_rd )begin 79 if(!rd_rst)begin 80 wr_ptr_gray_d1 <= 0; 81 wr_ptr_gray_d2 <= 0; 82 end 83 else begin 84 wr_ptr_gray_d1 <= wr_ptr_gray ; 85 wr_ptr_gray_d2 <= wr_ptr_gray_d1 ; 86 end 87 end 88 89 always @( sclk_wr )begin 90 if(!wr_rst)begin 91 rd_ptr_gray_d1 <= 0 ; 92 rd_ptr_gray_d2 <= 0 ; 93 end 94 else begin 95 rd_ptr_gray_d1 <= rd_ptr_gray ; 96 rd_ptr_gray_d2 <= rd_ptr_gray_d1; 97 end 98 end 99 100 //full 101 always @(posedge sclk_wr)begin 102 if(!wr_rst)begin 103 full <= 1'b0 ; 104 end 105 else if((rd_ptr_gray_d2[$clog2(DATA_DEPTH)-1:0]==wr_ptr_gray[$clog2(DATA_DEPTH)-1:0]) 106 && (rd_ptr_gray_d2[$clog2(DATA_DEPTH)]!=wr_ptr_gray[$clog2(DATA_DEPTH)]))begin 107 full <= 1'b1 ; 108 end 109 else 110 full <= 1'b0 ; 111 end 112 //empty 113 always @(posedge sclk_rd)begin 114 if(!rd_rst)begin 115 empty <= 1'b0 ; 116 end 117 else if(rd_ptr_gray==wr_ptr_gray_d2)begin 118 empty <= 1'b1 ; 119 end 120 else 121 empty <= 1'b0 ; 122 end 123 124 125 126 endmodule
tb測試代碼:

1 `timescale 1ns / 1ps 2 ////////////////////////////////////////////////////////////////////////////////// 3 // Company: 4 // Engineer: 5 // 6 // Create Date: 2020/06/27 22:23:25 7 // Design Name: 8 // Module Name: asyn_fifo_tb 9 // Project Name: 10 // Target Devices: 11 // Tool Versions: 12 // Description: 13 // 14 // Dependencies: 15 // 16 // Revision: 17 // Revision 0.01 - File Created 18 // Additional Comments: 19 // 20 ////////////////////////////////////////////////////////////////////////////////// 21 22 23 module asyn_fifo_tb(); 24 parameter DATA_WIDTH = 8; 25 26 parameter DATA_DEPTH = 16; 27 28 reg wr_clk; 29 reg wr_rst; 30 reg wr_en; 31 reg [DATA_WIDTH - 1 : 0] wr_data; 32 wire full; 33 34 reg rd_clk; 35 reg rd_rst; 36 reg rd_en; 37 wire [DATA_WIDTH - 1 : 0] rd_data; 38 wire empty; 39 40 initial begin 41 wr_clk = 0; 42 forever begin 43 #5 wr_clk = ~wr_clk; 44 end 45 end 46 47 initial begin 48 rd_clk = 0; 49 forever begin 50 #10 rd_clk = ~rd_clk; 51 end 52 end 53 54 initial begin 55 wr_rst = 0; 56 rd_rst = 0; 57 wr_en = 0; 58 rd_en = 0; 59 #30 60 wr_rst = 1; 61 rd_rst = 1; 62 63 //write data into fifo buffer 64 @(negedge wr_clk) 65 wr_data = $random; 66 wr_en = 1; 67 68 repeat(7) begin 69 @(negedge wr_clk) 70 wr_data = $random; // write into fifo 8 datas in all; 71 end 72 73 // read parts 74 @(negedge wr_clk) 75 wr_en = 0; 76 77 @(negedge rd_clk) 78 rd_en = 1; 79 80 repeat(7) begin 81 @(negedge rd_clk); // read empty 82 end 83 @(negedge rd_clk) 84 rd_en = 0; 85 86 //write full 87 # 150 88 89 @(negedge wr_clk) 90 wr_en = 1; 91 wr_data = $random; 92 93 repeat(15) begin 94 @(negedge wr_clk) 95 wr_data = $random; 96 end 97 98 @(negedge wr_clk) 99 wr_en = 0; 100 101 102 #50 $finish; 103 end 104 105 asyn_fifo #( 106 .DATA_WIDTH(DATA_WIDTH), 107 .DATA_DEPTH(DATA_DEPTH) 108 )asyn_fifo_inst ( 109 .sclk_wr (wr_clk), 110 .wr_rst (wr_rst), 111 .wr_en (wr_en), 112 .wr_data (wr_data), 113 .full (full), 114 .sclk_rd (rd_clk), 115 .rd_en (rd_en), 116 .rd_rst (rd_rst), 117 .rd_data (rd_data), 118 .empty (empty) 119 ); 120 endmodule
1、系統函數$clog2();求對數函數。使用方法很簡單,主要求指針的位寬來使用。例如:
1 parameter DATA_WIDTH = 8; 2 parameter DATA_DEPTH = 8; 3 4 reg [DATA_WIDTH - 1 : 0] fifo_buffer[0 : DATA_DEPTH - 1]; 5 6 reg [$clog2(DATA_DEPTH) - 1 : 0] wr_pointer = 0; 7 reg [$clog2(DATA_DEPTH) - 1 : 0] rd_pointer = 0; 8 9 $clog2(DATA_DEPTH) // = 3; 10 11 reg [$clog2(DATA_DEPTH) - 1 : 0] wr_pointer = 0; 12 reg [$clog2(DATA_DEPTH) - 1 : 0] rd_pointer = 0;
2、綜合屬性控制資源使用
1 (*ram_style = "distributed"*) reg [DATA_WIDTH - 1 : 0] fifo_buffer[0 : DATA_DEPTH - 1]; 則使用分布式ram來搭建存儲空間。
1 (*ram_style = "block"*) reg [DATA_WIDTH - 1 : 0] fifo_buffer[0 : DATA_DEPTH - 1]; 則使用塊ram來搭建存儲空間。
三、參考文獻
1、https://www.cnblogs.com/ylsm-kb/p/9068449.html
2、https://so.csdn.net/so/search/s.do?q=%E5%BC%82%E6%AD%A5FIFO&t=blog&u=Reborn_Lee