mcommit's message

ソフトウェア開発の仕事をしているsimotinといいます。記事の内容でご質問やご意見がありましたらお気軽にコメントしてください\^o^/

Hyper-Vの仮想マシンに"接続"できないときの対処法

タイトルの通りHyper-V仮想マシンに接続できなくなるという謎な現象が起こっていました。
仮想マシンを起動すると確かに起動はして普通に動いてはいるようですが、「接続」を選択しても仮想マシンの画面が表示されないのです。

つまり、仮想マシンのネットワークの設定ができていない状態だと手も足もでないという絶望的な状況です。

色々調べた結果、こちらの記事が参考になりました。

social.technet.microsoft.com

結論として、仮想マシンへの接続画面が表示されない場合は

C:\Windows\System32\vmconnect.exe

を起動すると接続用の画面が表示されるとのことです。
※もちろんHyper-Vを有効化しておく必要があります。

yoctoに入門してみる

ちょっと前にyoctoについて色々と調べる機会があったので理解している範囲で書いておきたいと思います。

yoctoを一言で説明すると?

yoctoはLinuxカーネルとユーザーランドのプログラムを含めてビルドするためのプロジェクトでしょうか。
元々はOpenEmbeddedというプロジェクトからスタートしたようですので、組込用のイメージを簡単にビルドすることをイメージしているんだと思います。

知っておくと何が嬉しいのか?

組み込みLinuxの開発をするときに役に立ちます。近年ではボードベンダーから提供されるBSP(Board Support Package)のビルドがyoctoを前提としていることが増えてきているようですのでむしろ避けては通れなくなるような気がしています。
ただし、自分でユーザーランドのアプリケーションを開発する場合は結局自分の為にレシピを書かないといけないので正直メリットはあまりないのではないかと思う今日この頃です。

概念と用語

基本的な概念

yoctoでは、レイヤーとレシピという概念が存在します。
レシピは文字通り、ビルドを行うのに必要な手順やソースコードの情報になります。ソースコードの場所やビルド時に必要な手順を .bb という拡張子のファイル書いておきます。レイヤーは複数のレシピをまとめたものです。このレイヤーとレシピの依存関係を解決してよしなにイメージをビルドしてくれるるのがyoctoを使うメリットになるそうです。
ユーザーランドのアプリケーションはmake/Makefileを使ってビルドするプロジェクトとして開発をすることが多いと思いますが、makeで生成する実行モジュールやライブラリのビルドをレシピとして管理する形になります。

poky

pokyとは何でしょうか?公式サイトには

Poky is a reference distribution of the Yocto Project®. It contains the OpenEmbedded Build System (BitBake and OpenEmbedded Core) as well as a set of metadata to get you started building your own distro. To use the Yocto Project tools, you can download Poky and use it to bootstrap your own distribution. Note that Poky does not contain binary files – it is a working example of how to build your own custom Linux distribution from source.

と書かれています。要するにビルドに必要なツール類やスクリプト等の一式との認識でよいかと思います。
pokyのレポジトリをcloneして入手します*1

bitbake

組み込み開発のような様々なターゲット向けにビルドを行うことを前提としたツールセットです。BitBakeは元々OpenEmbeddedの一部としてスタートしたそうです。現在の私の認識は、bitbakeはpokyで提供されるビルドに必要なコマンド群というイメージです。

実際にyoctoでビルドしてみよう

なんちゃってうんちくはこれくらいにして、実際に手を動かしてビルドして自作のイメージを作る流れを体験してみましょう!

ターゲット

BeagleBone Black用のイメージをビルドして実機で動作させてみました。
BeagleBone Blackはyoctoプロジェクトのリファレンスターゲットとして含まれています。
ちなみに標準だとQEMU用のイメージがビルドされますが、それだと組み込み感がないので...

環境構築

まずはビルドに必要なツールやライブラリをインストールします。

# essentials
sudo apt-get install gawk wget git-core diffstat unzip texinfo gcc-multilib build-essential chrpath

# need for GUI
sudo apt-get install libsdl1.2-dev xterm

# need for build Application Development kit 
sudo apt-get install autoconf automake libtool libglib2.0-dev

pokyの入手

# ビルド用ディレクトリ作成とpokyの入手
$ mkdir yocto
$ cd yocto
$ git clone -b zeus git://git.yoctoproject.org/poky.git
$ source ./poky/oe-init-build-env build_bbb

yoctoのリリースとバージョンについて

Releases - Yocto Project

を見ると、yoctoは1年に1~2回程度は新しいバージョンがリリースされています。
バージョンによってコマンドなどで非互換というか機能が変わったりするものもあるようです。

私の経験上、ビルドしたいレシピがバージョンによってはすんなりとビルドが通らないバージョンもあるのでエラーにはまって原因調査に時間を取られるよりは違うバージョンを試した方が無難な気がします。

今回試したyoctoのバージョンは記事を書いている時点の最新版で、3.0 Zeus になります。

ちなみにボードベンダーのBSPを使って開発をするときは yoctoのバージョンも決まっていて、

git clone -b jethro git://git.yoctoproject.org/poky.git

のようにバージョンに対応するブランチを指定してpokyを入手することが多いかと思います。

ビルド用初期化

oe-init-build-envはビルド時に使用する環境変数等を設定するスクリプトになっています。

oe-init-build-env ビルド用ディレクトリ名

として実行します。
既に同名のディレクトリがあればそのディレクトリに移動します。
oe-init-build-envを実行すると、指定したディレクトリにビルドに関する設定ファイルが生成されます。
環境変数として、PATH,BUILDDIR,BB_ENV_EXTRAWHITEといった変数が追加・更新されます。
ビルド時にはbitbakeコマンドを実行しますが、bitbakeコマンドがあるディレクトリへのパスの追加もこの oe-init-build-env がやってくれています。

ビルドターゲットの変更

conf/local.conf を編集しビルドターゲットを指定します。

MACHINE ?= "beaglebone-yocto"

の行のコメントアウトを削除して有効にします。
ここでの MACHINE ?= の ?= は代入だけど、最後に指定されたされた ?= が有効になります。
また、 MACHINEに関して、

MACHINE ??= "qemux86-64"

という行がありますが、 ??=はMACHINEに何値が設定されていない場合に ??= で指定された値を設定するという意味です。
要するに、最後に書いておけばデフォルト値として使えるよということだと思います。
QEMUで試してみたいという人はこのconf/local.confの MACHINE を変更する必要はありません。

ビルドの実行

デフォルトのレシピとしていくつかターゲット用イメージをビルドするレシピが含まれていますが、各イメージの詳細はいまいちよくわかりません。

ここでは最小構成のイメージをビルドするcore-image-minimalをビルドしてみます。

bitbake core-image-minimal

実行するとしばらく時間がかかりますので放置しておきましょう。
といっても途中でビルドがエラー終了したりすることはよくあるので、初めてのビルドであれば時々進捗具合をした方がよいかもしれません。

私の手元では、core i7-870の古いPCをSSDへ換装し、メモリも16GBにしてビルドマシンとして使っていますが2~3時間程度でビルドは無事完了しました。*2

ちなみに、yoctoではビルド結果の情報を保持していて、2回目以降のビルドでは依存関係をチェックして必要なレシピのみビルドしてくれます。

ビルドの生成物はどこにできているのか?

ビルドが終わったら何をすればいいのでしょうか。そもそも成果物はどこにできているのでしょうか?
結論でいうと tmpディレクトリの中にできているのですが、大まかなディレクトリ構成についても触れておきたいと思います。

ディレクトリ構成

bitbakeでビルドしたときにできるディレクトリ構成についてみてみます。
ビルド用ディレクトリ(今回の場合 build_bbb)には、

  • cache
  • conf
  • downloads
  • sstate-cache
  • tmp

ディレクトリができています。このうち、confディレクトリとその中身は oe-init-build-env を実行した際に生成されます。

cache

cacheは文字通りビルドに必要な情報のキャッシュ用フォルダのようです。レシピファイルの解析だけでもちょっと時間がかかったりするのでビルド時間を短縮するためにできるだけキャッシュしておこうというのがyoctoのコンセプトのようです。

downloads

yoctoではレシピをビルドする際にビルドに必要なソースコードをインターネットから入手できます。
例えばあるプロジェクトの最新のソースをgithubからダウンロードしたり、特定のバージョンのtar.gzファイルを取ってきたりとかです。
downloadsはそういったネットから入手したファイルを保持しておくディレクトリです。このディレクトリのパスはlocal.confの DL_DIR で定義されており変更することもできます。

sstate-cache

このディレクトリの役割はよく分かっていません。sstateをキャッシュしてくれているんですよ、きっと。

tmp

皆さんお待ちかねのディレクトリです。ビルドプロセスで出来た中間生成物や最終生成物はこのディレクトリ以下に含まれています。
yoctoではクロスコンパイルする際に必要なクロスコンパイラのビルド自体がレシピとして含まれていますが、ビルドされたクロスコンパイラはworkディレクトリにできています。

ビルド成果物の流れとして、

  1. tmp/work以下にとりあえず成果物ができる。
  2. ROM焼きとかブート用SDへの書き込みに必要なファイルはtmp/workからtmp/deploy/images 以下に展開される

という流れになります。

SDカードへの書き込み

話を戻して、SDカードからイメージを起動されるため、ビルドしてできたファイルをSDカードへコピーしていきます。

SDカードの準備

BeagleBoneBlackで使用するSDカードはブートローラ用のパーティションとROOTFS用のパーティションの最低2つのパーティションが必要になります。

以下の用にパーティションを設定します。

パーティションラベル ファイルシステム サイズ 注意事項
BOOT FAT32(LBA) 32MB BOOTフラグを有効にすること
ROOT ext4 残り全てのサイズ -

ここではコマンドの詳細は省略しますが、fdiskを使って適切に設定してください。

パーティションを適切に設定できたら、マウント用ディレクトリを作成し、マウントします。

$ sudo mkdir /media/$USER/BOOT
$ sudo mkdir /media/$USER/ROOT
$ sudo mount /dev/sdd1 /media/$USER/BOOT
$ sudo mount /dev/sdd2 /media/$USER/ROOT

SDカードの2つのパーティションをマウントしたら必要なファイルをコピーしていきます。

BOOTパーティション

まずはBOOTパーティションからです。
BOOTパーティションは文字通りLinuxのブートに必要なファイルを配置するパーティションです。
MLOやU-BOOTのイメージ、デバイスツリー等のファイルをコピーしていきます。

$ cd yocto/build_bbb/tmp/deploy/images/beaglebone-yocto/
$ sudo cp MLO /media/$USER/BOOT
$ sudo cp u-boot.img /media/$USER/BOOT
$ sudo cp zImage /media/$USER/BOOT
$ sudo cp am335x-bone.dtb /media/$USER/BOOT/
$ sudo cp am335x-boneblack.dtb /media/$USER/BOOT/
U-BOOT用ファイルの配置

uEnv.txtをBOOTパーティションにおいておくことで、起動方法を細かく指示できます。このuEnv.txtはyoctoのビルドでは生成されないので自分で書く必要があります。

ブートに必要な情報を書き込んだらSDカードのBOOTパーティションに配置します。

$ echo "bootdir=/boot" >> uEnv.txt
$ echo "bootpart=0:2" >> uEnv.txt
$ sudo cp uEnv.txt /media/USER/BOOT/

これでBOOTパーティションの準備は完了です。

ROOTパーティション

ROOTパーティションLinuxのルート(/)以下として見えるディレクトリ構成です。yoctoのビルドではtar.bz2形式でrootfs一式が圧縮されていますのでROOTパーティション以下に展開します。

sudo tar -xf core-image-minimal-beaglebone-yocto.tar.bz2 -C /media/$USER/ROOT

これでSDカードの準備ができました。

最後にアンマウントします。

$sudo umount /dev/sdd1 
$sudo umount /dev/sdd2

SDカードからの起動

書き込みが完了したSDカードをBeagleBoneBlackに差し込み起動します。
起動中のログはシリアルコンソールに出力されるのでUSB-シリアル変換ケーブルが必要になります。
私は秋月電子で買ったケーブルを愛用しています。

写真のようにつないでください。
ケーブルから見て、オレンジ:TX 黄色:RX 黒:GNDです。
f:id:simotin13:20191104161756j:plain

f:id:simotin13:20191104161747j:plain

シリアルコンソールの通信設定は、

  • ボーレート:115200
  • データビット:8bit
  • ストップビット:1bit
  • パリティ:None

です。

ログを見てみると無事起動できたようなのでrootユーザーでログインしてみます。パスワードはありません。

INIT: Entering runlevel: 5
Configuring network interfaces... cpsw 4a100000.ethernet: initializing cpsw version 1.12 (0)
SMSC LAN8710/LAN8720 4a101000.mdio:00: attached PHY driver [SMSC LAN8710/LAN8720] (mii_bus:phy_addr=4a101000.mdio:00, irq=POLL)
udhcpc: started, v1.31.0
udhcpc: sending discover
udhcpc: sending discover
cpsw 4a100000.ethernet eth0: Link is Up - 100Mbps/Full - flow control off
IPv6: ADDRCONF(NETDEV_CHANGE): eth0: link becomes ready
udhcpc: sending discover
udhcpc: sending select for 192.168.1.15
udhcpc: lease of 192.168.1.15 obtained, lease time 86400
/etc/udhcpc.d/50default: Adding DNS 192.168.1.1
done.
Starting syslogd/klogd: done

Poky (Yocto Project Reference Distro) 3.0 beaglebone-yocto /dev/ttyS0

beaglebone-yocto login: root
root@beaglebone-yocto:~# uname -a                                                                                                      
Linux beaglebone-yocto 5.2.17-yocto-standard #1 PREEMPT Mon Nov 4 01:49:00 UTC 2019 armv7l GNU/Linux
root@beaglebone-yocto:~# 

unameで見るとLinux beaglebone-yocto 5.2.17-yocto-standardとなっているので無事書き込めてそうです。

twitterを見てみると最初にBeagleBoneBlackで試したのは9月26日だったようです。記事を書くまで時間が空いてしまいました...


参考記事など

kiyomi2013.net

www.slideshare.net

qiita.com

上記、参考にさせて頂いた記事の皆様、ありがとうございます。


yoctoに関する日本語の書籍はないようですし、手を動かしてみないとだめですね。英語だと割と書籍が出ていて、私は以下の2冊を購入してみました。

Using Yocto Project with BeagleBone Black (English Edition)

Using Yocto Project with BeagleBone Black (English Edition)

Cookbookの方は割と満遍なく色んな情報が載っています。Using Yocto Project with BeagleBone Blackは分かりやすいですが、少し情報が古いです。実際、最新のpokyではBOOTパーティション用のファイルが現状のpokyの内容と違っているのでこの本に書かれている通りに試すことはできません。

上記はamazonのリンクですが、packt本家のサイトで買った方が安いです。

www.packtpub.com

ちなみに月数ドル払うと、「ダウンロードはできないけど、読み放題」みたいなプランもあります。サブスクの時代ですね。

まとめ

書いてみるとかなり長い記事になりました。yoctoはLinuxのボードベンダーの人達にとってはメリットがあるかもしれませんが
、覚えることが多く学習コストが高いので、組込アプリケーション開発者には正直あまりメリットがないような気がしています。

Linuxを採用する理由って、「サポートはないけどとりあえず全部入りで、無償で簡単に開発が始められるから」というのがあると思いますが、そもそも開発する際の学習コストが高かったり、よく分からないところで頻繁にはまるようだとLinuxを採用する意味ってあまり無くなってくるんじゃないかと思います。

感想

ざっと手順を押さえるだけでも調べること(結構書くこと)があって疲れた...

続けてレシピの自作ネタの方法とMakefileのプロジェクトをレシピ化する記事も書こうと思います。

*1:私の中ではyocto=poxyみたいな認識があります。pokyを使わなかったらどうするんだろう

*2:気がついたら終わっていたので正確な時間が分かりません...

POSIX メッセージキューについて調べてみた

Unix系OSでのIPCの手段として提供されている機能の中にメッセージキューという機能があります。
この機能はプロセス間のデータの受け渡しに便利ですが、そもそもプロセス間通信とかたまにしか使わないので備忘のためまとめておきたいと思います。

メッセージキューとは

プロセス間通信の手法の1つです。SystemVとPOSIXで2つメッセージキューが存在します。
ちなみにSystem-V版のメッセージキュー(msgget, msgrcv, msgsnd)は、詳解Unixプログラミング(第3版)では

詳解UNIXプログラミング 第3版

詳解UNIXプログラミング 第3版

「遅いしこれからは使わない方がいいぞ」

という扱いを受けています。

今回はPOSIXのメッセージキューについて紹介します。
itronだと任意のデータの受け渡しはメールボックス(snd_mbx,rcv_mbx)を使うと思いますが、POSIXのメッセージキューは似たような感じで使えます。

APIの簡単な説明

初期化

