3.8 異步FIFO(雙時鐘FIFO)

如上圖,X通過xclk將資料寫入FIFO,Y通過yclk將資料讀出,注意這里寫滿標志信號在寫時鐘域,空信號在讀時鐘域,
對比握手信號,異步FIFO用于對性能要求較高的設計中,尤其是時鐘延遲比系統資源更重要的環境中,
異步FIFO主要需要注意信號亞穩態的問題,
3.8.1 避免用二進制計數器實作指標
如果使用二進制計數,一次可能變換多位,這時就需要將多位資料同步到另一個時鐘域,很容易造成錯誤,
3.8.2 使用格雷碼取代二進制計數
格雷碼優勢是在一個數變成另一個數時,只有一位出現變化,所以其在轉換中最多只會一位錯誤,讀取時要么讀到舊值,要么讀到新值,但是不會讀到其他值,
3.8.2.1 同步指標的影響


需要注意的是在t6、t7的時候,由于同步電路需要兩個時鐘周期,所以fifo_full會多拉高兩個周期,這樣阻止了兩個周期的資料寫入,但是對資料的準確性是無害的,
同樣,空也會多兩個周期阻止FIFO讀訪問,


空判斷是將寫指標同步到讀時鐘域,在讀空后寫入FIFO,需要兩個周期讀時鐘域才能得到寫指標增加的資訊,所以空信號也會多延兩個周期,同樣這里組織了對FIFO的讀操作,也是無害的,
3.8.3 用格雷碼實作FIFO指標

1、將格雷碼轉換成二進制碼,
2、根據條件遞增二進制值,
3、二進制轉格雷碼,
4、保存到暫存器,
3.8.3.1 格雷碼到二進制轉換器

轉換公式:

所以可以將格雷碼右移再按位異或得到二進制碼
3.8.3.2 二進制-格雷碼轉換器

可以看出可以直接將二進制碼右移后于自身異或操作,計算出格雷碼,
3.8.3.3 格雷碼計數器的實作
將四個步驟結合起來,就可以實作一個格雷碼計數器,

3.8.4 FIFO滿和FIFO空的產生
為了判斷空滿,還需要額外一位對兩種情況進行區分,
最高位不同,其他位相同,FIFO滿,
所有位相同,FIFO為空,
由于存在XOR鏈路,最大操作頻率取決于格雷碼計數器速度,

圖里感覺又有點錯誤,滿判斷時,應該是最高位不同,
如圖,一共用了四個轉換器,如果直接使用格雷碼實作空滿操作就可以省掉這些轉換器,但是這會增加復雜度并需要額外的邏輯,
3.8.4.1 產生空滿的另一種方法
該方法創建兩個格雷碼計數器,一個n位,另一個n-1位,通過n位格雷碼的最高兩位異或,來產生一個n-1位格雷碼的MSB,其余n-2位與n位格雷碼的n-2位保持一致,

使用這兩個格雷碼計數器,就可以進行FIFO空滿判斷,
3.8.5 雙時鐘FIFO設計

3.8.5.1 FIFO空條件的產生
這里直接比較讀寫指標格雷碼,相比先將格雷碼轉化成二進制碼,直接對比格雷碼可以節省4個轉換器,
同步后的寫指標wr_ptr_sync和rd_gtemp(下一個將要寄存進入rd_ptr的格雷碼),(這里沒太搞懂為什么要用下一個rd_ptr的格雷碼,是為了提前一個周期輸出full和empty信號嗎?)(調代碼的時候發現確實應該使用下一個rd_ptr的格雷碼,不然產生空滿信號需要一個周期,空滿信號被采樣到用于控制FIFO讀寫又需要一個周期,會造成FIFO滿了還向FIFO中寫入的現象,)
3.8.5.2 FIFO滿條件的產生

