1. 程式人生 > 實用技巧 >Verilog實現之非同步fifo

Verilog實現之非同步fifo

  上節課我們介紹了,同步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          : [email protected]
  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 
View Code

  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