公開日:2023/10/07
title.png

27th Season


#27-6 FIFO回路の設計

 Cmod A7ボード(FPGA)を使って設計しているオリジナル16ビットCPUコアMoscoviumにて、「ちょっと、FIFOマクロが無駄に大きいよな〜」と、思ってしまいました。FIFO回路くらい、自前で設計して持っておく方が何かと便利なので、今回は汎用のFIFO回路を設計して行こうと思います。

FIFOマクロの特徴

 Vivadoが提供しているFIFOマクロには、以下の様な特徴があります。

 Moscoviumマイコンでは、UART8N1ユニットの受信バッファなどにFIFOマクロを使用しています。簡単に使えて便利なのですが、精々数100k[baud]程のUARTに1KバイトものFIFOバッファは冗長が過ぎます。本来なら数10バイトあれば十分です。
 数10バイトで充足するFIFOバッファの為に、貴重なRAMマクロを1個占有使用してしまうのは勿体ないです。RTLを使って小さなFIFOバッファを作成し、この無駄を省く事が今回のテーマです。

FIFO回路の目標仕様と課題

 以下を目標仕様として設計したいと思います。

  目標仕様:

 ソフトウェアでは、以下の記事でリングバッファを使ってFIFOを作成しました。

  「#13-8 DUEのWAVファイル再生を向上する

 正直ソフトウェアとしては、それほど難しくも無いプログラムです。しかし、これをハードウェアで実装するのは、相当に難易度が高いのです。
 しかも、読み出し側と書き込み側のクロックを非同期で動かすとなると、それはもう、職人技の範疇です。しっかりと戦略を練って進めた方が良いでしょう。

FIFO回路の設計

 まずは、ブロック図を書きながら、イメージを整理してみました。

a276_fifoblk.png

 上図を使って、以下解説を続けます。

インターフェイス

 以下の通り、Vivadoが提供するFIFOマクロの置き換えで使用可能なインターフェイスとします。

  【入力信号】:

  【出力信号】:

 FIFOビット幅、FIFO段数、先行出力閾値の指定は、RTLコンパイル時のオプションとし、実行時の動的変更はできません。
 また、書き込みデータビット幅と、読み出しデータビット幅を異なる値に設定可能な場合もありますが、今回のFIFO回路では対応しません。

ポインタ制御

 こちらに示したブロック図で、書き込みポインタptr_wr、および、読み出しポインタptr_rdは、「次にアクセスする分散RAMのアドレス」を示しています。

 つまり、ptr_wr=ptr_rdの場合、次に書き込もうとするアドレスptr_wrと、次に読み出そうとするアドレスptr_rdが一致してしまっているのだから、「読み出し可能なデータがない」⇒エンプティ(空)であることを示していることになります。
 この状態になったら、empty_rdフラグがアサートされます。empty_rd=1の状態でrd_n要求をアサートして読み出す行為は禁止です。もし読み出すと無視され、常にデータ0が読み出されます。

 通常の場合、書き込みデータが溜まって行き、追いかけるように読み出しポインタが追従するので、ptr_wr>ptr_rdで推移します。
 分散RAMはリングバッファになっていて、最大アドレスを超えるとアドレス0に戻って増加し続けます。

 読み出しが間に合わず、書き込みデータが滞留すると、いずれ、ptr_wr+1=ptr_rdとなって、「書き込み可能な分散RAM領域が残っていない」⇒フル(満)状態となります。
 この状態になったら、full_wrフラグがアサートされます。full_wr=1の状態でwr_n要求をアサートして書き込む行為は禁止です。もし書き込むと無視されます。

※エラーフラグ等は用意しないので、使用者がempty_rd/full_wrフラグをモニタしながら、禁止アクセスを実行しないように制御しなくてはなりません。


 上で説明した内容を、もう少し具体的に整理して行きます。