如圖,rd_ptr=7,wr_ptr=8時,會出現FIFO滿的錯誤宣告,使用二元n位格雷碼計數器可以輕松避免該情況,
以下三個條件為真時,滿標志拉高,
1)同步后的讀指標rd_ptr_sync的MSB應該與下一個寫指標的格雷碼wr_gtemp的MSB應該不同,
2)寫時鐘域兩位MSB異或應與讀指標一樣,
3)剩下的LSB都應該一樣,
(其實總結一下就是高兩位要正好相反,剩下的低位應該完全相等)
異步FIFO代碼
module Dual_Clock_FIFO#( parameter WIDTH=16, parameter DEPTH=8 ) ( input wclk, input rclk, input reset_n, input [WIDTH-1:0]wr_data, input wr_en, input rd_en, output reg[WIDTH-1:0]rd_data, output fifo_full, output fifo_empty ); localparam RAM_SIZE=(1<<DEPTH); reg [WIDTH-1:0]DPRAM[RAM_SIZE-1:0]; wire [DEPTH:0]wr_ptr,rd_ptr; reg [DEPTH:0]wr_ptr_bin,rd_ptr_bin; reg [DEPTH:0]wr_ptr_r,rd_ptr_r; reg [DEPTH:0]wr_ptr_sync,rd_ptr_sync; assign wr_ptr = (wr_ptr_bin >> 1) ^ wr_ptr_bin; assign rd_ptr = (rd_ptr_bin >> 1) ^ rd_ptr_bin; always @(posedge rclk or negedge reset_n) begin if(~reset_n) rd_data <= 'd0; else begin if(rd_en && ~fifo_empty) rd_data <= DPRAM[rd_ptr_bin]; end end always @(posedge wclk) begin if(wr_en && ~fifo_full) DPRAM[wr_ptr_bin] <= wr_data; end always @(posedge rclk or negedge reset_n) //two stages synchronizer begin: wr_ptr_sync_gen if(~reset_n)begin wr_ptr_r <= 'b0; wr_ptr_sync <= 'b0; end else begin wr_ptr_r <= wr_ptr; wr_ptr_sync <= wr_ptr_r; end end // always @ (posedge rclk or negedge reset_n) // begin: fifo_empty_gen // if (~reset_n) // fifo_empty <= 1'b1; // else // fifo_empty <= (rd_ptr == wr_ptr_sync); // end assign fifo_empty = (rd_ptr == wr_ptr_sync); always @(posedge wclk or negedge reset_n) //two stages synchronizer begin: rd_ptr_sync_gen if(~reset_n)begin rd_ptr_r <= 'b0; rd_ptr_sync <= 'b0; end else begin rd_ptr_r <= rd_ptr; rd_ptr_sync <= rd_ptr_r; end end wire rd_2nd_msb = rd_ptr_sync [DEPTH] ^ rd_ptr_sync [DEPTH - 1]; wire wr_2nd_msb = wr_ptr [DEPTH] ^ wr_ptr [DEPTH-1]; // always @ (posedge wclk or negedge reset_n) // begin: fifo_full_gen // if (~reset_n) // fifo_full <= 1'b0; // else // fifo_full <= ((wr_ptr [DEPTH] != rd_ptr_sync[DEPTH]) && // (rd_2nd_msb == wr_2nd_msb) && // (wr_ptr[DEPTH -2:0] == rd_ptr_sync[DEPTH-2:0])); // end assign fifo_full = ((wr_ptr [DEPTH] != rd_ptr_sync[DEPTH]) && (rd_2nd_msb == wr_2nd_msb) && (wr_ptr[DEPTH -2:0] == rd_ptr_sync[DEPTH-2:0])); always @(posedge rclk or negedge reset_n) begin:rd_ptr_gen if(~reset_n) rd_ptr_bin <= 'd0; else if(rd_en && ~fifo_empty) rd_ptr_bin <= rd_ptr_bin +1'b1; end always @(posedge wclk or negedge reset_n) begin:wr_ptr_gen if(~reset_n) wr_ptr_bin <= 'd0; else if(wr_en && ~fifo_full) wr_ptr_bin <= wr_ptr_bin +1'b1; end endmodule
testbench
`timescale 1ns / 1ps module tb_dual_clock_fifo(); reg wclk,rclk,rst_n,wr_en,rd_en; wire full,empty; reg [15:0]wr_data; wire [15:0]rd_data; initial begin wclk = 0; rclk = 0; rst_n = 0; wr_en = 0; rd_en = 0; wr_data = 0; #100 rst_n = 1; while(~full) begin #20 wr_en <= 1'b1; wr_data <= wr_data + 1'b1; end repeat(100) begin @(posedge wclk)begin wr_en = {$random} %2; end @(posedge rclk)begin rd_en = {$random} %2; end end while(~empty) begin #24 wr_en <= 1'b0; rd_en <= 1'b1; end $finish; end always #10 wclk = ~wclk; always #12 rclk = ~rclk; Dual_Clock_FIFO my_fifo( .wclk(wclk), .rclk(rclk), .reset_n(rst_n), .wr_data(wr_data), .wr_en(wr_en), .rd_en(rd_en), .rd_data(rd_data), .fifo_full(full), .fifo_empty(empty) ); endmodule
波形

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/539336.html
標籤:其他
