論理回路デザイン
ArchiTek home page
非同期FIFO

ポインタの非同期参照

コード(RTL)

/* **************************** MODULE PREAMBLE ********************************

        Copyright (c) 2011, ArchiTek
        This document constitutes confidential and proprietary information
        of ArchiTek. All rights reserved.
*/

// ***************************** MODULE HEADER *********************************

module afifo (
        iVld,
        iStall,
        iData,

        oVld,
        oStall,
        oData,

        iReset,
        iClk,

        oReset,
        oClk,

        reset_n

        );

// ************************ PARAMETER DECLARATIONS *****************************

        parameter               W       = 32;           // Data Length
        parameter               DR      = 2;            // Depth Radix

        parameter               D       = 1<<DR;

// *************************** I/O DECLARATIONS ********************************

        input                   iVld;
        output                  iStall;
        input   [W-1:0]         iData;

        output                  oVld;
        input                   oStall;
        output  [W-1:0]         oData;

        // 入力側の同期リセットとクロック
        input                   iReset;
        input                   iClk;

        // 出力側の同期リセットとクロック
        input                   oReset;
        input                   oClk;

        // グローバルな非同期リセットを用意
        input                   reset_n;

// ************************** LOCAL DECLARATIONS *******************************

        reg                     iStall;
        wire                    iStallD;

        reg                     oVld;
        wire                    oVldD;

        // グレーコードで入力ポインタを格納
        reg     [DR:0]          iGrPtr;                 // Gray Code Base
        wire    [DR:0]          iGrPtrD;
        wire    [DR:0]          iPtr;
        wire    [DR:0]          iPtrD;

        // グレーコードで出力ポインタを格納
        reg     [DR:0]          oGrPtr;                 // Gray Code Base
        wire    [DR:0]          oGrPtrD;
        wire    [DR:0]          oPtr;
        wire    [DR:0]          oPtrD;

        // 出力側で使う入力ポインタのダブルシンクロナイズ用のFF
        reg     [DR:0]          iGrPtr_s0;
        reg     [DR:0]          iGrPtr_s1;
        wire    [DR:0]          iPtr_s1;

        // 入力側で使う出力ポインタのダブルシンクロナイズ用のFF
        reg     [DR:0]          oGrPtr_s0;
        reg     [DR:0]          oGrPtr_s1;
        wire    [DR:0]          oPtr_s1;

        reg     [W-1:0]         oData;

        wire                    iAlloc          = iVld & !iStall;
        wire                    oAlloc          = oVld & !oStall;

        reg     [W-1:0]         mem[0:D-1];

// ****************************** MODULE BODY **********************************

// -----------------------------------------------------------------------------
// Input Domain
// 入力クロックドメイン側の処理(ポインタ以外は従来と変わらず)
always @(posedge iClk)
        if (iAlloc)
                mem[iPtr[DR-1:0]]
                                <= #1 iData;

always @(posedge iClk or negedge reset_n)
        if (!reset_n)
                iStall          <= #1 1'b0;
        else if (iReset)
                iStall          <= #1 1'b0;
        else
                iStall          <= #1 iStallD;

always @(posedge iClk or negedge reset_n)
        if (!reset_n)
                iGrPtr          <= #1 {DR+1{1'b0}};
        else if (iReset)
                iGrPtr          <= #1 {DR+1{1'b0}};
        else
                iGrPtr          <= #1 iGrPtrD;

// ポインタインクリメントのため、通常ポインタに変換
assign iPtr             = grayDecFunc(iGrPtr);
assign iPtrD            = iPtr + iAlloc;
// FFに格納するため、グレーコードに変換
assign iGrPtrD          = grayEncFunc(iPtrD);

// 比較は通常ポインタとダブルシンクロナイズした通常ポインタ
assign iStallD          = (iPtrD[DR] != oPtr_s1[DR])
                        & (iPtrD[DR-1:0] == oPtr_s1[DR-1:0]);

