2017年には新しい命令セットアーキテクチャである
はじめに
RISC-V
ISAとは、
RISC-Vは2010年頃からカリフォルニア大学バークレー校で設計が始まり、
RISC-Vが注目されている理由のひとつが、
それに対してRISC-V Foundationは複数の企業・
RISC-V用のツールチェインについて
残念ながらRISC-Vは、
特定のアーキテクチャの実機が存在しない場合や存在したとしてもコンパイルするのにリソースが足りない場合は、
幸い
もちろんツールチェインをビルドするためのツールチェインが必要になりますが、
LXDを使っているなら、
以下に、
1. LXDのインストール $ sudo snap install lxd 2. LXDの初期設定 $ sudo lxd init Do you want to configure a new storage pool (yes/no) [default=yes]? Name of the new storage pool [default=default]: Name of the storage backend to use (dir, btrfs, ceph, lvm, zfs) [default=zfs]: Create a new ZFS pool (yes/no) [default=yes]? Would you like to use an existing block device (yes/no) [default=no]? Size in GB of the new loop device (1GB minimum) [default=23GB]: Would you like LXD to be available over the network (yes/no) [default=no]? Would you like stale cached images to be updated automatically (yes/no) [default=yes]? Would you like to create a new network bridge (yes/no) [default=yes]? What should the new bridge be called [default=lxdbr0]? What IPv4 address should be used (CIDR subnet notation, “auto” or “none”) [default=auto]? What IPv6 address should be used (CIDR subnet notation, “auto” or “none”) [default=auto]? LXD has been successfully configured. 3. ユーザーをLXDグループに追加 $ sudo adduser $USER lxd 4. コンテナイメージの作成 $ lxc launch ubuntu:16.04 riscv $ lxc exec riscv bash 5. SSH公開鍵の登録 $ sudo -i -u ubuntu ssh-import-id gh:(GitHubのアカウント名) $ apt update $ apt full-upgrade -y
クロスコンパイル環境の構築
まずはRISC-Vのツールチェインをビルドするためのツールチェインを導入しましょう。
$ sudo apt install autoconf automake autotools-dev curl \ device-tree-compiler libmpc-dev libmpfr-dev libgmp-dev \ libusb-1.0-0-dev gawk build-essential bison flex texinfo \ gperf libtool patchutils bc zlib1g-dev pkg-config libncurses5-dev
次にriscv-toolsリポジトリとそのgit submoduleを導入します。submoduleのsubmoduleなどもダウンロードするため、
$ git clone https://github.com/riscv/riscv-tools.git $ cd riscv-tools $ git submodule update --init --recursive
submoduleのダウンロード後にディレクトリのサイズを確認したところ、
riscv-tools本体はただのビルドスクリプトで、
- riscv-isa-sim:RISC-Vの簡易シミュレーターです。
- riscv-fesvr:ホスト側で動作する
「Front-end server」 です。Host-Target InterFace (HTIF)を経由してターゲットプロセッサーと通信します。 - riscv-pk:最低限のシステムコールを処理するProxy Kernelです。ホスト上のシミュレーター上で動かすことを想定しています。
- riscv-opcodes:オペコードの定義リストです。
- riscv-tests:テストツールです。
- riscv-llvm:LLVMにRISC-Vサポートを追加したコードです。
- riscv-gnu-toolchain:GCCやglibc、
qemuなどGNUツールチェインやエミュレーターです。 - riscv-openocd:JTAG-ICEにも対応したデバッガです。
ビルドしたツールは環境変数RISCVの先にインストールされるようになっています。そこで今回はホームディレクトリに任意のディレクトリを作って、
$ mkdir $HOME/riscv/ $ export RISCV=$HOME/riscv/
あとはビルドスクリプトを実行するだけです。
$ time ./build.sh Starting RISC-V Toolchain build process (中略) RISC-V Toolchain installation completed! real 77m30.212s user 70m26.496s sys 5m59.994s
IvyBridge世代でi5-3317UのCPUと4GiBのメモリーで、build.
からはパラレルビルドする方法がわからなかったのですが、-j
」
コンパイル後の$RISCVの下には400MiB弱のバイナリが用意されることになります。
ツールチェインのテスト
インストールしたツールチェインを使って、
$ mkdir ~/test && cd $_ $ export PATH=$PATH:$RISCV/bin $ echo -e '#include <stdio.h>\n int main(void) { printf("Hello world!\\n"); return 0; }' > hello.c $ riscv64-unknown-elf-gcc -o hello hello.c $ file hello hello: ELF 64-bit LSB executable, UCB RISC-V, version 1 (SYSV), statically linked, not stripped
riscv-toolsは特に指定せずにビルドした場合、
実行したバイナリはRISC-V ISAのシミュレーターであるspikeから立ち上げたpk
$ which spike /home/ubuntu/riscv//bin/spike $ find $RISCV -name pk /home/ubuntu/riscv/riscv64-unknown-elf/bin/pk $ spike pk hello Hello world!
ちなみにspikeコマンドはデバッグ機能も備えていますので、-d
」-l
」
64bit向けNewlib用のツールチェインはriscv64-unknown-elf-
」
$ riscv64-unknown-elf- riscv64-unknown-elf-addr2line riscv64-unknown-elf-gcc riscv64-unknown-elf-gdb riscv64-unknown-elf-readelf riscv64-unknown-elf-ar riscv64-unknown-elf-gcc-7.2.0 riscv64-unknown-elf-gprof riscv64-unknown-elf-run riscv64-unknown-elf-as riscv64-unknown-elf-gcc-ar riscv64-unknown-elf-ld riscv64-unknown-elf-size riscv64-unknown-elf-c++ riscv64-unknown-elf-gcc-nm riscv64-unknown-elf-ld.bfd riscv64-unknown-elf-strings riscv64-unknown-elf-c++filt riscv64-unknown-elf-gcc-ranlib riscv64-unknown-elf-nm riscv64-unknown-elf-strip riscv64-unknown-elf-cpp riscv64-unknown-elf-gcov riscv64-unknown-elf-objcopy riscv64-unknown-elf-elfedit riscv64-unknown-elf-gcov-dump riscv64-unknown-elf-objdump riscv64-unknown-elf-g++ riscv64-unknown-elf-gcov-tool riscv64-unknown-elf-ranlib
「$RISCV/
」
$ spike /home/ubuntu/riscv/riscv64-unknown-elf/share/riscv-tests/benchmarks/dhrystone.riscv Microseconds for one run through Dhrystone: 393 Dhrystones per Second: 2544 mcycle = 196524 minstret = 196530
RISC-Vバイナリの中身
たとえばgdbを使って、
$ riscv64-unknown-elf-gdb hello -batch -ex 'disassemble /r main' Dump of assembler code for function main: 0x000000000001019a <+0>: 41 11 addi sp,sp,-16 0x000000000001019c <+2>: 06 e4 sd ra,8(sp) 0x000000000001019e <+4>: 22 e0 sd s0,0(sp) 0x00000000000101a0 <+6>: 00 08 addi s0,sp,16 0x00000000000101a2 <+8>: c9 67 lui a5,0x12 0x00000000000101a4 <+10>: 13 85 87 fd addi a0,a5,-40 # 0x11fd8 0x00000000000101a8 <+14>: ef 00 a0 20 jal ra,0x103b2 <puts> 0x00000000000101ac <+18>: 81 47 li a5,0 0x00000000000101ae <+20>: 3e 85 mv a0,a5 0x00000000000101b0 <+22>: a2 60 ld ra,8(sp) 0x00000000000101b2 <+24>: 02 64 ld s0,0(sp) 0x00000000000101b4 <+26>: 41 01 addi sp,sp,16 0x00000000000101b6 <+28>: 82 80 ret End of assembler dump.
各命令の意味はRISC-Vの
- Table 20.
1: Assembler mnemonics for RISC-V integer and floating-point registers. - Table 20.
2: RISC-V pseudoinstructions.
やっていることは至極単純です。
addi sp,sp,-16 スタックポインタを16バイト分減らす
sd ra,8(sp) スタック領域に呼び出し元のアドレスを保存
sd s0,0(sp) スタック領域に呼び出し元のフレームポインタを保存
addi s0,sp,16 フレームポインタに呼び出し時のスタックアドレスを保存
lui a5,0x12 a5レジスタに「0x12 << 12 = 0x12000」を保存
addi a0,a5,-40 # 0x11fd8 a0レジスタに「a5 - 40 = 0x11fd8」
つまり"Hello wolrd!"が保存されたアドレス」を保存
jal ra,0x103b2 <puts> puts("Hello world!")を呼び出し
li a5,0 a5レジスタに0を保存
mv a0,a5 a0レジスタ(mainの戻り値)にa5レジスタの内容をコピー
ld ra,8(sp) スタック領域から呼び出し元のアドレスを復帰
ld s0,0(sp) スタック領域から呼び出し元のフレームポインタを復帰
addi sp,sp,16 スタックポインタを元に戻す
ret 呼び出し元に戻る(raレジスタのアドレスに戻る)
最適化されていないため、
$ riscv64-unknown-elf-objdump -sj .rodata hello hello: ファイル形式 elf64-littleriscv セクション .rodata の内容: 11fd8 48656c6c 6f20776f 726c6421 00000000 Hello world!.... 11fe8 0a000000 00000000 ........
RISC-Vの命令長さは32bit固定ですが、
16bit幅の加算命令(s0 = sp + 16): 0x00000000000101a0 <+6>: 00 08 addi s0,sp,16 32bit幅の加算命令(a0 = a5 - 40): 0x00000000000101a4 <+10>: 13 85 87 fd addi a0,a5,-40 # 0x11fd8
RISC-Vの拡張性
RISC-Vの
RISC-Vは組み込みからハイパフォーマンスコンピューティングに至るまで、
基本整数命令と標準拡張は、
I: 基本整数命令
E: レジスタの数を減らしたIのサブセット
M: 整数の乗算除算命令
A: アトミック命令
F: 単精度の浮動小数点演算命令
D: 倍精度の浮動小数点演算命令
Q: 四倍精度の浮動小数点演算命令
L: 十進の浮動小数点演算命令(未定義)
C: 圧縮命令
B: ビット演算命令(未定義)
J: JavaのようなDynamic Translation用命令(未定義)
T: トランザクショナルメモリー用命令(未定義)
P: Packed-SIMD命令
V: ベクタ命令
N: ユーザーレベル割り込み
IMAFDQC以外は
たとえば
今回作ったツールチェインは、rv64imafdc
」
$ riscv64-unknown-elf-gcc -v Using built-in specs. COLLECT_GCC=riscv64-unknown-elf-gcc COLLECT_LTO_WRAPPER=/home/ubuntu/riscv/libexec/gcc/riscv64-unknown-elf/7.2.0/lto-wrapper Target: riscv64-unknown-elf Configured with: /home/ubuntu/riscv-tools/riscv-gnu-toolchain/build/../riscv-gcc/configure --target=riscv64-unknown-elf --prefix=/home/ubuntu/riscv --disable-shared --disable-threads --enable-languages=c,c++ --with-system-zlib --enable-tls --with-newlib --with-sysroot=/home/ubuntu/riscv/riscv64-unknown-elf --with-native-system-header-dir=/include --disable-libmudflap --disable-libssp --disable-libquadmath --disable-libgomp --disable-nls --src=../../riscv-gcc --enable-checking=yes --disable-multilib --with-abi=lp64d --with-arch=rv64imafdc 'CFLAGS_FOR_TARGET=-Os -mcmodel=medlow' Thread model: single gcc version 7.2.0 (GCC)
「C」
命令長は
前述の2つのaddi
を比べてみましょう。
16bit幅の加算命令(s0 = sp + 16): 0x00000000000101a0 <+6>: 00 08 addi s0,sp,16 32bit幅の加算命令(a0 = a5 - 40): 0x00000000000101a4 <+10>: 13 85 87 fd addi a0,a5,-40 # 0x11fd8
RISC-Vの標準ではリトルエンディアンなので、
- 16bitのaddi:0x0800 = 0000 1000 0000 0000
- 32bitのaddi:0xfd878513 = 1111 1101 1000 0111 1000 0101 0001 0011
太字で表示している最下位2ビットを比べてみると、
さらにISA仕様書を見れば他のビットの意味も確認できます。たとえば
16bitのaddi:0x0800
FNC [RD]OP
C.ADDI4SPN 0000 1000 0000 0000
^ ^^^^ ^^^
Non-Zero IMM[5:4|9:6|2|3]
OP = 00
FNC = 000
RD = 000
IMM = 0000010000 = 0x10
OP/
IMMは
RDは
まとめるとdisassembleした結果どおり
32bit版のaddiも同様にパースできます。
32bitのaddi:0xfd878513 [IMM ] [RS1 ]FNC [RD ][OP ] ADDI 1111 1101 1000 0111 1000 0101 0001 0011 OP = 0010011 FNC = 000 RD = 01010 = 10 RS1 = 01111 = 15 IMM = 1111 1101 1000 => 1111 1111 1101 1000 = -0x28 = -40
ADDI命令は符号拡張したIMMをRS1で指定されたレジスタに足して、
IMMは下位12ビットしか記録されていないので32ビット値
「Table 20.
2018年はRISC-Vで遊ぼう
いろいろなところで言われているように半導体プロセスの進化は限界に近づきつつあります。特に微細化による恩恵は、
グラフィックの用途に限定してGPUが進化してきたように、
いわゆる専用プロセッサは開発コストが大きくなりがちです。いろいろな
商用アーキテクチャの場合、
RISC-Vの場合は、
このようにオープン規格であるということは、
2018年の新たな目標として、