Verilog RTL 設計:異步FIFO的設計與驗證


之前的兩篇博文討論了同步FIFO的設計和驗證,其讀寫時鍾時相同的單一時鍾,應用范圍有限。

在實際的系統中,經常會遇到多個時鍾域傳輸數據的情況,此時需要數據在跨時鍾域上實現無縫傳輸,且不能有毛刺出現。異步FIFO讀寫時鍾是不相同的,因此可以實現某個頻率的寫時鍾寫入再由另一個頻率的讀時鍾讀出,也就能夠實現跨時鍾的傳輸數據了。

異步FIFO的結構框圖如下:

設計的關鍵:異步FIFO中如何產生空滿信號。

        根據同步FIFO方法二的設計經驗。空滿信號的產生可以根據讀寫的地址的比較來判斷。但是,在異步FIFO中,處於兩個不同時鍾域的讀寫地址該如何來比較呢?

        首先,對於空信號,只有在讀的時候才會來看FIFO是否為空,如果FIFO為空就不能讀數據了,因此空信號的產生是在讀時鍾域的。同理,對於滿信號,只有在寫的時候才會來看FIFO是否為滿,如果FIFO滿了就不能寫數據了,因此滿信號的產生是在寫時鍾域的。在進行讀寫地址的比較時,只能先對地址信號進行時鍾域同步,然后再進行比較。采用常規的異步處理的方法:打兩拍。

 