// -----------------------------------------------------------------------------
// Output Domain
// 出力クロックドメイン側の処理(ポインタ以外は従来と変わらず)
always @(posedge oClk)
        if (!oStall)
                oData           <= #1 mem[oPtrD[DR-1:0]];

always @(posedge oClk or negedge reset_n)
        if (!reset_n)
                oVld            <= #1 1'b0;
        else if (oReset)
                oVld            <= #1 1'b0;
        else
                oVld            <= #1 oVldD;

always @(posedge oClk or negedge reset_n)
        if (!reset_n)
                oGrPtr          <= #1 {DR+1{1'b0}};
        else if (oReset)
                oGrPtr          <= #1 {DR+1{1'b0}};
        else
                oGrPtr          <= #1 oGrPtrD;

// ポインタインクリメントのため、通常ポインタに変換
assign oPtr             = grayDecFunc(oGrPtr);
assign oPtrD            = oPtr + oAlloc;
// FFに格納するため、グレーコードに変換
assign oGrPtrD          = grayEncFunc(oPtrD);

// 比較は通常ポインタとダブルシンクロナイズした通常ポインタ
assign oVldD            = (iPtr_s1 != oPtrD);

// -----------------------------------------------------------------------------
// Sync FF
// ダブルシンクロナイズFFを作成(組み合わせなしの単純ラッチ)
always @(posedge oClk or negedge reset_n)
        if (!reset_n) begin
                iGrPtr_s0       <= #1 {DR+1{1'b0}};
                iGrPtr_s1       <= #1 {DR+1{1'b0}};
        end
        else if (oReset) begin
                iGrPtr_s0       <= #1 {DR+1{1'b0}};
                iGrPtr_s1       <= #1 {DR+1{1'b0}};
        end
        else begin
                iGrPtr_s0       <= #1 iGrPtr;           // 1st Sync
                iGrPtr_s1       <= #1 iGrPtr_s0;        // 2nd Sync
        end

always @(posedge iClk or negedge reset_n)
        if (!reset_n) begin
                oGrPtr_s0       <= #1 {DR+1{1'b0}};
                oGrPtr_s1       <= #1 {DR+1{1'b0}};
        end
        else if (iReset) begin
                oGrPtr_s0       <= #1 {DR+1{1'b0}};
                oGrPtr_s1       <= #1 {DR+1{1'b0}};
        end
        else begin
                oGrPtr_s0       <= #1 oGrPtr;           // 1st Sync
                oGrPtr_s1       <= #1 oGrPtr_s0;        // 2nd Sync
        end

// ダブルシンクロナイズしたポインタを通常ポインタに変換
assign iPtr_s1          = grayDecFunc(iGrPtr_s1);
assign oPtr_s1          = grayDecFunc(oGrPtr_s1);

// ************************** FUNCTIONS and TASKS ******************************

// 数値からグレーコードへの変換
function [DR:0] grayEncFunc;
        input   [DR:0]          in;

        begin
                grayEncFunc     = in ^ (in >> 1);
        end
endfunction

// グレーコードから数値への変換
function [DR:0] grayDecFunc;
        input   [DR:0]          in;
        reg     [DR:0]          mat;
        reg     [DR:0]          result;
        integer                 i;
        integer                 j;

        begin
                for (i=0; i<=DR; i=i+1) begin
                        for (j=0; j<=DR; j=j+1)
                                mat[j]          = (i+j > DR) ? 1'b0 : in[i+j];

                        result[i]       = ^mat;
                end

                grayDecFunc     = result;
        end
endfunction

endmodule

// *****************************************************************************
        

回路デザイン > 設計例 [FIFO] > >非同期FIFO    このページのTOP ▲

[1]
メタステーブルからステーブルになる時間は、FFのSetup時間に関連します。従来の回路でもクロック間隔が十分大きければ、メタステーブル期間を通り越して安定した制御信号が得られそうな気もします。

実際、Full/Emptyを1回だけサンプリングして制御する事例もあります。ただし、不安定になる確率は高くなります。100年に1回ミスする回路に対し、ダブルシンクロナイズを採用して1万年に1回ミスする回路の方がよいでしょう。