Intel FPGA Cyclone 10LPで自作RISC-Vを動作させた話
この記事を執筆した段階ではアセンブラのみ動作が確認できていました。その後C言語での動作確認も終えています。詳しくはこちらをご覧ください。
自作RISC-V向けにCライブラリの整備やPlatformDesigner連携をする - lynxeyedの電音鍵盤
この記事はQuartus Primeサイドから見たブログ記事にしたいと思います。
CPUを設計する、というお題は私自身16bit/32bitともにあるので*1、ちょうど良いレベルの難易度だったかなと思います。
奇しくも今年はRISC-Vは10周年だそうで。
riscv.org
ターゲットはintel FPGA Cyclone 10LPです。
github.com
git clone http://github.com/panda5mt/KyogenRV -b 0.2.6 --depth 1
プロジェクトルート/fpga/
がQuartus Primeプロジェクトになります。Quartus Prime Lite 20.1.1で動作確認。
前回のブログで紹介したあと、いろいろ反響がありました。(ブログアクセス数も1日で450人とか)
使ってくれた方もいました。
とりあえずCYC1000でKyogenRVうごいた。ピン定義設定して書き込んでも動かないのでなんでだ?と思ったら、メモリ初期化パスが絶対パスで書かれるからプログラムが書き込まれてなかったらしい。 pic.twitter.com/peHXwpJbHA
— Kenta IDA (@ciniml) 2020年12月13日
Avalon-MMつかいの魔術師がいじるとこうなる
ちなみに全部作り直すの面倒だったので、手元にあったNiosIIのプロジェクトにRV32を追加してヘテロマルチコアにしてあります https://t.co/OM8Oba8xaS
— 長船 俊@NicoTECH自造社団 (@s_osafune) 2020年12月13日
CPUは魔改造されてからが本番。
CPU諸元
- ISA
- User-Level ISA Version 2.2
- Privileged ISA Version 1.11 (マシンモード)
- 割り込み
- 外部割り込みのみ
- 実行形式
- キャッシュなし、5段パイプライン(フェッチ/デコード/実行/メモリアクセス/レジスタライトバック)、インオーダ実行方式
- 最高周波数
- 70MHz ~ 100MHz(intelFPGAのグレードによる)
- 開発に使った言語
- Chisel v.3.4
- 占有ロジックエレメント数
- 6000LE ~ 12000LEほど(命令メモリ容量や接続モジュールにより変動します)
RV32IのISAに準拠、マシンモードのみ(=スーパーバイザ非対応)ですが、仕様通りの例外(ecallなど)にも対応しています。
割り込みは外部割り込みのみ暫定対応。(タイマー割り込みはメモリマップドモジュールタイマーを設け、トリガーを外部割り込みへ接続し使うようにしてください)
Platform Designerで接続
昨今ではよほど小さなプロジェクトでない限りintel FPGAで動作する=Avalonバス接続可能を意味するレベルでメジャーになってきたと思います。
以下のいずれかの方法でプログラムメモリを接続します。
- 速度パフォーマンスが少し良い方法
命令バス、データバスは独立なので、命令読み込みウェイトがかかる時間が圧倒的に少ない。
今回作ったRV32Iはキャッシュを持たない設計にしたのでこの接続だと処理速度は向上する。デュアルポートRAMなので使用ロジックが若干増える。
- 消費論理回路が少し減る方法
命令バスが、データバスとシェアする方法。命令読み込みウェイトがたびたびかかる。
データバスがメモリ空間に頻繁にアクセスするプログラムだと、上記の接続方法より処理速度が1/2以下になることもある。でも使用ロジックは上記より少ない。
今回の設計では優先度をデータメモリ > 命令メモリとする。メモリアクセス頻度はその優先度の逆なのでBack Pressureで処理してくれる。
ま、でもそんなに変わんないと思う。組み込みCPUの用途でかんがえると永延とデータメモリにアクセスするのみ or 永延とCPU内部で演算しかしないといった極端な動作はあまり考えられないので。RTOS載せるのであればその段階でパフォーマンスの考慮してもいいかもしれない。
- Avalon-MMになんでこだわるのか
一言で言うならば、ペリフェラルまで作りたくないから。Platform Designerで思いつく限りのマイコンとしてのペリフェラルが用意されている。
そのためこちら側はペリフェラルの詳細ロジックを調べることなく、バス上にペリフェラルを繋げていけば良い。楽。
新たにペリフェラルを作る必要が生じても、規格に則って作れば良い。
CPU(マスタ)とペリフェラル(スレーブ)両方の検証とテストとか、恐ろしくてできない。
確かにRISC-V自作界隈、メモリやUARTやGPIOまで自作しているのが散見されます。
あるいはAXIの独自サブセットをつくってやはりペリフェラルまで自分で用意している。それも非難できない側面もあります。
AXIがあまりFPGAフレンドリな仕様をしていないうえ、Xのツールであるビなんとかが自由度高すぎるために設定が大変。(大変だった。キラキラGUIに騙される。もうやりたくない)
Avalon-MMはFPGAのバスとして(同期回路を接続する前提のバスとして)開発されていて、マスタが複数ある場合のアンチグリッドも自動でしてくれるので楽。AXIで疲弊するなら乗り換えも検討するのもいいと思います。Intelに。Cyclone10LP安いし。インテル入ってるって言えるし (古い
サンプルプログラム
- Lチカ
github.com
Platform Designerで0x8000番地に8bit幅のPIOを設置した場合の前提になります。
約0.5秒間隔(CPUが70MHz動作時)で0x55と0xAAを書き込むものです。LEDが接続されているとBlinkが確認できるはず。
ビルドには以下のコマンドをプロジェクトルートフォルダで実行します。(riscv-toolchainとpython3.7以上が必要です)
./build_asm.py ./mk_intel_hex.py
ビルドするとプロジェクトルートディレクトリ/fpga/chisel_generated
にインテルHex形式でバイナリが生成されるので、Platform DesignerでOn-Chip Memoryの初期値に設定しビルドすると動作します。
動作状況:
youtu.be
github.com
UARTでHello worldするサンプルです。9600baud,8N1で送信します。UARTはPlatform Designerで0x8020番地に設置します。
ビルドには以下のコマンドをプロジェクトルートフォルダで実行します。(riscv-toolchainとpython3.7以上が必要です)
./build_asm.py ./mk_intel_hex.py
ビルドするとプロジェクトルートディレクトリ/fpga/chisel_generated
にインテルHex形式でバイナリが生成されるので、Platform DesignerでOn-Chip Memoryの初期値に設定しビルドすると動作します。
動作風景:Raspberry Pi ZeroをOpenOCDドングルかつUSB-UARTの代用にしています。OpenOCD経由でQuartus Primeで生成させたsvfを書き込み、UARTにcuでコネクトしています。
youtu.be
- 自分で作成したアセンブルファイル
プロジェクトルートディレクトリ/src/sw/
に作成した*.sファイルを置きます。命令メモリ開始アドレスは0x0000としてください。
ビルドには以下のコマンドをプロジェクトルートフォルダで実行します。(riscv-toolchainとpython3.7以上が必要です)
./build_asm.py ./mk_intel_hex.py
ビルドするとプロジェクトルートディレクトリ/fpga/chisel_generated
にインテルHex形式でバイナリが生成されるので、Platform DesignerでOn-Chip Memoryの初期値に設定しビルドすると動作するはずです。