How an FPGA beginner (or me) start evaluation of VexRiscv.
前々回の宿題で、nextpnr を動かした後は picorv32 の詳細を勉強しようと思ってましたが、ちょっと方針を変更して、VexRiscv をいじってみようと思います。その理由としては、
- VexRiscv ではキャッシュコントローラが提供されていること(RAM をあまり持たない FPGA、SPI フラッシュメモリを持っている TinyFPGA BX では、命令キャッシュの有り無しでは性能に大きな差が出そうなこと)
- 5段パイプラインで設計されており、パイプラインハザードに対する bypass 処理も可能なこと
- picorv32 よりも f_MAX が高そうなこと(これは評価途中で分かります)
- Zephyr OS が対応しているらしいこと
などがあります。
FPGA と RISC-V 環境の動向(2019年編)
最初に、FPGA 用の RISC-V 実装を調べていると、いろいろなキーワードが見つかりますので、私がしばらく勉強してきたことを、簡単にまとめておきます。
VexRiscv
VexRiscv とは、Charles Papon さんという方が実装した RISC-V で、SpinalHDL というハードウェア記述言語で書かれています。
SpinalHDL はプログラミング言語 Scala のライブラリとして実装されており、近年のプログラミング言語の進歩を踏まえ、(やや古めかしい仕様となってきた)Verilog や VHDL よりも、高度で記述性の高い HDL となることを目指しているものです。SpinalHDL は読み込んだコードを Verilog や VHDL に変換出力することで、従来の HDL シミュレータや合成ツール(Yosys など)をそのまま利用できるようになっています。
VexRiscv は、2018年の RISC-V SoftCPU Contest で最優秀賞に輝いた実績を持ち、またさまざなコンフィグレーションオプションを持つことで、コンパクトな実装から高性能な実装まで、幅広く展開できることが特徴です。詳しくはこちらやこちらを参考にしてください。
SpinalHDL
SpinalHDL ライブラリを使うことで VexRiscv を Verilog に変換できるので、Verilog さえ読めれば利用できないことはないのですが、プログラムコードのコンバータ Cfront(古い!)や Ratfor(もっと古い!)を御存知の方は想像が付くように、自動生成されるコードのデバッグは面倒になりがちです。結局のところ、VexRiscv を活用するためには、ある程度 SpinalHDL の理解が欠かせないようです。
結論: また覚えなくてはいけない言語が増えました。
LiteX
LiteX の説明は非常に難しいのですが(私もまだ十分に理解できていない)、Migen というツールを用い、複数の RISC-V 実装(PicoRV32, VexRiscv)や LM32(LatticeMico32 をベースとしたソフトコア)、Mor1kx(OpenRISC の実装)に対応し、また LiteX Cores Ecosystem と呼ばれる多くの IP(Ethernet, PCIe, HDMI)などを組み合わせることで、自分の必要な仕様の SoC を、自分の望むターゲットボード(Digilent Nexys 4 や Arty)用に、容易(?)にビルドすることのできる「FPGA design/SoC builder」です。(分かります? 私にはいまひとつ価値が分からんのですが。)
ちょっと取っつきにくい点がいくつかあります。一つ目は、現在まだ Lattice iCE40 に対応できていないことで、二つ目は、Migen というツールの使い方を覚えなくてはならない、ということです。
あともう一つ、まだまだドキュメントが十分整理されているように見えず、仮に環境をセットアップしても、現状では十分に使いこなせないだろう、という印象が拭えません。
Migen
Migen とは、M-Labs という企業(もともとオープンソースの開発プロジェクトだったらしい)が提供しているツールで、Python とそのライブラリを用いて高水準の HDL 記述(FHDL と呼ぶそうです)を Verilog に変換するためのものです。なんか SpinalHDL に似ている気がしますね!? SpinalHDL と異なり、私が理解できる Python ベースという点には惹かれますが、
結論その 2: さらにまた覚えなくてはいけない言語が…
と、いつまで経っても脇道にそれ続けてしまう懸念があるので、ちょっと Migen は忘れようと思います。
timvideos LiteX Build Environment
最後に、timvideos/litex-buildenv というものを紹介して、今回の脱線コーナーを終わりにしたいと思います。
前述の LiteX プロジェクトはドキュメントが整備されていない(と思われる)、と書きましたが、その LiteX のサイト上で Very Quick start guide (for newcomers) として紹介されているのが、この timvideos LiteX Build Environment(以下、litex-buildenv )です。
litex-buildenv は LiteX を超えるさらにパワーツールで、コマンドをいくつか実行するだけでビルドからロード、テストまで実行できるようですが、詳細をあまりに多く隠しすぎていて、もはや、裏で何が動いているのかさっぱり分かりません。既存の開発ボードのために既存のデモをビルドするだけならともかく、自分で設計したボードのために自分で設計した SoC を載せようと思ったら、LiteX のコンセプトから Migen から十分に理解していないと難しそうです。(個人的には、このようなパワーツールはあまり好きでありません。)
ただし、litex-buildenv にはいろいろと解説記事が提供されており、それを読むといろいろ参考になることがありそうです。
本道に戻る
という訳で、まずは VexRiscv に戻りたいと思います。VexRiscv のサイトを見ながら、まずはいくつかのデモ(サンプル)RISC-V CPU や SoC を実装し、その回路規模と、できたら f_MAX を調べてみたいと思います。それがうまくいったら、今後、ちょっとだけ SpinalHDL を勉強し、自分で回路をカスタマイズできそうなものか実験してみたいと思います。
今回使用したバージョンは、以下の通りです。
- VexRiscv: コミット b290b25
- SpinalHDL: タグ v1.3.6(上記 VexRiscv コミットから自動でダウンロードされます)
- Yosys: コミット a73f965
- nextpnr: コミット dd7f7a5
VexRiscv 環境のインストール
VexRiscv のインストール方法は、こちらのサイトの Dependencies というセクションに説明されています。具体的には、Java、Java コンパイラ、sbt(言語 Scala や Java のためのビルドツール)、verilator(Verilog シミュレータ)をインストールすることになります。けっこう物入りですが、それでも LiteX や litex-buildenv よりは軽そうなので、ガマンします。
Verilog に変換してみる
VexRiscv には、テストベンチシミュレーション(およびレグレッションテスト)のための、2つのサンプル(demo)プロジェクトが用意されており、もちろんいずれも Verilog に変換できます。ただし、この 2つはシミュレーション用であるため、たぶんそのままでは実際の FPGA に搭載しても、意味のある使い方はできません。他に、Murax という SoC サンプルがあるので、それも評価してみます。
今回試すプロジェクトは、以下の 3つです。
- GenFull.scala(大きめな規模の RISC-V CPU)
- GenSmallest.scala(小さな規模の RISC-V CPU)
- Murax.scala(RISC-V を載せた SoC サンプル)
なお、GenFull ではテストベンチの設定により、デバッガ GDB を繋いで ELF 形式のプログラムを流し、動かすこともできます。本格的ですね! (今回は説明しませんので、VexRiscv のサイトを読んで試してください。)
GenFull
まず、GenFull からです。CPU のコンフィグーションは以下の通りです。(詳細は Scala ソースを参照ください。)
- I キャッシュあり(4Kバイト, 1ウェイ)
- Dキャッシュあり(4Kバイト, 1ウェイ)
- MMU あり
- RV32C(compressed 命令)拡張なし
- RV32M(乗算, 除算命令)拡張あり
- バレルシフタあり
- CSR: small
- パイプラインハザードのバイパス処理あり
- デバッグ機能あり
sbt で変換処理をします。(前回に一度 sbt コマンドを実行してあれば、変換自体は 5秒ほどで完了します。)
sbt "runMain vexriscv/demo/GenFull"
生成された Verilog ファイルは 7200行程度でした。Yosys で iCE40 用に合成してみましょう。
yosys -p 'synth_ice40 -top VexRiscv -blif VexRiscv.blif' VexRiscv.v === VexRiscv === Number of wires: 5841 Number of wire bits: 13317 Number of public wires: 5841 Number of public wire bits: 13317 Number of memories: 0 Number of memory bits: 0 Number of processes: 0 Number of cells: 8348 SB_CARRY 446 SB_DFF 71 SB_DFFE 1402 SB_DFFER 134 SB_DFFES 2 SB_DFFESR 50 SB_DFFESS 3 SB_DFFR 40 SB_DFFS 4 SB_DFFSR 1 SB_LUT4 6170 SB_RAM40_4K 25
さすがにちょっと大きいですね。これでは 8k デバイスには載らないかもです。
GenSmallest
続いて GenSmallest です。CPU のコンフィグーションは以下の通りです。
- I キャッシュなし
- Dキャッシュなし
- MMU なし
- RV32C(compressed 命令)拡張なし
- RV32M(乗算, 除算命令)拡張なし
- バレルシフタなし
- CSR: smallest
- パイプラインハザードのバイパス処理なし
- デバッグ機能なし
生成された Verilog ファイルは 3100行程度でした。Yosys で iCE40 用に合成してみましょう。
yosys -p 'synth_ice40 -top VexRiscv -blif VexRiscv.blif' VexRiscv.v === VexRiscv === Number of wires: 787 Number of wire bits: 3874 Number of public wires: 787 Number of public wire bits: 3874 Number of memories: 0 Number of memory bits: 0 Number of processes: 0 Number of cells: 1530 SB_CARRY 100 SB_DFF 50 SB_DFFE 385 SB_DFFER 44 SB_DFFES 1 SB_DFFESR 29 SB_DFFR 54 SB_DFFS 3 SB_LUT4 860 SB_RAM40_4K 4
かなり小さくなりました。これならば、小さな iCE40 にも載りそうです。
Murax
最後に、Murax という SoC サンプルを変換し、合成してみましょう。具体的には、Murax_iCE40_hx8k_breakout_board_xip というモジュールです。
- I キャッシュなし
- Dキャッシュなし
- MMU なし
- RV32C(compressed 命令)拡張なし
- RV32M(乗算, 除算命令)拡張なし
- バレルシフタなし
- CSR: smallest
- パイプラインハザードのバイパス処理なし
- デバッグ機能あり(?)
- RAM サイズ 8Kバイト
sbt "runMain vexriscv.demo.Murax_iCE40_hx8k_breakout_board_xip"
生成された Verilog ファイルは 8400行弱でした。これを Yosys で合成します。
yosys -p 'synth_ice40 -top Murax_iCE40_hx8k_breakout_board_xip -json Murax.json' \ Murax_iCE40_hx8k_breakout_board_xip.v === Murax_iCE40_hx8k_breakout_board_xip === Number of wires: 3092 Number of wire bits: 14869 Number of public wires: 3092 Number of public wire bits: 14869 Number of memories: 0 Number of memory bits: 0 Number of processes: 0 Number of cells: 4099 SB_CARRY 250 SB_DFF 74 SB_DFFE 1112 SB_DFFER 115 SB_DFFES 11 SB_DFFESR 89 SB_DFFESS 15 SB_DFFN 1 SB_DFFR 153 SB_DFFS 9 SB_DFFSR 32 SB_DFFSS 2 SB_GB 2 SB_IO 3 SB_LUT4 2205 SB_RAM40_4K 26
適当な .pcf ファイルを作って nextpnr を動かしてみましょう。
Info: Device utilisation: Info: ICESTORM_LC: 3334/ 7680 43% Info: ICESTORM_RAM: 26/ 32 81% Info: SB_IO: 19/ 256 7% Info: SB_GB: 8/ 8 100% Info: ICESTORM_PLL: 0/ 2 0% Info: SB_WARMBOOT: 0/ 1 0%
これなら iCE40 に十分に載りますね。最後にタイミングアナリシスです。
icetime -m -dlp8k Murax.asc // Reading input .asc file.. // Reading 8k chipdb file.. // Creating timing netlist.. // Timing estimate: 28.99 ns (34.49 MHz)
f_MAX が 34MHz を超えました。これは十分に利用できそうです。(実はこれが、私が VexRiscv に引っ張られた理由の一つです。)
最後にお約束のフロアビューです。
今後の宿題
宿題を忘れないようにまとめておきます。
- Murax_iCE40_hx8k_breakout_board_xip を TinyFPGA BX で動かしてみる(動かしてみたい!)
- SpinalHDL を少し勉強する
- Murax_iCE40_hx8k_breakout_board_xip に、SPI メモリを繋げないか検討する(私には難しいかも!)
- Verilator の使い方勉強(csail-csg/pyverilator も)
今日はここまで。