之前的兩篇博文討論了同步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的設計和驗證。