はじめに
だいぶ以前、最小限の RISC-V 命令だけ実装して遊んだときに下記のようなレジスタファイルを作りました。 後でいろいろパラメータ変えて計測しようと思いつつ完全に忘れていたので思い出したように実験です。
https://github.com/ryuz/jelly/blob/master/rtl/v2/jfive/jelly2_register_file.sv
下記が RISC-V 命令そのままの 書き込み1ポート/読み出し2ポートのレジスタファイルを構成したものです。
基本的には Xilinx の RAM32X1D を活用する方向で作成しており、書き込みと読み出しのアドレスが同じ場合に新しい値を読むようにするなどのロジックを加えています。基本的に LUTRAM がうまく活用できています。
これを拡張したら何が起こるのか備忘録程度に残しておきます。
いろいろ合成実験してみる
ポート数を増やしてみる
もしスーパースカラ実行などを行って IPC(Instructions per Cycle) を 1.0 以上にしたい場合、当然ポート数も増やさなければ複数命令を同時実行することが出来ません。
そこで順に増やしてみました。LUTRAM が使えなくなるので、ダイレクトに FF を消費するのだろうなと思いつつ実験です。
ものの見事にLUTやFF大量利用状態になってしまいました。下手すると増えた分で RISC-V コアがもう一個作れてしまいそうです(笑)。 レジスタは 32bit×32本のままで、ポートを増やすだけでどんどん肥大化していきます。もちろんここからレジスタ数や幅を増やすとその分増えてしまいますのでSIMD なんかやった日には大変なインパクトになりそうです。
bit幅を広げてみる
再びポート数を書き込み1、読み出し2に戻して今度はレジスタの幅を32bitから256bitに8倍に増やしてみました。
こちらは単純に8倍に増えただけでした。これは SIMD 命令のようなものを実装する分にはそれほどインパクトが無いことを示しています。
Block-RAMを使ってレジスタ数を増やしてみる
Block-RAM の WRITE_FIRST モードを使って 32本だったレジスタを 1024本まで増やしてみます。
こちらはメモリの構造上、書き込み1ポートが上限になりますが、ほぼ Block-RAM の機能だけで実現できています。 Block-RAM の貴重さ具合は状況によるとしか言えないかと思いますが、なかなか有効そうです。
レジスタ数を増やすと SMT(Simultaneous MultiThreading) のようなことをやったり、レジスタリネーミングを行ったりするのには有効そうです。
おわりに
プロセッサを作るような場合レジスタファイルはしばし重要なポジションにありますが、ことFPGAにおいてはFPGAのもともと持っている機能にうまくはめてやることは重要そうです。 特に書き込みポートを2つ以上にしようとするとほぼすべてのレジスタbitを LUTにする必要が出てきてインパクトが大きそうです(まあ、初めからわかってたことですが)。
複数命令同時実行をする場合も、汎用レジスタと浮動小数点用レジスタを分けるような、専用レジスタ化するような方法を模索して、ポート数の少ないレジスタファイルを複数うまく組み合わせるよう事を考えていくのがよさそうには思いました。
少数の読み出しポートを持った複数のレジスタファイルのほうが、全ての読み出しポートを備えたレジスタファイルより小さくかつ/または高速であると考えられるようになった。
という記述があったが、FPGAに限らずその通りなのではないかと思います。
追記
FPGA開発日記 の msyksphinz 様より、FPGA固有のこんなテクニックを教えて頂きました!(感謝)
こんにちは。FPGAでの読み書きポートの追加には、以下のような手法もあります。書き込みポートを増やすために、さらにRAMを活用する手法です。
— Masayuki@FPGA開発日記 (@dev_msyksphinz) 2024年4月1日
LVT Multiport RAM : https://t.co/3W0PWXT7yA
XOR Multiport RAM : https://t.co/r0lEM0TNf2
いろいろ目から鱗でしたが、ちゃんと調べるべきでした。 ただしレジスタ容量が増えるわけではなく、ポート数をn倍に増やすのにリソースがn倍以上必要なのはその通りなので、もっと良い方法があるならそちらが良いのだとは思います。とはいえ、FPGAでもマルチポートのレジスタファイルをそこそこリーズナブルに検証できるというのはとても有難い技法に思いました。