非同期FIFOを作ってみました
2017/09/24 (2017/10/06 updated)
使いまわせる非同期FIFOが欲しい
  • FPGAでFIFOを使おうとした場合は統合ツール内のIPとして提供されますが、ASICの場合「SDP RAMは提供するから自分でFIFOを作って下さい」と言われるケースもあります。

  • そんな理由でFIFOを作るわけですが、RAMの場合Read During Write(RDW)に注意すれば良いものの、FIFOのときはもう少し状況が面倒になってきます。例えば以下のように。
    • 何かFPGAのときと微妙に動きが違う
    • FPGAを違うメーカに変えたらフラグ信号のレイテンシが変わった
    • RAMの周辺論理が変わったの? FV取れないなら検証しなきゃだめじゃん

  • 結局FIFOに含まれるRAM制御論理の変更が問題につながると言えます。だとすれば、FIFOとしての制御回路は自前で作り、RAMの部分だけを置き換え想定するのが良いと思われます。RAMはRDWの動きが異なるかもしれませんが、そもそもFIFOの制御論理がRDWを起こさせないので置換のハードルはかなり下がるでしょう。

  • そこで今回は自前FIFOの例として非同期FIFOを作ってみたいと思います。

非同期FIFO設計のポイント
  • 非同期FIFO設計について以下のポイントを説明します。
    • Write / Readポインタの非同期渡し
    • Write full / Read emptyの判定方法

  • Write / Readポインタの非同期渡し
    • Write/Readの制御を行うため、Write側にはReadポインタの値、Read側にはWriteポインタの値を知らせることが必要です。ポインタのような多bit信号を非同期渡しする場合リコンバージェンスを防ぐために、通常はハンドシェイクを適用しますが、FIFOのポインタは「±1」の変化なのでグレイコードを経由する方法(*1)が使えます。


      図1 : グレイコードによる多bit信号非同期渡し

    • グレイコードは、値が「±1」変化したとき構成するbitが1箇所だけ変わるコードです。変換方法の考え方について詳しくはGoogle先生に聞いて頂くとして、ポインタの値をグレイコードに変換すれば、非同期渡しは多bitでもダブルFFで対応できることになります。


      図2 : バイナリ/グレイコード変換

  • Write full / Read emptyの判定方法
    • 同期FIFOであれば、Write/Readとも同一クロックドメインで管理できるので、判定に必要な論理を作るのにあまり困りませんが、非同期FIFOではポインタの値から判定するため、少し検討が必要です。


      図3 : Write/Readとポインタの関係

    • 一つの方法としては「ポインタの周回が同じか違うか」という情報を追加するというものです。Write/Read対象のメモリアドレスが等しくなったとき、WriteポインタとReadポインタの周回が異なれば Write full。周回含めて同じであれば Read emptyと判断できます。周回については最大1周差なので、Carryを1bitだけ追加すればOKです。


      図4 : 周回判定bit追加によるWrite full / Read empty判定

非同期FIFO: IPat_DcFifo
  • 今回作成した非同期FIFOモジュール IPat_DcFifo(*2) のソースファイルは下記になります。

  • IPat_DcFifoは、データ幅、深さ(2のべき乗)、非同期パイプの段数(2以上)、出力段FFの有無についてパラメータ設定可能な非同期FIFOです。図5に全体のブロック図を示します。


    図5: IPat_DcFifo ブロック図 (クリックで拡大版)

  • 上記のブロック図中にあるRamSdpWrap階層(Module名:IPat_RamSdpWrap)がRAM置き換え対象になります。見てわかると思いますが、ポート名はALTERA系に合わせています。

モジュール,パラメータ,ポートリスト
  • ブロック図中に置かれている各Moduleの持つ機能について表1にまとめました。また表2に構成変更用のパラメータリスト、表3にポートリストを示します。

    表1:IPat_DcFifo内モジュールとインスタンス一覧


    表2:IPat_DcFifo パラメータリスト

    * P_IPAT_DCFIFO_WRPIPE と P_IPAT_DCFIFO_RDPIPE
    • Grayコード化したポインタを非同期渡しする際のFF段数を指定します。2以上で任意整数です。

    表3:IPat_DcFifo ポートリスト

各機能とタイミングチャート
  • 図6にリセット操作のタイミングチャートを示します。
    • rst_nは非同期リセット信号ですが、IPat_DcFifo内部で同期化を行っているため、リセット解除には安定したクロック入力を必要とします。


    • 図6 : リセット操作のタイミングチャート

  • 図7にWrite動作のタイミングチャートを示します。
    • Writeリクエスト(wrreq)とデータ(data[])はwrclkの同期信号として入力します。
    • リセット直後の初期状態ではデータがFIFOに無いためrdempty==1になっていますが、Write動作によりFIFOにデータが蓄積されます。
    • Writ full(wrfull==1)の状態ではWriteができません。wrreqを1にしてもWrite動作は無効になります。すでにFIFOにWriteしたデータは保持されます。
    • WriteによるWriteポインタの移動がRead判定回路に伝搬するまで、非同期渡し部分のサイクル遅延が入ります。このためrdemptyが解除されてRead動作可能になるまで遅延が発生します。


      図7 : Write動作のタイミングチャート
      (P_IPAT_DCFIFO_AWIDTH=2, P_IPAT_DCFIFO_RDPIPE=2)

  • 図8にRead動作(Normalモード)のタイミングチャートを示します。
    • Readリクエスト(rdreq)はrdclkの同期信号として入力します。
    • Read empty(rdempty==1)の状態ではReadができません。


      図8 : Read動作のタイミングチャート
      (P_IPAT_DCFIFO_AWIDTH=2, P_IPAT_DCFIFO_RDPIPE=2, ID_PAT_DCFIFO_SAHEAD="FALSE")

  • 図9にRead動作(Show-aheadモード)のタイミングチャートを示します。
    • Write動作を検知すると、Read動作を開始してデータを出力します。
    • 複数ワードがWriteされている場合rdempty==0となりますが、最初のRead動作データ出力と同時になるため、Normal Readより1サイクル遅れます(図8と比較下さい)。
    • Show-aheadモードでは、rereq信号を"Request"ではなく"Ack"として使うと考えて下さい。


      図9 : Read動作のタイミングチャート
      (P_IPAT_DCFIFO_AWIDTH=2, P_IPAT_DCFIFO_RDPIPE=2, ID_PAT_DCFIFO_SAHEAD="TRUE")

今後の改良について
  • 今回作成して思ったのですが、今後3点ほど改良したいと考えています。
    • 1点目は、Show-aheadの対応です。正直作ってから「あっ、忘れてた」と思った内容です。これは近いうちに入れて、本ページをUpdateしたいと思います。
    • 2点目は、RAM Wrapper部分のModule名をFifoモジュール毎にユニーク化できるようにしようと考えています。具体的にはちょっとしたFIFOの生成ツールを作って対応します。
    • 3点目はECCです。ASIC RAMだとECC回路は外に置かれるケースが多いので、ECC生成スクリプトと組み合わせて回路を挿入できるようにしたいと考えています。
Notes
  • FIFOの深さは2のべき乗(2,4,8,16 ...)に制限されます。途中でAll-0に戻すと複数bitが変化してしまうからです。
  • 接頭辞のIPat_は、Altmo toolbox の IPを意味しています。念のため書きますが、本Moduleを使用したことで得られる全ての結果についてAltmoは責任を負いません。
Copyright(C) 2017 Altmo
本HPについて