mq_open関数を使います。
注意点として O_CREAT を指定して作成する際はキューのパラメータ(キューサイズ、1メッセージのサイズ)を指定可能です。
指定しなかった場合は/procに記載されているデフォルト値で動作します。

送受信

mq_send, mq_receive 関数を使います。

キューの削除

ディスクリプタは mq_close でクローズします。

注意点として、mq_closeしただけではキューは削除されません。

使わなくなったキューはmq_unlinkの呼び出しによって削除する必要があります。
キューはカーネルが管理するので mq_unlink しなければメモリリークの要因になるので注意が必要です。
逆に言うと unlink しなければ同じキューを再利用できます。

メリットとデメリット

メリット

キューであること

文字通り「メッセージ」の「キュー」であることが保証されていることでしょう。
プロセス間通信の方法として提供されている、名前付きパイプやとsocketでも任意のデータの受け渡しが可能ですのでまぁこの点はメリットとしては少し弱い気もします。

優先度が指定できる

優先度付きキューであるので、他のプロセス間通信と違って優先度が必要な場合作りこみが不要*1

送信と受信の呼び出し順を意識しなくてよい

unlink されない限りはキューにメッセージが残ります。
カーネルでキューを管理しているので、送信側プロセスで送信してから、受信側プロセスを起動してもメッセージを受信できます。
ただしキュー自体が存在することが前提なので誰がキューを作成するのか(mq_open時のO_CREATの指定)は考えておく必要がある

送信側が待たされない

例えば名前付きパイプ(FIOE)は受信側のプロセスが送信側のwriteするまで待たされます。
本当に同期的に動作するプログラムなら名前付きパイプで問題ないかもしれませんがそのような動作を期待するケースというはあまりないように思います。
メッセージキューであれば送信側はブロックされないので非同期的に何かを実行したい場合は便利です。

送受信ともタイムアウトが指定できる

受信を行う関数には、 mq_receive, mq_timedreceive の2関数がありますが、 mq_timedreceive 関数では timespec 構造体によるタイムアウト時間の指定ができます。
特に受信側でタイムアウトが指定できるのはありがたいですね。

例えば、常駐プロセスで

  • 要求があれば要求に従って処理をする。
  • 要求がなければ定期的に何か別のことをする。

みたいなプロセスを作りたい場合に受信待ちをタイムアウトさせて実装することができます。*2

socketより気軽に使える

双方向のIPCはsocketでもできますが上記の通り、優先度の概念とタイムアウトの機能があるのでユーザーが実装するコードは減ります。
タイムアウトのために recv を select するのだるくないですか...*3

デメリット

パフォーマンスの比較は他のIPCと試していないので何とも言えません。

C言語以外での実装があまりされていない

POSIXメッセージキューのAPIRubyPython,PHPでは実装されていません。使いたくなったら自分で実装する必要があります。
Webシステムでアプリケーションサーバとなるプロセスとの連携とかには少し不便かもしれません。
*4

いろいろと見てるとGo言語だと実装されていました。*5
godoc.org

双方向性がない

メッセージキューはただのキューなので双方向性はありません。
要求の結果を受け取る場合などは送信した側でも受信を行うようなキューを用意することで解決する必要があります。

サンプルコード

送信するプログラムと受信するプログラムの例を挙げておきます。
サンプルでは、受信側でキューの作成を行っているので受信側プログラム(mq_recv_sample.c)を先に起動する必要があります。
*6

Linuxでの実装

Linuxでは、ipc/mqueue.c, ipc/mqueue.h で実装されています。
Linuxカーネルの機能として実装されているので、glibcではmq_xxx関数は未サポートのシステムコールエラーを返すような実装になっていますね。

ちらっと実装を見てみましたが、受信側プロセスが待ち状態にある場合はエンキューせずに直接メッセージを渡すという工夫(pipelined_send)がされていました。
同時に1メッセージしか受け取らない場合はパフォーマンスがよいのかもしれません。

まとめ

ということで、POSIXメッセージキューはプロセス間で相手方にシーケンシャルな要求を行うのに便利な機能です。

追記

POSIXメッセージキューですがキュー作成するときにumaskによって権限が制約され、他のユーザーから上手くアクセスできない場合があります。mq_openする前に umask(0)することで回避できます。※キュー作成後はマスク値はもとに戻しましょう。

*1:優先度を作りこむのは多少なり手間はかかる

