コグノスケ


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

link もっと前
2020年2月16日 >>> 2020年2月3日
link もっと後

2020年2月16日

Zephyrのブート処理 - C言語と出会うまで

目次: Zephyr

以前(2020年2月1日の日記参照)Zephyrのエントリアドレスを調べましたが、もう少し先までZephyrのブート処理を調べます。復習となりますが、起動の仕方は下記のとおりです。

QEMU SpikeモードでZephyrを起動
$ 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 -s -S

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

...

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

ブート処理を追うにはデバッガが必須です(ブート部分でシリアル出力はできません)。これも前回の復習となりますが、QEMUはGDBでデバッグするためのオプションを持っています。オプション -sはGDBの接続をlocalhost:1234で受け付けます、という意味で、オプション -SはエミュレータをHalted状態で起動するという意味です。

GDBを接続すると、PCは0x1000を指しています。QEMU Spikeモードはリセット後0x1000から実行を開始するようです。何度かステップ実行siを行うと、0x1010から0x80000000にジャンプするはずです。

QEMUブート直後

   0x1000:      auipc   t0,0x0
   0x1004:      addi    a1,t0,32
   0x1008:      csrr    a0,mhartid
   0x100c:      lw      t0,24(t0)
=> 0x1010:      jr      t0        # 0x80000000にジャンプ

0x1000: 0x00000297      0x02028593      0xf1402573      0x0182a283
0x1010: 0x00028067      0x00000000      0x80000000      0x00000000
                                        ↑この値をロードする

特に難しい話ではなく0x1000 + 24 = 0x1018からロード(0x80000000が書かれている)して、ジャンプするだけです。ジャンプ先にはZephyrのエントリポイント __startがいます。

__start(0x80000000ジャンプ後)

// zephyr/soc/riscv/riscv-privilege/common/vector.S

SECTION_FUNC(vectors, __start)
        .option norvc;

        /*
         * Set mtvec (Machine Trap-Vector Base-Address Register)
         * to __irq_wrapper.
         */
        la t0, __irq_wrapper
        csrw mtvec, t0

        /* Jump to __initialize */
        tail __initialize


// zephyr/arch/riscv/core/isr.S

/*
 * Handler called upon each exception/interrupt/fault
 * In this architecture, system call (ECALL) is used to perform context
 * switching or IRQ offloading (when enabled).
 */
SECTION_FUNC(exception.entry, __irq_wrapper)
        /* Allocate space on thread stack to save registers */
        addi sp, sp, -__z_arch_esf_t_SIZEOF

        /*
         * Save caller-saved registers on current thread stack.
         * NOTE: need to be updated to account for floating-point registers
         * floating-point registers should be accounted for when corresponding
         * config variable is set
         */
        RV_OP_STOREREG ra, __z_arch_esf_t_ra_OFFSET(sp)
        RV_OP_STOREREG gp, __z_arch_esf_t_gp_OFFSET(sp)
        RV_OP_STOREREG tp, __z_arch_esf_t_tp_OFFSET(sp)
        RV_OP_STOREREG t0, __z_arch_esf_t_t0_OFFSET(sp)
        ...

__startは __irq_wrapper関数のアドレス(C言語の関数ではないので、関数と呼ぶのは変ですが)を割り込みベクタに設定したあと、__initializeにジャンプします。

__initialize

// 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

boot_master_core:

#ifdef CONFIG_INIT_STACKS
        ...
#endif

        /*
         * Initially, setup stack pointer to
         * _interrupt_stack + CONFIG_ISR_STACK_SIZE
         */
        la sp, _interrupt_stack
        li t0, CONFIG_ISR_STACK_SIZE
        add sp, sp, t0

#ifdef CONFIG_WDOG_INIT
        call _WdogInit
#endif

        /*
         * Jump into C domain. _PrepC zeroes BSS, copies rw data into RAM,
         * and then enters kernel z_cstart
         */
        call _PrepC