コンパイルオプション

 RTLとしては、以下の様なコンパイルオプション(`defineマクロ)を用意して、所望のFIFO回路を生成可能な様にしておこうと思います。

 目標仕様としては、「非同期クロックが使えること」としましたので、その様な回路を作り込んであります。
 しかし、非同期クロック間通信の基本はハンドシェイクであり、通信には大変時間がかかります。

  「#19-5 FPGAボードのAD変換器を使う」の非同期設計をご確認ください。

 つまり、例えば以下の様なケースが考えられます。

 まず、最初の応用として考えているUART8N1ユニットで使う場合は、読み出し/書き込み共同じクロック、すなわち同期クロックで使います。同期クロックで使う場合には、fsyncブロックにて信号を通過させれば、タイムラグは無くなります。
 つまり、そういうコンパイルオプション(FIFOIP_SYNC)も必要だったという事です。
 尚、同期クロックとして使う場合、clk_wrおよびclk_rd端子には、同じクロックを接続して使わなくては、なりません。

モジュール名ルール

 1本のRTLから、コンパイルオプションによって様々な構成のFIFO回路を作ることができるため、以下の様な命名ルールを定めておきました。

  FIFO回路命名ルール

 UART8N1ユニット用としては、8ビット×64段(同期クロック)を考えているので、「fifo8s64」がモジュール名となります。

分散RAMの推定

 分散RAMは、以下の様なreg宣言を使って記述しました。

  // memory
  (* ram_style = "distributed" *)
  reg [`FIFOIP_DBIT - 1:0] mem[0:`FIFOIP_AMAX - 1];


 (* 〜 *)は、ブロックコメント/* 〜 */に似ていますが、Verilog-HDLの属性指定記述です。主に、論理合成ツールに対して、属性を指定するために使用します。中身の「ram_style = "distributed"」は、恐らく、Xilinx Vivado固有の属性だと思われます。distributed(分散)を指定することで、RAMマクロではなく、分散RAMを推定するように指示しています。
 これが無いと、何らかの閾値を超えてサイズが大きくなった場合、RAMマクロを推定されてしまいます。最初、128バイト程の分散RAMを使おうとしただけでRAMマクロを推定されてしまいましたので、閾値は、かなり小さいみたいです。

※他にも、特定の信号名を論理合成後にも保存する「(* dont_touch = "yes" *)」等の属性があります。

論理検証

 以下の条件で単体検証を実行して仕上げました。

 その後、Moscoviumマイコンに組み上げて、結合検証もざっくり、こなしておきました。
 最後に、Cmod A7ボードに書き込んで、UART8N1ユニットから大量のデータを受信する、ブートモードなどが正常に動作していることも確認済みです。

デザイン

 従来型名のまま、UART8N1ユニットをインスタンスすると、デフォルトで64バイトFIFO回路が接続されるように変更してあります。
 万一、旧来の1KバイトFIFOマクロを使用したい場合には、以下のコンパイルオプションを設定すると、復活させることができます。

  // Compile option
  // I/O unit
  `define MCOC_UART_FIFO_SIZE 1024


 VHファイルに上の行を追加すると、従来の1KバイトFIFOマクロを使うようになります。たぶん、1Kバイトなんて巨大なFIFOは必要なかったはずですが、必要に応じて、構成を変更して使ってください。


 Cmod A7ボード用Moscoviumマイコンの設計データ(Vivadoプロジェクト)は、GitHubにて配布しております。以下バナーよりアクセスしてください。

btn_moscovium.png

 ダウンロード方法などについては、トップのREADME.mdをご覧ください。

 Cmod A7ボードへの書き込み方法は、こちらを参照してください。

コンパイル

 Moscoviumマイコン全体でコンパイルしても、「何の事やら?」と言う感じなので、mcoc_comn.vファイル内のmcoc_uartモジュール階層で単体コンパイルして見ました。mcoc_uartモジュール階層には、UART8N1ユニット本体回路と、受信FIFOバッファ回路が含まれています。

論理規模比較 今回のFIFO回路
fifo8s64(64バイト)
VivadoのFIFOマクロ
xpm_fifo_sync(1Kバイト)
mcoc_uart全体 LUT数(合算) 268 286
F/F数 250 297
   UART8N1
ユニット
LUT数 216 218
F/F数 223 223
FIFO ロジック
LUT数
40 68
F/F数 27 74
分散RAM
LUT数
12 0
18Kビット
RAMマクロ数
0 1

 Vivadoでコンパイルして結果報告を見ると、使用したLUT数がロジックのLUT数と分散RAMのLUT数に分離され、別々に報告されています。上表で、全体のLUT数では合算しています。
 結果として、今回のfifo8s64モジュールはVivadoのFIFOマクロと比べてもコンパクトに仕上がっています。

 勿論、本当に1Kバイト超のFIFOが必要なケースでは、FIFOマクロ(RAMマクロ)を使った方が費用は低く抑えられますので、適材適所で選定することが大事です。


 今まで小容量のFIFOマクロが使い難かったため、FIFOを搭載したかったけど回避したモジュールが有るのですが、今後は、今回設計したモジュールを応用することができます。自前(RTL)でFIFOモジュールを持っておくと、色々と応用が利くので、お勧めです。


btn_arduino.png

btn_prev.png btn_next.png

home