FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

RISC-Vの開発環境をcrosstool-ngとriscv-toolsでセットアップする方法

RISC-Vでのハードウェア・ソフトウェア開発をするにあたり、まず必要になるのがツール群だ。

RISC-Vのソフトウェアツール群は、riscv-toolsというリポジトリにまとめられており、まずはこのリポジトリをダウンロードしてコンパイラなどを構築する必要がある。 しかし、このリポジトリはサブリポジトリを含めコンパイラソースコードをすべてダウンロードするなど、すべてダウンロードすると数GBになってしまうためディスク容量を食ったり、ネットワーク要領を食ったりして非常に不便なものだった。

github.com

ところがここ最近、このriscv-toolsがアップデートされ、コンパイラなどの重たいツール群が除去された。 リポジトリが軽くなったのは良いことなのだが、ではコンパイラはどこから持ってくるのか?READMEを参照すると、crosstool-ngを使用する、と書いてある。 なるほど、crosstool-ngはもうRISC-Vに対応しているのか。ならば、crosstool-ngとriscv-toolsでRISC-Vの開発環境を構築できるはずだ。やってみよう。

crosstool-ngのインストール

crosstool-ngは以下のページからダウンロードする。

crosstool-ng.github.io

cd /tmp
curl -L http://crosstool-ng.org/download/crosstool-ng/crosstool-ng-1.24.0.tar.xz | tar Jx
cd crosstool-ng-1.24.0
./configure
make -j$(nproc)
sudo make install

これでct-ngコマンドが使えるようになった。

ct-ngコマンドで、サンプルターゲットを確認しよう。

ct-ng update-samples
ct-ng list-samples | grep riscv
$ ct-ng list-samples | grep riscv
[G..X]   riscv32-hifive1-elf
[G..X]   riscv32-unknown-elf
[G..X]   riscv64-unknown-elf
[G..X]   riscv64-unknown-linux-gnu

そこで、riscv64-unknown-elfをインストールしてみる。

$ ct-ng riscv64-unknown-elf
  CONF  riscv64-unknown-elf
#
# configuration written to .config
#

***********************************************************

Initially reported by: Paul Walmsley <paul.walmsley@sifive.com>
URL: https://www.sifive.com/

***********************************************************

WARNING! This sample may enable experimental features.
         Please be sure to review the configuration prior
         to building and using your toolchain!
Now, you have been warned!

***********************************************************

Now configured for "riscv64-unknown-elf"

ct-ng menuconfigで、PREFIXを設定してインストールする先を指定する。

f:id:msyksphinz:20190510015507p:plain
ct-ng menuconfigでのPREFIXの設定

ビルドする。

ct-ng build
[INFO ]  Performing some trivial sanity checks
[WARN ]  Number of open files 1024 may not be sufficient to build the toolchain; increasing to 2048
[INFO ]  Build started 20190509.232202
[INFO ]  Building environment variables
[WARN ]  Directory '/home/msyksphinz/src' does not exist.
[WARN ]  Will not save downloaded tarballs to local storage.
[EXTRA]  Preparing working directories
[EXTRA]  Installing user-supplied crosstool-NG configuration
[EXTRA]  =================================================================
[EXTRA]  Dumping internal crosstool-NG configuration
[EXTRA]    Building a toolchain for:
...
[INFO ]    Stripping all toolchain executables
[EXTRA]    Creating toolchain aliases
[EXTRA]    Removing installed documentation
[EXTRA]    Collect license information from: /tmp/riscv64/.build/riscv64-unknown-elf/src
[EXTRA]    Put the license information to: /home/msyksphinz/riscv64-ctng/share/licenses
[INFO ]  Finalizing the toolchain's directory: done in 40.42s (at 87:59)
[INFO ]  Build completed at 20190510.004957
[INFO ]  (elapsed: 87:55.45)
[INFO ]  Finishing installation (may take a few seconds)...
[87:59] / %

ここまでで、インストール先を確認する。PREFIX=/home/msyksphinz/riscv64-ctngにインストールした。