最初にmhartidというCPUコアのIDを読み取ります。0番をマスターコアとして、0番以外のコアは割り込み待ち(wfi)の無限ループに入ります。マスターコアはスタックポインタの初期化を行って、_PrepC() 関数を呼び出します。

ここからやっとC言語の世界です。

編集者:すずき(2023/09/24 12:02)

コメント一覧

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



2020年2月10日

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

目次: Zephyr

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より後発ですが、ビルド周りは退化したように感じます。不思議。

編集者:すずき(2023/09/24 12:06)

コメント一覧

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



2020年2月9日

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

目次: Zephyr

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

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のアドレスが逆だったために発生していたエラーです。アドレス修正後は表示されません)

パッチ

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

編集者:すずき(2023/09/24 12:05)

コメント一覧

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



2020年2月8日

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

目次: Zephyr

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()

...

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

編集者:すずき(2023/09/24 12:05)

コメント一覧

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



2020年2月7日

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

目次: Zephyr

やっと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を拝むには、シリアルのドライバを追加する必要があります。続きはまた次回。

編集者:すずき(2023/09/24 12:05)

コメント一覧

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



2020年2月6日

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

目次: Zephyr

今回は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つずつ解消していくことになります。続きはまた次回です。

編集者:すずき(2023/09/24 12:05)

コメント一覧

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



2020年2月5日

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

目次: Zephyr

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は通過できません。長くなってきましたので、続きはまた今度。

編集者:すずき(2023/09/24 12:05)

コメント一覧

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



2020年2月4日

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

目次: Zephyr

前回はデフォルトコンフィグが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は小文字で書くことが多いですが、マクロ名は大文字に変換しています。

編集者:すずき(2023/09/24 12:04)

コメント一覧

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



2020年2月3日

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

目次: Zephyr

前回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ボード上で動いたということですね。次回は面倒くさかった手動コンフィグの撲滅に挑みます。

編集者:すずき(2023/09/24 12:04)

コメント一覧

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



link もっと前
2020年2月16日 >>> 2020年2月3日
link もっと後

管理用メニュー

link 記事を新規作成

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

最近のコメント5件

  • link 24年10月1日
    すずきさん (10/06 03:41)
    「xrdpで十分動作しているので、Wayl...」
  • link 24年10月1日
    hdkさん (10/03 19:05)
    「GNOMEをお使いでしたら今はWayla...」
  • link 24年10月1日
    すずきさん (10/03 10:12)
    「私は逆にVNCサーバーに繋ぐ使い方をした...」
  • link 24年10月1日
    hdkさん (10/03 08:30)
    「おー、面白いですね。xrdpはすでに立ち...」
  • link 14年6月13日
    2048player...さん (09/26 01:04)
    「最後に、この式を出すのに紙4枚(A4)も...」

最近の記事3件

  • link 24年10月31日
    すずき (11/04 15:17)
    「[DENSOの最終勤務日] 最終勤務日でした、入門カードや会社のPCを返却してきました。在籍期間はNSITEXE(品川のオフィ...」
  • link 22年7月8日
    すずき (11/02 20:34)
    「[マンガ紹介 - まとめリンク] 目次: マンガ紹介一覧が欲しくなったので作りました。5作品乙女ゲームの破滅フラグしかない悪役...」
  • link 24年10月30日
    すずき (11/02 20:33)
    「[マンガ紹介] 目次: マンガ紹介お気に入りのマンガ紹介シリーズ。最近完結した短めの作品を紹介します。マイナススキル持ち四人が...」
link もっとみる

こんてんつ

open/close wiki
open/close Linux JM
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 2021年
open/close 2022年
open/close 2023年
open/close 2024年
open/close 過去日記について

その他の情報

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

合計:  counter total
本日:  counter today

link About www2.katsuster.net
RDFファイル RSS 1.0

最終更新: 11/04 15:17