概要
前々からやろうやろうと思って全然手が出せてなかった RISC-V ですが、本格的にやると 深みにはまりそうだったので、最小セット(rv32i)をコンパクトに SystemVerilog + Rust で 動くコアが書けることを目標にちょっとやってみました。
感想としては、「え、こんな簡単にできるの?」と言うぐらい、何も考えなくても 「命令セットの方が代りに考えてくれてる」という「良くできてるなぁー」と いう印象でした。
ISAから伝わってくる設計意図をどこまで汲み取れてるかはわかりませんが、 それがそのまま実装に落ちていくのでいろいろな意味で不思議な体験でした。
作ってみたもの
碌にテストもせずにLチカできた段階で放置してますが、一通りのものは github に置いております。
サンプルプロジェクト
「へー、これだけのソース量でRustが使えるCPUが出来ちゃうんだ」と言う程度のご参考までに。
少しだけ中身の説明(4段パイプライン版)
この手のものでシンプルによくあるのは
IF → ID → EX → MA → WB
という5段パイプラインかと思いますが、今回さらにシンプルにして 4段パイプラインで
IF → ID → EX → WB
として、メモリアクセス(MA)ステージをEXに押し込んだものを作ってみました。
なんで、そんなことしたかと言うと、EXとMAが別ステージだと、MAでロードする値を後続の命令のEXで使いたいときにRAWハザードでストールする必要があるからです。
今回きわめて単純かつ乱暴に作っているので、ストールは分岐の時しかしないという、いろいろと予測のしやすい作りです(それでもなんだかんだで200MHzぐらいで合成できてるので最近のFPGAすごいなと思います)。
また、今回は割り込み機能を実装していません。ZynqMPの場合、複雑なことをやるならRPU(Cortex-R5)とか使えばいいので、今のところシンプルにプログラマブルシーケンサが欲しい場合に特化しております。
もっとも割り込み機構を付けること自体はそれほど難しい話ではないので、「必要性を感じなかったのでやらなかった」というのが正しいかもしれません。
割り込みなどのコンテキストスイッチがあるとリアルタイム保証が難しくなるのもありますし、そもそも「インストラクションメモリやスタックメモリ増やして複数コンテキストやるぐらいなら、小さなコアいっぱい並べればいいじゃん」という割り切りで考えています(いまのところは)。
メモリサイズとか深く考えずに設定して合成してますが、下記ぐらいの規模になっており(注:2022-03-06時点のサイズです)、Ultra96V2 の規模からするとちょっとしたシーケンサとしては許容できなくもないレベルじゃないかと思います。
今後の話
私の考えるこれからのエッジFPGA像として
- IoT やるのに、ZynqMP の PSのようなLinuxの動くコアは必須
- エッジならではでPSでは成立しないアルゴリズムを混ぜないとFPGA使う面白みがない
- アルゴリズムをSoCにマップするのにCPUが得意な部位をPLにマッピングする意味はあまりない
というのがあります。
では、そういった条件下で「PL で RISC-V を作って嬉しいときはどんな時か?」、という話になってくるわけですが
あたりかなと、個人的に思っています。
今回前者をやってみたわけですが、いずれ気が向いたら後者も手を出すかもしれません。 所謂DSA(domain specific architecture)での活用と言ったところでしょうか?
その場合、特に、キャッシュやMMUが必要になってくるわけではなく
- PLが得意な専用演算エンジンと同じクロックで動く
- CPUの所為で専用演算エンジンがストールしたりしない
といったプリミティブなところが問われてくるので、高クロック仕様で、ストール無しで専用エンジンに制御やデータを供給できるアーキテクチャになってなるんじゃないかと思います。
ある程度深いパイプラインで高クロック化しつつ、分岐予測など入れてメインの演算ループだけはストールなく回るみたいなことに注力したコアが作れるといいのかなと思っております。
何にせよ、非常に小型なところから高性能なところまで手広くカバーした命令セットが自由に使える、というのは「コンパイラがそこにある」というだけでものすごくホビーの範囲を広げてくれているように思います。
周回遅れで触ってる感はありますが、なかなか楽しいISAに触れることができてうれしく思います。
おまけの話(Rustからみたソフトコア)
ちょっと違った見方として
- C + HLSで書いても良いけど、Rust + ソフトコアで置き換えられるケースも稀にあるかもね
- MicroBlaze って Rust 対応してなくない?
- Rust で書ける PicoBlaze みたいなのあると面白いかもね
といった、Rust 使いたいからと言う観点でみても面白いかもしれません。
6段パイプライン版を追記(2022/05/07)
よくある 5段パイプラインの構成にさらに1段追加したバージョンを追記しました。
load 命令後に符号拡張するためのステージを加えて 6段にしています。 FPGAのBlockRAM は同期メモリなのでアクセスに1サイクルかかり、そのあとに符号拡張が必要なのですが、 これがフォワーディング用のパスに入り込んでEXステージのクリティカルパスを増やすのを嫌ったためです。
今のところ KV260 で試した範囲で
4段パイプライン版 : 200~250MHz あたりに限界 6段パイプライン版 : 250~300MHz あたりに限界
といった感じです。6段で、いろいろオプションで頑張ると 300MHz 行ける時があるようですが、250MHz ぐらいが実用限界な気はしています。