//wr_addr_g sample twice
always @(posedge rclk or negedge rst_n)
    if(rst_n == 1'b0)
        {wr_addr_g_dly2,wr_addr_g_dly1} <= 'b0;
    else
        {wr_addr_g_dly2,wr_addr_g_dly1} <= {wr_addr_g_dly1,wr_addr_g};

//rd_addr_g sample twice
always @(posedge wclk or negedge rst_n)
    if(rst_n == 1'b0)
        {rd_addr_g_dly2,rd_addr_g_dly1} <= 'b0;
    else
        {rd_addr_g_dly2,rd_addr_g_dly1} <= {rd_addr_g_dly1,rd_addr_g};

 

 

 

        然后,我們知道地址是一位一位累加的多位寬數據,但在跨時鍾采樣時,多位寬的數據傳輸並被采樣時,如果不做處理很容易發生亞穩態,從而影響數據正確性。比如六位地址111111會在下一個時刻變成000000,在實際電路中,這個變化過程要持續很長一段時間,會由111111經歷6個狀態轉移到000000.比如111111 -> 101111 -> 100111 -> 100110 -> 100100 -> 000100 -> 000000。由於寫時鍾與讀時鍾域不同步,異步的寫時鍾很可能會在狀態不穩定的中間某個狀態采樣,這樣就會得到錯誤的讀指針,進而做出錯誤的狀態判斷,導致系統異常(亞穩態問題)。而且由於多位同時突變,憑借概率論常識可知發生錯誤的可能性很大。因此,將二進制的地址編碼轉換成對應的格雷碼,地址每次只有1個bit發生改變。其結果也不外乎兩種:遞增前原指針和遞增后新指針。顯然遞增后新指針是最新情況的反映,如果采樣到這個指針,那么和我們的設計預期是一致的,如果采樣到遞增前的原指針,假設現在采樣讀指針,那么最壞的情況就是把“不滿”判斷成了“滿”,使得本來被允許的寫操作被禁止了,但是這並不會對邏輯產生影響,只是帶來了寫操作的延遲。同樣的,如果現在采樣寫指針,那么最壞的情況就是把“不空”判斷成“空”,使得本來被允許的讀操作被禁止了,但是這也不會對邏輯產生影響,只是帶來了讀操作的延遲。二進制碼轉換成格雷碼,其法則是保留二進制碼的最高位作為格雷碼的最高位,而次高位格雷碼為二進制碼的高位與次高位相異或,而格雷碼其余各位與次高位的求法相類似。這樣就可以實現二進制到格雷碼的轉換了,總結就是移位並且異或,verilog代碼實現就一句:assign wgraynext = (wbinnext>>1) ^ wbinnext。

 

//binary code to gray code
assign  wr_addr_g = (wr_addr_b>>1) ^ wr_addr_b;
assign  rd_addr_g = (rd_addr_b>>1) ^ rd_addr_b;

 

 

 

        最后,需要解決的問題就是格雷碼如何進行比較產生空滿信號。空信號很簡單,當讀地址和寫地址相同時,即產生空信號。對於滿信號,參考下面的表格發現,格雷碼的0-15、1-14、2-13...都是最高位相反其余低位相同,同時,為判斷滿信號,又會多出一個最高位,而在FIFO滿時,讀寫地址的最高位也是相反的。所以總結如下:當讀寫地址的高2bit的相反,后幾位的bit相同時,寫滿;當讀寫地址相等時,讀空。

 

assign  empty = (rd_addr_g == wr_addr_g_dly2);
assign  full  = (wr_addr_g == {~rd_addr_g_dly2[ADDR_WIDTH:ADDR_WIDTH-1],rd_addr_g_dly2[ADDR_WIDTH-2:0]});

 

 

 

十進制數

二進制數

格雷碼

 

十進制數

二進制數

格雷碼

0

0000

0000

 

8

1000

1100

1

0001

0001

 

9

1001

1101

2

0010

0011

 

10

1010

1111

3

0011

0010

 

11

1011

1110

4

0100

0110

 

12

1100

1010

5

0101

0111

 

13

1101

1011

6

0110

0101

 

14

1110

1001

7

0111

0100

 

15

1111

1000

 

 

根據上述分析,異步FIFO的設計如下:

RTL:

 1 module async_fifo #(parameter FIFO_WIDTH = 8,
 2                               FIFO_DEPTH = 16,
 3                               ADDR_WIDTH = 4)
 4 (
 5   input   wire                  rclk,
 6   input   wire                  wclk,
 7   input   wire                  rst_n,
 8   input   wire                  wr_en,
 9   input   wire                  rd_en,
10   input   wire[FIFO_WIDTH-1:0]  wr_data,
11   output  reg [FIFO_WIDTH-1:0]  rd_data,
12   output  reg                   empty,
13   output  reg                   full
14 );
15 //memory
16 reg [FIFO_WIDTH-1:0]            mem[FIFO_DEPTH-1:0];
17 //address
18 reg [ADDR_WIDTH-1:0]            wr_addr,rd_addr;
19 //binary adderss
20 reg [ADDR_WIDTH:0]              wr_addr_b,rd_addr_b;
21 //gray address
22 wire[ADDR_WIDTH:0]              wr_addr_g,rd_addr_g;
23 //sample gray address
24 reg [ADDR_WIDTH:0]              wr_addr_g_dly1,wr_addr_g_dly2,rd_addr_g_dly1,rd_addr_g_dly2;
25 
26 //binary code to gray code
27 assign  wr_addr_g = (wr_addr_b>>1) ^ wr_addr_b;
28 assign  rd_addr_g = (rd_addr_b>>1) ^ rd_addr_b;
29 //addr
30 assign  wr_addr = wr_addr_b[ADDR_WIDTH-1:0];
31 assign  rd_addr = rd_addr_b[ADDR_WIDTH-1:0];
32 
33 //read & write allow
34 wire    rd_allow = rd_en && !empty;
35 wire    wr_allow = wr_en && !full;
36 
37 //write data
38 always @(posedge wclk or negedge rst_n)
39     if(rst_n == 1'b0)
40         wr_addr_b <= 'b0;
41     else if(wr_allow) begin
42         mem[wr_addr] <= wr_data;
43         wr_addr_b <= wr_addr_b + 1'b1;
44     end
45 
46 //read data
47 always @(posedge rclk or negedge rst_n)
48     if(rst_n == 1'b0)
49         rd_addr_b <= 'b0;
50     else if(rd_allow) begin
51         rd_data <= mem[rd_addr];
52         rd_addr_b <= rd_addr_b + 1'b1;
53     end
54 
55 //wr_addr_g sample twice
56 always @(posedge rclk or negedge rst_n)
57     if(rst_n == 1'b0)
58         {wr_addr_g_dly2,wr_addr_g_dly1} <= 'b0;
59     else
60         {wr_addr_g_dly2,wr_addr_g_dly1} <= {wr_addr_g_dly1,wr_addr_g};
61 
62 //rd_addr_g sample twice
63 always @(posedge wclk or negedge rst_n)
64     if(rst_n == 1'b0)
65         {rd_addr_g_dly2,rd_addr_g_dly1} <= 'b0;
66     else
67         {rd_addr_g_dly2,rd_addr_g_dly1} <= {rd_addr_g_dly1,rd_addr_g};
68 
69 assign  empty = (rd_addr_g == wr_addr_g_dly2);
70 assign  full  = (wr_addr_g == {~rd_addr_g_dly2[ADDR_WIDTH:ADDR_WIDTH-1],rd_addr_g_dly2[ADDR_WIDTH-2:0]});
71 
72 endmodule

Testbench:

 1 `timescale 1ns/1ps
 2 module async_fifo_tb();
 3 
 4 parameter       FIFO_WIDTH = 8,
 5                 FIFO_DEPTH = 16,
 6                 ADDR_WIDTH = 4;
 7 
 8 reg                     rclk,wclk,rst_n,wr_en,rd_en;
 9 reg [FIFO_WIDTH-1:0]    wr_data;
10 wire[FIFO_WIDTH-1:0]    rd_data;
11 wire                    empty,full;
12 
13 async_fifo 
14 #(  .FIFO_WIDTH (FIFO_WIDTH),
15     .FIFO_DEPTH (FIFO_DEPTH),
16     .ADDR_WIDTH (ADDR_WIDTH)
17 )
18 (
19   .rclk     (rclk   ),
20   .wclk     (wclk   ),
21   .rst_n    (rst_n  ),
22   .wr_en    (wr_en  ),
23   .rd_en    (rd_en  ),
24   .wr_data  (wr_data),
25   .rd_data  (rd_data),
26   .empty    (empty  ),
27   .full     (full   )
28 );
29 
30 initial begin
31     rst_n=0;
32     rclk=0;
33     wclk=0;
34     #5
35     rst_n=1;
36 end
37 
38 always #30 rclk=~rclk;
39 always #20 wclk=~wclk;
40 
41 initial begin
42     wr_en=0;
43     rd_en=0;
44     #50
45     wr_en=1;
46     wr_data=8'h0;
47     #40 wr_data=8'h1;
48     #40 wr_data=8'h2;
49     #40 wr_data=8'h3;
50     #40 wr_data=8'h4;
51     #40 wr_data=8'h5;
52     #40 wr_data=8'h6;
53     #40 wr_data=8'h7;
54     #40 wr_data=8'h8;
55     #40 wr_data=8'h9;
56     #40 wr_data=8'hA;
57     #40 wr_data=8'hB;
58     #40 wr_data=8'hC;
59     #40 wr_data=8'hD;
60     #40 wr_data=8'hE;
61     #40 wr_data=8'hF;
62     #60
63     wr_en=0;
64     rd_en=1;
65     #1000
66     $finish;
67 end
68 
69 `include "vpd.inc"
70 
71 endmodule

DVE波形:

 

 在此記錄異步FIFO的設計和驗證。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM