01signal.com

FPGA FIFOs: さまざまな機能とバリエーション

範囲

このページは、 FIFOsに関する 5 ページからなるシリーズの2 番目のページです。前のページで FIFO の基本を説明した後、一般的なバリエーションと追加機能について説明します。 FIFO は、多くの場合、以下で説明するオプションの組み合わせとして構成されます。

Single clock FIFOs

「baseline FIFO」は無関係な clocksに対して2つの inputs を持っていますが、両側の信号が同じ clockと同期していることがよくあります。この同じ clock を @wr_clk および @rd_clkに接続してもまったく問題ありません。しかし、 clock inputsの両方で同じ clock であるため、 clock domain crossingを使用する必要はありません。そのため、 FIFO には不要な logicが含まれています。 clock domains の詳細については、こちらを参照してください。

したがって、すべての FPGA ベンダーは、 FIFOに対して次の 2 つのカテゴリを提供しています。 Dual-clock FIFO と single-clock FIFO。他の名前がよく使用されます。 Independent Clock FIFO 対 Common Clock FIFO または Asynchronous FIFO 対 Synchronous FIFO。前ページでご紹介した「baseline FIFO」は dual-clock FIFOです。

すべての logic は同じ clockと同期しているため、Single-clock FIFOs には synchronization logicはありません。したがって、 reset input は、他の portsと同じ clock と同期している必要があります。

FPGA logicを無駄にしないことを除いて、 single clock FIFOs を使用するもう 1 つの正当な理由は、わかりやすくするためです。これは、2 つの clocks を使用する意図がないことを明確に表明する方法です。

要するに: FIFO が 2 つの clock domains間で接続できない場合は、 single-clock FIFOを使用してください。

FWFT FIFOs

前のページで強調したように、「baseline FIFO」からデータを読み取る手順は、 @rd_en を High に変更し、の clock cycleで FIFOの @dout output の値を取得することです。それはいくぶん直観に反しています。 データが既に FIFOにある場合、なぜそれを要求しなければならないのですか?なぜ FIFO は @dout portにそれを載せて、それを使用しても問題ないと私に言うことができないのですか?

したがって、まさにそれを行う一般的なバリアントがあり、それは First Word Fall Through FIFO (FWFT、 read-ahead、 show-ahead または look-aheadとも呼ばれる) と呼ばれます。 FWFT FIFO の反対は、しばしば "Standard FIFO" と呼ばれます (標準を教えてもらえますか?)。

アイデアは簡単です: FWFT FIFO が空でなくなると (データが書き込まれたため)、 @doutに最初のワードが表示されます。次に、 application logic は、 @rd_en をハイに保持することによってワードを読み取ります。したがって、違いは最初の単語に関するものです。

ただし、 ports の 2 つの意味が変更されていることを理解することで、 FWFT FIFO を理解しやすくなります。 FWFT FIFO 上の @rd_en は、実際には「 @doutのデータを消費したところです。次のデータを取得しても問題ありません」という意味であり、 @empty は実際には「@dout は無効です」という意味です。

変更されていないのは、 @empty が高い場合に @rd_en を高くしてはならないということです。無効なデータを消費したとは言えません。したがって、ルールは同じままですが、別の理由があります。

次の waveform は、 FWFT FIFO からの読み取りがどのように見えるかを示しています。

Example waveform for reading words from a FWFT FIFO

@rd_en が Low のときに @dout の最初の有効な値が表示され、有効な値が表示されると同時に @empty が Low に変化することに注意してください。先ほど述べたように、 @empty は FWFT FIFOでは「@dout が無効」を意味し、 waveform はそれを反映しています。

また、 @rd_en の最初のパルスは FIFOから新しい値を読み取ったのではなく、 @empty を再びハイに変化させたことにも注意してください。それに伴い、 @dout の値も同時に不明となりました。実際には、通常、 @empty が High に変化しても @dout は変化しませんが、それを当てにすることはできません。

この後、 FIFO はもう一度 @dout に値を置き、 @empty を Low に変更します。 application logic は 3 ワードを読み取り、 @rd_en を Low に変更します。全体として、 application logic は FIFOから 4 ~ 5 ワードを消費しました。

waveform は、 application logic が D4 の値を使用したかどうかを教えてくれないことに注意してください。 5 番目の単語を無視した可能性があります。つまり、4 単語しか消費しませんでした。または、5 番目の単語の値を使用した可能性があります。 waveform から明らかな唯一のことは、4 つの clock cyclesの後、 application logic が @rd_en を低く保持したため、 FIFO が @doutを更新し続けることができなかったことです。

もう 1 つ注意すべき点は、 FIFOのメモリ内にさらにデータがあるかどうかがわからないことです。この waveform の最後に @empty が低いという事実は、 @dout が有効であることを意味するだけです。

前のページの Verilog の例を変更してみましょう。もう一度、このコードは FIFOから出力されるすべての累積合計を計算します。

assign rd_en = !empty; // If @dout's value is valid, it's consumed.

always @(posedge rd_clk)
  if (!empty) // FIFO is FWFT, so !empty means @dout contains valid data
    sum <= sum + dout; // Don't try this at home: @sum is never reset.

前の例とは異なり、これは FWFT FIFOを使用するため、前の clock cycleで @rd_en の値を持つ register は必要ありません。代わりに、 @empty が Low のときに @dout を消費できます。この単純なルールは、 @empty がローのときに @rd_en がハイになるため機能します。したがって、 FIFO からの各ワードは、正確に 1 つの clock cycleに対して @dout で有効です。

FWFT のトピックを、少し関係のない点でまとめたいと思います。 「標準」FIFO と FWFT FIFO の違いは、 logicの任意の 2 つの modules 間のデータ フローに関する基本的な問題を反映しています。 受信側はデータを要求する必要がありますか?それとも、送信側はできるだけ早くデータを提示し、受信側は続行しても問題ないことを確認するだけですか?ある module が別の module にデータを渡すときは、常にこの質問を自問してください。特に、これらの modules がこの問題に同意するかどうかを自問してください。

Asymmetric FIFOs

@din と @doutの幅が異なる FIFO を定義することは一般的に許可されています。これは、たとえば、データが 32 ビット ワードで FPGA に到着するが、 application logic がそれらをバイトとして処理する場合、つまり、1 ワードあたり 8 ビットである場合に便利です。この場合、書き込み側の幅を 32 ビット、読み出し側の幅を 8 ビットに設定します。両側は、単一の write cycleで挿入された単語を消費するのに 4 つの read cycles が必要であることを除いて、通常どおりに動作します。

読み取り側が書き込み側より広い場合、期待どおりに動作します。 FIFO に書き込まれたデータは、書き込まれたデータが読み取り側のサイズのワードを埋めるまで、読み取り側では使用できません。

単語の並び順についてですが、 FIFOs はすべて Little Endianを使っているようです。たとえば、 FIFO は次のように 32 ビット ワードを 8 ビット ワードにパックします。 FIFO から読み取られる最初のワードのビット範囲は [7:0]、次に [15:8]、 [23:16] 、 [31:24]です。

ただし、この機能を使用する場合は、常にドキュメントを確認してください。

@empty および @fullでの combinatorial logic との依存関係

@empty および @full ポートには共通の欠点があります。 application logic は、同じ clock cycleでそれらに応答する必要があります。つまり、 @rd_en は、これら 2 つの signals が同じ clock cycle で高くならないようにするために、 @empty に依存する combinatorial function でなければなりません (前述のように、これは禁止されています)。同様に、 @wr_en は @fullに依存する combinatorial function でなければなりません。

combinatorial functions を使用すると、 timing constraintsを実現する上で障害になる可能性があります。これは、 clockの周波数が ( FPGAの仕様に比べて) 高く、 logic function が複雑な場合に問題になる可能性があります。問題の主な理由は、 @rd_en と @wr_en の両方が、データを生成または消費する logic で使用されることが多いことです。特に、多くの logic に対して clock enable を計算する logic function は、これらの信号に依存する可能性があります。たとえば、 FIFOからのデータを処理する長い pipeline がある場合、 FIFO からのデータ フローが一時的に停止すると、 pipeline 内のすべての logic がフリーズする必要があります。

正確に言うと、その combinatorial functionを回避する方法があります。たとえば、 @wr_en が registerとして宣言され、 @want_to_write が、 application logicが特定の時間に書き込む必要があることを表す信号であるとします。これを行うことができます:

always @(posedge wr_clk)
  wr_en <= want_to_write && !wr_en && !full;

これにより、 @wr_en が High になった後に @full が clock cycle でのみ High に変化する可能性があるため、 @wr_en と @full が同じ clock cycleで High になることはありません。式の !wr_en 部分により、 @wr_en が 2 つの連続する clocks cyclesの間にハイにならないようにします。したがって、 @full がハイに変化すると、最初の clock cycleの !wr_en のために @wr_en がローになります。 @wr_en は、 @full 自体のために Low のままです。

しかし、このソリューションでは、 @wr_en は半分の時間で Low でなければなりません。その結果、 FIFOのデータ レートの 50% のみが使用されます。これは通常、受け入れられません。

@rd_enでも同じ解決策が可能ですが、この解決策には半分のデータ レートを使用するという同じ問題があります。

この議論は、次のセクションにつながることを意図していました。 "almost" ports。

Almost full、 almost empty 、および同様の ports

FIFOには 2 つのオプション ポートを追加できます。 @almost_full port および/または @almost_empty port。

@almost_empty は @rd_clkと同期しており、 @emptyに似ていますが、わずかな違いがあります。 FIFO が空の場合、 @almost_empty はハイになりますが、 FIFOから読み取るワードが 1 つだけある場合同様です。

同様に、 @almost_full は @wr_clkと同期しており、 FIFO が満杯のときだけでなく、 FIFOに正確に 1 ワードを書き込める場合にもハイになります。

これら 2 つの output ports の名前は、 FPGA のベンダーとそれが提供するソフトウェアによって異なりますが、同じ機能を持つ ports を追加する可能性は常にあります。 FIFO の特定のバリアントがこれらの portsをサポートしていない場合があります。

これらの ports はどのように役立ちますか?まあ、これは完全にうまくいくので:

always @(posedge wr_clk)
  wr_en <= want_to_write && !almost_full;

combinatorial logicがなく、 write cyclesの半分をスキップする必要もありません。 @almost_full が High の場合、 @wr_en は同じ clock cycleで Low に変化しない場合がありますが、次の clock cycleでのみ変化します。その結果、 @almost_full が High に変化した後、1 回の書き込み操作が発生する可能性があります。しかし、1 語の場所があるので、それで問題ありません。

FIFO が満たされている間に @want_to_write が継続的にハイに保持されている場合、最後の書き込み操作によって FIFO が完全に満たされることに注意してください。そうしないと、 FIFO がほぼ満杯になる可能性があります。 @want_to_writeが原因で @wr_en が低くなり、そのために FIFO が完全にいっぱいにならない場合、二度とチャンスはありません。 @almost_full は、反対側が FIFOからデータを読み取る場合にのみローに変化するため、 FIFOには 2 ワード以上のスペースがあります。

これが問題になることはめったにありませんが、議論のために、これにより最後の単語が使用されることが保証されます。

always @(posedge wr_clk)
  wr_en <= want_to_write && (!almost_full || (!full && !wr_en));

この @wr_en の式は、正確に 1 語を記述しても問題ない場合を除いて、ほとんどの場合 @almost_full に依存しています。その場合にのみ、 @wr_en は @full と @wr_enに依存します。これは、上記の !wr_enを使用した式と同様です。

しかし、この @wr_en の最後の表現が役に立つかどうかは、私は真剣に疑っています。

@almost_empty の話は似ているので、これで問題ありません (ただし、これをコードにコピーしないでください)。

always @(posedge rd_clk)
  rd_en <= want_to_read && !almost_empty;

@almost_fullと同様に、最後の単語に問題があります。 @want_to_readが原因で @rd_en が低い場合、 FIFO がさらにデータで満たされるまでチャンスを失います。 @almost_fullの場合とは異なり、これはいくつかのシナリオでは間違いなく問題になる可能性があります。 @almost_empty が高くても FIFO が空でない場合は、 FIFO に読み取り用のデータがあり、このデータが FIFOに残っていることを意味します。

したがって、これが安全な方法です。

always @(posedge rd_clk)
  rd_en <= want_to_read && (!almost_empty || (!empty && !rd_en));

Fill counters

Application logic は多くの場合、操作をチャンクで実行します。たとえば、 FIFO から一定の長さのデータのパケットを読み取り、これらのパケットをいくつかの物理メディアを介して送信する logic 。データは FIFOに保存されるため、 application logic は読み取りを開始する前に、パケットを満たすのに十分なデータがあることを認識する必要があります。

同様に、 application logic は、多くの場合、 FIFOに格納するために一定量のデータを生成します。たとえば、外部メモリから burst のデータを読み取ります。 burstを完了するのに十分な場所が FIFO にない限り、操作は開始されません。

これらの目的のために、 FIFOs は通常、 fill counters、 programmable empty port 、および programmable full portをサポートします。 fill counters ( data countersと呼ばれることもあります) は、 FPGA のベンダーに大きく依存して、さまざまな形式や形状で提供されるため、 FIFOのドキュメントを注意深く読んでください。注意すべき主な問題は次の 3 つです。

また、 @almost_empty 、 @almost_fullの拡張版である programmable empty 、 programmable fullがあります。アイデアは、 fill counters の使用はほぼ確実に次のようなものであるためです。

assign dont_start_reading = (rd_data_count < 64);

その信号を直接提供して、それを prog_emptyと呼んでみませんか?もう一度、 FIFOのドキュメントを注意深く読んでください。

繰り返しになりますが、 FIFOの最後の言葉を読むことが重要な場合は、 logic が本当にそうするかどうかを自問してください。この質問は、 @almost_emptyに関する上記の議論に似ています。

言うまでもなく、必要に応じて、 FIFOを構成するときにこれらの追加の ports を要求する必要があります。

AXI interface

このトピックは直接関連していませんが、この用語は FIFOsのコンテキストでよく使用されるため、混乱を避けるために言及する価値があります。

AXI は、 ARMによって導入された AMBA 標準で定義されたインターフェイスのセットです。ご想像のとおり、 AXI インターフェイスを備えた FIFOs は、通常、 CPUの周辺機器として機能することを目的としています。

「baseline FIFO」のインターフェイスは、 AXI インターフェイスとは対照的に、「native」インターフェイスと呼ばれることがよくあります。

AXI インターフェイスには、主に 2 つのタイプがあります。 アドレスとデータを持つ bus である「通常の」 AXI (通常は AXI3、 AXI4 または AXI Lite)。 2 番目のタイプ AXI-S (streamed AXI) は、 streams のデータ (おそらくパケットに分割されます) を対象としています。

FIFO が AXI3 / AXI4 または AXI Liteとして構成されている場合、余分な logic が追加されるため、このインターフェイスを介して CPU へのアドレスを持つ周辺機器として接続できます。これについては、まったく別のトピックなので、これ以上詳しく説明しません。

しかし、 streaming インターフェースは FIFOの動作に多少似ているため、 AXI-S インターフェースの handshake signals を「native」インターフェースに変換することができます。 AXI-S には、同様に処理が必要な他の信号が含まれることが多いことに注意してください。

したがって、 FIFO に @axi_w_valid、 @axi_w_ready 、および @axi_w_dataとして書き込むための AXI-S 信号が与えられると、それらは「標準」 FIFO の ports に接続できます。

assign axi_w_ready = !full;
assign wr_en = axi_w_valid && axi_w_ready;
assign din = axi_w_data;

同様に、 FIFO、 @axi_r_valid、 @axi_r_ready 、および @axi_r_dataから読み取るための AXI-S 信号は、 FWFT FIFO の ports に接続できます。

assign axi_r_valid = !empty; // Non-empty means valid with FWFT FIFOs
assign rd_en = axi_r_valid && axi_r_ready;
assign axi_r_data = dout;

繰り返しますが、これが機能するには、 FIFO が FWFT バリアントでなければならないことに注意してください。

これで、 FIFOsに関するこのシリーズの 2 ページ目は終了です。次のページでは、 single-clock FIFO が Verilogにどのように実装されているかを示します。

このページは英語から自動翻訳されています。 不明な点は元のページを参照してください。
Copyright © 2021-2024. All rights reserved. (6f913017)