$ ls -l riscv64-ctng
total 764
dr-xr-xr-x 1 msyksphinz msyksphinz   4096  5月 10 00:49 bin
-r--r--r-- 1 msyksphinz msyksphinz 782011  5月 10 00:49 build.log.bz2
dr-xr-xr-x 1 msyksphinz msyksphinz   4096  5月 10 00:38 include
dr-xr-xr-x 1 msyksphinz msyksphinz   4096  5月 10 00:38 lib
dr-xr-xr-x 1 msyksphinz msyksphinz   4096  5月 10 00:38 libexec
dr-xr-xr-x 1 msyksphinz msyksphinz   4096  5月 10 00:26 riscv64-unknown-elf
dr-xr-xr-x 1 msyksphinz msyksphinz   4096  5月 10 00:49 share

このツール群は、PATHLD_LIBRARY_PATHを通しておく。次にriscv-toolsをダウンロードして、ビルドする。

cd riscv-tools
export PATH=${HOME}/riscv64-ctng-tools
MAKEFLAGS="-j$(nproc)" ./build.sh

これでしばらく待っているとビルドが完了する。これで、crosstool-ngを使ったRISC-Vコンパイラの構成とriscv-toolsのツール群の構築が完了した。

AWS F1インスタンス上のFireSimでBOOMコアをシミュレーションする試行(7. Firechipでオリジナルデザインを設計してシミュレーションする)

f:id:msyksphinz:20190405012417p:plain

FireSimの環境で、どうにかRocketシングルコアを動かすことができた。ここまでできれば、今度は様々なハードウェアをf1インスタンスで動かしてみたい。RocketはChiselというハードウェア記述言語で設計されているが、ChiselでオリジナルのモジュールをRocketに追加して、オリジナルのRocketコアを作れば、f1インスタンス上で動作させることができると思う。

FireSimのコアデザイン"Firechip"

FireSimは、RISC-Vコアを動作させるための環境そのものを指す。FireSimのリポジトリにはChiselで記述されたいくつかのデザインは入っているが、コアそのものではなく、周辺IPだったり、SoCを構成するための部品がメインになる。一方で、シミュレーションするコア自体は別のリポジトリで管理されている。それが"firechip"というリポジトリになる。

FireSimのリポジトリのサブリポジトリとして、"firechip"リポジトリが配置されている。

firesim
├── deploy
├── docs
├── platforms
│   └── f1
├── riscv-tools-install
├── scripts
├── sim
├── sw
├── target-design
│   ├── firechip
│   └── switch

このfirechipリポジトリは、Rocket-Chipのリポジトリと同様、単体でRTLシミュレーションを行うことができる。また、Rocket-Chipと同様、カスタマイズも可能だ。

cd firesim
cd target-design/firechip/verisim   # Verilatorを使う。VCSを使う場合はtarget-design/firechip/vsim
make clean && make run-regression-tests

つまり、FireChipを改造すればAWS f1インスタンス上でFireSimを使い、オリジナルに改造したRISC-Vコアを走らせることができる。どのように進めればよいか見ていく。

FireChipにカスタマイズアクセレレータを実装する

さて、実装方針が決まったので、FireChipに行列積アクセラレータを実装する。あらかじめ、今回はGitHubに公開されているfirechipのリポジトリをforkし、msyksphinz/firechip.gitで作業を行う。FireChipリポジトリをcloneし、セットアップを行う。この作業はAWSインスタンス上で行う必要はなく、ローカルマシンで実行しても問題ない。

git clone https://github.com/msyksphinz/firechip.git
cd firechip
git submodule update --init
cd rocket-chip
git submodule update --init
cd ..

行列アクセラレータを実装するために、以下のファイルを作成・修正する。

src/main/scala/example/Configs.scala
src/main/scala/example/MatrixMul.scala
  • src/main/scala/example/Configs.scala
class RoccMatrixMulConfig extends Config(
  new WithRoccMatrixMul ++ new DefaultExampleConfig)
  • src/main/scala/example/MatrixMul.scala
class MatrixMul(opcodes: OpcodeSet, val n: Int = 4)(implicit p: Parameters) extends LazyRoCC(opcodes) {
  override lazy val module = new MatrixMulModuleImp(this)
}

class MatrixMulModuleImp(outer: MatrixMul, n: Int = 4)(implicit p: Parameters) extends LazyRoCCModuleImp(outer)
    with HasCoreParameters {
...

コンフィグレーションはRoccMatrixMulConfigを指定した。Verilog生成時に、CONFIGオプションでRoccMatrixMulConfigを指定すると、所望のVerilogが生成される。

cd firechip/verisim
make CONFIG=RoccMatrixMulConfig

そして、上記で示したテストプログラムを実行する。プログラムは自前でコンパイルしても良いだが、firechip/testsに配置してMakefileを追加すると自動的にコンパイルしてくれる。

diff --git a/tests/Makefile b/tests/Makefile
index a199743..1102ecf 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -3,7 +3,7 @@ OBJDUMP=riscv64-unknown-elf-objdump
 CFLAGS=-mcmodel=medany -std=gnu99 -O2 -fno-common -fno-builtin-printf -Wall
 LDFLAGS=-static -nostdlib -nostartfiles -lgcc

-PROGRAMS = pwm blkdev accum charcount nic-loopback pingd big-blkdev input-stream
+PROGRAMS = pwm blkdev accum charcount nic-loopback pingd big-blkdev input-stream matrixmul

 default: $(addsuffix .riscv,$(PROGRAMS))

RTLシミュレーションを実行する。SW実行と比べて、ハードウェアアクセラレータを付加した場合にどれくらい高速化されるか検証した。

make CONFIG=FireSimRocketChipMatrixMulConfig
./simulator-example-RoccMatrixMulConfig ../tests/matrixmul.riscv

シミュレーションで正しく実行されることを確認した。

                    SW          HW
----------------------------------
 2 x 2 x 2 :        278        152
 4 x 4 x 4 :       1238        521
 8 x 8 x 8 :       9185       2691
16 x16 x16 :      70288      17621
18 x24 x28 :     209390      47615

Wi-Fi モジュールの付いたRISC-Vボード HiFive1 Rev.Bが届いた

SiFive社が販売しているRISC-Vボードの第3弾、Wi-Fiモジュールの搭載されたRISC-VボードHiFive1 Rev.Bが自宅に到着した。

f:id:msyksphinz:20190507220931p:plain
HiFive1 Rev.Bパッケージ
f:id:msyksphinz:20190507221025p:plain
HiFive1 Rev.B内容物

HiFive1 Rev.Bの仕様は、SoCの部分はHiFive1と一緒だ。

  • 動作周波数320MHz
  • RISC-Vコア Freedom FE310搭載。FE310-G002。サポート仕様はRV32IMAC
    • 32-bit RISC-Vアーキテクチャ、ハードウェア乗除算命令搭載、アトミック命令搭載、Compressed命令搭載
    • 16kB L1命令キャッシュ
    • 16kB SRAMクラッチパッドメモリ
  • 外部割込みピン 19本
  • UART, PWM搭載
  • JTAGデバッグサポート
  • 128-bit SPIフラッシュメモリ搭載
  • WiFiモジュール
    • ESP32-SOLO-1

搭載されているWirelessモジュールはESP32-SOLO-1というものらしい。調べてみるとこれ自体は日本の技適は取れている?ということはこのモジュールは日本国内で使っても大丈夫なのかな?

f:id:msyksphinz:20190507222026p:plain

まあボードを買ってみたものの触る時間がほとんどないのが問題だ。。。試行できるのはいつの日か。。。

AWS F1インスタンス上のFireSimでBOOMコアをシミュレーションする試行(6. Firesimで生成されたDCPをVivadoで表示する)

f:id:msyksphinz:20190405012417p:plain

FireSimのシミュレーションに使用する独自のAFIイメージを作成する。

AFIとは、Amazon FPGA Imageの略称で、AFIを作成しておけばすぐにAWS f1インスタンスにデザインをデプロイできる。

Amazon S3のアカウントをセットアップしていることを前提にする。 Amazon S3のコンソール上で[Create bucket]をクリックして新しいバケットをセットアップする。

[Bucket name]は"firesim-(アカウント名)"とする。 そうしないとfiresimがS3バケットを認識できない。 私の場合は、"firesim-msyksphinz"とした。次に[Next]をクリックする。

f:id:msyksphinz:20190506224239p:plain
FireSimで作成したFPGAイメージを格納するS3バケットを作成する。バケット名は”firesim-(アカウント名)”とすること。

そのまま[Next]をクリックし続け、[Create Bucket]をクリックして新しいバケットを生成する。

S3上でバケットの構築が完了すると、次にマネージャインスタンス上で作業する。firesim/deploy/config_build.iniを編集する。

[afibuild]

s3bucketname=firesim-msyksphinz

[builds]
# this section references builds defined in config_build_recipes.ini
# if you add a build here, it will be built when you run buildafi
firesim-singlecore-no-nic-lbp
# #firesim-singlecore-nic-lbp
# #firesim-quadcore-no-nic-lbp
# #firesim-quadcore-nic-lbp
# firesim-quadcore-no-nic-ddr3-llc4mb
# firesim-quadcore-nic-ddr3-llc4mb
# #fireboom-singlecore-no-nic-lbp
# fireboom-singlecore-no-nic-ddr3-llc4mb
# #fireboom-singlecore-nic-lbp
# fireboom-singlecore-nic-ddr3-llc4mb
# #firesim-supernode-singlecore-nic-ddr3-llc4mb
# #firesim-supernode-quadcore-nic-ddr3-llc4mb
# firesim-supernode-singlecore-nic-lbp


[agfistoshare]
firesim-singlecore-no-nic-lbp
# #firesim-singlecore-nic-lbp
# #firesim-quadcore-no-nic-lbp
# #firesim-quadcore-nic-lbp
# firesim-quadcore-no-nic-ddr3-llc4mb
# firesim-quadcore-nic-ddr3-llc4mb
# #fireboom-singlecore-no-nic-lbp
# fireboom-singlecore-no-nic-ddr3-llc4mb
# #fireboom-singlecore-nic-lbp
# fireboom-singlecore-nic-ddr3-llc4mb
# #firesim-supernode-singlecore-nic-ddr3-llc4mb
# #firesim-supernode-quadcore-nic-ddr3-llc4mb
# firesim-supernode-singlecore-nic-lbp

ターゲットとなるfiresim-singlecore-no-nic-lbpのみを残し、あとはコメントアウトする。 これをしっかり行わないと、ターゲットとなるイメージの数だけインスタンスが生成されてしまい、料金がかさむことになってしまう。

上記のように編集した後、firesim buildafiを実行する。処理が完了するのに、3~4時間程度かかる。 途中で以下のようなメッセージが連発されるが、辛抱強く待つこと(筆者は我慢できずGitHub Issueで質問したら、「我慢しろ」という回答が返ってきた)。

Current state: pending
Current state: pending
Current state: pending
Current state: pending
Current state: pending
...

辛抱強く待つと、

RTLの生成と、Vivadoでの論理合成とイメージの作成が行われ、その結果がS3に書き込まれる。 このイメージには、論理合成のログとVivadoのdcp(Design Check Point: プロジェクトファイルのようなもの)が格納されている。Vivadoでdcpファイルを開くと、どのようなデザインがどのようにFPGAマッピングされたのかチェックできる。

S3バケットを見てみる。"firesim-msyksphinz"バケットの中を覗いてみると、tarで圧縮されたファイルが格納されているのが分かると思う。

f:id:msyksphinz:20190506224319p:plain
FireSimで作成したFPGAイメージがS3バケットに格納されているか確認する。”firesim-msyksphinz”の中に入っているはず。
f:id:msyksphinz:20190506224341p:plain
FireSimで作成したFPGAイメージが、tarで圧縮されて格納されている。

FireSimのFPGAイメージはS3バケットに格納されているので、基本的にどのインスタンスを使ってVivadoを立ち上げても構わない(つまり、日本から遠いNorth Virginiaのインスタンスを使わなくても、TokyoのインスタンスFPGA Developer AMIを使ってインスタンスを立ち上げても問題ない)。しかし手っ取り早いのはFireSimのマネージャインスタンスを使う事だ。 AWSのコンフィグレーションも終わっているので、S3との連携も完了している。 なお、f1インスタンスで使用されているのは特殊なAWS用に開発された特殊なFPGAのようなので、FPGA Developer AMIに同梱されているVivado以外ではFPGAバイスを認識できなかった。 手元のローカルマシンでdcpを開こうと何度も挑戦したのだが、うまくいかなかった。 やはり、AMI上のVivadoを使うのが最も安心のようだ。

念のため、VivadoをオープンするためにはGUIを使用するので、f1インスタンスsshで接続している場合はX11フォワーディングの設定をしておく。 筆者は以下のパッケージを追加でインストールする必要があった。

sudo yum install xorg-x11-xauth.x86_64 xorg-x11-server-utils.x86_64 dbus-x11.x86_64

さて、以下のようにしてS3上に格納されているVivadoのDCPをマネージャインスタンスLinuxにダウンロードする。

aws s3 cp s3://firesim-msyksphinz/dcp/19_04_20-032417.Developer_CL.tar-192.168.4.9-VF6PT4V3DA.tar  .

tarを解凍し、dcpをオープンする。

tar xf 19_04_20-032417.Developer_CL.tar-192.168.4.9-VF6PT4V3DA.tar
cd to_aws
vivado 19_04_20-032417.SH_CL_routed.dcp

Vivadoがたちがあり、dcpをロードしている。 しばらくすると、以下のような画面になると思う。 FireSimが配置配線され、FPGA上にインスタンスされている様子が何となくだが分かると思う。

f:id:msyksphinz:20190506224550p:plain
FireSimで作成したFPGAイメージをVivadoでオープンする。FPGA上にインプリメントされている様子が、何となくだが分かる。

より詳細にブロックダイアグラムを見ていくと、どのようなモジュールがインスタンスされているのかわかると思う。FireSimはRISC-Vコアのほかに、PCI ExpressのモジュールやDDRのコントローラなどもインスタンスされていることが分かる。

f:id:msyksphinz:20190506224442p:plain
FireSimで作成したFPGAイメージを階層構造で確認してみる。RISC-Vコア以外に、DDRコントローラやPCI Expressなどがインスタンスされていることが分かる。
f:id:msyksphinz:20190506224522p:plain
FireSimで作成したFPGAイメージを階層構造で確認してみる。RISC-Vコア以外に、DDRコントローラやPCI Expressなどがインスタンスされていることが分かる。

RISC-VのVector Extensionの仕様概観

f:id:msyksphinz:20190505224354p:plain

少しRISC-VのVector Extensionについて調査した。

Vector Extensionは、いわゆるベクトル命令だ。1命令で複数のデータを扱う方法といえば、SIMD(Single Instruction Multiple Data)もあるが、RISC-Vではまずはベクトル命令が定義された。現代のアーキテクチャでは、ベクトル拡張よりもSIMD拡張が一般的だと思うが、RISC-Vではまずはベクトル拡張が定義されている。これはV拡張といわれ、MISAレジスタのビットではVビットで規定されている。ベクトル命令のエンコーディングはまだ規定されていない。いわゆるSIMD命令は、ベクトルレジスタのサイズは命令の中に組み込まれているが、ベクトル命令では、実装形態によってベクトルレジスタのサイズが決まる。

RISC-Vのベクトル拡張仕様は、日進月歩で改定が進んでいる。RISC-V User-Level ISA V2.2をもとに使用の概要を説明するが、この仕様が現在の公式だが、常にアップデートが行われており、現在はGitHub上で議論が行われている。ベクトル拡張についてより深く学びたいという型は、RISC-V Vector Extensionの仕様書を作成しているGitHubのリポジトリを参照すること。

RISC-Vのベクトル拡張では少し面白い仕様が取り込まれている。「動的レジスタ・タイピング」(dynamic type field)という機能によって、ベクトルレジスタが取り扱える型は動的に決定される仕様になっている。これは、例えばベクトルレジスタ0は整数型、ベクトルレジスタ1は単精度浮動小数点型、というように、ベクトルレジスタ毎にデータの型を指定できるようになる。この方式のメリットは、ベクトル命令と、ベクトル命令の取り扱えるデータ型を分離する、と言う事だ。たとえば、通常のSIMD命令であれば、

というように、命令に対してデータ型と演算幅が決まる。これに対して、RISC-Vのベクトル拡張では、命令にデータ型が付属しているのではなく、レジスタに対してデータ型が付随している。

RISC-Vのベクトル拡張を有効にすると、以下のレジスタが有効になる。

これを実現するために、RISC-Vのベクトル拡張では、以下に示したCSRが追加された。

CSR ベースISA 説明
vl 0x020 RV32, RV64, RV128 ベクトル長を示す。
vxrm 0x020 RV32, RV64, RV128 浮動小数点演算命令における丸めモード
vxsat 0x020 RV32, RV64, RV128 固定小数点演算命令におけるSaturation状態を示す。
vcsr 0x020 RV32, RV64, RV128
vcnpred 0x020 RV32, RV64, RV128 有効なプレディケートレジスタの数を示す。0から8までの値が格納される。
vcmaxw 0x020 RV32, RV64, RV128 ベクトルレジスタがサポートできる最大ビットサイズを示す。4×32=128ビットが定義されている。
vcmaxw1 0x020 RV32 RV32において、vcmaxwの[63:32]を参照するためのレジスタ
vcmaxw2 0x020 RV32, RV64 RV32において、vcmaxwの[95:64]を参照するためのレジスタ。RV64において、vcmaxwの[127:64]を参照するためのレジスタ
vcmaxw3 0x020 RV32 RV32において、vcmaxwの[127:96]を参照するためのレジスタ
vctype 0x020 RV32, RV64, RV128 ベクトルレジスタが格納しているデータ型を示す。4×32=128ビットが定義されている。
vctype1 0x020 RV32 RV32において、vctypeの[63:32]を参照するためのレジスタ
vctype2 0x020 RV32, RV64 RV32において、vctypeの[95:64]を参照するためのレジスタ。RV64において、vctypeの[127:64]を参照するためのレジスタ
vctype3 0x020 RV32 RV32において、vctypeの[127:96]を参照するためのレジスタ
vctypev0 0x020 RV32, RV64, RV128
vctypev1 0x020 RV32, RV64, RV128
... 0x020 RV32, RV64, RV128
vctypev31 0x020 RV32, RV64, RV128

これらを使用して、ベクトル拡張では「最大ベクトル長(maximum vector length: MVL)」という値が定義される。

vlレジスタは、ベクトル拡張の「アクティブベクトル長(active vector length)」を格納する。これは、計算ターゲットとなるベクトルレジスタの長さを指定する。このvlレジスタへは、専用命令setvlを使って格納する。setvlレジスタは、MVLよりも大きな値を設定することはできない。vlはMVLよりも大きな値を設定しようとすると、自動的にトランケートしてMVLよりも小さな値に抑える。AVLの値が2$$\times$$MVLよりも小さな場合が少し特殊だが、これはベクトル演算のパイプライン化により性能を最大限に引き出すための工夫だ。

vcmaxwのフィールドは、ベクトルレジスタに格納できる最大データ幅を示する。これは、以下の表に従って設定される。例えば、ベクトルレジスタでサポートされるデータが最大で64ビットならば、vcmaxwは1011となる。vcmaxwは4ビットのフィールドがベクトルレジスタの32個分、つまり128ビット用意する必要がある。RV128ならば128ビットのデータに対して一度に参照できるのだが、RV64とRV32ではそうもいかない。 そこで、RV64ではvcmaxw2レジスタ、さらにRV32のためにvcmaxw1,vcmaxw2, vcmaxw3レジスタが用意されている。

エンコーディング
無効 0000
8 1000
16 1001
32 1010
64 1011
128 1100
f:id:msyksphinz:20190505224138p:plain

なぜこのようなエンコーディングになっているかというと、次に登場するCSRのvctypeとのつじつまを合わせるためだ。各ベクトルレジスタには、4ビット幅のvctypeレジスタが付属している。このCSRは各ベクトルレジスタがどのようなサイズのデータを格納しているかを示しているので、vcmaxwレジスタとは異なり、明確に整数型と浮動小数点型を区別する。

vctypeレジスタエンコーディングが、整数型を取り扱う時は最上位ビットが1、浮動小数点型を取り扱う時は最上位ビットを0に設定する。この2つのレジスタの関係は、常にvctypeレジスタvcmaxwレジスタの値よりも小さくなければならない。

さらに、プレディケートレジスタについて説明しておく。これは、ベクトルレジスタのマスクをするためのレジスタだ。このプレディケートレジスタを使用することで、ベクトルレジスタの中で処理を適用するエレメントと、そうでないエレメントを自由に制御することができるようになる。

vcnpredレジスタは、プレディケートレジスタが何本使用できるかを指定する。プレディケートレジスタは、前述の通りvp0からvp7まで8本存在するが、vpnpredは0から8までの値を取ることがでく。0を書き込むと全てのプレディケートレジスタを使用できなくなる。一方で8以上の値を書き込むと不正命令例外が発生する。

さらに、これまで説明したベクトルレジスタの設定をより高速に実行するための命令が存在する。これはvcfgd命令で、csrrw命令の一種なのだが、どのベクトルレジスタをどのデータ型に割り当てるのかを一気に設定することができる。

ベクトル命令の例

最後に、ベクトルレジスタをどのようにして使うのかを例で見てみる。プログラム[refs:vec_daxpy]のコードは、RISC-Vのベクトル拡張を使用して書いたベクトル同士の加算を行うためのコードだ(RISC-V User-Level ISA V2.2より抜粋)。

# 32ビット整数型どうしのベクトル配列の加算コード
# ベクトルレジスタは32ビット整数型をサポートするように構成されているものとする。
# a0 : ベクトル長Nを保持している。
# a1 : 加算結果を格納するベクトルのポインタを保持している。
# a2 : 加算するベクトル1つ目のポインタを保持している。
# a3 : 加算するベクトル2つ目のポインタを保持している。
loop: 
  setvl t0, a0    # Nをvlに設定する。t0に現在のvlの値を設定する。
  vld   v0, a2    # v0に1つ目のベクトルをロードする。
  sll   t1, t0, 2 # t1はベクトル長Nに相当するバイト数を返す(32ビット整数なので×4)
  add   a2, t1    # a2をロードしたバイト数だけ進める
  vld   v1, a3    # v1に2つ目のベクトルをロードする。
  add   a3, t1    # a3をロードしたバイト数だけ進める。
  vadd  v0, v1    # v0とv1を加算し、その結果をv0に格納する。
  sub   a0, t0    # ベクトル命令で処理した分だけa0の値を減らす。
  vst   v0, a1    # 加算した結果をメモリにストアする。
  add   a1, t1    # 結果ベクトルのポインタを進め®う。
  bnez  a0, loop  # ベクトル要素が残っていればループを繰り返す。そうでなければ終了する。

setvl命令により、まず1回のループ(つまり、1回のベクトル命令で処理できるデータの数)が設定される。次に加算対象となるベクトルをv0, v1にロードする。そしてベクトル演算により加算を行い、その結果をメモリに格納する。そしてベクトルサイズを1回のループで実行したデータ数で減算し、残りの処理すべきデータ数を計算する。残りのデータ数が0よりも小さくなるまでこの処理を繰り返して、全てのベクトルが処理されるまで続ける。

RISC-Vの命令セット一覧表のページを作った

RISC-Vの命令セットは、riscv.orgに掲載されているRISC-V User-Level ISA Manual V2.2 を見ればほとんどを把握することができる。

また、RISC-V Reader(日本語版 RISC-V 原典) にも命令セット一覧表が掲載されており、これを見ればRISC-Vの命令セットの概要を把握することができ、しかもRISC-Vの命令数は比較的少ないとあってかなり分かりやすい。

しかし、いちいち命令のエンコーディングや命令フォーマットを検索するのに(特に最近はLLVMにハマっていてRISC-Vのエンコーディングを調べる機会が多いのだ)、いちいちpdfを検索するのはAdobe Readerの検索性が低いので面倒だし、RISC-V Readerも紙媒体なので目で検索するのに時間がかかる。

だったら自分でWebで参照できるページを作ればいいじゃないということで、自作でRISC-Vの命令セット一覧表のWebページを作ることにした。

msyksphinz-self.github.io

私の把握しているRISC-Vの命令セットはほとんど抑えたはずだ。これからはこちらのページを研鑽していって、なるべくPDFを開かなように努力していきたい。

このページはどうやって作ったかというと、自分で手動で作るのは手間がかかるので、自作RISC-V ISSの命令デコーダの力を借りることにした。

github.com

以下のように、json形式でRISC-Vの命令エンコーディングをすべて格納している。実際にRISC-V命令セットシミュレータでは、これをスクリプトで処理して命令デコーダを自動生成するようにしている。 これを活用するわけだ。

[
    {
        "name":"lui        r[11:7],h[31:12]",
        "length":"32", "dlength":"32",
        "field": ["XXXXX", "XX", "XXXXX", "XXXXX", "XXX", "XXXXX", "01101", "11"],
        "category":"ALU", "func_suffix":"",
        "impl":[""],
        "inst_ctrl":["RD_R3", "OP_SIGN_ADD", "IMM_U"]
    },
    {
        "name":"auipc      r[11:7],h[31:12]",
        "length":"32", "dlength":"32",
        "field": ["XXXXX", "XX", "XXXXX", "XXXXX", "XXX", "XXXXX", "00101", "11"],
        "category":"ALU",
        "func_suffix":"",
        "impl":[""],
        "inst_ctrl":["R1_PC", "RD_R3", "OP_SIGN_ADD", "IMM_U"]
    },
...

という訳で、命令のビットフィールド一覧表は上記のjsonを使って(ほぼ)自動的に吐き出した。もちろん、一部おかしいところがあるのでそれは修正したけれども。

次に、命令の説明については、RISC-V User-Level ISA Manualの英文からそれらしいものをひたすら引き抜いていった。 ここは自動生成ではないのでかなり時間がかかる。もっといい方法、というかまとめページがあればよかったのだが、なかったので手動で頑張った(まあ無かったのでこういうページを作ろうと思ったのだけれど)。

さらに、命令の動作について形式的な記法で一覧しているページがない。こういうやつ。

f:id:msyksphinz:20190505004807p:plain

なので、この部分はRISC-V Readerの命令セット一覧表から拝借した。RISC-V Readerは、英語版と日本語版は有料配布だけれども、中国語版、スペイン語版、ポルトガル語版は無料で配布されている。 なんという太っ腹。 というかなぜそんななか日本語版は有料なのだ。。。

riscbook.com

さらに面倒なのはC系列の命令だ。16ビットでデコードするような形式でISSに格納していなかったので、ほとんど手動で修正した。これが一番時間がかかった。

とりあえず、Restructured Textで作ったので、ローカルで確認する分にはリポジトリをダウンロードしてmakeするだけで参照できる。

git clone https://github.com/msyksphinz/riscv-isa-pages.git
cd riscv-isa-pages
make html  # build/html にHTMLページが作られる。

これをとりあえずriscv-isadocリポジトリにコピーした。ちょっとやり方がダサいのだが、ほかに良い方法があったら教えてほしい。

cp -r $(Restructured Textのリポジトリ)/build/html/* $(GitHub Pagesのリポジトリ)/

これで上記のリンク先のウェブサイトが表示されるようになっている。どうにかして2つのリポジトリをマージする方法はないものか。エンコーディングの間違いなどの指摘やPull Requestは大歓迎なのだが、リポジトリが2つに分かれれているとPull Requestもどちらに送ればよいのかわかりにくいし格好悪い。

あと、追記したい情報もたくさんある。CSRの情報やABIの情報なども一括で管理しておきたい。このあたりは拡張していくつもり。

「ファクトフルネス」を読んだ

巷で話題になっている書籍「ファクトフルネス」を読んだ。ゴールデンウィークで時間が少しできたので一気に読みこんだ。

f:id:msyksphinz:20190503220340p:plain

単なる創造や、メディアの一方的な情報だけを信頼していると、実はより広い目で見るとそれは誤っている、という話を様々なストーリーを織り交ぜながら説明する。

世界は悪くなっているように見えて、実際のところ少しずつ良くなっている、ということ。そしてどのようなこともデータを中心に判断しないととんでもない間違いを犯す可能性があるということ。

ファクトフルネス "fact-fulness" という名前の通り「ファクト」を中心に考えないと世界を誤った方向に見てしまう可能性が高い、という話だ。

このことを説明するために、第1章から第10章にかけて、様々な視点からデータの重要性について説明している。 実際、時間がなくても、各省の最後に書かれているまとめを読めば何とかなる気がする。

しかし、この本の本質は最初の50ページ程度にまとめられている。 最初の50ページに掲載されている質問とその回答を読めば、この本で言いたいことは掴めたも同然だ。

多くの人々は世界を誤ったように想像していること、そして世界に関する質問に対してはチンパンジー(つまりランダム)よりも正答率が低いということ。 それを見破るためには、正確な事実と正確なデータに基づく判断が必要ということだ。