
デジタルエレクトロニクスにおいて、FIFO(先入れ先出しの頭文字)は、入力データを内部メモリに格納し、格納されたデータを受信順に出力するデジタル回路です。 [ 1 ]データは必要に応じて格納され、格納された最も古いデータが必要に応じて出力されます。これらの操作はどちらも独立して行われ[ 2 ]、場合によっては異なる速度で行われます[ 3 ] 。これは、データの同時格納と出力が必要であり、最も古いデータがメモリ内を順次伝播してから出力されるシフトレジスタとは対照的です。[ 2 ]
FIFOは主に、メモリのアドレス読み出しレジスタと書き込みレジスタとして機能する一対のカウンタ、アドレス指定可能なメモリ配列、およびステータスと制御ロジックで構成されています。[ 4 ]
FIFOに格納されるデータは通常、固定のワードサイズ(ビット数)を持ち、これは一般的にFIFO幅と呼ばれます。格納可能なデータの最大数はFIFO深度と呼ばれます。幅と深度によって、FIFOの記憶容量が決まります。
FIFOは、2つのハードウェアデバイス間、またはハードウェアデバイスとソフトウェア間でデータを交換するために使用されます。[ 5 ]これらは、異なるクロックドメインで動作する回路間でのデータ交換、および有限間隔で異なるデータレートで動作するデータ生成器と消費者間のバッファリングとフロー制御によく使用されます。[ a ] [ 1 ]

FIFOには書き込みポートと読み出しポートと呼ばれる2つの電気インターフェースがあり、それを通して外部回路とデータとステータス情報を交換します。[ 6 ] [ 3 ]
FIFOのポートに接続された外部回路は、FIFOにデータを格納するデータプロデューサー(データソースまたは書き込みエージェントとも呼ばれる)と、格納されたデータを取得するデータコンシューマー(データシンクまたは読み取りエージェントとも呼ばれる)で構成されています。[ 6 ] [ 7 ]より具体的には、プロデューサーは書き込みポートにデータを送信し、書き込みポートからステータス情報を受信し、コンシューマーは読み取りポートからデータとステータスの両方を受信します。
各ポートは、接続されている外部回路のクロックドメインで動作します。つまり、ポートと外部回路は共通のクロック信号を共有します。[ b ]書き込みポートはプロデューサーのクロックドメインで動作するため、すべての書き込みポート信号はプロデューサーのクロックに同期されます。同様に、読み出しポートはコンシューマーのクロックドメインで動作します。
すべてのFIFOは、同期式または非同期式のいずれかに分類されます。

同期FIFOは、読み出しポートと書き込みポートに共通のクロックを使用する電子FIFOです。[ 8 ]
読み出しと書き込み操作は同じクロック領域で行われるため、FIFOの状態信号(バッファレベル、しきい値フラグ)は、データ生成側とデータ消費側の両方でメタステーブル状態を引き起こすことなく安全に使用できます。したがって、同期FIFOは単一の共有状態信号を出力します。

非同期FIFOは、読み出しポートと書き込みポートに異なるクロックを使用する電子FIFOです。[ 8 ]
メタステーブル状態によるエラーを回避するため、各クロックドメインごとに専用のステータス信号(バッファレベル、しきい値フラグ)が出力されます。メタステーブル状態を回避するために使用される回路は、空のFIFOにデータが書き込まれる場合、または満杯のFIFOからデータが読み出される場合に、(同期FIFOと比較して)ステータスフラグの更新に余分な遅延をもたらします。
同時読み出しおよび書き込み操作を容易にするため、すべての非同期 FIFO [ 1 ]および多くの同期 FIFO のメモリはデュアルポートであり、通常はレジスタ ファイルまたはデュアルポート RAM (ランダム アクセス メモリ) で構成されています。非同期 FIFO では、メモリは任意のクロック ドメインで動作するロジックからアクセスできる非同期回路です。[ 1 ]
FIFOに書き込まれたり読み取られたりするデータは通常、同じ固定ワードサイズ(ビット数)を持ち、これは一般的にFIFO幅[ 6 ]として知られており、内部メモリのネイティブワードサイズと一致します。
FIFOに格納できる最大ワード数は、FIFO深度として知られています。[ 6 ]必須ではありませんが、回路を簡素化し、速度性能を向上させる傾向があるため、深度は通常2の整数乗になります。[ 9 ]
オーバーフローを避けるためには、FIFO の深さは、データ生成率と消費率の最悪ケースの差によって生じるデータを格納するのに十分な大きさでなければならず、[ 10 ]プロデューサーとコンシューマーの平均帯域幅が一致していなければなりません。[ 1 ]また、非同期 FIFO では、通常、ステータス遅延を許容するために少量の追加深度が設けられます。[ 10 ]
FIFOは、次にアクセスするメモリ位置のアドレス(ポインタ)を格納するために2つのメモリアドレス レジスタ(MAR)を使用する循環バッファとして実装されます。 [ 1 ]読み出しMAR(RMAR)は、データが読み出される次の位置を示し、書き込みMAR(WMAR)は、データが書き込まれる次の位置を示します。[ 11 ]各MARは特定のポートに関連付けられており、そのポートのクロック ドメインで動作します。
各 MAR はカウンタとして実装され、データが転送されるたびにカウントが増加します (WMAR は FIFO 書き込み時に増加、RMAR は FIFO 読み出し時に増加)。[ 3 ]最初は両方の MAR が最初のメモリ位置を指し、FIFO は空です。[ 11 ]書き込みアドレスが読み出しアドレスに達すると FIFO は満杯になり、読み出しアドレスが書き込みアドレスに達すると空になります。[ 1 ] MAR がメモリの最後の位置をアドレス指定すると、次のインクリメント時に自動的に最初のメモリ アドレスに戻ります。[ 3 ]
特定のFIFOにおいて、MARはバイナリカウンタまたはグレイコードカウンタのいずれかとして実装できます。バイナリカウンタは、FIFO回路を簡素化できるため、同期FIFOで一般的に好まれます。グレイコードカウンタ、またはグレイコードエンコードされたバイナリカウンタは、メタステーブル状態によるエラーを回避するために、非同期FIFOで使用されます。
多くの FIFO は、現在のバッファレベル (格納されているワード数) を示すバイナリ値を出力します。[ 11 ] FIFO の設計によっては、カウンタでレベルを追跡するか、ポインタ演算を使用してレベルを計算するかのいずれかでこれを行います。同期 FIFO はどちらの方法も使用できますが、非同期 FIFO はポインタ演算の使用に限定されます。[ 8 ]

