link もっと前
   2020年 2月 10日 -
      2020年 2月 1日  
link もっと後

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

日々

link permalink

link 編集する

Zephyr OS で遊ぼう その 9 - Zephyr 2.2.0 に対応する

Zephyr は開発中なので、ときおり内部構造が変わり、過去と互換性がなくなってしまうことがあります。ボードや SoC の定義も例外ではありません。

前回までの説明では Zephyr 2.1.0 を使用していましたが、2.1.0 から 2.2.0 の間で、ボードの defconfig の書き方が変更されました。具体的に言うと、

  • ボード側: アーキテクチャ CONFIG_RISCV を書かなくて良くなった
  • SoC 側: CONFIG_RISCV を depends on → select に変える必要が生じた

下記のパッチにあるような変更をすると、2.2.0 でも Hoge ボードと Spike SoC の対応が適用できます。

それぞれ 1行ずつしか変更していませんので、すぐわかると思います。

Zephyr の不思議なコンフィグ

Zephyr は Linux と同じ Kconfig を採用していますが、コンフィグの起点に対する考え方がだいぶ違います。Zephyr は最初に cmake で「ボード名(-DBOARD オプション)」を指定します。すなわちコンフィグの起点はボードです。一方の Linux は export ARCH=x86 のようにアーキテクチャを設定の起点にします。

Zephyr 2.1.0 では、アーキテクチャ(CONFIG_RISCV など)、SoC(CONFIG_SOC_RISCV_SIFIVE_FREEDOM など)、ボードの依存関係は下記のようになっていました。

2.1.0 の依存関係
アーキテクチャ(ボードの defconfig が y にする)← アーキテクチャが依存の起点
↑depends on
SoC(ボードの defconfig が y にする)
↑depends on
ボード(ボードの defconfig が y にする)← Zephyr はなぜかボードがコンフィグの起点

依存関係とコンフィグの起点が矛盾しているため menuconfig が異常な動きをします。おそらく ninja menuconfig を弄れば矛盾に気づくと思いますが、Zephyr はなぜかボードが未対応のアーキテクチャや SoC(例えば qemu_riscv32 で CONFIG_ARM を選択できる)を有効にできます。Zephyr 2.1.0 だと 2つ、おかしな設定ができます。

  • qemu_riscv32 ボードで未対応のアーキテクチャ(例えば CONFIG_X86)を選択すると、SoC の選択肢がおかしくなる(X86 だと言っているのに、RISC-V 用の SoC が出てくる)
  • qemu_riscv32 ボードで未対応の SoC(例えば CONFIG_SOC_LITEX_VEXRISCV)を選択すると、ボードの一覧が空になって選べなくなる(これは 2.2.0 でもできる)

実際に ninja menuconfig で、qemu_riscv32 が未対応のアーキテクチャを選ぶと下記のようになります。

Zephyr 2.1.0 の変なコンフィグ
# qemu_riscv32 のデフォルト

    Modules  --->
    Board Selection (QEMU RISCV32 target)  --->
    Board Options  ----
    SoC/CPU/Configuration Selection (SiFive Freedom SOC implementation)  --->
    Hardware Configuration  --->
    RISCV Options  --->
    Architecture (RISCV architecture)  --->
    General Architecture Options  --->
    General Kernel Options  --->
    ...


# ボードが対応していないアーキテクチャを選択すると、SoC が勝手に変わる、ボードは何も選べなくなる

    Modules  --->
    Board Selection  ----
    Board Options  ----
    SoC/CPU/Configuration Selection (LiteX VexRiscv system implementation)  --->    ★なぜか RISC-V 用の SoC が出てくる
    Hardware Configuration  ----
    Architecture (x86 architecture)  --->    ★x86 に変えた
    General Architecture Options  --->
    General Kernel Options  --->
    ...

そもそも x86 が選択可能な時点でおかしいですが、x86 を選択したのに x86 じゃない SoC が選択肢に出てくるなど、かなりおかしな動きです。

中途半端に直った Zephyr 2.2.0

Zephyr 2.2.0 では依存関係は下記のように、少しだけ整理されました。

2.2.0 の依存関係
アーキテクチャ(SoC が強制的に y にする、menuconfig では触れない)
↑select
SoC(ボードの defconfig が y にする)
↑depends on
ボード(ボードの defconfig が y にする)← ここが起点

アーキテクチャを変えることが出来なくなりました。前よりマシですが、やはり変なところは残っていて、未対応の SoC を選択するとボードの選択肢が消えてしまいます。

Zephyr 2.2.0 の変なコンフィグ設定
# qemu_riscv32 のデフォルト

    Modules  --->
    Board Selection (QEMU RISCV32 target)  --->
    Board Options  ----
    SoC/CPU/Configuration Selection (SiFive Freedom SOC implementation)  --->
    Hardware Configuration  --->
    RISCV Options  --->
    ...


# SoC を無理やり未対応のものに変えると、ボードが選べなくなる

    Modules  --->
    Board Selection  ----
    Board Options  ----
    SoC/CPU/Configuration Selection (LiteX VexRiscv system implementation)  --->
    Hardware Configuration  ----
    RISCV Options  --->
    ...

個人的には Zephyr の Kconfig の使い方は変だな、と思います。依存関係があることはわかっているのだから、依存の根本の方から設定(要は、アーキテクチャ → SoC → ボード、の順)したら良いのに、Zephyr はなぜかボードの設定から固定していて、変な動きになっています。

Linux はどうしているかというと、最初に export ARCH=x86 とします。すなわち、アーキテクチャを設定の起点にしています。自然です。特に変な動きもしません。

Zephyr の方が Linux より後発ですが、ビルド周りは退化したように感じます。不思議。

[編集者: すずき]
[更新: 2020年 2月 16日 18:21]

コメント一覧

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



link permalink

link 編集する

Zephyr OS で遊ぼう その 8 - 独自のシリアルドライバの実装

前回はシリアルドライバの枠組み、というか、何も実装されていないドライバを追加しました。今回はシリアルドライバを実装します。

Zephyr のドライバについてはドキュメント(Device Driver Model - Zephyr Project Documentation)があります。ドライバの定義や初期化を司る API が Driver API の節に書いてあります。

今回作成する UART ドライバでは初期化は要りませんが、ドライバの API をいくつか実装したいので DEVICE_AND_API_INIT() を使います。

UART のドライバで実装できる API の一覧はドキュメント(UART - Zephyr Project Documentation)に書いてあります。API はたくさんありますが、poll_in, poll_out があれば動作するようなので、この 2つを作ります。

コードは下記のようにしました。

QEMU Spike モード用のシリアルドライバ実装

/*
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @brief UART driver for the Spike Simulator
 */

#include <kernel.h>
#include <arch/cpu.h>
#include <drivers/uart.h>

volatile u64_t tohost;
volatile u64_t fromhost;

void uart_spike_poll_out(struct device *dev, unsigned char c)
{
        u64_t cmd;

        // QEMU spike mode
        u8_t dev_no = 1;
        u8_t cmd_no = 1;
        u64_t payload = c;

        cmd = ((u64_t)dev_no << 56) |
                ((u64_t)cmd_no << 48) |
                (payload & 0xffffffffffffULL);

        tohost = cmd;

        while (fromhost == 0)
                ;
        fromhost = 0;
}

static int uart_spike_poll_in(struct device *dev, unsigned char *c)
{
        return -1;
}

static int uart_spike_init(struct device *dev)
{
        return 0;
}

static const struct uart_driver_api uart_spike_driver_api = {
        .poll_in          = uart_spike_poll_in,
        .poll_out         = uart_spike_poll_out,
};

DEVICE_AND_API_INIT(uart_spike_0, DT_INST_0_SPIKE_UART_SPIKE_LABEL,
                    uart_spike_init, NULL, NULL,
                    PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
                    (void *)&uart_spike_driver_api);

このままビルドすると DEVICE_AND_API_INIT() に渡しているラベル DT_INST_0_SPIKE_UART_SPIKE_LABEL が未定義だと怒られます。これについては後ほど作り方を説明します。

ラベルが未定義のエラー
zephyr/drivers/serial/uart_spike.c:73:35: error: 'DT_INST_0_SPIKE_UART_SPIKE_LABEL' undeclared here (not in a function); did you mean 'DT_COMPAT_SPIKE_UART_SPIKE'?
 DEVICE_AND_API_INIT(uart_spike_0, DT_INST_0_SPIKE_UART_SPIKE_LABEL,
                                   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
../include/device.h:106:11: note: in definition of macro 'DEVICE_AND_API_INIT'
   .name = drv_name, .init = (init_fn),     \
           ^~~~~~~~
[89/97] Linking C static library zephyr/libzephyr.a
ninja: build stopped: subcommand failed.

初期化は DEVICE_AND_API_INIT() で指定します。引数の意味は DEVICE_AND_API_INIT() のドキュメントを見たほうが良いでしょう。

引っかかりそうなところは dev_name にダブルクォートを「付けない」こと、一番に初期化されるように PRE_KERNEL_1 にすることくらいでしょうか。

QEMU Spike モードの文字出力

文字出力 poll_out の実装は、グローバル変数に謎の数字を書くだけの不思議実装になっていますが、これは QEMU Spike モードの Host, Guest 間インタフェースの仕様に従っているためです。

ちなみに QEMU 側はこんなコードになっています。

QEMU Spike モードの文字出力

// https://github.com/qemu/qemu/blob/master/hw/riscv/riscv_htif.c

...

static void htif_handle_tohost_write(HTIFState *htifstate, uint64_t val_written)
{
    uint8_t device = val_written >> 56;
    uint8_t cmd = val_written >> 48;
    uint64_t payload = val_written & 0xFFFFFFFFFFFFULL;
    int resp = 0;

...

    } else if (likely(device == 0x1)) {
        /* HTIF Console */
        if (cmd == 0x0) {
            /* this should be a queue, but not yet implemented as such */
            htifstate->pending_read = val_written;
            htifstate->env->mtohost = 0; /* clear to indicate we read */
            return;
        } else if (cmd == 0x1) {
            qemu_chr_fe_write(&htifstate->chr, (uint8_t *)&payload, 1);
            resp = 0x100 | (uint8_t)payload;
        } else {
            qemu_log("HTIF device %d: unknown command\n", device);
        }
    } else {

...

    htifstate->env->mfromhost = (val_written >> 48 << 48) | (resp << 16 >> 16);
    htifstate->env->mtohost = 0; /* clear to indicate we read */
}

途中の if 文を見るとわかるように、device=1, cmd=1 にすると文字が出力できます。また QEMU が文字出力を終えたときに fromhost が 0 以外の値に書き換わります(なので、Zephyr のシリアルドライバ側では fromhost をポーリングで監視する実装にしています)。

シリアルドライバのラベル定義と bindings

DT_INST_0_SPIKE_UART_SPIKE_LABEL のようなラベルはユーザが定義するのではなく、zephyr/dts/bindings 以下のファイルに存在する compatible と、デバイスツリーを突き合わせて自動生成するみたいです。命名規則については、以前 Zephyr OS で遊ぼう その 3(2020年 2月 4日の日記参照)で紹介したとおりです。

zephyr/dts/bindings/serial/spike,uart-spike.yaml

# SPDX-License-Identifier: Apache-2.0

description: UART for Spike Simulator

compatible: "spike,uart-spike"

include: uart-controller.yaml

デバイスツリーの compatible の突き合わせとラベルの自動生成は、zephyr/scripts/dts/gen_defines.py と edtlib.py 辺りでやっていることは間違いなさそうなのですが、詳細な仕組みはまだ追えていません。

若干もやっとしますが YAML ファイルを作成して、compatible をデバイスツリーで使用したものと合わせると、目的のラベル DT_INST_0_SPIKE_UART_SPIKE_LABEL が定義され、ビルドが通ります。

Hello world

ビルドが通ったら実行します。

QEMU Spike モードで Hello world
$ /usr/bin/qemu-system-riscv32 -nographic -machine spike -net none -chardev stdio,id=con,mux=on -serial chardev:con -mon chardev=con,mode=readline -kernel zephyr/build/zephyr/zephyr.elf

qemu-system-riscv32: clint: time_hi write not implemented
qemu-system-riscv32: clint: time_lo write not implemented
qemu-system-riscv32: clint: time_hi write not implemented
*** Booting Zephyr OS build zephyr-v2.1.0-1699-gbb40304f2531  ***
Hello World! hoge

やっと Hello world が拝めました。長かった。 (2/19 追記: qemu-system-riscv32: clint: time_hi write not implemented のエラーメッセージは mtime と mtimecmp のアドレスが逆だったために発生していたエラーです。アドレス修正後は表示されません)

パッチ

今まで説明してきた変更をパッチとしてまとめておきます。

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

コメント一覧

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



link permalink

link 編集する

Zephyr OS で遊ぼう その 7 - 独自のシリアルドライバの枠組み

Zephyr のビルドに成功し、gdb で追跡したところ Hello world の main 関数まで到達していることもわかりました。ここまでくればあとは文字出力だけです。

まず Hoge ボードの defconfig に CONFIG_UART_SPIKE=y を加えます。cmake に「そんなコンフィグはない」と怒られるようになりますが、これから作るので OK です。

シリアルドライバの Kconfig, CMake

シリアルドライバのディレクトリは zephyr/drivers/serial にあります。ディレクトリの構造を見ると、CMakeLists.txt, Kconfig.*, ソースコード uart_*.c が並んでいます。この 3つの組を追加もしくは変更すれば良さそうです。

zephyr/drivers/serial/Kconfig.spike

# SPDX-License-Identifier: Apache-2.0

menuconfig UART_SPIKE
        bool "Spike Simulator serial driver"
        depends on SOC_RISCV_SPIKE
        select SERIAL_HAS_DRIVER
        help
          This option enables the Spike Simulator serial driver.

名前は何でも良いですが、Kconfig.spike という名前にしました。Kconfig.soc とは違って、ファイルを足すだけではダメで、シリアルドライバの Kconfig から読んでもらう必要があります。

zephyr/drivers/serial/Kconfig に追加する行

...

source "drivers/serial/Kconfig.litex"

source "drivers/serial/Kconfig.rtt"

source "drivers/serial/Kconfig.xlnx"

source "drivers/serial/Kconfig.spike"    # ★この行を足す★

endif # SERIAL

Kconfig を追加して cmake をするとまだ怒られます。CMakeList.txt も変更する必要があるとのことです。

Kconfig 追加後の cmake
-- Configuring done
CMake Error at ../../cmake/extensions.cmake:372 (add_library):
  No SOURCES given to target: drivers__serial
Call Stack (most recent call first):
  ../../cmake/extensions.cmake:349 (zephyr_library_named)
  ../../drivers/serial/CMakeLists.txt:3 (zephyr_library)


CMake Generate step failed.  Build files cannot be regenerated correctly.
FAILED: build.ninja

CMakeLists.txt は他の行に習って追加します。CMakeLists.txt を変更しただけだと uart_spike.c が無いと言われるので、touch uart_spike.c で空のファイルを作成します。

zephyr/drivers/serial/CMakeLists.txt に追加する行

...

zephyr_library_sources_if_kconfig(uart_liteuart.c)
zephyr_library_sources_ifdef(CONFIG_UART_RTT_DRIVER uart_rtt.c)
zephyr_library_sources_if_kconfig(uart_xlnx_ps.c)
zephyr_library_sources_if_kconfig(uart_spike.c)    # ★この行を足す★

...

他の行に習って追加するだけではつまらないので、CMake のスクリプトも眺めます。今回使用した zephyr_library_sources_if_kconfig() と zephyr_library_sources_ifdef() との違いは、ソースコードのコメントにある説明がわかりやすいです。

ファイル名を大文字にして、CONFIG_ と連結させた名前(今回のケースだと CONFIG_UART_SPIKE)を生成して、コンフィグが有効ならソースコードをビルド対象に加えるという意味です。Kconfig のコンフィグは y, n, m の 3値を取りますが、Zephyr のスクリプトでは、コンフィグが有効だと y という値が入り、無効だと未定義になるようです。

zephyr_library_sources_if_kconfig の実装

# zephyr/cmake/extensions.cmake

...

# zephyr_library_sources_if_kconfig(fft.c)
# is the same as
# zephyr_library_sources_ifdef(CONFIG_FFT fft.c)

...

function(zephyr_library_sources_if_kconfig item)
  get_filename_component(item_basename ${item} NAME_WE)
  string(TOUPPER CONFIG_${item_basename} UPPER_CASE_CONFIG)
  zephyr_library_sources_ifdef(${UPPER_CASE_CONFIG} ${item})
endfunction()

...

function(zephyr_library_sources_ifdef feature_toggle source)
  if(${${feature_toggle}})
    zephyr_library_sources(${source} ${ARGN})
  endif()
endfunction()


# zephyr/cmake/extensions.cmake

...

function(zephyr_library_sources source)
  target_sources(${ZEPHYR_CURRENT_LIBRARY} PRIVATE ${source} ${ARGN})
endfunction()

...

以上の変更でビルドが通り、シリアルドライバを実装する枠が完成しました。続きは次回。

[編集者: すずき]
[更新: 2020年 2月 16日 18:23]

コメント一覧

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



link permalink

link 編集する

Zephyr OS で遊ぼう その 6 - 独自の SoC 定義でビルド

やっと cmake を通過したので、いよいよビルドに挑みます。

最初の ninja 実行結果
In file included from ../include/arch/cpu.h:25,
                 from ../include/kernel_includes.h:34,
                 from ../include/kernel.h:17,
                 from zephyr/arch/riscv/core/offsets/offsets.c:16:
../include/arch/riscv/arch.h:25:10: fatal error: soc.h: No such file or directory
 #include <soc.h>
          ^~~~~~~
compilation terminated.
ninja: build stopped: subcommand failed.

他の soc.h を見ると、soc_common.h, devicetree.h というヘッダをインクルードしているようです。それに習っておきます。

zephyr/soc/riscv/riscv-privilege/spike/soc.h

/*
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @file SoC configuration macros for the Spike series processor
 */

#ifndef __RISCV_SPIKE_SOC_H_
#define __RISCV_SPIKE_SOC_H_

#include <soc_common.h>
#include <devicetree.h>

#endif /* __RISCV_SPIKE_SOC_H_ */

再びビルドすると、今度は PLIC_* がないと怒られます。

soc.h 追加後の ninja 実行結果
zephyr/drivers/interrupt_controller/intc_plic.c: In function 'riscv_plic_irq_enable':
zephyr/drivers/interrupt_controller/intc_plic.c:45:41: error: 'DT_PLIC_IRQ_EN' undeclared (first use in this function); did you mean 'PLIC_IRQS'?
  volatile u32_t *en = (volatile u32_t *)DT_PLIC_IRQ_EN;
                                         ^~~~~~~~~~~~~~
                                         PLIC_IRQS

RISC-V Privileged 系の SoC では PLIC という割り込みコントローラのドライバが自動的に有効になります。今回、動作させるに当たって PLIC のサポートは不要なので、zephyr/boards/riscv/hoge/hoge_defconfig で無効にします。具体的には CONFIG_PLIC=n を足します。

この後 cmake からやり直してみると、

soc.h 追加後の ninja 実行結果
zephyr/drivers/timer/riscv_machine_timer.c: In function 'set_mtimecmp':
zephyr/drivers/timer/riscv_machine_timer.c:28:31: error: 'RISCV_MTIMECMP_BASE' undeclared (first use in this function)
  volatile u32_t *r = (u32_t *)RISCV_MTIMECMP_BASE;
                               ^~~~~~~~~~~~~~~~~~~
zephyr/drivers/timer/riscv_machine_timer.c:28:31: note: each undeclared identifier is reported only once for each function it appears in
zephyr/drivers/timer/riscv_machine_timer.c: In function 'mtime':
zephyr/drivers/timer/riscv_machine_timer.c:47:31: error: 'RISCV_MTIME_BASE' undeclared (first use in this function)
  volatile u32_t *r = (u32_t *)RISCV_MTIME_BASE;
                               ^~~~~~~~~~~~~~~~
[87/94] Linking C static library zephyr/kernel/libkernel.a
ninja: build stopped: subcommand failed.

CLINT のレジスタ mtime, mtimecmp のアドレスを必要としているようです。

CLINT のレジスタはどこに?

他の SoC を眺めてみると、CLINT のレジスタアドレスを定義する場所は soc.h のようです。しかし肝心のアドレスがわかりません。QEMU のドキュメントに載っていれば簡単でしたが、見当たらなかったので QEMU のソースコードを見ます。

QEMU の Spike, CLINT 関連のソースコード

// https://github.com/qemu/qemu/blob/master/hw/riscv/spike.c

static const struct MemmapEntry {
    hwaddr base;
    hwaddr size;
} spike_memmap[] = {
    [SPIKE_MROM] =     {     0x1000,    0x11000 },
    [SPIKE_CLINT] =    {  0x2000000,    0x10000 },
    [SPIKE_DRAM] =     { 0x80000000,        0x0 },
};


// https://github.com/qemu/qemu/blob/master/include/hw/riscv/sifive_clint.h

enum {
    SIFIVE_SIP_BASE     = 0x0,
    SIFIVE_TIMECMP_BASE = 0x4000,
    SIFIVE_TIME_BASE    = 0xBFF8
};

ベースアドレスが 0x02000000 で、レジスタのオフセットアドレスもわかりました。soc.h に追記します。
(2/19 訂正: MTIMECMP_BASE と MTIME_BASE のアドレスが逆だったので、修正しました)

zephyr/soc/riscv/riscv-privilege/spike/soc.h に追記する内容

/* Timer configuration */
#define RISCV_MTIMECMP_BASE          0x02004000
#define RISCV_MTIME_BASE             0x0200bff8

再びビルドします。やっとビルドをパスしました。

ビルド成功
$ ninja

[0/1] Re-running CMake...
-- Zephyr version: 2.1.99
-- Selected BOARD hoge
-- Loading zephyr/boards/riscv/hoge/hoge.dts as base
hoge.dts.pre.tmp:22.17-25.5: Warning (simple_bus_reg): /soc/serial: missing or empty reg/ranges property
  also defined at hoge.dts.pre.tmp:37.8-39.3
Devicetree header saved to 'zephyr/build/zephyr/include/generated/devicetree_unfixed.h'
Parsing zephyr/Kconfig
Loaded configuration 'zephyr/build/zephyr/.config'
No change to configuration in 'zephyr/build/zephyr/.config'
No change to Kconfig header in 'zephyr/build/zephyr/include/generated/autoconf.h'
-- Cache files will be written to: /home/katsuhiro/.cache/zephyr
-- Configuring done
-- Generating done
-- Build files have been written to: zephyr/build
[89/94] Linking C executable zephyr/zephyr_prebuilt.elf
Memory region         Used Size  Region Size  %age Used
             RAM:       12752 B        16 KB     77.83%
        IDT_LIST:          25 B         2 KB      1.22%
[94/94] Linking C executable zephyr/zephyr.elf

まだめでたしめでたし、ではありません。

実行、デバッグ

生成されたバイナリを実行すると何かエラーが表示されるものの、Hello world は表示されません。残念です。

実行しても何も表示されない
$ /usr/bin/qemu-system-riscv32 -nographic -machine spike -net none -chardev stdio,id=con,mux=on -serial chardev:con -mon chardev=con,mode=readline -kernel zephyr/build/zephyr/zephyr.elf

qemu-system-riscv32: clint: time_hi write not implemented
qemu-system-riscv32: clint: time_lo write not implemented
qemu-system-riscv32: clint: time_hi write not implemented

このままでは全く何も実行されていないのか、惜しいところまで実行されたのか判別が付きません。以前ご紹介した(2020年 1月 31日の日記参照)デバッグオプション -s と -S を使い、gdb で追跡します。

復習ですが、オプション -s は GDB の接続を localhost:1234 で受け付け、-S はエミュレータを Halted 状態で起動し gdb から再開要求をするまで動かないで待っている、という意味です。

main 関数まで到達しているようだ
$ riscv64-zephyr-elf-gdb zephyr/build/zephyr/zephyr.elf

GNU gdb (crosstool-NG 1.24.0.60-a152d61) 8.3.1
Copyright (C) 2019 Free Software Foundation, Inc.

...

(gdb) target remote localhost:1234

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


(gdb) b main

Breakpoint 1 at 0x800006d4: file zephyr/samples/hello_world/src/main.c, line 12.


(gdb) c

Continuing.

Breakpoint 1, main ()
    at zephyr/samples/hello_world/src/main.c:12
12              printk("Hello World! %s
", CONFIG_BOARD);

ターゲット(QEMU)に接続し、main にブレークを設定して、実行を継続しています。Hello world の main 関数に到達していることがわかります。素晴らしいですね。printk は呼ばれているものの、文字が出ないことが問題だとわかりました。

Hello world を拝むには、シリアルのドライバを追加する必要があります。続きはまた次回。

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

コメント一覧

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



link permalink

link 編集する

Zephyr OS で遊ぼう その 5 - 独自の SoC 定義(続き)

今回は cmake をパスするまで頑張ります。

エラー内容の調査

今後の方針のため cmake のエラーを見ます。どうやら SOC_FAMILY_RISCV_PRIVILEGE がないとか、SOC_SERIES_RISCV_SPIKE とは何だ?とか、Kconfig 周りの設定で怒っているようです。何も定義していないので当然ですね。

hoge.dts を変更して cmake したときのエラー
warning: UART_CONSOLE (defined at drivers/console/Kconfig:47) was assigned the value 'y' but got the
value 'n'. Check these unsatisfied dependencies: SERIAL_HAS_DRIVER (=n). See
http://docs.zephyrproject.org/latest/reference/kconfig/CONFIG_UART_CONSOLE.html and/or look up
UART_CONSOLE in the menuconfig/guiconfig interface. The Application Development Primer, Setting
Configuration Values, and Kconfig - Tips and Best Practices sections of the manual might be helpful
too.


warning: RISCV_MACHINE_TIMER (defined at drivers/timer/Kconfig:153) was assigned the value 'y' but
got the value 'n'. Check these unsatisfied dependencies: SOC_FAMILY_RISCV_PRIVILEGE (=n). See
http://docs.zephyrproject.org/latest/reference/kconfig/CONFIG_RISCV_MACHINE_TIMER.html and/or look
up RISCV_MACHINE_TIMER in the menuconfig/guiconfig interface. The Application Development Primer,
Setting Configuration Values, and Kconfig - Tips and Best Practices sections of the manual might be
helpful too.


warning: The choice symbol BOARD_HOGE (defined at boards/riscv/hoge/Kconfig.board:3) was selected
(set =y), but no symbol ended up as the choice selection. See
http://docs.zephyrproject.org/latest/reference/kconfig/CONFIG_BOARD_HOGE.html and/or look up
BOARD_HOGE in the menuconfig/guiconfig interface. The Application Development Primer, Setting
Configuration Values, and Kconfig - Tips and Best Practices sections of the manual might be helpful
too.


zephyr/boards/riscv/hoge/hoge_defconfig:4: warning: attempt to assign the value 'y' to the undefined symbol SOC_SERIES_RISCV_SPIKE

zephyr/boards/riscv/hoge/hoge_defconfig:5: warning: attempt to assign the value 'y' to the undefined symbol SOC_RISCV_SPIKE

error: Aborting due to Kconfig warnings

CMake Error at zephyr/cmake/kconfig.cmake:216 (message):
  command failed with return code: 1
Call Stack (most recent call first):
  zephyr/cmake/app/boilerplate.cmake:461 (include)
  CMakeLists.txt:5 (include)

すぐに Kconfig を追加と行きたいところですが、その前に少しやることがあります。

ボードの defconfig を見直す

Hoge ボードの defconfig で余計なもの(SiFive FE310 SoC 用の設定)を定義しているので、削りましょう。SiFive 由来のドライバの設定、前回のデバイスツリーで定義していない PINMUX や GPIO の設定を削ります。

hoge_defconfig から SiFive 用の設定を削る

diff --git a/boards/riscv/hoge/hoge_defconfig b/boards/riscv/hoge/hoge_defconfig
index 3f9626815f..32c4e9e90d 100644
--- a/boards/riscv/hoge/hoge_defconfig
+++ b/boards/riscv/hoge/hoge_defconfig
@@ -1,19 +1,12 @@
 # SPDX-License-Identifier: Apache-2.0
 
 CONFIG_RISCV=y
-CONFIG_SOC_SERIES_RISCV_SIFIVE_FREEDOM=y
-CONFIG_SOC_RISCV_SIFIVE_FREEDOM=y
+CONFIG_SOC_SERIES_RISCV_SPIKE=y
+CONFIG_SOC_RISCV_SPIKE=y
 CONFIG_BOARD_HOGE=y
 CONFIG_CONSOLE=y
 CONFIG_PRINTK=y
 CONFIG_SERIAL=y
-CONFIG_UART_SIFIVE=y
-CONFIG_UART_SIFIVE_PORT_0=y
 CONFIG_UART_CONSOLE=y
-CONFIG_PLIC=y
-CONFIG_PINMUX=y
-CONFIG_PINMUX_SIFIVE=y
 CONFIG_RISCV_MACHINE_TIMER=y
-CONFIG_GPIO=y
-CONFIG_GPIO_SIFIVE=y
 CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC=10000000

これで余計なコンフィグが原因で怒られることはないはずです。

SoC 用の Kconfig の追加

基本的に SoC の Kconfig を追加する場合、zephyr/soc/アーキテクチャ名/SoC 名/Kconfig.soc を足せば良いですが、RISC-V Privileged 系の SoC は少し様子が違い、Kconfig が 2段構成になります。

Kconfig の依存関係
# zephyr/soc/Kconfig

  source "$(SOC_DIR)/$(ARCH)/*/Kconfig.soc"


# zephyr/soc/riscv/riscv-privilege/Kconfig.soc

  source "soc/riscv/riscv-privilege/*/Kconfig.series"

ファイルを足すべき場所がわかりました。早速 Kconfig.series を足します。内容は隣にある SiFive のディレクトリ(sifive-freedom)などを参考にします。

zephyr/soc/riscv/riscv-privilege/spike/Kconfig.series

# RISCV_SPIKE SOC implementation

# SPDX-License-Identifier: Apache-2.0

config SOC_SERIES_RISCV_SPIKE
        bool "SPIKE series SOC implementation"
        depends on RISCV
        select SOC_FAMILY_RISCV_PRIVILEGE
        help
          Enable support for Spike series SOC

再度 cmake を実行すると SOC_SERIES_RISCV_SPIKE のエラーが消えるはずです。残りの SOC_RISCV_SPIKE はどこで定義すべきでしょうか?

RISC-V Privileged の Kconfig は 2つある

RISC-V Privileged や他のディレクトリを見ると気づくと思いますが、SoC の Kconfig には 2つ系列があります。

riscv-privilege/Kconfig.soc -> riscv-privilege/spike/Kconfig.series のラインと、
riscv-privilege/Kconfig -> riscv-privilege/spike/Kconfig.soc のラインです。先程、追加したのは前者です。名前が紛らわしいのは RISC-V Privileged 系列の実装の良くないところですね……。

RISC-V Privileged の Kconfig 依存関係(再整理)

# zephyr/Kconfig

  source "Kconfig.zephyr"


# zephyr/Kconfig.zephyr

  source "$(SOC_DIR)/Kconfig"    # SOC_DIR = soc


# zephyr/soc/Kconfig

  choice
  	prompt "SoC/CPU/Configuration Selection"

  source "$(SOC_DIR)/$(ARCH)/*/Kconfig.soc"    # soc/riscv/riscv-privilege/Kconfig.soc

  endchoice

  menu "Hardware Configuration"
  osource "$(SOC_DIR)/$(ARCH)/Kconfig"
  osource "$(SOC_DIR)/$(ARCH)/*/Kconfig"    # soc/riscv/riscv-privilege/Kconfig


# zephyr/soc/riscv/riscv-privilege/Kconfig.soc

  source "soc/riscv/riscv-privilege/*/Kconfig.series"


# zephyr/soc/riscv/riscv-privilege/Kconfig

  config SOC_FAMILY_RISCV_PRIVILEGE    # riscv-privilege/spike/Kconfig.series の select で有効にする
  config SOC_FAMILY
  config RISCV_HAS_PLIC

  ...

  source "soc/riscv/riscv-privilege/*/Kconfig.soc"

仕組みがわかったところで、後者の Kconfig.soc も追加しましょう。内容はやはり SiFive の Kconfig を参考にします。ありがとう SiFive さん。

zephyr/soc/riscv/riscv-privilege/spike/Kconfig.soc

# RISCV_SPIKE SOC configuration options

# SPDX-License-Identifier: Apache-2.0

choice
        prompt "Spike series SOC implementation"
        depends on SOC_SERIES_RISCV_SPIKE

config SOC_RISCV_SPIKE
        bool "Spike SOC implementation"
        select ATOMIC_OPERATIONS_C

endchoice

SOC_SERIES_RISCV_SPIKE は、先程 Kconfig.series にて定義しました。ATOMIC_OPERATIONS_C はアトミック演算を行う関数を定義するためのコンフィグです。これを指定しないと、後ほどリンクする際に atomic_cas() がないと言われます。

アトミック演算の定義箇所

//zephyr/include/sys/atomic.h

#ifdef CONFIG_ATOMIC_OPERATIONS_BUILTIN
static inline bool atomic_cas(atomic_t *target, atomic_val_t old_value,
                          atomic_val_t new_value)
{
        return __atomic_compare_exchange_n(target, &old_value, new_value,
                                           0, __ATOMIC_SEQ_CST,
                                           __ATOMIC_SEQ_CST);
}
#elif defined(CONFIG_ATOMIC_OPERATIONS_C)
__syscall int atomic_cas(atomic_t *target, atomic_val_t old_value,
                         atomic_val_t new_value);

#else
extern int atomic_cas(atomic_t *target, atomic_val_t old_value,
                      atomic_val_t new_value);
#endif

これでもまだ cmake に怒られます。linker.ld がどうのこうのと言っています。パスもなにやらおかしいです。

Kconfig 追加後の cmake のエラー
CMake Error at ../../CMakeLists.txt:372 (message):
  Could not find linker script:
  'zephyr/soc/riscv//linker.ld'.
  Corrupted configuration?

この linker.ld を探しているのは cmake です。下記の部分で探しています。

cmake が linker.ld を探している箇所

# zephyr/CMakeLists.txt

...

if(CONFIG_HAVE_CUSTOM_LINKER_SCRIPT)
  set(LINKER_SCRIPT ${APPLICATION_SOURCE_DIR}/${CONFIG_CUSTOM_LINKER_SCRIPT})
  if(NOT EXISTS ${LINKER_SCRIPT})
    set(LINKER_SCRIPT ${CONFIG_CUSTOM_LINKER_SCRIPT})
    assert_exists(CONFIG_CUSTOM_LINKER_SCRIPT)
  endif()
else()
  # Try a board specific linker file
  set(LINKER_SCRIPT ${BOARD_DIR}/linker.ld)    # ★★★これ、もしくは★★★
  if(NOT EXISTS ${LINKER_SCRIPT})
    # If not available, try an SoC specific linker file
    set(LINKER_SCRIPT ${SOC_DIR}/${ARCH}/${SOC_PATH}/linker.ld)    # ★★★これ★★★
  endif()
endif()

if(NOT EXISTS ${LINKER_SCRIPT})
  message(FATAL_ERROR "Could not find linker script: '${LINKER_SCRIPT}'. Corrupted configuration?")
endif()

デバッグメッセージ(message(SOC_PATH ${SOC_PATH}) を書き足すなど)を出して確認すると、SOC_PATH が空になっています。まだ何か足りないようですので、調べます。SOC_PATH を定義しているのは下記の部分です。

cmake が SOC_PATH を決める箇所

# zephyr/cmake/app/boilerplate.cmake

...

set(SOC_NAME   ${CONFIG_SOC})
set(SOC_SERIES ${CONFIG_SOC_SERIES})
set(SOC_FAMILY ${CONFIG_SOC_FAMILY})

if("${SOC_SERIES}" STREQUAL "")
  set(SOC_PATH ${SOC_NAME})
else()
  set(SOC_PATH ${SOC_FAMILY}/${SOC_SERIES})
endif()

CONFIG_SOC もしくは CONFIG_SOC_FAMILY と CONFIG_SOC_SERIES を定義する必要があるようです。探してみると CONFIG_SOC_FAMILY は zephyr/soc/riscv/riscv-privilege/Kconfig で定義されています。

もう一方の CONFIG_SOC_SERIES は Kconfig.defconfig.series というとても変な名前の Kconfig ファイルで定義するようです。

CONFIG_SOC_SERIES の定義箇所
$ cd zephyr/soc/riscv/riscv-privilege
$ grep -r 'config SOC_SERIES'

miv/Kconfig.defconfig.series:config SOC_SERIES
miv/Kconfig.series:config SOC_SERIES_RISCV32_MIV
sifive-freedom/Kconfig.defconfig.series:config SOC_SERIES
sifive-freedom/Kconfig.series:config SOC_SERIES_RISCV_SIFIVE_FREEDOM

この Kconfig はどこから参照されているのでしょうか?これも一応、調べておきましょう。

Kconfig.defconfig.series の依存関係

# zephyr/Kconfig

  source "Kconfig.zephyr"

# zephyr/Kconfig.zephyr

  source "$(SOC_DIR)/$(ARCH)/*/Kconfig.defconfig"    # SOC_DIR = soc, ARCH = riscv


# zephyr/soc/riscv/riscv-privilege/Kconfig.defconfig

  source "soc/riscv/riscv-privilege/*/Kconfig.defconfig.series"

Kconfig.defconfig.series も他の SoC に習って追加しましょう。Spike に PLIC は実装されていないので、RISCV_HAS_PLIC は n にしています。また Flash ROM もないので、XIP(Execution In Place)を無効にしています。

zephyr/soc/riscv/riscv-privilege/spike/Kconfig.defconfig.series

# SPDX-License-Identifier: Apache-2.0

if SOC_SERIES_RISCV_SPIKE

config SOC_SERIES
        default "spike"

config RISCV_HAS_PLIC
        default n

config NUM_IRQS
        default 16

config XIP
        default n

endif #SOC_SERIES_RISCV_SPIKE

肝心の linker.ld を追加することも忘れないようにします。これも他の SoC を見ればわかるはず。

zephyr/soc/riscv/riscv-privilege/spike/linker.ld

/*
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @brief Linker script for the Spike series processor
 */

#include <arch/riscv/common/linker.ld>

これで Kconfig 周りが整ったはずです。

最後の一手は CMakeList.txt

これでクリアかと思いきや、まだ cmake に怒られます。もう一手だけ必要です。

cmake 最後のエラー
CMake Error at ../../soc/riscv/riscv-privilege/CMakeLists.txt:4 (add_subdirectory):
  The source directory

    /home/katsuhiro/share/projects/oss/zephyr/soc/riscv/riscv-privilege/spike

  does not contain a CMakeLists.txt file.


-- Configuring incomplete, errors occurred!

CMakeLists.txt がないと言っています。このファイルには何を書くのが正解なのか、私もイマイチ理解できないですが、他の SoC を見ると、下記の一行を書いておけば良さそうです。

zephyr/soc/riscv/riscv-privilege/spike/CMakeLists.txt

# SPDX-License-Identifier: Apache-2.0

zephyr_sources()

ここまで変更すると cmake のエラーが解消します。良く見ると何やら UART_CONSOLE がどうのこうのと文句を言っていますが、後ほど対応します。

Hoge ボード + Spike SoC で cmake 成功

$ cmake -G Ninja -DBOARD=hoge ../samples/hello_world/
-- Zephyr version: 2.1.99
-- Found PythonInterp: /usr/bin/python3 (found suitable version "3.7.6", minimum required is "3.6")
-- Selected BOARD hoge
-- Loading zephyr/boards/riscv/hoge/hoge.dts as base
hoge.dts.pre.tmp:22.17-25.5: Warning (simple_bus_reg): /soc/serial: missing or empty reg/ranges property
  also defined at hoge.dts.pre.tmp:37.8-39.3
Devicetree header saved to 'zephyr/build/zephyr/include/generated/devicetree_unfixed.h'
Parsing zephyr/Kconfig
Loaded configuration 'zephyr/boards/riscv/hoge/hoge_defconfig'
Merged configuration 'zephyr/samples/hello_world/prj.conf'
Configuration saved to 'zephyr/build/zephyr/.config'
Kconfig header saved to 'zephyr/build/zephyr/include/generated/autoconf.h'

warning: UART_CONSOLE (defined at drivers/console/Kconfig:47) was assigned the value 'y' but got the
value 'n'. Check these unsatisfied dependencies: SERIAL_HAS_DRIVER (=n). See
http://docs.zephyrproject.org/latest/reference/kconfig/CONFIG_UART_CONSOLE.html and/or look up
UART_CONSOLE in the menuconfig/guiconfig interface. The Application Development Primer, Setting
Configuration Values, and Kconfig - Tips and Best Practices sections of the manual might be helpful
too.

-- The C compiler identification is GNU 8.3.0
-- The CXX compiler identification is GNU 8.3.0
-- The ASM compiler identification is GNU
-- Found assembler: /home/katsuhiro/x-tools/riscv64-zephyr-elf/bin/riscv64-zephyr-elf-gcc
-- Cache files will be written to: /home/katsuhiro/.cache/zephyr
-- Configuring done
-- Generating done
-- Build files have been written to: zephyr/build

この後は ninja のビルドエラーがたくさん出るので、1つずつ解消していくことになります。続きはまた次回です。

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

コメント一覧

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



link permalink

link 編集する

Zephyr OS で遊ぼう その 4 - 独自の SoC 定義

Hoge ボードの定義を一通り実装しました。Hoge ボードの SoC は qemu_riscv32 と同じ SiFive FE310 にしたことを覚えているでしょうか?Zephyr の移植に興味のある方からすると「ボード名を変えただけで、qemu_riscv32 のコピーでは?」と感じるはずです。そんな悲しみの声にお答えすべく、次は SoC の変更を行います。

今回、ターゲットとする SoC は QEMU の Spike モードです。このモードは、特別なハードウェアが必要なく手軽、偶然にも現状の Zephyr が対応していない、簡単なドライバの実装が 1つだけ必要、など学習にピッタリです。

Spike はシミュレータのみで、物理的に存在する SoC ではありません。ですが名前がないと説明しづらいので、以降の説明では Spike SoC と呼びます。

RISC-V SoC ディレクトリの構造と Spike SoC の定義場所

将来複雑になるかもしれませんが、今は簡単な構造です。zephyr/soc/riscv の下に SoC が定義されており、litex-vexriscv(LiteX VexRiscv), openisa_rv32m1(OpenISA RV32M1), riscv-privilege(RISC-V Priviledged Architecture 対応 SoC)の 3つのディレクトリが存在しています。このうち riscv-privilege は、さらに内部にいくつかの SoC の定義を抱えています。

Spike は RISC-V Priviledged に対応しているので、今回は riscv-privilege シリーズの 1つとして、新たな SoC を定義すると良さそうです。

ボードの定義を改造

Hoge ボードの定義を見直してみると、Kconfig.board に "depends on SOC_RISCV_SIFIVE_FREEDOM" の記述があり、hoge.dts に #include <riscv32-fe310.dtsi> の記述があります。とりあえずこれを下記のように変更します。

Kconfig.board と hoge.dts の変更点

diff --git a/boards/riscv/hoge/Kconfig.board b/boards/riscv/hoge/Kconfig.board
index 7ece0d6dee..c90614a391 100644
--- a/boards/riscv/hoge/Kconfig.board
+++ b/boards/riscv/hoge/Kconfig.board
@@ -2,4 +2,4 @@

 config BOARD_HOGE
        bool "Hoge target"
-       depends on SOC_RISCV_SIFIVE_FREEDOM
+       depends on SOC_RISCV_SPIKE


diff --git a/boards/riscv/hoge/hoge.dts b/boards/riscv/hoge/hoge.dts
index 21678f49ea..ee0c6589c4 100644
--- a/boards/riscv/hoge/hoge.dts
+++ b/boards/riscv/hoge/hoge.dts
@@ -2,7 +2,7 @@

 /dts-v1/;

-#include <riscv32-fe310.dtsi>
+#include <riscv32-spike.dtsi>

 / {
        model = "Hoge";

この状態で cmake をやり直します。念のため build ディレクトリのファイルを全て削除してから、実行したほうが良いでしょう。

Kconfig.board と hoge.dts を変更して cmake
$ cmake -G Ninja -DBOARD=hoge ../samples/hello_world/

-- Zephyr version: 2.1.99
-- Found PythonInterp: /usr/bin/python3 (found suitable version "3.7.6", minimum required is "3.6")
-- Selected BOARD hoge
-- Loading zephyr/boards/riscv/hoge/hoge.dts as base
In file included from <command-line>:
zephyr/boards/riscv/hoge/hoge.dts:5:10: fatal error: riscv32-spike.dtsi: No such file or directory
 #include <riscv32-spike.dtsi>
          ^~~~~~~~~~~~~~~~~~~~
compilation terminated.
CMake Error at zephyr/cmake/dts.cmake:140 (message):
  command failed with return code: 1
Call Stack (most recent call first):
  zephyr/cmake/app/boilerplate.cmake:460 (include)
  CMakeLists.txt:5 (include)


-- Configuring incomplete, errors occurred!

エラーをみるに riscv32-spike.dtsi がないと言って怒られているようですから、追加しましょう。

SoC のデバイスツリー

Zephyr のデバイスツリーは zephyr/dts 以下にあります。今回は RISC-V 一派の SoC を追加しますから、zephyr/dts/riscv の下に riscv32-spike.dtsi を作りましょう。

riscv32-spike.dtsi

/* SPDX-License-Identifier: Apache-2.0 */

/ {
        #address-cells = <1>;
        #size-cells = <1>;
        compatible = "spike-dev";
        model = "spike";
        soc {
                #address-cells = <1>;
                #size-cells = <1>;
                compatible = "spike-soc", "simple-bus";
                ranges;

                clint: clint@2000000 {
                        compatible = "riscv,clint0";
                        reg = <0x02000000 0x10000>;
                        reg-names = "control";
                };

                mem: mem@80000000 {
                        compatible = "spike,mem";
                        reg = <0x80000000 0x4000>;
                        reg-names = "mem";
                };

                uart0: serial {
                        compatible = "spike,uart-spike";
                        label = "uart_0";
                };
        };
};

最低限のデバイスだけ定義しています。

clint
割り込みコントローラ(Core Local Interruptor)です。タイマーのレジスタ(mtime, mtimecmp)を持っているコアです。
mem
SRAM 領域です。QEMU は数百 MB くらいメモリ領域があった気がしますが、そんなに要らないので 16KB にしています。
uart0
シリアルデバイスです。実はこの名前は適切ではありません。Spike は特殊なゲスト、ホスト間の通信により文字出力を実現していて、UART ハードウェアを模している訳ではないからです。レジスタ領域の定義が不要なのも、ハードウェアアクセスではないからです。

このファイルを加えてもまだ cmake に怒られます。Spike SoC 用のデバイスツリーに出てこないノード(gpio, spi など)を参照している箇所があるためです。ボード側の定義からばっさり削除します。

hoge.dts のさらなる変更

diff --git a/boards/riscv/hoge/hoge.dts b/boards/riscv/hoge/hoge.dts
index 21678f49ea..e572d5a769 100644
--- a/boards/riscv/hoge/hoge.dts
+++ b/boards/riscv/hoge/hoge.dts
@@ -11,34 +11,10 @@
        chosen {
                zephyr,console = &uart0;
                zephyr,shell-uart = &uart0;
-               zephyr,sram = &dtim;
-               zephyr,flash = &flash0;
+               zephyr,sram = &mem;
        };
 };
 
-&gpio0 {
-       status = "okay";
-};
-
 &uart0 {
        status = "okay";
-       current-speed = <115200>;
-       clock-frequency = <16000000>;
-};
-
-&spi0 {
-       status = "okay";
-
-       #address-cells = <1>;
-       #size-cells = <0>;
-       reg = <0x10014000 0x1000 0x20400000 0xc00000>;
-       flash0: flash@0 {
-               compatible = "issi,is25lp128", "jedec,spi-nor";
-               size = <134217728>;
-               label = "FLASH0";
-               jedec-id = [96 60 18];
-               reg = <0>;
-               // Dummy entry
-               spi-max-frequency = <0>;
-       };
 };

これでもまだ cmake は通過できません。長くなってきましたので、続きはまた今度。

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

コメント一覧

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



link permalink

link 編集する

Zephyr OS で遊ぼう その 3 - ボードのデフォルトコンフィグ

前回はデフォルトコンフィグが x86 になっていたり、ドライバを有効にしないとリンクエラーになったり、おかしなことが起きていました。対策として手動でコンフィグを変えまくりましたが、本来は必要ありません。Zephyr のボード定義では、ボードごとにデフォルトコンフィグを持つことができるからです。

他のボード(zephyr/boards/riscv/qemu_riscv32/qemu_riscv32_defconfig など)を見ると、ボード名_defconfig というファイルの中に CONFIG_ABCD=y という文がたくさんあります。このファイルに追加すると反映されそうです。
(2/19 訂正: CONFIG_SYS_CLOCK_TICKS_PER_SEC=100 を追加しないとタイマー割り込みが入りすぎて動かなくなるため、修正しました)

ボードの defconfig: zephyr/boards/riscv/hoge/hoge_defconfig

# SPDX-License-Identifier: Apache-2.0

CONFIG_RISCV=y
CONFIG_SOC_SERIES_RISCV_SIFIVE_FREEDOM=y
CONFIG_SOC_RISCV_SIFIVE_FREEDOM=y
CONFIG_BOARD_HOGE=y
CONFIG_CONSOLE=y
CONFIG_PRINTK=y
CONFIG_SERIAL=y
CONFIG_UART_SIFIVE=y
CONFIG_UART_SIFIVE_PORT_0=y
CONFIG_UART_CONSOLE=y
CONFIG_PLIC=y
CONFIG_PINMUX=y
CONFIG_PINMUX_SIFIVE=y
CONFIG_RISCV_MACHINE_TIMER=y
CONFIG_GPIO=y
CONFIG_GPIO_SIFIVE=y
CONFIG_SYS_CLOCK_TICKS_PER_SEC=100
CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC=10000000

CONFIG_BOARD_HOGE を除けば、ほぼ qemu_riscv32_defconfig と同じです。

ボードの defconfig を追加した後の cmake とビルド
$ cd build
$ rm -r *

$ cmake -G Ninja -DBOARD=hoge ../samples/hello_world/

-- Zephyr version: 2.1.99
-- Found PythonInterp: /usr/bin/python3 (found suitable version "3.7.6", minimum required is "3.6")
-- Selected BOARD hoge
-- Loading zephyr/boards/riscv/hoge/hoge.dts as base
Devicetree header saved to 'zephyr/build/zephyr/include/generated/devicetree_unfixed.h'
Parsing zephyr/Kconfig
Loaded configuration 'zephyr/boards/riscv/hoge/hoge_defconfig'
Merged configuration 'zephyr/samples/hello_world/prj.conf'
Configuration saved to 'zephyr/build/zephyr/.config'
Kconfig header saved to 'zephyr/build/zephyr/include/generated/autoconf.h'
-- The C compiler identification is GNU 8.3.0
-- The CXX compiler identification is GNU 8.3.0
-- The ASM compiler identification is GNU
-- Found assembler: /home/katsuhiro/x-tools/riscv64-zephyr-elf/bin/riscv64-zephyr-elf-gcc
-- Cache files will be written to: /home/katsuhiro/.cache/zephyr
-- Configuring done
-- Generating done
-- Build files have been written to: zephyr/build


$ ninja

[0/1] Re-running CMake...
-- Zephyr version: 2.1.99
-- Selected BOARD hoge
-- Loading zephyr/boards/riscv/hoge/hoge.dts as base
Devicetree header saved to 'zephyr/build/zephyr/include/generated/devicetree_unfixed.h'
Parsing zephyr/Kconfig
Loaded configuration 'zephyr/build/zephyr/.config'
No change to configuration in 'zephyr/build/zephyr/.config'
No change to Kconfig header in 'zephyr/build/zephyr/include/generated/autoconf.h'
-- Cache files will be written to: /home/katsuhiro/.cache/zephyr
-- Configuring done
-- Generating done
-- Build files have been written to: zephyr/build
[97/102] Linking C executable zephyr/zephyr_prebuilt.elf
Memory region         Used Size  Region Size  %age Used
             ROM:       12761 B        12 MB      0.10%
             RAM:        4048 B        16 KB     24.71%
        IDT_LIST:         553 B         2 KB     27.00%
[102/102] Linking C executable zephyr/zephyr.elf

この状態で cmake から実行すると、ビルド時に文句を言われなくなります。build ディレクトリには、以前 cmake を実行したときの設定ファイルが残っていますので、一度全て削除してから再度 cmake を実行した方が良いでしょう。特に -DBOARD を変更するときは削除したほうが良いです(cmake にも同様の内容を注意されるはず)。

次回以降は、独自の SoC の追加に挑もうと思います。

補足: デバイスツリーと DT_INST_ マクロの関係

前回は説明を省きましたが、デバイスツリーから DT_INST_ なんとかマクロへの変換ルールは下記にあります。

DT_INST_ マクロを生成している箇所

# zephyr/cmake/dts.cmake

...

  #
  # Run gen_defines.py to create a .conf file and a header file
  #

  set(CMD_NEW_EXTRACT ${PYTHON_EXECUTABLE} ${ZEPHYR_BASE}/scripts/dts/gen_defines.py
  --dts ${BOARD}.dts.pre.tmp
  --bindings-dirs ${DTS_ROOT_BINDINGS}
  --conf-out ${DEVICETREE_CONF}
  --header-out ${DEVICETREE_UNFIXED_H}
  --dts-out ${PROJECT_BINARY_DIR}/zephyr.dts # As a debugging aid
  )

...


# zephyr/scripts/dts/gen_defines.py

def main():
    global conf_file
    global header_file
    global flash_area_num

    ...

    for node in sorted(edt.nodes, key=lambda node: node.dep_ordinal):
        write_node_comment(node)

        # Flash partition nodes are handled as a special case. It
        # would be nicer if we had bindings that would let us
        # avoid that, but this will do for now.
        if node.name.startswith("partition@"):
            write_flash_partition(node, flash_area_num)
            flash_area_num += 1

        if node.enabled and node.matching_compat:
            write_regs(node)
            write_irqs(node)
            write_props(node)
            write_clocks(node)
            write_spi_dev(node)
            write_bus(node)
            write_existence_flags(node)

    ...

def write_existence_flags(node):
    # Generate #defines of the form
    #
    #   #define DT_INST_<instance no.>_<compatible string> 1
    #
    # for enabled nodes. These are flags for which devices exist.

    for compat in node.compats:
        instance_no = node.edt.compat2enabled[compat].index(node)
        out(f"INST_{instance_no}_{str2ident(compat)}", 1)

...

def str2ident(s):
    # Converts 's' to a form suitable for (part of) an identifier

    return s.replace("-", "_") \
            .replace(",", "_") \
            .replace("@", "_") \
            .replace("/", "_") \
            .replace(".", "_") \
            .replace("+", "PLUS") \
            .upper()

基本的にノードの compatible がそのまま使われますが、C 言語のマクロ名として使用できない文字はアンダースコアに置換されます。compatible は小文字で書くことが多いですが、マクロ名は大文字に変換しています。

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

コメント一覧

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



link permalink

link 編集する

Zephyr OS で遊ぼう その 2 - ボードを追加してビルド

前回 ninja を実行したときに嫌というほどエラーが出ました。エラーを追う前に、コンフィグがあっているかチェックしたいと思います。

Zephyr は Linux Kernel と似たコンフィグのシステム(Kconfig)を持っています。使い方も Linux と似ており、ninja menuconfig とすると、ケバケバしい色(私の環境だと真っ白+黄色になる……)の画面が表示されます。


menuconfig の画面

なぜか Architecture が x86 となっていたり、SoC が LiteX になっていたり、色々おかしいです。なぜこうなってしまったかは後にして、手当たり次第でそれらしい値に直すと、Board Selection に今回追加した Hoge target が出現します。

menuconfig で手修正する箇所
$ ninja menuconfig

Architecture (x86 architecture)  --->
  ( ) RISCV architecture を選択

SoC/CPU/Configuration Selection (LiteX VexRiscv system implementation)  --->
  ( ) SiFive Freedom SOC implementation を選択

コンフィグがそれらしくなったら ninja でビルドします。

menuconfig で手修正後のビルド
$ ninja

zephyr/samples/hello_world/src/main.c:12:30: error: 'CONFIG_BOARD' undeclared (first use in this function); did you mean 'CONFIG_ARCH'?
  printk("Hello World! %s\n", CONFIG_BOARD);
                              ^~~~~~~~~~~~
                              CONFIG_ARCH

...

In file included from ../include/devicetree.h:12,
                 from ../soc/riscv/riscv-privilege/sifive-freedom/soc.h:15,
                 from ../include/arch/riscv/arch.h:25, 
                 from ../include/arch/cpu.h:23,
                 from ../include/kernel_includes.h:34, 
                 from ../include/kernel.h:17,
                 from zephyr/drivers/interrupt_controller/intc_plic.c:13:
zephyr/drivers/interrupt_controller/intc_plic.c: In function 'riscv_plic_irq_enable':
zephyr/include/generated/devicetree_fixups.h:8:25: error: 'DT_INST_0_SIFIVE_PLIC_1_0_0_IRQ_EN_BASE_ADDRESS' undeclared (first use in this function)
 #define DT_PLIC_IRQ_EN  DT_INST_0_SIFIVE_PLIC_1_0_0_IRQ_EN_BASE_ADDRESS
                         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

...

先程よりはマシになりましたが、まだエラーが山のように出ます。エラーの種類を大別すると CONFIG_BOARD の定義がない、DT_INST_ なんちゃらの定義がない、に分けられます。

最初の CONFIG_BOARD については、いかにもボード側で定義しなければならなさそうな名前です。おなじみ他のボードを参照(boards/riscv/qemu_riscv32/Kconfig.defconfig など)すると、Kconfig.board で定義した config BOARD_HOGE が定義されているときに限り、CONFIG_BOARD にボード名のデフォルト値を設定していました。

つまり、このように書けば良いみたいです。

Kconfig.defconfig

# SPDX-License-Identifier: Apache-2.0

if BOARD_HOGE

config BOARD
	default "hoge"

endif

それ以外の DT_INST_ なんちゃらの定義については、少し複雑です。

Zephyr のデバイスツリーの扱い

Linux ではデバイスツリーファイル(*.dts)をデバイスツリーコンパイラ dtc でコンパイルして、Flattened Device Tree(fdt)という形式のバイナリにします。fdt はカーネル内部に組み込んだり、ブートローダが起動時にカーネルに渡すなどして利用されます。

しかし Zephyr はそもそも dtc を使わず、Python で処理します。デバイスツリーを書き間違えると Python スクリプトが怒ってくるのですが、これが理由みたいです。なんだと?と思う方は Zephyr のドキュメント(Devicetree - Zephyr Project Documentation)をご参照ください。下記に引用します。

Note: In addition to the Python code above, the standard dtc DTS compiler is also run on the devicetree. This is just to catch any errors or warnings it generates. The output is unused.

ドキュメントの Generated macros の章を見ると、デバイスツリーから DT_INST_ なんちゃら、というマクロを生成する、という説明があり、ビルドエラーの原因となったマクロの仲間のようです。

デバイスツリーを足そう

推測するにデバイスツリーを何も定義していないことが、DT_INST_ なんちゃらマクロが存在しないことの原因ではないでしょうか?他のボード(boards/riscv/qemu_riscv32/qemu_riscv32.dts など)がデバイスツリーをどうしているか、見ながら真似してみます。

hoge.dts

/* SPDX-License-Identifier: Apache-2.0 */

/dts-v1/;

#include <riscv32-fe310.dtsi>

/ {
	model = "Hoge Board";
	compatible = "hoge,hoge";

	chosen {
		zephyr,console = &uart0;
		zephyr,shell-uart = &uart0;
		zephyr,sram = &dtim;
		zephyr,flash = &flash0;
	};
};

&gpio0 {
	status = "okay";
};

&uart0 {
	status = "okay";
	current-speed = <115200>;
	clock-frequency = <16000000>;
};

&spi0 {
	status = "okay";

	#address-cells = <1>;
	#size-cells = <0>;
	reg = <0x10014000 0x1000 0x20400000 0xc00000>;
	flash0: flash@0 {
		compatible = "issi,is25lp128", "jedec,spi-nor";
		size = <134217728>;
		label = "FLASH0";
		jedec-id = [96 60 18];
		reg = <0>;
		// Dummy entry
		spi-max-frequency = <0>;
	};
};

ほぼ全てコピーしただけです。ビルドしてみると、最後の方まで行きますが、いくつかシンボルがないと言われます。ログはフルパスが出ていて鬱陶しいので意味が残る程度に削っています。

デバイスツリー定義後のビルドエラー
$ ninja

...

riscv64-zephyr-elf/bin/ld: zephyr/arch/arch/riscv/core/libarch__riscv__core.a(isr.S.obj): in function `.L0 ':
zephyr/arch/riscv/core/isr.S:244: undefined reference to `_sw_isr_table'
riscv64-zephyr-elf/bin/ld: zephyr/kernel/libkernel.a(sched.c.obj): in function `z_reset_time_slice':
zephyr/kernel/sched.c:276: undefined reference to `z_clock_elapsed'
riscv64-zephyr-elf/bin/ld: zephyr/kernel/libkernel.a(timeout.c.obj): in function `elapsed':
zephyr/kernel/timeout.c:69: undefined reference to `z_clock_elapsed'
collect2: error: ld returned 1 exit status
%
ninja: build stopped: subcommand failed.

これらのシンボルはドライバが提供するもののようですので、コンフィグでそれらしきものを ON にしていきます。

コンフィグ追加
General Architecture Options  --->
  Interrupt Configuration  --->
    [ ] Use generated IRQ tables を選択

General Kernel Options  --->
  [ ] Execute in place

Device Drivers  --->
  [ ] Serial Drivers  ----
    [ ] Enable UART Interrupt support を選択
    [ ] SiFive Freedom serial driver (NEW)  ----
      [ ] Enable SIFIVE Port 0 (NEW)  ---- を選択

  [ ] Console drivers  ---
    [ ] Use UART for console (NEW) を選択

  Timer Drivers  --->
    [ ] RISCV Machine Timer を選択

  [ ] GPIO Drivers  ----
    [ ] SiFive Freedom Processor GPIO driver (NEW)  ---- を選択

  [ ] Enable board pinmux driver  ----
    [ ] SiFive Freedom SOC pinmux driver (NEW)  ----

このように全て手動で設定する必要はない(後々不要となる)ので、あまり詳しくなる必要はないですが、

SiFive 関連
SiFive の SoC なのでコンフィグを片っ端から ON にしています
Use generated IRQ tables
_sw_isr_table シンボルの定義に関わっています
Execute in Place(XIP)
ON にしないと、ビルドしたときに RAM サイズから溢れているというエラーで怒られます
RISCV Machine Timer
z_clock_elapsed シンボルの定義に関わっています

コンフィグがそれらしくなったら ninja でビルドして、実行します。

コンフィグ追加後、ビルド&実行
$ ninja

...

Memory region         Used Size  Region Size  %age Used
             ROM:       13165 B        12 MB      0.10%
             RAM:        3808 B        16 KB     23.24%
        IDT_LIST:         569 B         2 KB     27.78%
[98/98] Linking C executable zephyr/zephyr.elf

$ /usr/bin/qemu-system-riscv32 -nographic -machine sifive_e -net none -chardev stdio,id=con,mux=on -serial chardev:con -mon chardev=con,mode=readline -kernel /home/katsuhiro/share/projects/oss/zephyr/build/zephyr/zephyr.elf

*** Booting Zephyr OS build zephyr-v2.1.0-1471-g7e7a4426d835  ***
Hello World! hoge

実行できました。Hello World の後ろがボード名(CONFIG_BOARD)です。無事、アプリが hoge ボード上で動いたということですね。次回は面倒くさかった手動コンフィグの撲滅に挑みます。

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

コメント一覧

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



link permalink

link 編集する

Zephyr OS で遊ぼう その 1 - ボードを追加して cmake

先日(2020年 1月 31日の日記参照)RISC-V 32bit 版の Zephyr OS が動作しました。気分を変えて、別のボードへの移植に挑戦してみたいと思います。

名前は何でも良いのですが、とりあえず hoge ボードということにします。-DBOARD=hoge を指定して cmake を実行すると、そんなものはないと怒られ、猛烈な勢いでヘルプメッセージが出るとともに、サポートされているボードの一覧が出てきます。

存在しないボードを指定したときの、ボード一覧
$ cmake -G Ninja -DBOARD=hoge ../samples/hello_world/

-- Zephyr version: 2.1.99
-- Found PythonInterp: /usr/bin/python3 (found suitable version "3.7.6", minimum required is "3.6")
-- Selected BOARD hoge
No board named 'hoge' found
see usage:

...

Supported Boards:

...

  riscv:
    hifive1
    hifive1_revb
    litex_vexriscv
    m2gl025_miv
    qemu_riscv32
    qemu_riscv64
    rv32m1_vega_ri5cy
    rv32m1_vega_zero_riscy
  x86:
    acrn
    gpmrb
    minnowboard
    qemu_x86_64
    qemu_x86_coverage
    qemu_x86
    qemu_x86_nommu
    up_squared

...

まず、この一覧に hoge ボードを載せるのが第一段階です。ボードの一覧を調べている cmake のスクリプトを見ます。

ボード一覧を調べている箇所

# zephyr/cmake/app/boilerplate.cmake

...

foreach(root ${BOARD_ROOT})
  # NB: find_path will return immediately if the output variable is
  # already set
  find_path(BOARD_DIR
    NAMES ${BOARD}_defconfig
    PATHS ${root}/boards/*/*
    NO_DEFAULT_PATH
    )
  if(BOARD_DIR AND NOT (${root} STREQUAL ${ZEPHYR_BASE}))
    set(USING_OUT_OF_TREE_BOARD 1)
  endif()

...

これで探しているように見えます。boards/*/* ディレクトリに「ボード名_defconfig」という名前のファイルがあれば良さそうです。足してから cmake を再度、存在しないボード名(hoge2 とかで良いです)で実行しましょう。

defconfig を足した後のボード一覧
$ mkdir boards/riscv/hoge
$ touch boards/riscv/hoge/hoge_defconfig

$ cmake -G Ninja -DBOARD=hoge2 ../samples/hello_world/

...

  riscv:
    hifive1
    hifive1_revb
    hoge        ★hoge が出てきた★
    litex_vexriscv
    m2gl025_miv
    qemu_riscv32
    qemu_riscv64
    rv32m1_vega_ri5cy
    rv32m1_vega_zero_riscy

...

やりました。ボードの一覧に hoge が出ました。BOARD=hoge にして cmake を実行しましょう。

defconfig を足した後の cmake
$ cmake -G Ninja -DBOARD=hoge ../samples/hello_world/

-- Zephyr version: 2.1.99
-- Selected BOARD hoge
Parsing zephyr/Kconfig
zephyr/scripts/kconfig/kconfig.py: Kconfig.zephyr:26: 'zephyr/boards/riscv/hoge/Kconfig.defconfig' not found (in 'source "$(BOARD_DIR)/Kconfig.defconfig"'). Check that environment variables are set correctly (e.g. $srctree, which is set to 'zephyr'). Also note that unset environment variables expand to the empty string.
CMake Error at zephyr/cmake/kconfig.cmake:214 (message):
  command failed with return code: 1
Call Stack (most recent call first):
  zephyr/cmake/app/boilerplate.cmake:461 (include)
  CMakeLists.txt:5 (include)

-- Configuring incomplete, errors occurred!

まだ色々とエラーが出ています。エラーメッセージは Kconfig.defconfig がないと言っています。このファイルを足すと、今度は Kconfig.board がないと言われますので、両方とも足します。

Kconfig.defconfig, Kconfig.board を足した後の cmake
$ touch boards/riscv/hoge/Kconfig.defconfig
$ touch boards/riscv/hoge/Kconfig.board

$ cmake -G Ninja -DBOARD=hoge ../samples/hello_world/

-- Zephyr version: 2.1.99
-- Found PythonInterp: /usr/bin/python3 (found suitable version "3.7.6", minimum required is "3.6")
-- Selected BOARD hoge
Parsing zephyr/Kconfig
Loaded configuration 'zephyr/boards/riscv/hoge/hoge_defconfig'
Merged configuration 'zephyr/samples/hello_world/prj.conf'

warning: <choice> (defined at boards/Kconfig:19) defined with type unknown

error: Aborting due to non-whitelisted Kconfig warning 'warning: <choice> (defined at boards/Kconfig:19)
defined with type unknown'. If this warning doesn't point to an actual problem, you can add it to
the whitelist at the top of zephyr/scripts/kconfig/kconfig.py.

CMake Error at zephyr/cmake/kconfig.cmake:214 (message):
  command failed with return code: 1
Call Stack (most recent call first):
  zephyr/cmake/app/boilerplate.cmake:461 (include)
  CMakeLists.txt:5 (include)


-- Configuring incomplete, errors occurred!

今度は choice が無いと言っています。エラーを出しているのは下記の cmake ファイルです。

エラーを出している kconfig.cmake
# zephyr/cmake/kconfig.cmake

execute_process(
  COMMAND
  ${PYTHON_EXECUTABLE}
  ${ZEPHYR_BASE}/scripts/kconfig/kconfig.py
  ${input_configs_are_handwritten}
  ${KCONFIG_ROOT}
  ${DOTCONFIG}
  ${AUTOCONF_H}
  ${PARSED_KCONFIG_SOURCES_TXT}
  ${input_configs}
  WORKING_DIRECTORY ${APPLICATION_SOURCE_DIR}
  # The working directory is set to the app dir such that the user
  # can use relative paths in CONF_FILE, e.g. CONF_FILE=nrf5.conf
  RESULT_VARIABLE ret
  )
if(NOT "${ret}" STREQUAL "0")
  message(FATAL_ERROR "command failed with return code: ${ret}")
endif()

このまま kconfig.py を見ても良いですが、おそらく時間のムダです。親切なエラーメッセージが出ているからです。メッセージの言うとおり boards/Kconfig を見ます。

choice のエラーの原因
# boards/Kconfig

...

# Note: $BOARD_DIR might be a glob pattern

choice
	prompt "Board Selection"

source "$(BOARD_DIR)/Kconfig.board"    ★このファイルが空★

endchoice

先ほど作成した Kconfig.board が空っぽだったため、choice の選択肢が存在せず怒られているようです。書く内容については、別のボード(例えば zephyr/boards/riscv/qemu_riscv32/Kconfig.board)を参照すると良いと思います。今回はこんな内容にしました。

Kconfig.board を書き換え

# SPDX-License-Identifier: Apache-2.0
 
config BOARD_HOGE
	bool "Hoge target"
	depends on SOC_RISCV_SIFIVE_FREEDOM

この定義だと SiFive の Freedom が搭載されていることになりますが、depends on の先に手を出すには、SoC の定義を加えなければなりません。一度に紹介しても訳がわからないので、今回はボードに焦点を絞ります。

書き換えた後にもう一度 cmake を実行すると、成功します。

Kconfig.board を書き換えた後に cmake 実行
$ cmake -G Ninja -DBOARD=hoge ../samples/hello_world/

-- Zephyr version: 2.1.99
-- Found PythonInterp: /usr/bin/python3 (found suitable version "3.7.6", minimum required is "3.6")
-- Selected BOARD hoge
Parsing zephyr/Kconfig
Loaded configuration 'zephyr/boards/riscv/hoge/hoge_defconfig'
Merged configuration 'zephyr/samples/hello_world/prj.conf'
Configuration saved to 'zephyr/build/zephyr/.config'
-- The C compiler identification is GNU 8.3.0
-- The CXX compiler identification is GNU 8.3.0
-- The ASM compiler identification is GNU
-- Found assembler: /home/katsuhiro/x-tools/riscv64-zephyr-elf/bin/riscv64-zephyr-elf-gcc
-- Cache files will be written to: /home/katsuhiro/.cache/zephyr
-- Configuring done
-- Generating done
-- Build files have been written to: zephyr/build


$ ninja

[0/1] Re-running CMake...
-- Zephyr version: 2.1.99
-- Selected BOARD hoge
Parsing zephyr/Kconfig
Loaded configuration 'zephyr/build/zephyr/.config'
No change to 'zephyr/build/zephyr/.config'
-- Cache files will be written to: /home/katsuhiro/.cache/zephyr
-- Configuring done
-- Generating done
-- Build files have been written to: zephyr/build
[5/83] Building C object zephyr/CMakeFiles/offsets.dir/arch/riscv/core/offsets/offsets.c.obj
FAILED: zephyr/CMakeFiles/offsets.dir/arch/riscv/core/offsets/offsets.c.obj
ccache /home/katsuhiro/x-tools/riscv64-zephyr-elf/bin/riscv64-zephyr-elf-gcc -DBUILD_VERSION=zephyr-v2.1.0-1471-g7e7a4426d835 -DKERNEL -D_FORTIFY_SOURCE=2 -D__ZEPHYR__=1 -I../kernel/include -I../arch/riscv/include -I../include -Izephyr/include/generated -I../soc/riscv/litex-vexriscv -isystem ../lib/libc/minimal/include -isystem /home/katsuhiro/x-tools/riscv64-zephyr-elf/lib/gcc/riscv64-zephyr-elf/8.3.0/include -isystem /home/katsuhiro/x-tools/riscv64-zephyr-elf/lib/gcc/riscv64-zephyr-elf/8.3.0/include-fixed -Os -imacroszephyr/build/zephyr/include/generated/autoconf.h -ffreestanding -fno-common -g -mabi=ilp32 -march=rv32ima -imacroszephyr/include/toolchain/zephyr_stdint.h -Wall -Wformat -Wformat-security -Wno-format-zero-length -Wno-main -Wno-pointer-sign -Wpointer-arith -Wno-unused-but-set-variable -Werror=implicit-int -fno-asynchronous-unwind-tables -fno-pie -fno-pic -fno-strict-overflow -fno-reorder-functions -fno-defer-pop -fmacro-prefix-map=zephyr/samples/hello_world=CMAKE_SOURCE_DIR -fmacro-prefix-map=zephyr=ZEPHYR_BASE -ffunction-sections -fdata-sections -std=c99 -nostdinc -MD -MT zephyr/CMakeFiles/offsets.dir/arch/riscv/core/offsets/offsets.c.obj -MF zephyr/CMakeFiles/offsets.dir/arch/riscv/core/offsets/offsets.c.obj.d -o zephyr/CMakeFiles/offsets.dir/arch/riscv/core/offsets/offsets.c.obj   -c zephyr/arch/riscv/core/offsets/offsets.c
In file included from ../include/sys/atomic.h:468,
                 from ../include/kernel_includes.h:21, 
                 from ../include/kernel.h:17,
                 from zephyr/arch/riscv/core/offsets/offsets.c:16:
zephyr/include/generated/syscalls/atomic.h:25:19: error: conflicting types for 'atomic_cas'
 static inline int atomic_cas(atomic_t * target, atomic_val_t old_value, atomic_val_t new_value)
                   ^~~~~~~~~~
In file included from ../include/kernel_includes.h:21, 
                 from ../include/kernel.h:17,
                 from zephyr/arch/riscv/core/offsets/offsets.c:16:
../include/sys/atomic.h:44:20: note: previous definition of 'atomic_cas' was here
 static inline bool atomic_cas(atomic_t *target, atomic_val_t old_value,

せっかく cmake がうまくいった、と思ったのも束の間で、ninja は見るのが嫌になるくらい大量のエラーを表示して失敗します。続きはまた今度。

[編集者: すずき]
[更新: 2020年 2月 16日 18:21]

コメント一覧

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



link permalink

link 編集する

Zephyr のエントリアドレス

Zephyr を qemu_riscv32 ボード向けにコンフィグし、ビルドディレクトリ下で ninja run を実行すると、最終的に下記のコマンドが実行され、QMEU が起動します。

QEMU の起動コマンド
$ /usr/bin/qemu-system-riscv32 -nographic -machine sifive_e -net none -chardev stdio,id=con,mux=on -serial chardev:con -mon chardev=con,mode=readline -kernel zephyr/build/zephyr/zephyr.elf -s -S

GDB で追うとわかりますが、QEMU の実行開始アドレスは 0x1000 です。0x1000 には 0x20400000 にジャンプするコードだけが置かれています。

0x1000 にあるコード

   0x1000:      lui     t0,0x20400
=> 0x1004:      jr      t0
   0x1008:      unimp
   0x100a:      unimp

HiFive1 実機であれば 0x20400000 は QSPI Flash がマッピングされているアドレスです(SiFive FE310-G000 Manual v2p3 を参照)。QEMU の場合は ELF ファイル build/zephyr/zephyr.elf のセクション情報通りに配置されます。

0x1000 にあるコード
$ riscv64-zephyr-elf-readelf -a build/zephyr/zephyr.elf  | less

...

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        20400000 000094 000010 00  AX  0   0  4
  [ 2] exceptions        PROGBITS        20400010 0000a4 000258 00  AX  0   0  4
  [ 3] text              PROGBITS        20400268 0002fc 002ad4 00  AX  0   0  4
  [ 4] sw_isr_table      PROGBITS        20402d3c 002dd0 000200 00  WA  0   0  4
  [ 5] devconfig         PROGBITS        20402f3c 002fd0 000060 00   A  0   0  4
  [ 6] rodata            PROGBITS        20402f9c 003030 000215 00   A  0   0  4
  [ 7] datas             PROGBITS        80000000 003248 000018 00  WA  0   0  4
  [ 8] initlevel         PROGBITS        80000018 003260 000060 00  WA  0   0  4
  [ 9] _k_mutex_area     PROGBITS        80000078 0032c0 000014 00  WA  0   0  4
  [10] bss               NOBITS          80000090 0032e0 00013c 00  WA  0   0  8
  [11] noinit            NOBITS          800001d0 0032e0 000e00 00  WA  0   0 16

...

ジャンプ先の 0x20400000 には vector というセクションが対応していることがわかります。ELF ファイルを逆アセンブルしてみると、

0x20400000 にあるコード

zephyr/zephyr.elf:     file format elf32-littleriscv


Disassembly of section vector:

20400000 <__start>:

        /*
         * Set mtvec (Machine Trap-Vector Base-Address Register)
         * to __irq_wrapper.
         */
        la t0, __irq_wrapper
20400000:       00000297                auipc   t0,0x0
20400004:       01028293                addi    t0,t0,16 # 20400010 <__irq_wrapper>
        csrw mtvec, t0
20400008:       30529073                csrw    mtvec,t0

        /* Jump to __initialize */
        tail __initialize
2040000c:       4b40106f                j       204014c0 <__initialize>

以上のように __start という関数が居るようです。実装は zephyr/soc/riscv/riscv-privilege/common/vector.S にあります。

Zephyr とデバイスツリー

このエントリポイントアドレスはどのように決まるのでしょう?Zephyr では、ボード用のデバイスツリー zephyr/boards/riscv/qemu_riscv32/qemu_riscv32.dts にある Flash の先頭アドレスを変えると ELF のエントリポイント(0x20400000)も追従します。

ボード用のデバイスツリーの chosen ノード

        chosen {
                zephyr,console = &uart0;
                zephyr,shell-uart = &uart0;
                zephyr,sram = &dtim;
                zephyr,flash = &flash0;
        };

この chosen 以下を検知しているようです。スクリプト scripts/dts/gen_defines.py を見ると、SPI のときだけ特殊処理になっていて、それ以外は reg の値を先頭アドレスとみなしています。

Flash のサイズを見ている箇所

# zephyr/scripts/dts/gen_defines.py

...

def write_flash_node(edt):
    # Writes output for the top-level flash node pointed at by
    # zephyr,flash in /chosen

    node = edt.chosen_node("zephyr,flash")

    out_comment(f"/chosen/zephyr,flash ({node.path if node else 'missing'})")

    if not node:
        # No flash node. Write dummy values.
        out("FLASH_BASE_ADDRESS", 0)
        out("FLASH_SIZE", 0)
        return

    if len(node.regs) != 1:
        err("expected zephyr,flash to have a single register, has "
            f"{len(node.regs)}")

    if node.on_bus == "spi" and len(node.bus_node.regs) == 2:
        reg = node.bus_node.regs[1]  # QSPI flash
    else:
        reg = node.regs[0]

    out("FLASH_BASE_ADDRESS", hex(reg.addr))
    if reg.size:
        out("FLASH_SIZE", reg.size//1024)

...

Flash のアドレスは DT_FLASH_BASE_ADDRESS というマクロで参照できます。エントリーポイントを最終的に決めるのはリンカースクリプトです。SoC で独自のリンカースクリプトを実装することもできるようになっていますが、RISC-V の多くの SoC は、下記のスクリプトを使っています。

エントリーポイントを決めている箇所

// zephyr/include/arch/riscv/common/linker.ld

MEMORY
{
#ifdef CONFIG_XIP
    ROM (rx)  : ORIGIN = DT_FLASH_BASE_ADDRESS, LENGTH = KB(DT_FLASH_SIZE)
#endif
    RAM (rwx) : ORIGIN = CONFIG_SRAM_BASE_ADDRESS, LENGTH = KB(CONFIG_SRAM_SIZE)
    /* Used by and documented in include/linker/intlist.ld */
    IDT_LIST  (wx)      : ORIGIN = 0xFFFFF7FF, LENGTH = 2K
}

Zephyr のエントリポイントがどうやって決まるのか、少しわかった気がします。

[編集者: すずき]
[更新: 2020年 2月 16日 18:21]

コメント一覧

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



link もっと前
   2020年 2月 10日 -
      2020年 2月 1日  
link もっと後

管理用メニュー

link 記事を新規作成

合計:  counter total
本日:  counter today

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

最終更新: 3/15 02:33

カレンダー

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

最近のコメント 5件

  • link 13年11月28日
    すずき 「ご指摘ありがとうございます。投稿の古さに...」
    (更新:03/08 17:39)
  • link 13年11月28日
    yut 「古いので、見てもらえるか不明ですので、、...」
    (更新:03/08 13:16)
  • link 13年11月28日
    yut シムズ 「古い投稿に対して、申し訳ありません。\n...」
    (更新:03/08 13:14)
  • link 19年09月01日
    すずき 「私も正直びっくりです。間違って違う製品を...」
    (更新:09/04 23:39)
  • link 19年09月01日
    hdk 「車向けの製品の中でも、車載コンピューター...」
    (更新:09/02 23:20)

最近の記事 3件

link もっとみる
  • link 20年03月10日
    すずき 「[誕生日] 37歳になりました。おめでとう俺、ありがとう俺。30代...」
    (更新:03/15 02:33)
  • link 20年03月14日
    すずき 「[GCC を調べる - その 7 - machine mode] ...」
    (更新:03/15 02:21)
  • link 20年03月06日
    すずき 「[GCC を調べる - その 6 - GCC の regist] ...」
    (更新:03/11 22:47)

こんてんつ

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 サイトの情報