目次: C言語とlibc
前回の続きです。musl libc 1.2.4のpthread_barrier_wait()の実装と動作の概要です。バリアに到達したスレッドがNスレッド(N >= 2)あるとすると、大きく分けて3つの役割に分かれます。役割名は私が適当に付けました。正式な名前はあるのかな?
いっぺんに説明すると意味不明なので、順番に説明します。今回はLastとOthersです。
自分がLastかどうか判定する条件はバリア変数の_b_instがNULL以外であり、インスタンスのカウントがバリア同期するスレッド数と一致(= 最後に到達した1スレッド)することです。Lastのやることはバリア変数(pthread_barrier_t)に設定されたインスタンスを消すことと、バリア同期を待っているOthersを起こすことです。
Lastがこの時点でb->_b_instをNULLにしていること、ローカル変数instを使っていてb->_b_instを使わないことには理由があって、次のバリア処理とのオーバーラップと関係しています。同時に説明するとややこしいので、次回ご説明しようと思います。
OwnerとLast以外は全部Othersスレッドとなります。OthersのやることはLastが来るまで待つことです。
LastとOthersの到達側に関連するコードは下記のとおりです。
// musl/src/thread/pthread_barrier_wait.c
int pthread_barrier_wait(pthread_barrier_t *b)
{
int limit = b->_b_limit;
struct instance *inst;
/* Trivial case: count was set at 1 */
if (!limit) return PTHREAD_BARRIER_SERIAL_THREAD;
/* Process-shared barriers require a separate, inefficient wait */
if (limit < 0) return pshared_barrier_wait(b);
/* Otherwise we need a lock on the barrier object */
while (a_swap(&b->_b_lock, 1))
__wait(&b->_b_lock, &b->_b_waiters, 1, 1);
inst = b->_b_inst;
/* First thread to enter the barrier becomes the "instance owner" */
if (!inst) {
//★★Ownerのスレッドの処理は省略(その1をご覧ください)★★
}
/* Last thread to enter the barrier wakes all non-instance-owners */
if (++inst->count == limit) {
//★★Lastのスレッドはこちら★★
b->_b_inst = 0;
a_store(&b->_b_lock, 0);
if (b->_b_waiters) __wake(&b->_b_lock, 1, 1);
a_store(&inst->last, 1);
if (inst->waiters)
__wake(&inst->last, -1, 1);
} else {
//★★Othersのスレッドはこちら★★
a_store(&b->_b_lock, 0);
if (b->_b_waiters) __wake(&b->_b_lock, 1, 1);
__wait(&inst->last, &inst->waiters, 0, 1);
}
細かい部分は省いてシーケンスの例を1つだけ示せば下記のようになります。
pthread_barrier_wait()のLastとOthersの到達側シーケンスの一例
見たままなので解説することがないですね。全員でインスタンスのカウントを+1して、最後のスレッドLastだけが特殊な処理を行います。
脱出側のやることは単純ですが役割分担が少しややこしいです。到達側と同様にLastとOthersに役割が分かれますが、到達側のLast = 脱出側のLastとは限らないからです。
脱出時のLastになる条件はインスタンスのカウント値が1であることです。到達時にLastであったかOthersであったかは無関係です。脱出時のLastのやることは、インスタンスのfinishedを+1して待機中のOwnerスレッドを再開させることです。
脱出時のOthersになる条件はLastではない、インスタンスのカウント値が1以外であることです。
LastとOthersの脱出側に関連するコードは下記のとおりです。
// musl/src/thread/pthread_barrier_wait.c
int pthread_barrier_wait(pthread_barrier_t *b)
{
int limit = b->_b_limit;
struct instance *inst;
//★★略★★
/* Last thread to exit the barrier wakes the instance owner */
if (a_fetch_add(&inst->count,-1)==1 && a_fetch_add(&inst->finished,1))
__wake(&inst->finished, 1, 1);
return 0;
}
細かい部分は省いてシーケンスの例を1つだけ示せば下記のようになります。
pthread_barrier_wait()のLastとOthersの脱出側シーケンスの一例
到達側のLastスレッドの処理では待機していたOthersスレッド達を全員再開させ、Lastも処理を再開します。するとLast + Othersスレッド全てがいっぺんに脱出側の処理を開始します。先ほど説明したとおり、どのスレッドが脱出側のLastになるかは運次第です。
実装の特徴はアトミックアクセスですかね。a_fetch_add(x, -1)はポインタxの指す先をアトミックに-1して、返り値でxの以前の値を返す関数です……といわれてもわかりにくいですよね。4スレッド(Owner, Others1, Others2, Last)の場合を書きましょうか。Ownerスレッドはカウント値を+1しないので、脱出処理開始時のカウント値は4 - 1 = 3です。
スレッド | -1したあとのカウント値 | a_fetch_add()の返り値 |
---|---|---|
Others 1 | 2 | 3 |
Others 2 | 1 | 2 |
Last | 0 | 1 |
ちなみにアトミックアクセス以外の方法(if文とカウント値の変更など)では正常に動作しません。判定と値変更の間に他のスレッドが処理を行う可能性があるからです。
続きはまた今度。
目次: C言語とlibc
参考: 図を書くために使った PlantUMLのコードです。
前回の続きです。musl libc 1.2.4のpthread_barrier_wait()の実装と動作の概要です。バリアに到達したスレッドがNスレッド(N >= 2)あるとすると、大きく分けて3つの役割に分かれます。役割名は私が適当に付けました。正式な名前はあるのかな?
今回はインスタンスがなぜローカル変数として確保されるかを紹介します。1回目と2回目のバリアが重なるところがポイントです。
各スレッドはOwnerかLastかOthersになりますが、役割は毎回のバリア同期処理ごとに変わります。例えばバリアとバリアの間の処理時間が同じだとすると、あるバリアのOwnerは次のバリアではLastになる可能性が高いです。Ownerはバリアから最後に脱出しますので、その間に他のスレッドの処理が進んで次のバリア同期処理のOwnerになるからです。
3スレッドあって下記のように役割が変化する場合を考えてみます。
更に考えてみるとバリアとバリアの間の処理時間が非常に短い場合、1回目のバリアのOwner(スレッド1、Owner1, Last2)がバリアを脱出する前に、違うスレッドが2回目のバリアのOwner(スレッド2、Others1, Owner2)となってバリアに到達している可能性があります。シーケンス例は下記のようになります。
もしバリアのインスタンスをグローバル変数などに確保した場合、1回目のバリアのOwner(スレッド1、Owner1, Last2)によるインスタンスの破棄と、2回目のバリアのOwner(スレッド2、Others1, Owner2)のインスタンスの初期化がぶつかって、お互いに内容を壊しあうため正常に動作しません。
この問題を解決にはインスタンスをOwnerスレッド固有の領域に置くと良いです。1回目のバリアのOwnerスレッドと、2回目のバリアのOwnerスレッドがそれぞれ別の領域に置けば、お互いに壊し合うことがなくなります。
スレッドごとの固有の領域として使えるのは、
TLS(Thread Local Storage) ヒープから確保したメモリ(mallocなど) ローカル変数があります。インスタンスを各Ownerスレッド固有の領域においたとき、2回分のバリア動機処理のシーケンス例は下記のようになります。
バリアのインスタンスは複数スレッド間で共有する領域です。ローカル変数を複数スレッド間で共有すると、ローカル変数が破棄されたときに問題が発生するので、ヒープを使うことが多いでしょう。
しかし前回説明したようにバリア同期処理の場合はローカル変数の寿命をうまく制御できるため、複数スレッド間で共有しても問題が起きません。インスタンスをローカル変数に確保すると、ヒープに比較して高速なメモリ割当が可能です。
目次: GCC
GCCはlibcの関数呼び出しを別の関数呼び出しに置き換える最適化(名前がわからないので以降「libc関数呼び出し最適化」と呼びます)を行います。例を挙げると、
時には勝手にlibcの関数呼び出しを置き換えてほしくないこともあると思います。libc関数呼び出し最適化を抑えることができるコンパイラオプションfno-builtinを指定したときの挙動についてメモしておきます。
GCCのバージョンは下記のとおりです。
$ gcc --version gcc (Debian 12.2.0-14) 12.2.0 Copyright (C) 2022 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
テストに使うプログラムは下記の通りです。
int printf(const char *a, ...);
void _start(void)
{
printf("\n");
}
特に何も指定しなければ、1文字出力するprintfはputchar呼び出しに置き換えられるはずです。確認のためnostdlibオプションを付けてコンパイルし、リンクエラーを見るとprintfの呼び出しがどの関数に置き換えられたかわかります。
$ gcc -O2 -g -Wall a.c -nostdlib /usr/bin/ld: /tmp/cc1D673J.o: in function `_start': /home/katsuhiro/share/test_gcc_no_builtin/a.c:5: undefined reference to `putchar' collect2: error: ld returned 1 exit status
リンクエラーは「putcharがない」と出ました。printfの呼び出しがputcharの呼び出しに置き換えられたことがわかりますね。次はfno-builtinオプションを指定します。
$ gcc -O2 -g -Wall a.c -nostdlib -fno-builtin /usr/bin/ld: /tmp/cc6JbRD2.o: in function `_start': /home/katsuhiro/share/test_gcc_no_builtin/a.c:5: undefined reference to `printf' collect2: error: ld returned 1 exit status
リンクエラーは「printfがない」と出ました。fno-builtinオプションによってprintfからputcharへの置き換えが抑制されたことがわかります。
動作結果を見る限りfno-builtinオプションでlibc関数呼び出し最適化が抑制されましたが、この挙動がオプションの仕様か?と聞かれると正直良くわかりません。
仕様はGCCのマニュアル(Other Builtins (Using the GNU Compiler Collection (GCC)))に書かれていて、説明によれば残りのビルトイン関数(printfを含む)は最適化のために提供されている("The remaining functions are provided for optimization purposes.")とあります。
私はこのような動作をしているものと推測しています。正しいかどうかは知りません……。
最適化はビルトイン関数に頼る必要はありません。つまりfno-builtinオプションで抑制できないようなlibc関数呼び出し最適化があっても不思議ではありません。今のところそのような例は見つけていませんが、もしあったとすると抑制する方法はあるのでしょうか?うーん??
辞書(家にあるのは新明解国語辞典 第四版)を引いていると、語句の漢字表記に○とか▽のような謎の記号が書いてあります。今までずっと意味がわかっていなかったのですが、三省堂が素敵な解説文をサイトに載せてくれていました。
詳細は解説(第29回 ×とか▽とか、これは何だ? - 【 】見出し(漢字表記)についている○▽× - 三省堂 ことばのコラム)を読んでいただければと思いますが、そのなかに▽は音訓表に載っていない読みのこと、と書いてあります。音訓表とはなんぞ?
検索してみると、文化庁が作成している常用漢字の読み方(文化庁 | 国語施策・日本語教育 | 国語施策情報 | 常用漢字表の音訓索引)のことらしいです。
常用漢字の一覧(文化庁 | 国語施策・日本語教育 | 国語施策情報 | 内閣告示・内閣訓令 | 常用漢字表(平成22年内閣告示第2号))を文化庁が作成しているのは知っていたんですけど、読み方まで一覧にしてくれていたんですね。知らなかったなあ……。
昼頃だったか?Twitterで何かトラブルが起きていたらしくて、初ログインしたときのような画面になっていました。
Twitterの使い始めがどんな画面だったか全く覚えていませんが、たぶんこんな画面なんでしょう。今だと別アカウントを登録しない限り、見る機会がない画面でしょうね。
目次: サンタ
去年(2022年12月24日の日記参照)と同様に、飛行機の位置をリアルタイムに表示するFlightradar24というサービス(サイトへのリンク)にてサンタが出現しました。クリスマス・イブの日にサンタが出現するのは毎年恒例のお約束演出です。私が見たときはインド上空を飛んでいました。
2021年のサンタは表示された対地速度は40ktsなのに、サンタの位置から計算した速度はマッハ2 = 1300ktsと変な状態でしたが、2023年と今年は対地速度の表示は727kts(1346.4km/h, マッハ1.1くらい)です。
00:32:10時点の位置(緯度27.15451度、経度76.90161度)
00:33:10時点の位置(緯度26.26621度、経度76.42773度)
去年同様、ある程度の時間をあけ(今回は1分間)でどれくらい進んだか計算します。今回も距離の計算には国土地理院のページを使いました、緯度経度から距離を一発で計算してくれて便利です(サイトへのリンク)。
2地点間の距離は約109.1km、時間差は60秒から計算すると、対地速度は約6,540km/hです。地上でのマッハ5(ただし、サンタが飛んでいる高さ38,000ftsだとマッハ数はもっと高く出る)です。世界最速の飛行機であるロッキードSR-71ブラックバードでさえマッハ3.3といわれているので、サンタさんバチクソ速いですね。
推測ですが24日、25日で北極から世界一周してまた北極へ行くために、緯度、経度は対地速度に関係なく適当に変えているんじゃないか?という気がしてきました。別の時間に測ったら全然違う速度になってませんかね?面倒なので検証しませんけど……。来年の速度も気になります。覚えていたらまた計算してみましょう。
目次: GCC
最近はRISC-V向けのクロスコンパイラをビルドすることが多かったのですが、昔を思い出してARM向けも作るか〜と気軽にやってみたらハマりました。
最初にトライしたのはarm-unknown-linux-gnueabi(昔の32bit ARM向け、ARM9とか)で、特に問題なくビルドできました。次にトライしたのはaarch64-unknown-linux-gnu(64bit ARM向け、最近のCortexとか)ですが、リンクエラーが発生してcrt1.oやcrti.oが見つからないと言ってきます。
$ aarch64-unknown-linux-gnu-gcc -Wall -O2 -g -static a.c /path/to/aarch64-unknown-linux-gnu/lib/gcc/aarch64-unknown-linux-gnu/12.0.1/../../../../aarch64-unknown-linux-gnu/bin/ld: cannot find crt1.o: No such file or directory /path/to/aarch64-unknown-linux-gnu/lib/gcc/aarch64-unknown-linux-gnu/12.0.1/../../../../aarch64-unknown-linux-gnu/bin/ld: cannot find crti.o: No such file or directory /path/to/aarch64-unknown-linux-gnu/lib/gcc/aarch64-unknown-linux-gnu/12.0.1/../../../../aarch64-unknown-linux-gnu/bin/ld: cannot find -lc: No such file or directory collect2: error: ld returned 1 exit status
最初はglibcのビルドに失敗したのかと思いましたが、crt1.oはsysroot/usr/lib64ディレクトリの下に存在していました。
$ find -name crt1.o ./aarch64-unknown-linux-gnu/sysroot/usr/lib64/crt1.o
ファイルが存在しているのに見つからないのは、GCCが自動的に付与する-Lオプションが間違っている可能性大です。-vオプションでGCCのオプションを調べます。
--sysroot=/path/to/aarch64-unknown-linux-gnu/aarch64-unknown-linux-gnu/sysroot -Bstatic -X -EL -maarch64linux crt1.o crti.o /path/to/aarch64-unknown-linux-gnu/lib/gcc/aarch64-unknown-linux-gnu/12.0.1/crtbeginT.o -L/path/to/aarch64-unknown-linux-gnu/lib/gcc/aarch64-unknown-linux-gnu/12.0.1 -L/path/to/aarch64-unknown-linux-gnu/lib/gcc/aarch64-unknown-linux-gnu/12.0.1/../../../../aarch64-unknown-linux-gnu/lib/../lib64 -L/path/to/aarch64-unknown-linux-gnu/aarch64-unknown-linux-gnu/sysroot/lib/../lib64 ★★相対パスが入っている★★ -L/path/to/aarch64-unknown-linux-gnu/lib/gcc/aarch64-unknown-linux-gnu/12.0.1/../../../../aarch64-unknown-linux-gnu/lib -L/path/to/aarch64-unknown-linux-gnu/aarch64-unknown-linux-gnu/sysroot/lib /tmp/ccQ9tEVu.o --start-group -lgcc -lgcc_eh -lc --end-group /path/to/aarch64-unknown-linux-gnu/lib/gcc/aarch64-unknown-linux-gnu/12.0.1/crtend.o crtn.o
困ったことにsysroot/usr/lib64を指定する-Lオプションがありません。なぜでしょう。
手掛かりになりそうなのは、sysroot/lib64ディレクトリの指定の仕方で、lib/../lib64のように一度libを経由しています。実はsysroot/usr/lib64も同じでsysroot/usr/libを経由している可能性があります。
ディレクトリの構造を調べるとsysroot/libは存在するものの、sysroot/usr/libは存在しませんでした。試しに空のsysroot/usr/libディレクトリを作成し、再度コンパイルするとエラーが解消しました。やったー。エラーだった時と何が違うか知るために-vオプションでGCCのオプションを調べます。
--sysroot=/path/to/aarch64-unknown-linux-gnu/aarch64-unknown-linux-gnu/sysroot -Bstatic -X -EL -maarch64linux /path/to/aarch64-unknown-linux-gnu/aarch64-unknown-linux-gnu/sysroot/usr/lib/../lib64/crt1.o /path/to/aarch64-unknown-linux-gnu/aarch64-unknown-linux-gnu/sysroot/usr/lib/../lib64/crti.o /path/to/aarch64-unknown-linux-gnu/lib/gcc/aarch64-unknown-linux-gnu/12.0.1/crtbeginT.o -L/path/to/aarch64-unknown-linux-gnu/lib/gcc/aarch64-unknown-linux-gnu/12.0.1 -L/path/to/aarch64-unknown-linux-gnu/lib/gcc/aarch64-unknown-linux-gnu/12.0.1/../../../../aarch64-unknown-linux-gnu/lib/../lib64 -L/path/to/aarch64-unknown-linux-gnu/aarch64-unknown-linux-gnu/sysroot/lib/../lib64 -L/path/to/aarch64-unknown-linux-gnu/aarch64-unknown-linux-gnu/sysroot/usr/lib/../lib64 ★★やっぱりlib/../lib64になっている★★ -L/path/to/aarch64-unknown-linux-gnu/lib/gcc/aarch64-unknown-linux-gnu/12.0.1/../../../../aarch64-unknown-linux-gnu/lib -L/path/to/aarch64-unknown-linux-gnu/aarch64-unknown-linux-gnu/sysroot/lib -L/path/to/aarch64-unknown-linux-gnu/aarch64-unknown-linux-gnu/sysroot/usr/lib /tmp/ccLe9HUo.o --start-group -lgcc -lgcc_eh -lc --end-group /path/to/aarch64-unknown-linux-gnu/lib/gcc/aarch64-unknown-linux-gnu/12.0.1/crtend.o /path/to/aarch64-unknown-linux-gnu/aarch64-unknown-linux-gnu/sysroot/usr/lib/../lib64/crtn.o
やはりsysroot/usr/lib64を指定するパスにはsysroot/usr/libが含まれていました。原因は「最終のディレクトリに辿り着く途中のパスが存在しないから」と言えそうです。しかしどうしてGCCが勝手に-Lオプションを消すのかは謎のままで、若干スッキリしません。うーん。
目次: Zephyr
Zephyr RTOSのインストール、ビルドの手順が少し変わったので改めて紹介したいと思います。
基本的にはZephyrのGetting Startedに記載の通りですが、実行する上での補足や引っかかるところを説明したいと思います。
以降、作業するディレクトリは~/workとします。
ZephyrのGetting Startedを順に実行します。既存のPython環境を壊さないようにInstall within virtual environmentの手順を使うことをお勧めします。
# apt-get install git cmake ninja-build gperf \ ccache dfu-util device-tree-compiler wget \ python3-dev python3-pip python3-setuptools python3-tk python3-wheel xz-utils file \ make gcc gcc-multilib g++-multilib libsdl2-dev libmagic1 # apt-get install python3-venv $ cd ~/work/zephyr-sdk-0.16.4/ $ python3 -m venv _venv $ source ~/work/zephyr-sdk-0.16.4/_venv/bin/activate
Python venvを有効にするとシェルのプロンプトの頭に(_venv)のような環境名が付くようになります。venv用のディレクトリはどこに作成しても良いです。私はZephyr SDKの下に作ることが多いです。作成済みのvenvを再度使うときはsource ~/work/zephyr-sdk-0.16.4/_venv/bin/activateを実行します。
続きを実行しましょう。westを使ってZephyrと周辺ツール群をダウンロードします。
$ pip install west $ cd ~/work $ west init zephyr === Initializing in /home/katsuhiro/work/zephyr --- Cloning manifest repository from https://github.com/zephyrproject-rtos/zephyr Cloning into '/home/katsuhiro/work/zephyr/.west/manifest-tmp'... remote: Enumerating objects: 968377, done. remote: Counting objects: 100% (17/17), done. remote: Compressing objects: 100% (13/13), done. remote: Total 968377 (delta 8), reused 8 (delta 4), pack-reused 968360 Receiving objects: 100% (968377/968377), 600.07 MiB | 20.80 MiB/s, done. Resolving deltas: 100% (733897/733897), done. Updating files: 100% (31355/31355), done. --- setting manifest.path to zephyr === Initialized. Now run "west update" inside /home/katsuhiro/work/zephyr. $ cd ~/work/zephyr $ west update (大量にリポジトリがクローンされますのでしばし待ちます) $ west zephyr-export Zephyr (/home/katsuhiro/work/zephyr/zephyr/share/zephyr-package/cmake) has been added to the user package registry in: ~/.cmake/packages/Zephyr ZephyrUnittest (/home/katsuhiro/work/zephyr/zephyr/share/zephyrunittest-package/cmake) has been added to the user package registry in: ~/.cmake/packages/ZephyrUnittest $ pip install -r ~/work/zephyr/zephyr/scripts/requirements.txt (大量にモジュールがインストールされますのでしばし待ちます)
基本的にはコマンドを実行して待つだけです。たぶん。
SDKをインストールします。アーカイブはGitHubのreleaseページから取得できます。特にこだわりがなければ、現状の最新版である0.16.4を使ってください。
アーカイブがたくさんあって迷うと思いますが、今回はサイズが小さくてインストールするツールチェーンを選択できるzephyr-sdk-0.16.4_linux-x86_64_minimal.tar.xzを使います。
$ wget https://github.com/zephyrproject-rtos/sdk-ng/releases/download/v0.16.4/zephyr-sdk-0.16.4_linux-x86_64_minimal.tar.xz $ tar xf zephyr-sdk-0.16.4_linux-x86_64_minimal.tar.xz $ cd ~/work/zephyr-sdk-0.16.4/ $ ./setup.sh -c -t riscv64-zephyr-elf Zephyr SDK 0.16.4 Setup Installing 'riscv64-zephyr-elf' toolchain ... toolchain_linux-x86 100%[===================>] 105.07M 24.7MB/s 時間 4.3s All done.
何も引数を付けずにすべてインストールしても良いです(が、結構時間が掛かります)。今回はRISC-V向けのツールチェーンがあれば良いので-t riscv64-zephyr-elfを付けることで時間短縮しました。注意点としては-cオプションを忘れないようにしてください。Zephyrビルド時にエラーが発生します。
次にhosttoolsをインストールします。要らない場合もありますが、今回はQEMUを使って動作確認したいのでインストールしましょう。
$ ./zephyr-sdk-x86_64-hosttools-standalone-0.9.sh Zephyr Yocto Toolchain SDK installer version 0.9 ================================================ Enter target directory for SDK (default: /opt/zephyr-sdk/0.9): /home/katsuhiro/work/zephyr-sdk-0.16.4/host You are about to install the SDK to "/home/katsuhiro/work/zephyr-sdk-0.16.4/host". Proceed [Y/n]? y Extracting SDK..................done Setting it up...done SDK has been successfully set up and is ready to be used. Each time you wish to use the SDK in a new shell session, you need to source the environment setup script e.g. $ . /home/katsuhiro/work/zephyr-sdk-0.16.4/host/environment-setup-x86_64-pokysdk-linux
上記の作業を全て終えた後のディレクトリ構造はこんな感じです。
$ tree -L 2 ~/work /home/katsuhiro/work |-- zephyr | |-- bootloader | |-- modules | |-- tools | `-- zephyr ★Zephyr RTOSのコード |-- zephyr-sdk-0.16.4 | |-- _venv | |-- cmake | |-- host ★hosttools | |-- riscv64-zephyr-elf ★RISC-V用ツールチェーン | |-- sdk_toolchains | |-- sdk_version | |-- setup.sh | `-- zephyr-sdk-x86_64-hosttools-standalone-0.9.sh `-- zephyr-sdk-0.16.4_linux-x86_64_minimal.tar.xz
Zephyr開発環境はセットアップ手順は短くなったとは思うんですけど、中身は複雑になってトラブルが起きたときの解決が困難になった印象です。昔より依存するツールが増えているのかな……?
使用したいツールに応じて環境を有効化すると良いです。
#### Python venv環境を有効化 $ source ~/work/zephyr-sdk-0.16.4/_venv/bin/activate #### QEMUなどhosttoolsを使う場合、hosttoolsを有効化 $ source ~/work/zephyr-sdk-0.16.4/host/environment-setup-x86_64-pokysdk-linux #### デバッガなどを直接実行するならばツールチェーンにパスを通す $ export PATH=~/work/zephyr-sdk-0.16.4/riscv64-zephyr-elf/bin:$PATH
私はいちいち選ぶのが面倒なので、全部有効にしております。
Zephyr開発環境が正常にセットアップできたかどうか確かめるため、QEMU向けにビルドしましょう。
$ cd ~/work/zephyr/zephyr $ west build -p always -b qemu_riscv64 samples/hello_world/ (ログは省略)
ビルドできたので実行したいところですが、なぜかwestを使ってQEMUで実行する方法が見当たりません。仕方ないのでcmakeでビルド&実行します。こちらのやり方も覚えておいて損はないでしょう……。
$ cd ~/work/zephyr/zephyr $ rm -r build $ cmake -B build -G Ninja -DBOARD=qemu_riscv64 samples/hello_world Loading Zephyr default modules (Zephyr repository). -- Application: /home/katsuhiro/work/zephyr/zephyr/samples/hello_world -- CMake version: 3.22.1 -- Found Python3: /home/katsuhiro/work/zephyr-sdk-0.16.4/_venv/bin/python (found suitable version "3.10.12", minimum required is "3.8") found components: Interpreter -- Cache files will be written to: /home/katsuhiro/.cache/zephyr -- Zephyr version: 3.5.99 (/home/katsuhiro/work/zephyr/zephyr) -- Found west (found suitable version "1.2.0", minimum required is "0.14.0") -- Board: qemu_riscv64 -- ZEPHYR_TOOLCHAIN_VARIANT not set, trying to locate Zephyr SDK -- Found host-tools: zephyr 0.16.4 (/home/katsuhiro/work/zephyr-sdk-0.16.4) -- Found toolchain: zephyr 0.16.4 (/home/katsuhiro/work/zephyr-sdk-0.16.4) -- Found Dtc: /home/katsuhiro/work/zephyr-sdk-0.16.4/host/sysroots/x86_64-pokysdk-linux/usr/bin/dtc (found suitable version "1.6.0", minimum required is "1.4.6") -- Found BOARD.dts: /home/katsuhiro/work/zephyr/zephyr/boards/riscv/qemu_riscv64/qemu_riscv64.dts -- Generated zephyr.dts: /home/katsuhiro/work/zephyr/zephyr/build/zephyr/zephyr.dts -- Generated devicetree_generated.h: /home/katsuhiro/work/zephyr/zephyr/build/zephyr/include/generated/devicetree_generated.h -- Including generated dts.cmake file: /home/katsuhiro/work/zephyr/zephyr/build/zephyr/dts.cmake Parsing /home/katsuhiro/work/zephyr/zephyr/Kconfig Loaded configuration '/home/katsuhiro/work/zephyr/zephyr/boards/riscv/qemu_riscv64/qemu_riscv64_defconfig' Merged configuration '/home/katsuhiro/work/zephyr/zephyr/samples/hello_world/prj.conf' Configuration saved to '/home/katsuhiro/work/zephyr/zephyr/build/zephyr/.config' Kconfig header saved to '/home/katsuhiro/work/zephyr/zephyr/build/zephyr/include/generated/autoconf.h' -- Found GnuLd: /home/katsuhiro/work/zephyr-sdk-0.16.4/riscv64-zephyr-elf/bin/../lib/gcc/riscv64-zephyr-elf/12.2.0/../../../../riscv64-zephyr-elf/bin/ld.bfd (found version "2.38") -- The C compiler identification is GNU 12.2.0 -- The CXX compiler identification is GNU 12.2.0 -- The ASM compiler identification is GNU -- Found assembler: /home/katsuhiro/work/zephyr-sdk-0.16.4/riscv64-zephyr-elf/bin/riscv64-zephyr-elf-gcc -- Using ccache: /usr/bin/ccache -- Configuring done -- Generating done -- Build files have been written to: /home/katsuhiro/work/zephyr/zephyr/build $ ninja -C build ninja: Entering directory `build' [1/99] Preparing syscall dependency handling [2/99] Generating include/generated/version.h -- Zephyr version: 3.5.99 (/home/katsuhiro/work/zephyr/zephyr), build: zephyr-v3.5.0-3603-g603c3af895b0 [98/99] Linking C executable zephyr/zephyr.elf Memory region Used Size Region Size %age Used RAM: 36140 B 256 MB 0.01% IDT_LIST: 0 GB 2 KB 0.00% Generating files from /home/katsuhiro/work/zephyr/zephyr/build/zephyr/zephyr.elf for board: qemu_riscv64 [99/99] cd /home/katsuhiro/work/zephyr.../zephyr/zephyr/build/zephyr/zephyr.elf $ ninja -C build run ninja: Entering directory `build' [0/1] To exit from QEMU enter: 'CTRL+a, x'[QEMU] CPU: riscv64 *** Booting Zephyr OS build zephyr-v3.5.0-3603-g603c3af895b0 *** Hello World! qemu_riscv64
実行できました。もしエラーが出る場合は次のトラブルシューティングもご参照ください。
Zephyr SDKセットアップ時に-cオプションを付けない = Zephyr SDK cmake packageのインストールを忘れていると、Zephyrのビルド時に長々とエラーが出て怒られます。
CMake Error at /home/katsuhiro/work/zephyr/zephyr/cmake/modules/FindZephyr-sdk.c make:109 (find_package): Could not find a package configuration file provided by "Zephyr-sdk" (requested version 0.16) with any of the following names: Zephyr-sdkConfig.cmake zephyr-sdk-config.cmake Add the installation prefix of "Zephyr-sdk" to CMAKE_PREFIX_PATH or set "Zephyr-sdk_DIR" to a directory containing one of the above files. If "Zephyr-sdk" provides a separate development package or SDK, be sure it has been installed. Call Stack (most recent call first): /home/katsuhiro/work/zephyr/zephyr/cmake/modules/FindHostTools.cmake:53 (find_package) /home/katsuhiro/work/zephyr/zephyr/cmake/modules/dts.cmake:9 (find_package) /home/katsuhiro/work/zephyr/zephyr/cmake/modules/zephyr_default.cmake:129 (include) /home/katsuhiro/work/zephyr/zephyr/share/zephyr-package/cmake/ZephyrConfig.cmake:66 (include) /home/katsuhiro/work/zephyr/zephyr/share/zephyr-package/cmake/ZephyrConfig.cmake:92 (include_boilerplate) CMakeLists.txt:5 (find_package)
Zephyr SDKのセットアップは2回実行しても問題ないので-cオプションを付けてやり直しましょう。
今年は奥さんの実家である鳥取に帰省しました。
夫婦お互いの実家が近ければそれぞれ3日ずつ滞在などできますが、我が家の場合は結構遠い(奥さんは鳥取、私は北海道)のです……。仕方ないので帰省のルールとして、
というルールで運用していました。なので鳥取での年越しは2年に1回となりますが、近年はCOVID-19騒ぎで帰れなかったり、自分達もCOVIDに感染したりでルール運用が乱れ、鳥取での年越しのチャンスがありませんでした。
たぶん2019年末以来じゃないかな?つまり4年ぶり?
目次: Linux
Visual Studio Code(以降VSCode)のSSH Remote Extensionを使って、LinuxマシンのGDBを起動、OpenOCDに接続、RISC-Vボードをデバッグする方法をメモしておきます。
以前紹介した方法(2021年3月3日の日記参照)と似ていますがWindows用のGDBバイナリをビルドする必要がないという利点があります。
今回はSSH Remote Extensionのインストールと設定です。
最初にExtensionsからSSH Remoteをインストールします。
REMOTE EXPLORER(矢印1)からNew Remoteを選択して(矢印2)GDBがインストールされているLinuxマシンを指定します(矢印3)。指定方法は例にある通りusername@hostnameです。
SSHの接続先設定を保存するファイル名を指定します。私は特にこだわりがないので、デフォルトのC:\Users\(ユーザー名)\.ssh\configを使いました。
SSHの設定を追加したら、REMOTES (TUNNELS/SSH)のRefreshを押します(矢印1)。追加した設定が表示されたらConnect in Current Windowを押します(矢印2)。
接続先のマシンはLinuxか?Windowsか?macOSか?と聞かれます。今回はLinuxを使っているのでLinuxを選択します。
接続しようとしているホストのfingerprintが表示され、本当に接続するか?と質問されます。初めて接続するホストの場合は必ず聞かれます。continueを押してください。
もしこの質問が2回目以降の接続で表示される場合は、異なるホストが同じIPアドレスを使用しているなど何らかの異常が発生している可能性があります。迂闊にcontinueを押さず、本当に接続して良いか確認してください。
パスワードを入力してください。
接続に成功するとWelcome画面が表示されるはずなので、Open Folder(矢印1)を押して、デバッグしたいソースコードがあるディレクトリのパスを指定(矢印2)します。
続きはまた今度。
< | 2023 | > | ||||
<< | < | 12 | > | >> | ||
日 | 月 | 火 | 水 | 木 | 金 | 土 |
- | - | - | - | - | 1 | 2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 | - | - | - | - | - | - |
合計:
本日: