RISC-V ソフトコア VexRiscv の始め方(その1)

(更新

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 も)

今日はここまで。

お気軽に御相談ください

お問い合わせを頂いた後、継続して営業活動をしたり、ニュースレター等をお送りしたりすることはございません。
御返答は 24時間以内(営業時間中)とさせて頂いております。必ず返信致しますが、時々アドレス誤りと思われる返信エラーがございます。返答が届かない場合、大変お手数ではございますが別のメールアドレスで督促頂けますと幸いです。