link もっと前
   2020年 2月 28日 -
      2020年 2月 19日  
link もっと後

link 未来から過去へ表示(*)
link 過去から未来へ表示

日々

link permalink

link 編集する

Zephyr と RISC-V とマルチプロセッサ

昨日(2020年 2月 20日の日記参照)の続きです。

シリアルのみですが、Zephyr を QEMU RISC-V 32 の virt モードに移植しました。やっと Zephyr が 4 CPU で動くぞ、などと思っていましたが、全然ダメでした。2つ目以降の CPU は全く動きませんでした。

CPU 0 は動作するが、他の CPU は loop_slave_core から動かない
$ riscv64-zephyr-elf-gdb zephyr/build/zephyr/zephyr.elf

(gdb) info threads
  Id   Target Id                    Frame
* 1    Thread 1.1 (CPU#0 [halted ]) 0x80000f70 in arch_cpu_idle ()
    at zephyr/soc/riscv/riscv-privilege/common/idle.c:21
  2    Thread 1.2 (CPU#1 [halted ]) loop_slave_core ()
    at zephyr/arch/riscv/core/reset.S:45
  3    Thread 1.3 (CPU#2 [halted ]) loop_slave_core ()
    at zephyr/arch/riscv/core/reset.S:45
  4    Thread 1.4 (CPU#3 [halted ]) loop_slave_core ()
    at zephyr/arch/riscv/core/reset.S:45

理由は簡単で、Zephyr の RISC-V 版は SMP に対応していないからです。何を言ってるんだ?って?ええ、実は私も今この瞬間まで知りませんでした。ビックリしました。

リセット付近のソースコードをみると、

Zephyr のリセットベクタ

// zephyr/arch/riscv/core/reset.S

/*
 * Remainder of asm-land initialization code before we can jump into
 * the C domain
 */
SECTION_FUNC(TEXT, __initialize)
        /*
         * This will boot master core, just halt other cores.
         * Note: need to be updated for complete SMP support
         */
        csrr a0, mhartid
        beqz a0, boot_master_core

loop_slave_core:
        wfi
        j loop_slave_core

以上のように、はっきり書いてあります。SMP は一番大事な機能だと思っていただけに、未対応とは思わなんだ。

あー、今日の 16550 との戦いは一体何だったんだろう。しょんぼりですわ……。

メモ: 技術系の話は Facebook から転記しておくことにした。大幅に加筆した。

[編集者: すずき]
[更新: 2020年 2月 22日 03:01]

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



link permalink

link 編集する

Zephyr の 16550 シリアルドライバ

先日は Zepphyr を QEMU の Spike モードに移植しましたが、Spike モードだと CPU 数は 1 以外選べないので、マルチプロセッサにできません。これは知りませんでした。最初に調べておけば良かったですね……。

マルチプロセッサで Zephyr を動かしてみたかったので、QEMU RISC-V 32 の virt モード(qemu-system-riscv32 の machine を virt にすること)に、前回同様にシリアルとメモリだけ動くような SoC とボードの定義ファイルを作りました。

QEMU virt モードでは、シリアルのハードウェアとして 16550 をエミュレーションします。Zephyr 側には既に 16550 用のドライバがあるので、わざわざ実装する必要もありません。とても楽です、素晴らしいです、とか何とか思いつつ動かしてみると、シリアルドライバがクラッシュします。んあー。

シリアルドライバが動かない理由

Zephyr を GDB で追うと 16550 の LCR レジスタを読もうとして、ロードアクセスフォルトが起きていました。アドレスのオフセットを見ると、0xc になっています。正しいオフセットは 3 のはずです。

シリアルドライバの実装を見ると、オフセットの計算が 0x3 x 4 = 0xc となっていて、おそらくレジスタサイズが 4バイト単位だと思ってアクセスしているのでしょう。

世の中にはレジスタが 4バイトの実装もあるかもしれませんが、残念なことに QEMU は由緒正しい(?)実装なので 16550 のレジスタは「1バイト」単位です。4バイト単位でアクセスすると、全く関係ない領域が破壊されます。困りました。

問題を解決するには、SoC の定義ファイルのどこか(soc.h 辺りかな?)で、
#define DT_NS16550_REG_SHIFT 0
を定義し、レジスタのサイズが 2^0 つまり 1バイト単位であることを 16550 シリアルドライバに教える必要があるみたいです。とても大事なことだと思いますが、全くドキュメントに書いていないように見えます。

そこそこコード見ないとわからないので、つらいです……。

シリアルドライバが動いた

無事シリアルが動作したので、QEMU を 4 CPU のマルチプロセッサ設定で起動し、GDB で覗いてみます。

QEMU virt モードでマルチプロセッサと、その確認
$ qemu-system-riscv32 -nographic -machine virt -net none -chardev stdio,id=con,mux=on -serial chardev:con -mon chardev=con,mode=readline -kernel zephyr/build/zephyr/zephyr.elf -cpu rv32 -smp cpus=4 -s -S

qemu-system-riscv32: warning: No -bios option specified. Not loading a firmware.
qemu-system-riscv32: warning: This default will change in a future QEMU release. Please use the -bios option to avoid breakages when this happens.
qemu-system-riscv32: warning: See QEMU's deprecation documentation for details.
*** Booting Zephyr OS build v2.2.0-rc1-123-gcaca3f60b012  ***
threadA: Hello World from QEMU RV32 virt board!
threadB: Hello World from QEMU RV32 virt board!
threadA: Hello World from QEMU RV32 virt board!
threadB: Hello World from QEMU RV32 virt board!
...


$ riscv64-zephyr-elf-gdb zephyr/build/zephyr/zephyr.elf 

...

Type "apropos word" to search for commands related to "word"...
Reading symbols from build/zephyr/zephyr.elf...

(gdb) target remote localhost:1234
Remote debugging using localhost:1234
0x00001000 in ?? ()

(gdb) info threads
  Id   Target Id                    Frame
* 1    Thread 1.1 (CPU#0 [running]) 0x00001000 in ?? ()
  2    Thread 1.2 (CPU#1 [running]) 0x00001000 in ?? ()
  3    Thread 1.3 (CPU#2 [running]) 0x00001000 in ?? ()
  4    Thread 1.4 (CPU#3 [running]) 0x00001000 in ?? ()

確かに 4 CPU が存在していることがわかります。Zephyr のログは GDB 側で continue すると出てきます。アプリケーションは、hello_world ではなく samples/synchronization を使っています。

メモ: 技術系の話は Facebook から転記しておくことにした。大幅に加筆した。

[編集者: すずき]
[更新: 2020年 2月 22日 02:55]

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



link permalink

link 編集する

Zephyr のブート処理 - ドライバの初期化情報とリンカーの妙技

引き続き、ドライバの初期化に重要な役割を果たしている __device_PRE_KERNEL_1_start[] の謎を追っていきます。謎は「__device_PRE_KERNEL_1_start[] の実体はバイナリにあるもののソースコードには見当たらない」ことです。前回まででわかったことを復習すると、

  • __device_PRE_KERNEL_1_start[] の要素である初期化情報(__device_sys_init_init_static_pools3 など)は、各ドライバの宣言 SYS_INIT(), DEVICE_AND_API_INIT() などの API で作成される
  • __device_sys_init_init_static_pools3 は .init_PRE_KERNEL_130 セクションに配置される

配列の要素がどうやってできたかわかったので、残る謎は下記になります。

  • __device_PRE_KERNEL_1_start[] を構成する方法(__device_sys_init_init_static_pools3 などを集めてくる方法)

これまた復習になりますが、__device_PRE_KERNEL_1_start[] は initlevel セクションに配置されていました。

ドライバの初期化情報が配置されているセクション
$ riscv64-zephyr-elf-readelf -a zephyr/build/zephyr/zephyr.elf

...

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] vector            PROGBITS        80000000 000060 000010 00  AX  0   0  4
  [ 2] exceptions        PROGBITS        80000010 000070 000258 00  AX  0   0  4
  [ 3] text              PROGBITS        80000268 0002c8 002604 00  AX  0   0  4
  [ 4] sw_isr_table      PROGBITS        8000286c 0028cc 000080 00  WA  0   0  4
  [ 5] devconfig         PROGBITS        800028ec 00294c 000030 00   A  0   0  4
  [ 6] rodata            PROGBITS        8000291c 00297c 000305 00   A  0   0  4
  [ 7] datas             PROGBITS        80002c24 002c84 000014 00  WA  0   0  4
  [ 8] initlevel         PROGBITS        80002c38 002c98 000030 00  WA  0   0  4    ★ここに配置される★
  [ 9] _k_mutex_area     PROGBITS        80002c68 002cc8 000014 00  WA  0   0  4
  [10] bss               NOBITS          80002c80 002cdc 000140 00  WA  0   0  8
  [11] noinit            NOBITS          80002dc0 002cdc 000e00 00  WA  0   0 16
  ...

思い出していただきたいのは、配列の要素は .init_PRE_KERNEL_130 セクションに置かれていて、initlevel セクションではなかったことです。つまり誰かがわざわざ initlevel セクションに置き直しています。

そんな芸当ができるのはリンカーしかいませんので、initlevel をキーワードにリンカースクリプトを探します。

initlevel セクションを作るリンカースクリプト
// zephyr/include/linker/common-ram.ld

...

        SECTION_DATA_PROLOGUE(initlevel,,)
        {
                DEVICE_INIT_SECTIONS()
        } GROUP_DATA_LINK_IN(RAMABLE_REGION, ROMABLE_REGION)


// zephyr/include/linker/linker-defs.h

/*
 * generate a symbol to mark the start of the device initialization objects for
 * the specified level, then link all of those objects (sorted by priority);
 * ensure the objects aren't discarded if there is no direct reference to them
 */

#define DEVICE_INIT_LEVEL(level)                                \
                __device_##level##_start = .;                   \
                KEEP(*(SORT(.init_##level[0-9])));              \
                KEEP(*(SORT(.init_##level[1-9][0-9]))); \

/*
 * link in device initialization objects for all devices that are automatically
 * initialized by the kernel; the objects are sorted in the order they will be
 * initialized (i.e. ordered by level, sorted by priority within a level)
 */

#define DEVICE_INIT_SECTIONS()                  \
                __device_init_start = .;        \
                DEVICE_INIT_LEVEL(PRE_KERNEL_1) \
                DEVICE_INIT_LEVEL(PRE_KERNEL_2) \
                DEVICE_INIT_LEVEL(POST_KERNEL)  \
                DEVICE_INIT_LEVEL(APPLICATION)  \
                __device_init_end = .;          \
                DEVICE_BUSY_BITFIELD()          \

DEVICE_AND_API_INIT の宣言を思い出すと、ドライバなどで宣言される初期化セクションの名前は .init_(level)(prio) のような名前でした。DEVICE_INIT_LEVEL() はその法則性に従ってセクションを集めます。最終的に集められたセクションは、全て initlevel セクションに配置されます。

例えば DEVICE_INIT_SECTIONS() の 2行目にある DEVICE_INIT_LEVEL(PRE_KERNEL_1) ですと、.init_PRE_KERNEL_1(数字) という名前のセクションを集めます。

というわけで、やっと謎が解けました。マクロやリンカースクリプトの見事な連携プレイですね。

prio の制限とエラーチェック

実装を見て気づいたと思いますが、DEVICE_AND_API_INIT() の prio に渡せる数字は 2桁が上限です。DEVICE_INIT_LEVEL() は [0-9] もしくは [1-9][0-9] のパターンしかマッチしません。

試しに優先度を 3桁にするとどうなるでしょう?mempool.c では CONFIG_KERNEL_INIT_PRIORITY_OBJECTS を prio に渡していたので、menuconfig でコンフィグを変えてみます。

3桁の prio にしてはいけない
$ ninja menuconfig

General Kernel Options  --->
  Initialization Priorities  --->
    (30) Kernel objects initialization priority

このパラメータを 300 に変更すると…?

riscv64-zephyr-elf/bin/ld: Undefined initialization levels used.
collect2: error: ld returned 1 exit status

リンク時にエラーで怒られました。これはどうやっているのでしょう?実はそんなに難しくありません。initlevel セクションを作るときとほぼ同じ仕組みです。

initlevel で拾えなかった .init_* 系セクションの検知

// zephyr/include/linker/common-ram.ld

        /* verify we don't have rogue .init_<something> initlevel sections */
        SECTION_DATA_PROLOGUE(initlevel_error,,)
        {
                DEVICE_INIT_UNDEFINED_SECTION()
        }
        ASSERT(SIZEOF(initlevel_error) == 0, "Undefined initialization levels used.")


// include/linker/linker-defs.h

/* define a section for undefined device initialization levels */
#define DEVICE_INIT_UNDEFINED_SECTION()         \
                KEEP(*(SORT(.init_[_A-Z0-9]*))) \

この initlevel_error セクションでは、initlevel セクションが拾い損ねた .init_* 系のセクションを全て集めます。もし 1つでもセクションが拾えた場合、initlevel_error セクションのサイズが 0 ではなくなるため、ASSERT に引っかかる仕組みになっています。リンカースクリプトで ASSERT や SORT ができるとは知らなかったですね……。

エラーチェックもきっちり作られていて Zephyr はさすが良くできているなあと思います。

[編集者: すずき]
[更新: 2020年 2月 19日 22:11]

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



link もっと前
   2020年 2月 28日 -
      2020年 2月 19日  
link もっと後

管理用メニュー

link 記事を新規作成

合計:  counter total
本日:  counter today

link About www.katsuster.net
RDF ファイル RSS 1.0
QR コード QR コード

最終更新: 2/22 03:01

カレンダー

<2020>
<<<02>>>
------1
2345678
9101112131415
16171819202122
23242526272829

最近のコメント 5件

  • link 19年09月01日
    すずき 「私も正直びっくりです。間違って違う製品を...」
    (更新:09/04 23:39)
  • link 19年09月01日
    hdk 「車向けの製品の中でも、車載コンピューター...」
    (更新:09/02 23:20)
  • link 19年07月18日
    hdk 「あっ、AAMはマニュアルのオペレーション...」
    (更新:07/25 00:02)
  • link 19年07月18日
    すずき 「AAM(ASCII Adjust AX ...」
    (更新:07/24 22:22)
  • link 19年07月18日
    hdk 「加算減算は符号のありなしどちらも命令が同...」
    (更新:07/24 07:25)

最近の記事 3件

link もっとみる
  • link 20年02月21日
    すずき 「[Zephyr と RISC-V とマルチプロセッサ] 昨日(20...」
    (更新:02/22 03:01)
  • link 20年02月20日
    すずき 「[Zephyr の 16550 シリアルドライバ] 先日は Zep...」
    (更新:02/22 02:55)
  • link 19年03月28日
    すずき 「[マンガ紹介] お気に入りのマンガ紹介シリーズ。[マンガ紹介 その ] ...」
    (更新:02/21 20:37)

こんてんつ

open/close wiki
open/close Java API

過去の日記

open/close 2002年
open/close 2003年
open/close 2004年
open/close 2005年
open/close 2006年
open/close 2007年
open/close 2008年
open/close 2009年
open/close 2010年
open/close 2011年
open/close 2012年
open/close 2013年
open/close 2014年
open/close 2015年
open/close 2016年
open/close 2017年
open/close 2018年
open/close 2019年
open/close 2020年
open/close 過去日記について

その他の情報

open/close アクセス統計
open/close サーバ一覧
open/close サイトの情報