こんにちは、永和システムマネジメントの内角低め担当、はたけやま( @htkymtks )です。
みなさん、RISC-Vをご存知ですか?RISC-VはCPUの命令セットアーキテクチャ(ISA)のひとつで、使用料のかからないオープンソースライセンスで提供されていることや、命令セットの美しさから注目を集めています。私も以前にRubyでRISC-Vシミュレータを作ったりしてました。
今回はRISC-Vを用いて、OSもライブラリも使用しないベアメタル環境で動作するプログラムを作成してみようと思います。
インストール
まずはRISC-Vのクロスコンパイラとエミュレータをインストールします。クロスコンパイラのビルドには約1時間ほどかかるので、時間の余裕がある時に行ってください。
$ brew tap riscv-software-src/riscv
$ brew install riscv-tools qemu
以下のコマンドを実行し、それぞれのバージョンが正しく表示されれば、インストールは成功です。
$ riscv64-unknown-elf-gcc --version
$ qemu-system-riscv64 --version
クロスコンパイラとは、あるアーキテクチャ(ターゲットシステム)向けの実行可能コードを、異なるアーキテクチャ(ホストシステム)上でコンパイルするためのツールです。今回はターゲットシステムがRISC-V、ホストシステムがARM64(Apple M1)に設定されたGCCを利用します。
エミュレータにはQEMUを使用します。QEMUは様々なCPUアーキテクチャをサポートするエミュレータで、32ビット及び64ビットのRISC-Vが対応しています。
サンプルプログラム
レジスタs0に値(0x4649 = ヨロシク)をセットして無限ループするサンプルプログラムを用意します。
.text
.globl _start
.type _start, @function
_start:
# レジスタ s0 へ即値 0x4649 をロード
li s0, 0x4649
loop:
# 無限ループ
j loop
リンカスクリプト
QEMU(正確にはVirtio)では、アドレス0x80000000番地からプログラムの実行が開始されます。そのため、以下のようなリンカスクリプトを用意し、プログラムの開始位置のアドレスが0x80000000番地になるようにします。
MEMORY {
RAM (RWX) : ORIGIN = 0x80000000, LENGTH = 0x40000000
}
SECTIONS {
.text : {
*(.text)
_end = .; /* 後日 malloc で使う予定 */
}
}
ビルド
それではサンプルプログラムをビルドしましょう。アセンブラを使用してアセンブリコード sample1.S からオブジェクトファイル sample1.o を生成し、オブジェクトファイルから実行ファイル sample1.elf を生成します。
$ riscv64-unknown-elf-as sample1.S -o sample1.o
$ riscv64-unknown-elf-ld -Tmy_baremetal.ld --no-relax sample1.o -o sample1.elf
生成された sample1.elf を逆アセンブルしてみましょう。プログラムが0x80000000番地からスタートすることが確認できます。
$ riscv64-unknown-elf-objdump -S sample1.elf
sample1.elf: file format elf64-littleriscv
Disassembly of section .text:
0000000080000000 <_start>:
80000000: 00004437 lui s0,0x4
80000004: 6494041b addiw s0,s0,1609
0000000080000008 <loop>:
80000008: 0000006f j 80000008 <loop>
実行
先ほど作成したELFファイルを指定してQEMUを実行します。
qemu-system-riscv64 -M virt -monitor stdio -bios sample1.elf
引数の意味は以下のとおり。
-bios sample1.elf
-M virt
- ターゲットとなるボードを指定します。今回は「RISC-V VirtIO board = virt」を使用します
-monitor stdio
- QEMUモニタをターミナルに表示するために指定します
実行結果を確認
QEMUを起動してプログラムを実行すると、QEMUモニタも一緒に立ち上がります。「info registers」コマンドでレジスタの中身を確認すると、レジスタ「x8/s0」に「4649」がセットされていることがが確認できます。また、プログラムカウンタ(PC)の値が「80000008」であることから、無限ループしていることが分かります。
動作確認を終えたら、QEMUモニタで「quit」と入力してQEMUを終了させます。
終わりに
以上、QEMU上のRISC-Vでベアメタルプログラミングを行う方法を紹介しました。次回はこの上で何か面白いアプリケーションを動かしてみようと思います。