非同期FIFO
- 非同期設計に関しては文献等豊富にありますので詳細は言及しません。簡単にやり方だけを言うと、非同期信号をサンプリングする場合、発生するメタステーブル(FFの不安定状態)を2つ以上のFFを並べてサンプリングすることで安定化させます(ダブルシンクロナイズ[1])。
- また、制御に関する信号は1つの制御信号に抽出および付属データにエンコードし、非同期FIFO等を用いて一括処理することで制御信号のタイミングずれ(わずかな差でも同期化すると大きな差になる)を防ぎます。
- 非同期FIFOは異なるクロック系との接続において、制御系・データ系にかかわらず頻繁に使用可能です。非同期系のトラブルはシミュレーションでは発見しにくいことから、動作が確実な非同期FIFOを積極的に使うことが多くなります。
- 従来のFIFOと異なるのはポインタの扱い方になります。ここでは、一般的に使われるグレイコードを利用した方式を述べます。
ポインタの非同期参照
- 制御信号は同期化のためFFで2回以上サンプリングしなければなりません。FIFO制御の基本であるFullとEmptyに応用すると2サイクル以上の遅延が生じ、遅延期間中の処理を禁止しなければ不整合が生じます。これではせっかくのFIFOのスループットが台無しになるため、制御の元となるポインタの同期化が必要になります。
- 同期化したポインタとの比較でFull/Emptyを生成するため、制御上従来のFIFOと同じになります。ただし、入力から出力までレイテンシはサンプリングクロックで2サイクル以上の遅延が生じます。スループットは関係ありません。
- ポインタの同期化にはグレイコードを用いて、サンプリングしても変化が1ビットだけに抑えます。こうすると、サンプリングしたポインタはインクリメントした数字かそのままの数字かに限定できます。
- これらを要約すると、以下になります。
- ポインタを格納するFFはグレイコードで表現し、入出力ともダブルシンクロナイズしたポインタを作る
- ポインタ比較時は、自らのクロックドメインに属するポインタと、異なるクロックドメインに属するダブルシンクロナイズしたポインタを用いる
- 比較とデータアクセスのため、グレイコード表記と通常表記の変換器を適宜使用する(青○)
- なお、入力と出力でクロックドメインを分離するため、それぞれのクロックと同期リセットを用意します。
コード(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 ▲