ESM アジャイル事業部 開発者ブログ

永和システムマネジメント アジャイル事業部の開発者ブログです。

はじめてのRISC-Vベアメタルプログラミング(ヨロシク編)

こんにちは、永和システムマネジメントの内角低め担当、はたけやま( @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 # 4649 <_start-0x7fffb9b7>
 
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でベアメタルプログラミングを行う方法を紹介しました。次回はこの上で何か面白いアプリケーションを動かしてみようと思います。