*2:itronのrcv_mbxでもこの手のことはよくやりますね

*3:私はだるい。どうしても select するなら5000兆円欲しい...

*4:PHPではなぜかSystemV版のメッセージキューが実装されています

*5:さすがGo言語、システムコールのサポートが充実してますね

*6:キューが既に作成されている状態であれば順番を意識する必要はありません

RV32C ~圧縮命令ってなんだ?~

モナリザ本の第7章を読んでみました。

RISC-V原典  オープンアーキテクチャのススメ

RISC-V原典 オープンアーキテクチャのススメ

前回の記事でRISC-Vのアセンブラについて調べていて気になっていた圧縮命令について書かれた章です。章といいつつこの章は実は参考文献のリストなどをあわせても7ページしかありません。

RISCのデメリット

RISC系のCPUは命令セットがシンプルな分アセンブラのコード量が多くなりがちです。シンプルな命令の組み合わせつつ、パイプラインを有効活用する事で命令あたりのクロックサイクルを少なくすることがRISCの戦略ということになりますが、命令を組み合わせる分、使用する命令数(サイズ)はCISC系と比べて多くなります。*1

使用する命令数(サイズ)が多くなるということは、テキストセクションのサイズが大きくなるということで、これは組込ソフトにおいてプログラム使用するROMのサイズが大きくなります。

マイコンの値段は内蔵ROMのサイズで変わってきますので、プログラム領域もデータ領域も(もちろん使用するメモリサイズも)小さいに越したことはありません。

前置きが長くなりましたが、ようするにRISCではプログラムサイズが多くなるというのがデメリットとして存在するわけですが、この問題に対するRISC-Vとしての解答がRV32Cの圧縮命令ということのようです。

圧縮命令の特徴

命令長が短い版の命令セットがあるという点ではARMのThumb命令も同じですのであまり珍しく感じませんでした。

RISC-Vの圧縮命令の特徴は何かあるのでしょうか。
7章を読んでみて分かったのですが、この圧縮命令はアセンブラとリンカだけが意識し、コンパイラ開発者やアセンブリ言語プログラマは圧縮命令を意識する必要がないそうです。

お前は何を言っているんだ?


という気持ちになったのですが、要するに1つの命令のニーモニックは通常のRV32IでもRV32C(圧縮命令)でも共通になるということです。

気になったので改めて確認してみました。

実験

前回の記事、
mcommit.hatenadiary.com

でも少し触れましたが、同じli(load immediate)命令であってもエンコードのされ方に違いがあります。
asコマンドでアセンブリした場合、


$ cat tmp.s
li a0,0
$ riscv64-unknown-elf-as tmp.s # a.out が生成される
$ riscv64-unknown-elf-objdump.exe -S a.out
a.out: file format elf64-littleriscv

Disassembly of section .text:

0000000000000000 <.text>:
0: 00000513 li a0,0

00000513という32bitエンコードされています。

では、C言語で同様のアセンブリを期待したコードを書いて試してみます。


$ cat tmp.c
int return_zero(void)
{
return 0;
}

$ riscv64-unknown-elf-gcc tmp.c -c -O1
$ riscv64-unknown-elf-objdump.exe -S tmp.o

tmp.o: file format elf64-littleriscv


Disassembly of section .text:

0000000000000000 :
0: 4501 li a0,0
2: 8082 ret

C言語コンパイラを通すとなんと16bitエンコードされています。
*2

コンパイラ開発者・アセンブリプログラマは圧縮命令を意識しなくてよい

というのはつまりこういうことなんだと思います。
ここでは同じ li a0, 0という命令が16bitにも32bitにもエンコードされました。

これはありがたい反面、コンパイラ(アセンブラ)の実装によっては思わぬトラブルを生み出しそうな気もします。
組込ソフト開発の場合、書いたコードがどういったバイナリになっているかをシビアに意識しないといけないケースもありますので出来れば明示的にコントロールしたいところです。

一般的にはこういった場合コンパイラに対するオプションとかで指定できたりすると思うのですが、gccの場合どう対応しているのでしょうか。まだそのあたりは調べれていませんが気になります。

*1:あと速度最適化の選択肢が少なくなると思います

*2:コンパイル時に-O1をつけましたがこれは最適化を付けないと冗長なアセンブラが吐かれるためです。-O1なしでも16bitでエンコードされます

