このブログは、株式会社フィックスターズのエンジニアが、あらゆるテーマについて自由に書いているブログです。
以前から何度かブログにも登場している通り、フィックスターズでは社内有志で勉強会を半年区切りぐらいで開催しています。
そして今期は「RISC-V勉強会」になりまして、早速『RISC-V原典』の輪読をしているのですが、「やっぱり実機もほしいよね」ということで、HiFive Unleashedを会社で購入しました。
この記事は、そのHiFive UnleashedでDebianを動かしてみるところまでの軌跡という名の手順のご紹介です。
RISC-Vとは、近年とても注目されている命令セット・アーキテクチャー(ISA)で、詳細な説明は先述の『RISC-V原典』に譲るとして、ここでは簡単な特徴を紹介しておきます
目的を書いておかないと「なんで?」となる箇所も多いかと思うので、最初に少し書いておきます。
端的に言えば、このRISC-V、まさに「組み込みSoCからスパコンまで」お客さまのご要望とあらばどんなプロセッサー上でもソフトウェアを高速化するフィックスターズでもとても重要なものだから、ということです。
現時点で眼前のお仕事でRISC-Vプロセッサーを使うというのは(少なくとも私は)見かけていませんが、ポスト京を筆頭に、独自プロセッサー・ISAから広く普及しているものへ移行しようとするのが時代の流れのようです。
ですので、それほど遠くない将来において、それらのうち少なくともいくつかはRISC-Vを適用することが容易に予想されます。
加えて、単純に、フィックスターズには「普通じゃないプロセッサー・アーキテクチャーを面白いと思うエンジニア」が多く集まっているので、みんなの関心注目度も高いです。
また、既に世の中の多くの方がRISC-Vに注目し、既に多くの技術情報・ブログを書いておられますが、その多くは現状ではやはり「RISC-Vプロセッサーをどのように作るか・実装するか」に着目しているように見えます。
一方、我々の多くはやはり「ソフトウェア」屋さんなので、ハードウェア的にどう実装するかというよりは、「そのISAやチップの上でどのようにソフトウェア・アプリケーションを実装するのが良いか」が多くの関心を引くところです。
というわけで、現時点で入手できる実機のRISC-V開発環境のうち、おそらく最強の環境であるHiFive Unleashedを使って、いまのうちにRISC-Vについて学んでおこうというのが本勉強会の趣旨です。
先述の通り、HiFive Unleashedは、本記事執筆時点で入手できるRISC-Vの実機のうち、おそらく世界最強の開発環境となっています。その特徴は
で、まさに「ソフトウェア屋さんがRISC-Vを学ぶための環境」としてはとても最適です。
ということで、最後の「Linuxが動作する」を具体的にするため、実際にDebianを動かしてみました。
(本当は勉強会で発表してからにしようかとも思ったんですが、5月大型連休中に試そうとしている方々の参考になればということで、先にブログ記事にしました)
Debianを動かす前に、まずは普通に動かしてみましょう。
この手順の詳細は、公式のGetting Startedにあります。本記事はv1p1に基づいています。
HDMI等の画面出力端子はありませんので、操作はCUIコンソールでやります。sshでつなぐこともできますが、DHCPで割り当てられたIPアドレスを調べるのがちょっと面倒なので、初回はシリアル接続で試すことをオススメします。
また、ここではWindowsから接続する方法を紹介します。MacやLinux等の場合についてはGetting Startedの5.2に書かれていますので、その通りやってください。
buildroot login:
と聞かれます。idはroot、初期パスワードはsifiveでログインしましょう注意点として、(当然ですが)先に電源を入れてからTeraTermを接続しないと、画面に何も出てきません。間違えた場合or再起動したあとは、接続し直してください。
halt
コマンドを実行します(※shutdown
コマンドはありません)Power off
が出るまで待ちます前述の通り、初期出荷時のままでBuildrootが入っているので、Linuxとしてはそのまま使うことができます。
しかし、この環境はかなりの最小環境で、例えばGCC等のコンパイラはありませんし、当然パッケージマネージャーなんてものはありません。
公式フォーラムでもSiFiveの人が開発等したかったらまともなOSを入れてねと回答しています。
ということで、普段使っているのに近いLinux distroを入れましょう。本記事執筆時点ではFedoraとDebianがしっかり使えそうですが、ここではDebianを選択することにします(社内ではUbuntuが多く使われているため)。
他のdistroもありそうなので、興味がある方はぜひ試してみてください(そしてブログ記事等でぜひ教えてください!)
Debianを入れる前に、まず、標準のBuildrootを自力でビルドし、SDカードを書き換えられるか試してみることをオススメします。
また、操作ホスト環境はUbuntu(16.04)を推奨します(Getting Startedによる)。他の環境でもできなくはないでしょうが、少なくとも調べた限りWindows/WSLからでは少し厄介そうですので、Windowsしかない場合はVMを立ち上げたほうが良さそうです。
/dev/sdX
(Xは環境による…他になければsdbが多そう)に見えているのですが、これは普通にmount
できません。が、問題ないです。$ sudo apt-get install device-tree-compiler autoconf automake autotools-dev bc bison build-essential curl flex gawk gdisk git gperf libgmp-dev libmpc-dev libmpfr-dev libncurses-dev libssl-dev libtool patchutils python screen texinfo unzip zlib1g-dev
$ git clone https://github.com/sifive/freedom-u-sdk.git
$ cd freedom-u-sdk
$ git submodule update --init --recursive
$ make -j8
注意点として、submodule updateとmakeにはとても時間がかかるので気長に待つことと、かなり重いビルドがかかるのでmake -jの並列ビルド数を多くしすぎないようにすること、あと、ビルド前の依存パッケージに”device-tree-compiler”が”Additional”と書かれていましたが(少なくとも私の環境では)必須でした。
$ sudo gdisk /dev/sdX
を実行後、dで全パーティションを消去、oで新しくMBRを作って、wで書き込んでください。$ sudo make DISK=/dev/sdX format-boot-loader
でSDカードに書き込みます。この時、Getting Startedにある通り、失敗することがあります(私の環境では必ず初回は失敗しました)。成功して完了するまで何度かやってください。これでちゃんとBuildrootが起動し操作できるようになっていれば、とりあえずSDカードの中身を書き換える操作はできました。
さてここからが本題のDebianを動かすところです。ここでは、Debian wikiに従って、第2パーティションに入れる方法をとります。
$ sudo make DISK=/dev/sdX format-demo-image
を実行します(format-boot-loader→format-demo-imageに変更)。Debianのビルド済みイメージがダウンロードされ、自動でSDカードに展開されます。少し時間がかかります(20分ぐらい?)。# mount /dev/mmcblk0p2 /mnt
# mount -t proc /proc /mnt/proc
# cp /etc/resolv.conf /mnt/etc/resolv.conf
# chroot /mnt/ /bin/bash
# ntpdate ntp.nict.jp
# export DEBIAN_FRONTEND=noninteractive DEBCONF_NONINTERACTIVE_SEEN=true
# export LC_ALL=C LANGUAGE=C LANG=C
# dpkg --configure -a
# apt update
# apt upgrade
cp resolv.conf
(DNSの設定)とntpdate
(時刻の更新)を忘れないようにしてください。これがないとapt
が動きません(特に、時刻は電源を落とす度に毎回消えて1970年に戻るので、忘れないように)。
これで普通のDebianとほぼ同じように使えるようになりました!
せっかく動くようになったので、少しソフトウェアの比較をしてみたいと思います。とは言え、普通のIntel CPUと実行性能を比較しても仕方ないので(どう考えてもIntel CPUの方が強いに決まってる)、ここでは以下の単純な疎行列ベクトル積(ELL形式)のコードをコンパイルした時のアセンブリの比較してみましょう。
※2019-05-07追記:以下のコードは、Twitterでのご指摘を受けて修正されたものです。結果的にx86_64の方は修正前に比べて少し短く出力されました。参考に、修正前のコードは付録に残してあります。また、ここでの比較は単純かつ素朴なものであり、厳密さを求めたものではないことに注意してください。あらゆる条件を考慮し正しい手法を用いた比較は今後の勉強会でなされてそのうちまたブログ記事になると期待しています。
// gcc -std=c11 -O2 -S main.c
#include<stddef.h>
#define MAX_NONZERO (32)
void SpMV(double y[restrict], const double a_data[restrict], const size_t a_column[restrict], const size_t a_nonzero[restrict], const double x[restrict], const size_t n)
{
for(size_t i = 0; i < n; ++i)
{
double y_i = 0;
const size_t nnz = a_nonzero[i];
for(size_t idx = 0; idx < nnz; ++idx)
{
const double a_ij = a_data[i*MAX_NONZERO + idx];
const size_t j = a_column[i*MAX_NONZERO + idx];
const double x_j = x[j];
const double ax = a_ij * x_j;
y_i += ax;
}
y[i] = y_i;
}
}
x86_64 | RISC-V(RV64IMAFDC) |
---|---|
アセンブリ出力 | |
|
|
オブジェクト(.o)サイズ[byte] | |
1344 | 1288 |
GCCバージョンがちょっと古い(freedom-u-sdk推奨のUbuntu 16.04を使っている)ので少し不公平とはいえ、RISC-Vの方がオブジェクトサイズも小さく、かつアセンブリも短く読みやすいという結果になりました。
というわけで、RISC-V実機であるHiFive UnleashedでDebianを動かすことができました。
今後、今期の勉強会で私を含めて多くのエンジニアが色々触ったり実行したりしてみる予定です。またそれらの結果もぜひ紹介したいと思いますので、お楽しみに!
謝辞:本記事に含まれている内容には、フィックスターズのエンジニア各位からの助言・協力してもらった内容が多く含まれています。
特に、aptを動かすためのresolv.confとntp周りの設定はyuki-itoによるものです。
また、Fixstars SolutionsのFarhanには、RISC-V North America Roadshow 2019にて勉強会に適した開発ボードを調査してもらい、最終的にHiFive Unleashedに決めたのは彼の報告によるものです。
// gcc -std=c11 -O2 -S main.c
#include<stddef.h>
#define MAX_NONZERO (32)
void SpMV(double y[restrict], const double a_data[restrict], const double a_column[restrict], const size_t a_nonzero[restrict], const double x[restrict], const size_t n)
{
for(size_t i = 0; i < n; ++i)
{
double y_i = 0;
const size_t nnz = a_nonzero[i];
for(size_t idx = 0; idx < nnz; ++idx)
{
const double a_ij = a_data[i*MAX_NONZERO + idx];
const size_t j = a_column[i*MAX_NONZERO + idx];
const double x_j = x[j];
const double ax = a_ij * x_j;
y_i += ax;
}
y[i] = y_i;
}
}
x86_64 | RISC-V(RV64IMAFDC) |
---|---|
アセンブリ出力 | |
|
|
オブジェクトサイズ[byte] | |
1616 | 1288 |
a_column
の型が、doubleではなく正しくはsize_tであるべきでした。
本文中の修正後と比べると、x86_64は1616→1344と小さくなり、行数もかなり減りました。差分を見ても、多くの箇所で変更があります。
一方、RISV-Vの方はほとんど変化せず、24行目のfldがld(倍精度ロードが普通のロード)になり、27行目のfcvt(double→ulong)変換がなくなっただけで「型を変更しただけ」というのが素直に現れていて、分かりやすいですね。
Thanks for the acknowledgement. Glad to help anytime 🙂