同期FIFOでは、双方向バイナリカウンタでバッファレベルを追跡することでバッファレベルを監視できます。[ 8 ]データがFIFOに書き込まれるとカウントが増加し、データが読み出されるとカウントが減少します。カウンタによって出力されるバイナリカウント値は、バッファレベルを直接示します。
この方法は、読み出しポートと書き込みポートが異なるクロックドメインで動作するため、カウンタを2つの異なるクロックで制御する必要がある非同期FIFOには適していません。[ 8 ]
非同期 FIFO および多くの同期 FIFO では、バッファ レベルは現在のメモリ アドレスから計算されます。FIFO が空でも満杯でもない場合は、レベルが書き込みアドレスと読み出しアドレスの差に等しいため、これは簡単に実行できます。しかし、FIFO が空または満杯の場合、アドレスは等しくなるため、レベルの計算に使用すると曖昧になります。[ 11 ]したがって、空か満杯かを区別するために、各 MAR にはメモリをアドレス指定するために必要なビットに加えて 1 ビット追加するのが一般的です。結果として得られる拡張アドレス (XADDR) は FIFO レベルの計算に使用され、XADDR の最上位ビット(MSB) 以外のすべてのビット (つまり、LSB) がメモリ アドレスとして使用されます。
一般的に、MOD-カウンター(カウンターと(異なる出力状態)は、FIFOメモリに使用され、データワード。例えば、MOD-32 MARは、16ワードのメモリを持つFIFOのアドレスを生成するために使用されます。
MARがバイナリカウンタの場合、バッファレベルはMAR出力値の差になります。その他の出力エンコーディング (例えば、グレイコード) の場合、差分を計算する前に MAR 出力をバイナリに変換する必要があります。いずれの場合も、差分は通常 MOD-バイナリ減算器。結果として生じる算術的借り(つまり、桁上がり)は破棄されます。
FIFOは、特定のデータレベルのしきい値が満たされているかどうかを示す個別のステータス信号(フラグ)を出力します。すべてのFIFOは、満杯と空のフラグを出力し、一部は、半分満杯、ほぼ満杯、ほぼ空などの追加のフラグも出力する場合があります。[ 1 ]
ステータス信号は、同期された読み出しアドレスと書き込みアドレスを直接比較することによって、または利用可能な場合は FIFO レベルから生成されます。後者の場合、ステータス信号は組み合わせ論理を介して FIFO レベルから導出されます。または、満杯の場合は、MSB を抽出することによって導出されます。FIFO の設計によっては、ほぼ満杯およびほぼ空のフラグ (実装されている場合) のしきい値は、アプリケーション固有の定数である場合もあれば、プログラム可能である場合もあります。[ 3 ]
FIFOステータスフラグは、重要なしきい値に達したことを外部データプロデューサーとコンシューマーに通知するために使用されます。通常、プロデューサーとコンシューマーは、このような通知に応じて、それぞれFIFOへの書き込みと読み取りを一時停止または再開します。プロデューサーまたはコンシューマー(あるいはその両方)は、ステータスをポーリングしたり、割り込みを介してステータスを受信したりするプロセッサである場合もあれば、ステータス信号を使用してフロー制御を管理する純粋なハードウェアである場合もあります。
多くのFIFOでは、書き込みイネーブル信号と読み出しイネーブル信号を判別するために、内部的にフルフラグとエンプティフラグが使用されます。フルフラグがアサートされると書き込みは禁止され、データはメモリに書き込まれず、書き込みMARもインクリメントされません。同様に、エンプティフラグがアサートされると読み出しは禁止され、読み出しMARはインクリメントされず、通常は前回読み出されたデータが出力されます。
ポインタ演算によってバッファレベルを計算するFIFOでは、バイナリ減算器への安定した入力を確保するために、読み出しアドレスと書き込みアドレスを共通のクロックに同期させる必要があります。同期FIFOでは、読み出しポートと書き込みポートが同じクロックを使用するため、この要件は本質的に満たされます。しかし、非同期FIFOでは、読み出しポートと書き込みポートに異なるクロックを使用するため、バッファレベルは各ポートのクロックドメインで個別に計算する必要があります。そのため、各ポートの現在のアドレスを他のポートのクロックに同期させる必要があり、このプロセスはクロックドメインクロッシングと呼ばれます。
クロックドメインクロッシングは通常、一方のポートの現在のアドレスを、もう一方のポートによってクロックされる2つ以上のレジスタに順次渡すことによって実現されます。最初のレジスタはアドレスをもう一方のポートのクロックに同期させ、後続のレジスタは結果として生じるメタステーブル状態を軽減します。
バイナリ アドレスをインクリメントすると、多くの場合、同時に複数のビットが変化します (たとえば、0111 から 1000 へ)。[ 10 ]変化するビットが変化する瞬間に近すぎるタイミングでサンプリングされると (サンプリング レジスタのセットアップ 時間またはホールド 時間を無視して)、一部は以前の状態でサンプリングされ、その他は新しい状態でサンプリングされるため、宛先クロック ドメインで、以前のアドレスとも新しいアドレスとも一致しない無効なアドレスが生成されます。[ 10 ]このようなことが起こると、他のクロック ドメインで計算されるバッファ レベルが不正になります。
このようなエラーを防ぐため、アドレスはグレイコード値として他のクロックドメインに送信されます。[ 10 ]アドレスのインクリメントごとにグレイコードビットが1つだけ変化するため、ビットが変化するときにサンプリングすると、他のクロックドメインで受信されるアドレスは、以前のアドレスまたは新しいアドレスのいずれかと一致することが保証されます。
一部のFIFOでは、以下に示すように、MARをグレイコードカウンタとして実装し、グレイコードアドレスを直接出力することでこれを実現しています。
あるいは、バイナリエンコード出力を持つMARを、バイナリ-グレイコード変換器と組み合わせて使用することもできます。この場合、バイナリビットが異なるタイミングで変化することによって生じるグレイコードのグリッチを排除するため、変換器の出力はクロックドメインをまたぐ前にリクロックする必要があります。
各ポートは、クロックドメイン同期回路を介して、自身のMARアドレスを他のポートに送信し、他のポートのアドレスをグレイコードで受信します。各ポート内では、受信したアドレスがバイナリに変換され、ポートのクロックドメインにおけるバッファレベル信号とステータスフラグ信号の生成に使用されます。グレイコードMARを使用するFIFOでは、これは通常、以下のように実装されます。
メモリ アドレス レジスタがインクリメントされても、その変更は他のクロック ドメインではすぐには検出されません。[ 10 ]これは、クロック ドメインをまたぐ過程で、アドレスが一連の同期レジスタを順次通過する必要があるため、他のドメインへの到着が遅れるからです。この遅延により、アドレスが FIFO ステータスの計算に使用されるため、ステータス レイテンシが増加します。より具体的には、この遅延の間、他のクロック ドメインの見かけ上の FIFO レベルは変化せず、結果としてそのしきい値フラグも古くなる可能性があります。
特定の条件下では、この高いステータス遅延により、FIFO を介した効率的なデータフローが妨げられる可能性があります。[ 10 ]例えば、コンシューマが FIFO が満杯のときにワードを読み取った場合、書き込みポートのfullフラグはすぐには否定されません。これにより、プロデューサーは安全に書き込める場合でも一時的に新しい FIFO 書き込みを保留せざるを得なくなり、プロデューサーのデータが保留中の場合はパフォーマンスが低下します。同様に、プロデューサーが空の FIFO に書き込んだ場合、読み取りポートのemptyフラグはすぐには否定されないため、コンシューマはデータが実際に利用可能になったときに読み取りを保留することになります。
データ生成元(書き込み側)がメモリが満杯の FIFO に書き込むと、オーバーフローが発生します。[ 1 ] [ 4 ]このような書き込みが正常に進行すると、新しいデータワードが最も古い格納ワードを上書きし、その過程で書き込みポインタ(書き込み MAR から出力される値)が読み出しポインタに追いつき、FIFO レベルが破損して FIFO に 1 つのワードしか含まれていないように見えます。[ 8 ]これを回避するために、FIFO は通常、新しいワードまたは最も古い格納ワードのいずれかをドロップ(破棄)することで、正しい FIFO レベルを維持するように設計されています。いずれの場合も、通常はデータが失われたことを外部回路に知らせる通知信号が出力されます。
オーバーフローが差し迫ると、FIFO への書き込みをブロックすることで新しいデータワードを破棄することができます。[ 4 ]これは通常、以下に示すように、FIFO の書き込みイネーブルをフルフラグでゲートすることによって実現されます。[ 8 ]このような実装では、フルフラグはバッファが新しいデータを受け入れないことを示します。この回路は書き込みポートのクロックドメインでのみ動作するため、同期 FIFO と非同期 FIFO の両方に適しています。
あるいは、オーバーフローが差し迫っている場合、最も古い格納ワードを削除してそのメモリ位置を再利用することにより、新しいデータワード用にメモリを再割り当てすることができます。[ 4 ]これは、通常どおり新しいワードを書き込む(したがって、最も古い格納ワードを上書きする)と同時に、読み出しMARをインクリメントすることによって行われます。両方のMARが同時にインクリメントされるため、見かけ上のバッファレベルは変わりません。両方のMARは書き込みポートのクロックによってインクリメントされるため、読み出しMARを読み出しポートのクロックドメインでインクリメントする必要がある非同期FIFOには、このプロセスは適していません。
FIFOは、データレートが異なるデータ生成者と消費者を接続するためによく使用されます。[ 9 ] FIFOは、生成されたがまだ消費されていないデータをバッファリングすることでこの機能を果たし、生成レートと消費レートを分離します。これにより、生成者はデータが利用可能になったときにFIFOにデータを書き込み、消費者は必要なときにのみデータを読み取ることができるため、データ損失を回避しながら両方のエージェントのタイミング要件を満たすことができます。[ 9 ]
データ生成速度と消費速度は、以下のような様々な理由により異なる場合がある。
多くのシステムは、それぞれ異なるクロック ドメインで動作する複数の論理回路で構成されており、それらの回路は相互に通信する必要があります。[ 1 ]これを実現するには、そのような各回路は、異なるクロック ドメインで動作する回路とデータを交換する必要があります。しかし、データを別のクロック ドメインに直接送信すると、メタステーブル状態によりデータが破損する可能性があるため、信頼性が低くなります。[ 9 ]この問題は、非同期 FIFO を介してデータを渡すことで回避され、メタステーブル状態が解消され、伝送されるデータの完全性が保証されます。[ 9 ]
システムによっては、データ生成元からのワードの到着時刻を知る必要がある。[ 4 ]一般的な例としては、レーダー、ソナー、制御システムにおけるデータ収集などが挙げられる。通常、このような情報は、各データワードが到着したときにタイムスタンプを付けること(現在の時刻と関連付けること)によって取得され、データワードとその到着時刻からなる順序対が効果的に作成される。
場合によっては、プロセッサが十分な精度でタイムスタンプを付与できないことがあります(例えば、可変割り込みレイテンシ[ 4 ]や、処理速度よりも速くデータが到着するなどの要因による)。このような場合、FIFOを使用して受信データに正確なタイムスタンプを付与することができます。到着した各データワードは、現在の時刻と連結され、その結果が単一の複合ワードとしてFIFOに書き込まれます。プロセッサは、(都合の良い後ほど)FIFOから複合ワードを読み出す際に、複合ワードを構成要素であるデータワードに簡単に分割し、タイムスタンプを付与することができます。
アプリケーションによっては、FIFO はイベントに先行するデータを収集する必要があります。[ 2 ]例えば、デジタル オシロスコープは、トリガー 信号の直前のアナログ信号サンプルを記録するためによく使用され、トリガーの前に発生した信号波形を表示することができます。
このような記録を容易にするため、FIFOはオーバーフロー時に最も古いサンプルを上書きするように設計されています。これにより、FIFOが満杯になった場合でも、新しいデータを受け入れ続け、最大容量まで最新のデータのみを自動的に保持することができます。
動作時には、記録が開始され、FIFOに受信サンプルが蓄積され始めます。記録が十分に長く続くと、FIFOは満杯になり、新しいサンプルが受信されるたびに、最も古い記録サンプルが破棄されて新しいサンプルのためのスペースが確保されます。トリガーが到着すると、記録は停止し、記録されたサンプルはFIFOから読み取ることができます。記録されたデータより前の、失われた(上書きされた)データは無関係であるため、オーバーフローは無視されます。