始めに
シストリックアレイ( Systolic array )とは、行列乗算を効率的に行うための演算器アレイです。乗算加算器を縦横に並べてデータを順番に食わせることによって乗算に必要な乗算加算の組み合わせをすべて計算してくれます。例えばGoogle TPUなどはディープラーニング推論向けにシストリックアレイを搭載しております。今回はAMD製Arty A7での動作を目標に、16bit整数シストリックアレイの模型を実験できるようにverilogコードを作成しました。模型なのでまだまだ制約はありますが、手元のArty A7 T35で6x6のシストリックアレイの動作を確認できております。ただし、演算器はverilogで書いてあるため、ほかのFPGAへもポーティングは可能と考えられます。
本レポジトリのコードは単体にriscv-rv32iのCPUを持ち、ではなく、my-riscv-rv32iのCPUと接続して動作するように簡易的なDMA IOバスでのI/Fを付けております。実験の際は、my-riscv-rv32iも併せての使用となります。以下の使用手順も併用を前提としております。
(本内容はレポジトリのREADME.mdとほぼ同じになります。)
レポジトリ
・my-systolic シストリックアレイのレポジトリ
・my-riscv-rv32i 併用するriscv CPUのレポジトリ
2023/12/25追記
・動作確認とれたので、NxN 拡張及び動作確認方法を追記しました。
2024/1/6追記
・my-riscv-rv32iのData RAMを16Kwordに拡張して、my-systolic array 6x6で30x30行列乗算の動作を確認しました。
2024/2/8追記
・my-riscv-rv32iの大幅改変のため、スナップショットをmy-systolicに取り込みました。使用方法をアップデートしております。
仕様
・16bitシストリックアレイ演算器 コードジェネレータによりアレイ部分の大きさは可変(8x8の64演算器まで生成実験しましたがタイミングがだんだん厳しくなります)
・16bit x 16bit ⇒ 32bitでの内部加算をして、総和結果を16bitに絞って出力。この際オーバーフロー/アンダーブローは飽和処理をする。
・飽和フラッグを各乗算に対して1ビットずつ別途出力
・16bit read/write I/Oバス I/F
・Arty A7 35Tで、75MHzで動作実績
制約
・Gather/Scatter機能は無し。部分行列を自力で読み出し、自分で並べ替える必要あり
使用方法
RTLシミュレーション(デフォルト 2x2を使用する場合)
・本レポジトリおよび、my-riscv-rv32iをクローンしてください。
・my-riscv-rv32iのスナップショットをmy-systolic内に移しました。
・./fsimディレクトリを作成し、以下のファイルをコピーしてください。
・rtls
my-systolic/systolic/*.v
my-systolic/fpga/fpga_all_top.v
my-systolic/sim/fullsimtop.v
my-systolic/cpu/*.v
my-systolic/io/*.v
my-systolic/mon/*.v
my-systolic/sim/pll_sim.v
・fpga_all_top.vを開いて、defineをTANG_PRIMERに変更
・my-systolic/asm のディレクトリに入り、以下を実行
$ ./riscv-asm1.pl systolic_test_whole_gen.asm > ../fsim/test.txt
$ python3 ../tools/matrix_file_gen.py 64 a2.csv b2.csv c2.txt
$ ../tools/split.pl c2.txt
$ mv test*.txt ../fsim
・my-systolic/fsimに入り、以下を実行
これで実行に必要なファイルはfsimにそろいました。各自のRTLシミュレータでシミュレーション実行してください。筆者はintelの無償提供版のModelsimを使用しています。
合成(Arty A7)
・上記fsim/ディレクトリ内のうち、fullsimtop.vおよびpll_sim.vを除くファイルを使用します。
・fsim/fpga_all_top.vのdefineオプションをArty_A7に変更します。
・fsim/uart_if.vの周波数オプションを75MHzにします。
・vivadoを立ち上げ、上記使用ファイルおよび、タイミングとしてsyn/riscv_io_pins.xdcを指定します。
・合成前にPLLを75MHzでインスタンスします。
・あとは合成⇒インプリメント⇒ビットストリーム作成⇒ビットストリームアップロードで完了します。
実機動作
・シミュレーションで使用したtest.txtおよびc2.txtが、実行コードおよびデータパターンファイルとなります。なければ上記に従って作成してください。
・teratarmを立ち上げ、シリアル端末を立ち上げます。設定は、端末⇒改行コード 受信:AUTO、シリアルポート 9600bps,8bit,none,1bit,noneとします。
・qを打つと、エコーバックのqが返ってきます。モニターの詳しい使い方は、my-riscv-rv32i/README-mdを参考にしてください。
・i00000000と打ちます
・ファイル⇒ファイル転送で、上記test.txtを指定します
・qを打ちます
・w00000000と打ちます
・ファイル⇒ファイル転送で、上記c2.txtを指定します
・qを打ちます
・g00000000と打つと、テストプログラムが実行されます。Lチカが開始されたら、テストはパスとなります。白くLEDが点灯したままであると、failしております。
上記テストプログラムは、以下のことをしています。
・行列Aの1列目データをIO DMA書き込みします
・行列Aの2列目データをIO DMA書き込みします
・行列Bの1列目データをIO DMA書き込みします
・行列Bの2列目データをIO DMA書き込みします
・シストリックアレイをスタートさせます
・ここまでを3回繰り返します(2回以上動作することを確認するため)
・結果行列のパート1をIO DMA読み出しします
・結果行列のパート2をIO DMA読み出しします
・結果行列のパート3をIO DMA読み出しします
・結果行列のパート4をIO DMA読み出しします
・読み出した結果とあらかじめ設定していた結果を比較して、全部一致したら、Lチカに飛ぶ
NxN拡張版作成方法
・tools/gettop.pl n > file でsystolic4.v相当のファイルを作成します。
・tools/getiobuf.pl n > file でiobuf.v相当のファイルを作成します。
・サンプルとしてn=4,6の場合のファイルをothers/の下に置いてあります。
ファイルの使い方、動作確認の方法
(1) シミュレーション、合成
・使いたいsystolic*.v及びiobuf*.vを準備してください。
・fpga_all_top.vの中のsysctolic4 を定義しているところの名前を変更してください。
・あとは論理シミュレーション、合成ともn=2の場合と同一にできます。合成はArty A7で6x6サイズでも50MHzで動作します。
(2) プログラム及び動作確認
・nがいくつでも動作するプログラム systolic_test_whole_gen.asm を準備しました。アセンブラでバイナリテキストを作成してください。
$ ./riscv-asm1.pl systolic_test_whole_gen.asm > systolic_test_whole_gen.txt
・ツールを使ってMxMの行列のCSVを2つ作成します。乱数で発生するpythonがtoolsの下にあります。
$ python3 ../tools/rand_matrix.py m i > a1.csv
ただし、m:行列のサイズ、i:整数の-min/max値 たとえば25を指定すれば[-25,25]の整数を出力します。
・次に行列演算パラメータ一式をツールを使って作成します。
$ python3 ../tools/matrix_file_gen2.py n a.csv b.csv c.txt
ただし、n: シストリックアレイのサイズ a.csv, b.csv: 行列のcsv c.txt:出力ファイル名
・シミュレーションの場合は、出力したc.txtをさらにツールで4分割します。
$ ./split.pl c.txt
test0.txt, test1.txt, test2.txt, test3.txt が出力されます。
・合成結果をArty A7にのっけて動作させる場合は、モニタのi00000000でsystolic_test_whole_gen.txtを また、w00000000でc.txtを書き込んでください。g00000000で実行し、Lチカが動作したらシストリックアレイで計算した結果と、pythonでの計算結果が一致しています。何かのLEDの色で止まっている場合は、それぞれの場所で止まっています。
終わりに
やはり昨今のAIブームを考えると、行列演算は高速に行えたくて、その構造としてシストリックアレイは魅力的でした。手元のArty A7もriscv-rv32iを作って以降、有効利用されていなかったので、調べてみるとそれなりにDSPブロック(DSP48E1)があることがわかり、これを使って簡易手金実験できないかというのが作成の始まりでした。仕様にも書いた通り、まだまだ手抜きなDMA(というか、シストリックアレイはDMAがキモな気がします)なので、これからですが、また気が向いたらもう少し複雑なDMAをサポートしてみようと思います。では。
# おまけ:図面の殴り書き(一部バージョン古い可能性あり)