RISC-V 用のクロスコンパイラを使ってみる

公開日:
最終更新日:
RISC-V 用のクロスコンパイラ riscv-gnu-toolchain を使ってみる

RISC-V 用のプロセッサのテストをするためにクロスコンパイラを導入してみました。
意外と詰まりどころがあったりしたので、インストールやコンパイルの方法をまとめておきたいと思います。

以下の二つの記事を参考にして書いています。

1. 使用したもの

riscv-gnu-toolchain が今回のメインのクロスコンパイラです。
一応コミットのバージョンも書いておきましたが、最新のもので基本的には問題ないと思います。

2. インストール

以下のコマンドに倣って実行すればインストールできるはずです。
--prefixについては自分がこのクロスコンパイラをインストールしたいパスを入れてください。
また、--with-arch では自分が入れたい ISA のみを基本指定すればいいのですが、筆者の環境では a (Atomic 拡張) がないとエラーが出て怒られました。
一方、c (Compressed 命令) を入れると、クロスコンパイル後に生成されるバイナリが 32 ビットではなく、16 ビットに圧縮されてしまうので、自作プロセッサのテストをしたいという今回の用途には向きません。
浮動小数点命令をサポートしたい場合は f (Float) や d (Double) を追加で入れるといいと思います。

拡張については詳しくは「オープンソース CPU「RISC-V」の研究」などに載っています。

$ git clone https://github.com/riscv/riscv-gnu-toolchain
$ ./configure --prefix=/opt/riscv32 --with-arch=rv32ima --with-abi=ilp32d
$ make linux

かなり時間がかかると思いますが、これがエラーなく終了するとクロスコンパイラが使えるようになっているはずです。
使いやすいように PATH を通しておきましょう。
/opt/riscv32 の部分は先程 --prefix で指定したパスに適宜変更してください。

export PATH=/opt/riscv32/bin:$PATH

これを .bashrc.zshrc などに追記して、以下のコマンドを実行してください。(.bashrc に追記した場合だけ書いておきますが、他のシェルの場合も同じです。)

$ source ~/.bashrc

一度やってうまくいかなかった場合は、全てディレクトリごと削除して git clone からやり直すのがいいと思います。

3. ソースコード作成

今回は fib(10) を求めるプログラムをコンパイルしてみましょう。
以下の GitHub にあげている部分を参考にしながら進めていきます。

まず、再帰関数を用いて fib.c を実装しましょう。

int fib(int n) {
    if(n <= 1) return 1;
    return fib(n-1) + fib(n-2);
}

int main() {
    fib(10);
    for(;;) {}
    return 0;
}

return の前に for (;;) {} を入れて無限ループにすることで、main 関数から抜けないようにしています。

次に、不要な初期化ルーチンが走らないように、以下の start.S を実装します。

.section .text.init;
.globl _start
_start:
    call main

さらに、命令が 0 番地から実行されるように、以下の link.ld を作成します。

OUTPUT_ARCH( "riscv" )
ENTRY(_start)

SECTIONS
{
    . = 0x00000000;
    .text.init : { *(.text.init) }
    .tohost : { *(.tohost) }
    .text : { *(.text) }
    .data : { *(.data) }
    .bss : { *(.bss) }
    _end = .;
}

4. コンパイル

これまで 3 つのファイル (fib.cstart.Slink.ld) を作成したと思いますが、これを元に次はコンパイルを行っていきます。

まず、前二つのファイルを実行ファイルにコンパイルします。
riscv32-unknown-linux-gnu-gcc の部分は OS によっては (?)、riscv32-unknown-elf-gcc のこともあるようです。
-march の部分はインストールの際に指定した --with-arch と同じにすればいいはずです。

$ riscv32-unknown-linux-gnu-gcc -march=rv32ima -c -o start.o start.S
$ riscv32-unknown-linux-gnu-gcc -march=rv32ima -c -o fib.o fib.c

次に、先程生成した start.ofib.olink.ld を使ってリンクしていきます。

$ riscv32-unknown-linux-gnu-ld fib.o start.o -Tlink.ld -nostartfiles -static -o fib.elf

さらに、コンパイルを進めてバイナリに変換します。

$ riscv32-unknown-linux-gnu-objcopy -O binary fib.elf fib.bin

ここまでで、基本的には終わりなのですが、Verilog で使いやすいようにフォーマットを変形して出力しておきましょう。

fib.bin はバイナリファイルなのですが、これを二進数の Verilog 用の表記に直すのは意外と難しかったです。
具体的にどうしたのかは以下の記事を参考にしてください。

実行したコードは以下のようになっていて、一度 hexdump で 16 進数に変換してから hex2bin.sh で処理をしています。

$ hexdump -v -e '/4 "%08X" "\n"' fib.bin > fib.hex
$ ./hex2bin.sh fib.hex > fib.b
#!/bin/sh
while read line; do
	printf '32'\''b'
	echo "obase=2; ibase=16; ${line}" | bc | printf "%32s" $(cat) | sed -e 's/ /0/g'
	echo ','
done < $1

fib.hexfib.b のファイルは GitHub にあげているので参考にしてください。

また、disassemble をしたい場合には以下のようにして実行できます。

$ riscv32-unknown-linux-gnu-objdump -d fib.elf > fib.dump

これらをいちいち全て手で打って実行するのは面倒なので、 Makefile にまとめておきました。
適宜少し修正すれば使えるはずなので、ご利用ください。

使い方としては、ARG にコンパイルしたいファイル名 (拡張子抜き) を指定して、以下のようなコマンドを打つだけです。

$ make ARG=fib

さらに、今は C 言語からコンパイルしていますが、アセンブリからコンパイルしたい場合には、25 ~ 26 行目を以下のようにコメントアウトして make コマンドを先程と同様に打ってください。

# $(ARG).S: $(ARG).c
# 	$(CC) $(CFLAGS) -S -o $(ARG).S $(ARG).c

5. まとめ

今回は RISC-V の自作プロセッサをテストするために、riscv-gnu-toolchain というクロスコンパイラを使う方法を紹介しました。
インストールもオプションで意外と手こずったり、コンパイルもややこしかったりとつまりどころは意外とありました。
この記事が少しでも参考になれば嬉しいです 👍

コメントを残す

メールアドレスが公開されることはありません。

CAPTCHA