【RISC-V CPU を作ろう | Part 0】概要と開発環境の構築
こんにちは。るくみるです。
色々やってたら週報を書く時間がなくなってしまいました。
そこで、今週からは週報の代わりに、
「RISC-V CPU を作ろう」
というシリーズを始めたいと思います。私自身も CPU を自作するのは初めてです。初心者目線で勉強の様子をお届けできればと思っていますので、生暖かい目で見守っていただければと思います。
RISC-V とは
RISC-V(リスクファイブ) とは、x86-64 や ARM のような命令セットアーキテクチャ(ISA)の1つで、ほんの16年前に登場したばかりの新しい ISA です。
RISC-V は、従来のアーキテクチャにはなかった以下のような革新的な特徴を備えています。
- オープンな標準規格として公開されており、誰でも自由に利用・改変・配布が可能であること
- 設計がモジュール化されており、設計者が必要な機能だけを選択して実装できること
- 極めてシンプルで理解しやすい命令セットを持ち、学習コストが低いこと
- 組み込みシステムから高性能コンピュータまで、幅広い用途に対応できること
Intel や AMD のマイクロプロセッサで用いられる x86-64 は、数十年の後方互換性の維持という足枷のために、命令セットが肥大化し非常に複雑になっています。また、スマートフォンや Apple Silicon で採用されている ARM はライセンス料が高額であるため、教育目的や研究開発の妨げとなることも少なくありません。一方、RISC-V はその自由さとシンプルさから、教育や研究向けの ISA の標準として急速に普及しています。
実際に、世界の出荷済みプロセッサのうち約20%が RISC-V ベースである(ほとんどはマイコンや組み込み分野向け)と言われており、その圧倒的な拡張性と設計の自由さから、今後もその割合は増加していくと予想されています。
これらの思想や背景は、書籍 『RISC-V 原典』 にて詳細に解説されているので、興味のある方はぜひ手に取ってみてください。120ページくらいなのですぐに読めると思います。
ということで、このシリーズでは、RISC-V の CPU のゼロからの自作に挑戦していこうと思います。ゴールはまだ定かではありませんが、 「ベアメタル(OS なし)環境で、C 言語で書かれた簡単なプログラムを実行すること」 を最初の目標にしていきましょう。
環境構築
VS Code 上に以下のような環境を作成しました。
拡張機能『Dev Containers』を用いてコンテナ化しているので、必要な方は GitHub で公開している こちらのリポジトリ をクローンしていただくと、あとは VS Code で開くだけで同じ環境が再現できるようになっています。
git clone https://github.com/Ruk3k/RISC-V-CPU.git
CPU 本体の設計
C++ でエミュレータを記述してもよかったのですが、ハードウェア屋を志す身としてはやはり HDL で書きたい。ということで、今回は CPU 本体を Verilog(SystemVerilog) で記述することにしました。
シミュレーション環境
- シミュレータ:オープンソースの Verilator を採用しました。Verilog を C++ へ変換することで、Linux 環境下での高速なシミュレーションを可能にしています。
- ビルドシステム:CMake を導入し、Verilator が生成した C++ モデルと、クロック生成・レジスタダンプを行う自作の C++ テストベンチのコンパイル工程を自動化しています。
- 波形解析:シミュレーション結果は FST 形式で出力されるようにし、VS Code 拡張機能の 『Surfer』 を用いてエディタ上で直接タイミングチャートを観測できるようにしています。
実装方針
命令セットの種類
RISC-V の命令セットはモジュール化されており、必要な命令セットのみを選択して実装することができます。RISC-V の命令セットには以下のような種類があります。
- RV32I:32ビット整数の基本命令セット。
- RV32M:乗算・除算命令の拡張セット。
- RV32A:アトミック命令の拡張セット。
- RV32F:単精度浮動小数点命令の拡張セット。
- RV32D:倍精度浮動小数点命令の拡張セット。
これらすべてを実装した RV32IMAFD は RV32G(General) と呼ばれ1、RISC-V の標準的なセットに相当します。さらに、圧縮命令を追加する RV32C や、ベクトル演算をサポートする RV32V などの拡張、64ビット命令をサポートする RV64I といった別の基本命令セットも存在します。
さらに、仕様で定義されている命令セット以外にも、独自に命令を追加したり、特定の用途に特化したカスタム命令セットを設計することが可能です。そのため、特定分野に最適化されたプロセッサを設計することができるとして、産業界からも注目されています。
まずは、簡単のために、32ビットの基本整数命令セットである RV32I の実装を目標にしていきましょう。
RV32I の命令形式
(図は 公式ドキュメント から引用しました)
RV32I では、32ビットの汎用レジスタ32本(x0〜x31) と、プログラムカウンタ(PC)1本 が用意されています。このうち、x0 はつねに 0 を保持し、書き込みが無視されるという特別な仕様を持っています。
また、RV32I の命令は以下のような 6種類の命令形式 に分類されます。
表1:RV32I の命令形式一覧
| 形式 | 説明 | 指定するもの |
|---|---|---|
| R 形式 | レジスタ間の演算を行う命令形式 | 3つのレジスタ |
| I 形式 | 即値を用いた演算やロード命令に用いられる命令形式 | 2つのレジスタと即値 |
| S 形式 | ストア命令に用いられる命令形式 | 2つのレジスタと即値 |
| B 形式 | 分岐命令に用いられる命令形式 | 2つのレジスタと即値 |
| U 形式 | 即値を用いた命令に用いられる命令形式 | 1つのレジスタと即値 |
| J 形式 | ジャンプ命令に用いられる命令形式 | 1つのレジスタと即値 |
命令長はすべて32ビットで統一されています。命令の種類によって命令の各フィールドの割り当ては異なりますが、レジスタの指定などの共通する部分は同じ位置に配置されるよう工夫されています。
以下に、RV32I の命令を一覧で示します。
実装の優先順位
本シリーズでは、RV32I 命令セットの実装を以下のような順番で進めたいと思っています。
- 算術命令の実装(R 形式・I 形式) ADD、ADDI、SUB
- 論理命令の実装(R 形式・I 形式) AND、OR、XOR など
- 比較命令の実装(R 形式・I 形式) SLT、SLTI など
- 即値ロード命令の実装(U 形式) LUI、AUIPC
- シフト命令の実装(R 形式・I 形式) SLL、SRL、SRA など
- ジャンプ命令の実装(J 形式・I 形式) JAL、JALR
- 分岐命令の実装(B 形式) BEQ、BNE、BLT など
- ロード・ストア命令の実装(I 形式・S 形式) LW、SW など
- その他の命令の実装 FENCE、ECALL など
各ステップごとに対応する命令を実装し、テストベンチを作成して動作確認を行います。
全7回くらいで RV32I の命令すべての実装を目指します。
おわりに
初めてのシリーズ企画です。更新ペースは遅いかもしれませんが、ゆったりとやっていきたいと思いますので、気長にお付き合いいただければと思います。
週報も気が向いたら書きたいですが、しばらくはパソコンカタカタ生活が続きそうかも。参考書籍と公式リファレンスガイドを相手に、にらめっこしながら絶賛勉強中です。
それでは、すていちゅーんどってことで。また次の記事でお会いしましょう。
参考文献
RISC-V: ISA Specifications(公式ドキュメント)
公式のリファレンスガイドです。こちら からアクセスできます。
RISC-V 原典
RISC-V の設計思想や背景、命令セットの詳細について解説された書籍で、日本語訳が存在します。初心者にも分かりやすく書かれており、RISC-V を学ぶ上での入門書として最適かと思います。本文自体は120ページ程度で、付録としてすべての命令の解説が後ろに記載されています。
Computer Organization and Design RISC-V Edition
コンピュータアーキテクチャの必読書、通称『パタヘネ』の RISC-V 版です。RISC-V の命令セットのみならず、コンピュータ設計の基礎を学ぶのにも最適な一冊です。日本語訳版がないところがネックですが……。
Footnotes
-
厳密には RV32IMAFD に加えて Zicsr と Zifencei を含めたセットが RV32G と定義されています。 ↩