このページは、 FIFOsに関する 5 ページのシリーズの 4 番目です。このページでは、 overflow が発生する可能性に対処する方法を提案します。
序章
FIFOを使用する多くのアプリケーションでは、到着するデータのフローを制御することはできません。たとえば、 data acquisition アプリケーションでは、 logic はキャプチャされたデータを FIFOに直接書き込みます。 FIFO がいっぱいになると、このデータは失われます。この種のアプリケーションでは、 FIFO のリーダーは、 FIFO がいっぱいにならないように、 FIFO 内のデータを十分な速度で消費する責任があります。
ただし、 FIFO がいっぱいになると、データの損失によりデータの連続性が失われます。 FIFO は書き込みの試行を無視します。その結果、 FIFO の内容は正しくありません。この状況は overflowと呼ばれます。
overflow は何らかの故障が原因で発生するため、避ける必要があります。ただし、 overflow が発生した場合でも、このイベントを検出し、被害を軽減することが重要です。このための戦略を以下に提案します。
改造された FIFO
主な取り組みは、常に overflow の発生を防ぐことです。しかし、それが失敗した場合、残されているのは、 FIFOから誤ったデータが消費されないようにすることだけです。言い換えれば、 FIFO から読み取られるすべてのデータは連続していて正しいということです。
推奨される解決策は修正された FIFOです。これは次の 2 つの点で異なります。
- overflow が発生すると (つまり、 FIFO がいっぱいになると)、 FIFO がリセットされるまで、 FIFO にデータを書き込むことはできません。
- overflowの後に FIFO が空になると、 FIFO は reset が発生するまで空のままであると報告します。この状況を EOFと呼びます。
最初の機能は、変更された FIFO から読み取られるデータが常に連続していて正しいことを保証します。 FIFO は、連続性が切断されるとデータの書き込みを拒否します。 2 番目の機能 (EOF) は、 FIFOを使用する logic に問題が発生したというメッセージを送信します。これにより、 logic にデータ フローを再開する機会が与えられます。
EOF という名前は End of Fileに由来しています。 この修正された FIFO は、 Xillybus IP coreに基づくdata acquisition アプリケーションに役立ちます。この種のアプリケーションでは、 Xillybus IP core がデータをコンピュータに転送します。簡単なコンピュータ プログラムは、 FIFOからデータを読み取るために標準 file I/O API を使用します。言い換えれば、コンピューター プログラムは通常の方法でファイルを開き、そこからデータを読み取ります。 FIFOの EOF では、このファイルは通常のファイルの終わりに達したときと同様に動作します。これは、読み取るデータがもうないという事実に対する自然な反応です。
Verilogでの実装
これは、上で提案した変更された FIFO を実装する Verilog module の例です。
module eof_fifo
(
input rst,
input wr_clk,
input rd_clk,
input [31:0] din,
input wr_en,
input rd_en,
output [31:0] dout,
output full,
output empty,
output eof
);
reg rst_sync;
reg rst_cross;
reg fifo_has_been_full;
reg fifo_has_been_nonfull;
reg has_been_full_cross;
reg has_been_full;
assign ok_to_write = !rst_sync && !full && !fifo_has_been_full;
assign eof = empty && has_been_full;
always @(posedge wr_clk)
begin
if (!full)
fifo_has_been_nonfull <= 1;
else if (rst_sync)
fifo_has_been_nonfull <= 0;
if (full && fifo_has_been_nonfull)
fifo_has_been_full <= 1;
else if (rst_sync)
fifo_has_been_full <= 0;
end
// Clock domain crossing logic: asynchronous -> wr_clk
always @(posedge wr_clk)
begin
rst_cross <= rst;
rst_sync <= rst_cross;
end
// Clock domain crossing logic: wr_clk -> rd_clk
always @(posedge rd_clk)
begin
has_been_full_cross <= fifo_has_been_full;
has_been_full <= has_been_full_cross;
end
fifo fifo_ins
(
.rst(rst),
.wr_clk(wr_clk),
.rd_clk(rd_clk),
.din(din),
.wr_en(wr_en && ok_to_write),
.rd_en(rd_en),
.dout(dout),
.full(full),
.empty(empty)
);
endmodule
この module は、 standard FIFOの instantiation と、追加の logicで構成されていることは明らかです。この moduleの ports は、 standard FIFOの ports とほぼ同じであることに注意してください。違いは 1 つだけです。 改造された FIFO には @eofという名前の port があります。
また、 @din と @dout の幅は 32 bitsであることに注意してください。異なる幅の FIFO が必要な場合、必要な変更は、 moduleの先頭でこれらの ports を宣言することだけです。
改造された FIFO には @full portがありますが、これは必ずしも有用ではありません。 FIFO に書き込む logic は、この portを無視することもできます。 FIFO がいっぱいになってしまったらどうしようもありません。いずれにしても、 FIFO はその後のデータの書き込み試行を無視します。ほとんどのアプリケーションでは、 FIFO がいっぱいになったという知識はあまり役に立ちません。 @eof が High になるまで待ってから、メカニズム全体を再起動するのが最善です。
overflow以降の書き込み操作の禁止
すでに述べたように、この module は standard FIFOに基づいています。この FIFOの ports はすべて、1 つの portを除いて、 eof_fifoの portsに直接接続されています。 @wr_en。この port は代わりに「wr_en && ok_to_write」に接続されます。したがって、 FIFO がいっぱいになった後に書き込み操作を停止するために @ok_to_write が使用されることは明らかです。この wire の定義は次のとおりです。
assign ok_to_write = !rst_sync && !full && !fifo_has_been_full;
この式から、書き込み操作を妨げる 3 つの状況があることがわかります。
- FIFO をリセットすると
- FIFO がいっぱいの場合
- 過去に FIFO がいっぱいになったとき
最初の 2 つの条件は簡単です。 @fifo_has_been_fullで表される 3 番目の条件に注目してみましょう。
always @(posedge wr_clk)
begin
if (!full)
fifo_has_been_nonfull <= 1;
else if (rst_sync)
fifo_has_been_nonfull <= 0;
if (full && fifo_has_been_nonfull)
fifo_has_been_full <= 1;
else if (rst_sync)
fifo_has_been_full <= 0;
end
FIFO がいっぱいになると、@fifo_has_been_full は High になります。 @full と @fifo_has_been_nonfull が両方とも High の場合、この register は High に変化します。
最初の部分は驚くべきことではありません。 @full は FIFOの "full" portに接続されています。しかし、なぜ @fifo_has_been_nonfull が必要なのでしょうか?その理由は、 FIFO が reset 状態に保たれる限り、 FIFO は「full」をハイに維持することが多いためです。この機能の目的は、 FIFO がまだデータを受信する準備ができていないことを application logic に伝えることです。 @fifo_has_been_nonfull の目的は、このシナリオで @fifo_has_been_full が誤ってハイになるのを防ぐことです。
FIFO がリセットされると、@fifo_has_been_full は Low に変化します。言い換えれば、 overflow が発生した後に、 FIFO をリセットすることが、修正された FIFO で通常の動作を再開する唯一の方法です。 @rst は asynchronous resetであることに注意してください。したがって、正しい clock domainに属する reset signal を作成するには、 logic を追加する必要があります。この信号は @rstのコピーである @rst_syncです。
EOFの生成
次の 2 つの条件が両方とも満たされる場合、 @eof port は高になります。
- FIFO 内のすべてのデータが消費されました: FIFO は空です。
- これ以上データは FIFOに書き込まれません。 @fifo_has_been_full は過去に FIFO がいっぱいだったので高いです。したがって、 FIFO がリセットされるまで @ok_to_write は Low のままになります。
これらの条件に対する Verilog コードの式は次のとおりです。
assign eof = empty && has_been_full;
この式は @fifo_has_been_nonfullではなく @has_been_full に基づいていることに注意してください。 @eof と @empty はどちらも @rd_clkの clock domainに属します。ただし、 @fifo_has_been_nonfull は @wr_clkの clock domainに属します。したがって、 @fifo_has_been_nonfull は、 clock domain crossingによって @rd_clkの clock domain にコピーされます。このコピーは @has_been_fullです。
したがって、 @eof は、「 FIFO が空であるだけでなく、 FIFOをリセットするまで満たされない」ことを意味します。
結論
多くのアプリケーションでは、 overflow が発生しないことを保証することは不可能です。このイベントが許容できない場合は、次の方法で発生時の被害を軽減することができます。 この戦略は、 overflowまでに書き込まれたデータを通過させ、それ以降はデータを書き込まないようにすることです。結局 FIFO は空っぽになってしまいます。これが発生すると、 FIFO は、 @eof portのおかげでこれ以上データが到着しないことを報告します。
この方法では、 FIFOから読み取られるデータの連続性が保証されます。 FIFOがいっぱいになった結果、途中でデータが失われることはありませんでした。これにより、このメソッドは data acquisition アプリケーションに役立ちます。
このページでは、この戦略に基づいて修正された FIFO を実装する方法を示しました。この修正された FIFO は、 standard FIFOの drop-in replacement として使用できます。唯一の重要な違いは、 FIFO を使用する logic は @eof portに注意する必要があることです。 この port が High になると、 logic は FIFO をリセットし、データ フローを再開する必要があります。
FIFOsに関するこのシリーズの 4 ページ目はこれで終わりです。次のページでは、 「standard FIFO」を FWFT FIFO に、またはその逆に変換する方法と、 timingを改善する方法を示します。