RISC-Vのアセンブラについて調べてみた

昨日はFreedom Studioでデバッグする方法について書きましたが今日はRICV-Vに慣れるためにレジスタアセンブラについて少し調べてみました。
mcommit.hatenadiary.com

概要

HiFive1 Rev.Bで使用されているCPUはFE310-G002。命令セットはRV32IMACというISAらしい。
名前を知ったところで現時点では何も分からない。

よく出てくる命令

とりあえずCのコードの逆アセを眺めて出てきた命令を列挙してみました。

命令 意味(英語) 処理
jal Jump and Link 関数呼び出し
add Add 加算(レジスタ)
addi Add Immediate 加算(直値)
sw store word メモリへのストア(word)
sd store double メモリへのストア(double)
li load Immediate レジスタへのロード

jal

X86のCALL、ARMのBL(Branch with Link)命令相当。
RISC-Vでは$raがARMのリンクレジスタに相当するらしい。

add, addi

レジスタを使った加算はadd。直値を使う場合はaddiになる。
レジスタか直値で命令が違うのはあまりみたことがなかった。
デコードするときに扱える値の範囲が変わるというメリットはあるかもしれない。

sw,sd

オフセット(レジスタ名)
例). 8($sp)
のような表記になります。一度覚えてしまうとわかりやすい表現方法のような気がします。

gcc の冗長さ

最適化をかけないと関数の入り口と出口で、使おうが使うまいが愚直にプロローグコードとエピローグコード(ローカル変数用領域の確保と解放)を出力しています。
また reutrn 0 や return 1 のような直値を返す関数であっても一度a5レジスタを介してa0に書き込みをしています...
MIPS流のお作法なのかもしれないがさすがに冗長ですよね...

なお、この状況なコードは -O1 の最適化をつけると一瞬にして素直なコードになりました。


直値のエンコードの謎

Cで0を返すコード(return 0;)をコンパイルしてみました。
0を返す機械語が4501になっていたので不思議に思い0~5までの直値を返す関数のアセンブラを見てみました。

int return_zero(void)
{
    return 0;
}

int return_one(void)
{
    return 1;
}

int return_two(void)
{
    return 2;
}

int return_three(void)
{
    return 3;
}

int return_four(void)
{
    return 4;
}

int return_five(void)
{
    return 5;
}

結果、

0000000000000002 <return_zero>:
   2:	4501                	li	a0,0
   4:	8082                	ret

0000000000000006 <return_one>:
   6:	4505                	li	a0,1
   8:	8082                	ret

000000000000000a <return_two>:
   a:	4509                	li	a0,2
   c:	8082                	ret

000000000000000e <return_three>:
   e:	450d                	li	a0,3
  10:	8082                	ret

0000000000000012 <return_four>:
  12:	4511                	li	a0,4
  14:	8082                	ret

0000000000000016 <return_five>:
  16:	4515                	li	a0,5
  18:	8082                	ret

0→4501を開始として、1:4505 2:4509 3:450d ... と直値は4ずつ増えてエンコードされているようです。
どこまでつづくのかは分かりませんが、ぱっと見た限りでは、4 * n + 1 がエンコードされた値になります。

ちなみに、関数の戻り値は a0 レジスタで受け渡しするようです。

圧縮命令ってなんだ?


li a0, 1

を tmp.sというファイルに保存して、riscv64-unknown-elf-asでアセンブルしてみました。objdumpで見てみると、上記のC言語からコンパイルしたコードとは違うコードが生成されました。
上記のC言語からコンパイルしたアセンブラでは16bitでエンコードされています。
ところがアセンブラエンコードされたコードは同じニーモニックにも関わらず 4byteでエンコードされています。

調べてみるとRISC-Vには圧縮命令という命令セットがあり、短い命令長でエンコードされるようです。雰囲気的にはARMのThumb命令と似たような感じでしょうか...
ただし、ARMの場合は命令のアラインメント位置でThumb命令かどうかの判別ができますがRISC-Vの場合ぱっと見では32bit命令なのか圧縮命令なのか分かりませんね。

つまり、上記の

0000000000000002 <return_zero>:
   2:	4501                	li	a0,0
   4:	8082                	ret

のような2byteでエンコードされた命令は圧縮命令になります。

