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

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

link permalink

link 編集する

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

パッチ

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

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

コメント一覧

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



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

管理用メニュー

link 記事を新規作成

合計:  counter total
本日:  counter today

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

最終更新: 9/24 21:43

カレンダー

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

最近のコメント 5件

  • link 20年09月20日
    hdk 「最近は音楽聞く時やビデオ視聴時はミニコン...」
    (更新:09/24 21:43)
  • link 20年09月20日
    すずき 「ありゃー、同じ壊れ方ですね。\n新たなヘ...」
    (更新:09/24 00:23)
  • link 20年09月20日
    hdk 「うちのATH-AD300もやはり頭にプラ...」
    (更新:09/23 12:26)
  • link 20年07月10日
    すずき 「鳥のゲームは知りませんでした。色々やって...」
    (更新:08/11 18:59)
  • link 20年07月12日
    すずき 「小学生でサイトに投稿はスゴイです。そして...」
    (更新:08/11 18:59)

最近の記事 3件

link もっとみる
  • link 20年09月19日
    すずき 「[SHARP のマスク] 今となっては、高級マスクになってしまった...」
    (更新:09/23 04:48)
  • link 20年09月20日
    すずき 「[ヘッドフォンが壊れた] 以前(2012年 11月 8日の日記参照...」
    (更新:09/23 03:06)
  • link 20年09月22日
    すずき 「[3DMark] Steam のセールで 3DMark が意味不明...」
    (更新:09/23 02:55)

こんてんつ

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