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

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

日々

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 もっと前
   2020年 2月 9日 -
      2020年 2月 9日  
link もっと後

管理用メニュー

link 記事を新規作成

合計:  counter total
本日:  counter today

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

最終更新: 2/22 03:01

カレンダー

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

最近のコメント 5件

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

最近の記事 3件

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

こんてんつ

open/close wiki
open/close Java API

過去の日記

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

その他の情報

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