見比べてみた限りでは、li命令は 32bit命令では 0513 が下位ワードに来るようですので1ワードの特定のビットでスムーズにデコードできるようになっているんだと思います。
圧縮命令は4501や4505なので14bit目のようにも見えます。この辺は真面目に命令セットの仕様を見てみないとよくわかりません。

直値が4ずつ増えるのも圧縮命令と関係がありそうな気もします。

とりあえず手元のモナリザ本を開いてみると7章にRV32C:圧縮命令の章がありました。

RISC-V原典  オープンアーキテクチャのススメ

RISC-V原典 オープンアーキテクチャのススメ

今日は疲れたので、明日はこの章と第2章のRV32I:RISC-V基本整数ISAの章を読んでみたいと思います。

HiFive1 RevB を買った

SiFive社から販売されているHiFive1 RevBのボードを買いました。

f:id:simotin13:20190527001529j:plain
Hifive1 Rev.B

目次

  • 目次
  • 注文してから届くまで
    • 郵送について
  • 開発の始め方
  • デバッグ
    • フォーラムについて
  • 感想

注文してから届くまで

GW前になんとなく欲しくなってポチっていました。元々HiFive1の存在は知っていましたが単純にArduino互換ボードで開発の自由度が高そうではなかったのであまり興味がもてませんでした。
今回購入したHiFive1 Rev.Bはオンボードでデバッガが搭載されているのでRISC-Vアーキテクチャの勉強に使えそうです。

CrowdSupply order detail
CrowdSupplyの注文明細

注文履歴を見ると4/25に注文しています。届いたのは5/22なのでほぼ1か月かかっています。
といっても入荷日が5/9だったようなので実質的には2週間もあれば届くようですね。

郵送について

商品はUSPSで発送されます。
発送後はネットで最新の情報が見られるので、自分が注文した商品がいまどこにあるのかワクワクしながら届くのを待つことができます。
履歴を見ているとアメリカのオレゴン州ポートランドを出発して、成田に到着するまで約3日程度でした。*1
ここまではかなりスムーズに配達されているように感じたのですが、関税の関係のためか成田についてから何日も待たされます。

開発の始め方

ドキュメント類の入手

とりあえずマニュアルやら回路図やらを公式サイトからダウンロードします。
www.sifive.com

Getting Started Guide が提供されているのでまずはこれを読むのがよさそうです。
https://sifive.cdn.prismic.io/sifive%2F8d7b8385-64e3-4914-8608-8568412c8aae_hifive1b-getting-started-guide.pdf

この Getting Started Guide は開発環境や基板の概要について説明してくれています。全部で26ページとボリュームはないので一読しましょう。

Freedom E310-G002 Manualはチップのハードウェアマニュアルですね。開発環境の構築が終わったらじっくり読んでいきたいですね。*2
https://sifive.cdn.prismic.io/sifive%2F9ecbb623-7c7f-4acc-966f-9bb10ecdb62e_fe310-g002.pdf

*1:なぜか途中で逆方向のダラスに到着しているのを見たときは少し不安になりました

*2:116PしかないのでこちらもH/Wマニュアルにしては簡潔な気がしますが

続きを読む

MySQLサーバに外部から接続できないとき

自分用メモ。

MySQLサーバにホスト外から接続できず調べるのに時間がかかりましたのでメモを残しておきます。
ただし、これは開発環境として利用する場合の設定ですので、本番の環境としてはこのような設定はしないでください

ユーザー権限の設定

例えばrootユーザでどこからでも接続されたい場合は


grant all privileges on *.* to root@"%" identified by 'rooのpassword' with grant option;

mysqlにログインして実行します。

bind-addressの設定

さて、ユーザ設定をしたのはいいのですが外部から接続できません。
色々調べた結果、/etc/mysql/mysql.conf.d/mysqld.cnf に*1


bind-address = 127.0.0.1
の記載を変更してあげる必要がありました。

bind-address = 0.0.0.0

う~ん...bindアドレスも設定ファイルで制御できるようになっているんですね。
知りませんでした。


詳解MySQL 5.7 止まらぬ進化に乗り遅れないためのテクニカルガイド (NEXT ONE)

詳解MySQL 5.7 止まらぬ進化に乗り遅れないためのテクニカルガイド (NEXT ONE)

*1:/etc/mysql/mysql.conf.d/mysqld.cnfは私の使っている環境Linux Mint(Ubuntu系での例です)