RISC-Vのエミュレータを作っている

この記事はImaizumi Lab Advent Calendarの1日目です。

更新履歴

2020/12/2 誤字を修正

はじめに

ゼミでModern Computer Architecture and Organizationという本をやっていて、RISC-Vについて解説した章の担当になったので余興としてRISC-Vのエミュレータを作りました。

この記事は特に目的はないのですが、強いていうならRISC-Vのエミュレータを作りたい人がどうすれば最低限の実装ができるのかを示したいと思います。

記事執筆時点での最新のコミットはこちらです。 github.com

記事執筆時点では、基本命令セットのRV32Iをほぼ実装しています。ほぼと書いているのは、マルチスレッド処理で必要な命令を実装していないからです。(現状はシングルスレッドの実装のため)

注意

実装の正しさは保証しません。現時点でバグがあることを確認しています。

また、筆者はエミュレータやCPUには明るくないので、誤りを含んでいる可能性があります。

最低限の実装の概略

まずは実行する機械語をメモリ(として使う配列)に格納します。そしてプログラムカウンタを用意し、0にセットします。

CPUはフェッチ→デコード→実行のサイクルを繰り返します。

  • フェッチ...バイナリを4byteずつ取り出す。リトルエンディアンに注意
  • (この間でプログラムカウンタがインクリメントされる)
  • デコード...どの命令に当たるのか、指定されたレジスタは何番かなどを解釈
  • 実行...対応する命令を実行

これをひたすら繰り返す*1処理を書けばokです。

デコードは、opcode(全てのフォーマットの先頭から7bit)でフォーマットが決まるのでそれを元にrd, rs1, immなどの数値を解釈します。

rd, rs1, immなどの用語についてはRISC-Vの仕様書をみてください。

実行はopcodeとfunct3(一部例外あり)を元に実行する命令を決定します。

実行してみる

RISC-Vの公式が出しているテストをとりあえず使っています。

github.com

objcopyで機械語部分だけをコピーしたものを作成し、先頭からプログラム中のメモリ(として使う配列)にコピーしていきます。

objcopyはこんなコマンドです。

riscv64-unknown-elf-objcopy -O binary rv32ui-v-xori rv32ui-v-xori.bin

このコマンドを使用するにはRISC-V Toolchainのインストールが必要です。

Macを使っている場合はhomebrew-riscvで入れるのが一番楽で失敗もないかと思います。

github.com

テストを完璧に動作させるにはおそらくシステムコールの実装が必要と思われるのですが、そこまでできていないので現状はデバッグでプログラムカウンタとレジスタの中身と命令を表示し、正しく解釈されているかをdumpファイル(riscv-testsの中に入ってます)と見比べてデバッグしています。

本当は現状の実装で動くテストを書きたい。

余力があれば直接ELFファイルを解釈してもいいと思います。私の実装では現状実装していますが、うまく動かないのでobjcopyしたものを動かしています。

参考資料

RISC-V 公式仕様書

riscv.org 基本的にはこれを読みながら実装していくことになります。ただ、これだけだとわかりづらい情報があるので、次に紹介する資料も合わせてみると実装しやすいかと思います。

riscv/riscv-opcodes

github.com

RISC-Vの公式が出している、オペコードの一覧表です。 opcodeやfunct3が分かりやすいので、実際に命令を解釈するときにわかりやすいかと思います。

まとめ

意外とエミュレータを作るのは簡単なので、皆さんも是非やってみてください。

*1:無限ループなのでどこかで終了させなくてはならない。私は一旦フェッチしたバイナリが0なら終了させてます