コグノスケ


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

link もっと前
2020年12月17日 >>> 2020年12月4日
link もっと後

2020年12月17日

年末の帰省

実家と話し合って、今年末の北海道への帰省を取りやめました。年末がクッソ暇になりました。

帰省用の飛行機チケットを解約したんですが、ANAが最近やってる「あんしん変更キャンペーン」(ANA公式サイト)により、運賃や解約ルールが意味不明になってます。

買ったチケットは、

  • 行き: 羽田→千歳のスーパーバリュー75H
  • 帰り: 千歳→羽田のスーパーバリュー75H

計95,040円です。

解約の仕方で払戻金が全然違う

解約の仕方をいくつか試したところ、払戻金額の見積もりがべらぼうに変わり驚きました。

  • パターン1行き帰り同時解約: 払戻金額46,020円
  • パターン2帰り解約→行き解約: 払戻金額51,120円+16,000円くらい
  • パターン3帰り解約→行きを安い便(バリュー3Jとか)に変更→行き解約: 払戻金額51,120円+10,800円+30,640円

各パターンで何が起きているのでしょう?個人的な予想に過ぎませんが、

パターン1
スーパーバリュー75は搭乗2週間前の場合、キャンセル料50%なので、「あんしん変更キャンペーン」がなければこんなもんです。
パターン2
解約処理がイカレたのか、「あんしん変更キャンペーン」により、帰りだけ解約=予約変更と見なすのか、何だか知りませんが、キャンセル料なしで帰りのスーパーバリュー75の全額がしれっと返ってきます。意味がわからん。
パターン3
まず、現在、運賃の逆転現象が発生しています。通常は、
スーパーバリュー75< バリュー3
ですが、最近のコロナによる需要減で、
スーパーバリュー75> バリュー3
になっているので、本来高いはずのチケットに変えると1万円返ってくる謎現象が発生しています。この時点で意味不明ですが、バリュー3のキャンセル料は5%ですから、手数料も含めて2000円くらいで解約できます。キャンセル料50%はどこ行った?

バリュー系チケットの値段は需要連動(ANAの解説)らしく、11月のコロナ再流行で需要が急激に落ち込み、スーパーバリュー75Hとバリュー3Jの逆転現象が発生したのでしょう。この需要連動という想定そのものが「直前に需要が急減する」という事態を想定していない節がありますね。

もし10月くらいにスーパーバリュー系を予約した人は、1万円単位で大損してるはずです。年末に飛行機に乗る人は要確認です。

ANAの想定はパターン1なのか?

今のANAの一番良くないところは、普通に解約すると5万円近く大損するところですが、ANAのQAを見る限り「コロナ関連で解約の特別対応はしない、所定のキャンセル料を払え」とあるのでANAとしてはパターン1の解約でANAに5万円寄付せよ、という意思なんでしょうか?

私は、パターン3を試してみたので、手数料含めて2,500円くらいで解約できちゃったように見えます。

予約システムのバグだったんですかね?ちゃんと払い戻しされるかなあ??不安だわあ……。

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

編集者:すずき(2021/08/02 11:11)

コメント一覧

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



2020年12月13日

Zephyrと浮動小数点数命令のサポート その4 - ztestの仕組み、後編

目次: Zephyr

前半ではztest_test_suite() の実装を見ました。戻って見るのは面倒だと思うのでztestの使い方の例を再掲しておきます。

ztestの使い方(再掲)

// zephyr/tests/lib/sprint/src/main.c

void test_main(void)
{
	ztest_test_suite(test_sprintf,    //★変数名★
			 ztest_unit_test(test_sprintf_double),     //★テスト1つ目★
			 ztest_unit_test(test_sprintf_integer),    //★テスト2つ目★
			 ztest_unit_test(test_vsprintf),
			 ztest_unit_test(test_vsnprintf),
			 ztest_unit_test(test_sprintf_string),
			 ztest_unit_test(test_sprintf_misc));
	ztest_run_test_suite(test_sprintf);    
}

後半のztest_run_test_suite() マクロの実装を見ます。

ztest_run_test_suiteマクロ

// zephyr/subsys/testsuite/ztest/include/ztest_test.h

/**
 * @brief Run the specified test suite.
 *
 * @param suite Test suite to run.
 */
#define ztest_run_test_suite(suite) \
	z_ztest_run_test_suite(#suite, _##suite)


// zephyr/subsys/testsuite/ztest/src/ztest.c

void z_ztest_run_test_suite(const char *name, struct unit_test *suite)
{
	int fail = 0;

	if (test_status < 0) {
		return;
	}

	init_testing();    //★何もしない★

	PRINT("Running test suite %s\n", name);
	PRINT_LINE;
	while (suite->test) {
		fail += run_test(suite);    //★テストを実行★
		suite++;

		if (fail && FAIL_FAST) {
			break;
		}
	}
	if (fail) {
		TC_PRINT("Test suite %s failed.\n", name);
	} else {
		TC_PRINT("Test suite %s succeeded\n", name);
	}

	test_status = (test_status || fail) ? 1 : 0;
}

#ifndef KERNEL

...

#else

...

static int run_test(struct unit_test *test)
{
	int ret = TC_PASS;

	TC_START(test->name);
	//★テスト関数1つに対し、1つスレッドを作る★
	k_thread_create(&ztest_thread, ztest_thread_stack,
			K_THREAD_STACK_SIZEOF(ztest_thread_stack),
			(k_thread_entry_t) test_cb, (struct unit_test *)test,
			NULL, NULL, CONFIG_ZTEST_THREAD_PRIORITY,
			test->thread_options | K_INHERIT_PERMS,
				K_NO_WAIT);

	k_thread_name_set(&ztest_thread, "ztest_thread");
	k_thread_join(&ztest_thread, K_FOREVER);

	phase = TEST_PHASE_TEARDOWN;
	test->teardown();
	phase = TEST_PHASE_FRAMEWORK;

...

#endif /* !KERNEL */

関数run_test() は非常に特徴的で、ztestでは各ユニットテストを実行する際に、専用のスレッドを生成する仕組みになっています。浮動小数点数命令がIllegal Instruction例外になってしまうのは、この仕組みが原因です。

勘の良い人はZephyrのドキュメント(k_thread_create() へのリンク)を見ただけで、何が悪いかわかるかも。

手掛かりはAPIのoptions引数がtest->thread_options | K_INHERIT_PERMSになっていることです。前半で説明したとおりtest->thread_options = 0であり、K_INHERIT_PERMS以外のオプションは指定されません。スレッド内で浮動小数点数命令を使いたいならK_FP_REGSの指定が要るのでは?と思った方、その通りです。大正解。

浮動小数点数命令を使えるようになる仕組み

K_FP_REGSが答えですよと言われても、何だそれ?と思うほうが普通です(私もそうでした)。Zephyrのスレッド生成関数をざっと追いかけましょう。

スレッド生成関数、入り口

// (build_dir)/zephyr/include/generated/syscalls/kernel.h

static inline k_tid_t k_thread_create(struct k_thread * new_thread, k_thread_stack_t * stack, size_t stack_size, k_thread_entry_t entry, void * p1, void * p2, void * p3, int prio, uint32_t options, k_timeout_t delay)
{
#ifdef CONFIG_USERSPACE
	if (z_syscall_trap()) {
		uintptr_t more[] = {
			*(uintptr_t *)&p2,
			*(uintptr_t *)&p3,
			*(uintptr_t *)&prio,
			*(uintptr_t *)&options,
			*(uintptr_t *)&delay
		};
		return (k_tid_t) arch_syscall_invoke6(*(uintptr_t *)&new_thread, *(uintptr_t *)&stack, *(uintptr_t *)&stack_size, *(uintptr_t *)&entry, *(uintptr_t *)&p1, (uintptr_t) &more, K_SYSCALL_K_THREAD_CREATE);
	}
#endif
	compiler_barrier();
	//★今回、ユーザー空間は未使用なので、引数を同じ順で渡すだけ★
	return z_impl_k_thread_create(new_thread, stack, stack_size, entry, p1, p2, p3, prio, options, delay);
}


// zephyr/kernel/thread.c

#ifdef CONFIG_MULTITHREADING
k_tid_t z_impl_k_thread_create(struct k_thread *new_thread,
			      k_thread_stack_t *stack,
			      size_t stack_size, k_thread_entry_t entry,
			      void *p1, void *p2, void *p3,
			      int prio, uint32_t options, k_timeout_t delay)
{
	__ASSERT(!arch_is_in_isr(), "Threads may not be created in ISRs");

	/* Special case, only for unit tests */
#if defined(CONFIG_TEST) && defined(CONFIG_ARCH_HAS_USERSPACE) && !defined(CONFIG_USERSPACE)
	__ASSERT((options & K_USER) == 0,
		 "Platform is capable of user mode, and test thread created with K_USER option,"
		 " but neither CONFIG_TEST_USERSPACE nor CONFIG_USERSPACE is set\n");
#endif

	z_setup_new_thread(new_thread, stack, stack_size, entry, p1, p2, p3,
			  prio, options, NULL);    //★スレッドの情報初期化★

	if (!K_TIMEOUT_EQ(delay, K_FOREVER)) {
		schedule_new_thread(new_thread, delay);
	}

	return new_thread;
}

ここまでは入り口です。ユーザー空間を使わない限り、多少チェックが入っているくらいで、ほぼ素通りします。

スレッド生成関数、本体

/*
 * The provided stack_size value is presumed to be either the result of
 * K_THREAD_STACK_SIZEOF(stack), or the size value passed to the instance
 * of K_THREAD_STACK_DEFINE() which defined 'stack'.
 */
char *z_setup_new_thread(struct k_thread *new_thread,
			 k_thread_stack_t *stack, size_t stack_size,
			 k_thread_entry_t entry,
			 void *p1, void *p2, void *p3,
			 int prio, uint32_t options, const char *name)
{
	char *stack_ptr;

...

	z_waitq_init(&new_thread->base.join_waiters);

	/* Initialize various struct k_thread members */
	z_init_thread_base(&new_thread->base, prio, _THREAD_PRESTART, options);    //★スレッド生成(共通部分)★
	stack_ptr = setup_thread_stack(new_thread, stack, stack_size);

#ifdef KERNEL_COHERENCE
	/* Check that the thread object is safe, but that the stack is
	 * still cached!
	 */
	__ASSERT_NO_MSG(arch_mem_coherent(new_thread));
	__ASSERT_NO_MSG(!arch_mem_coherent(stack));
#endif

	arch_new_thread(new_thread, stack, stack_ptr, entry, p1, p2, p3);    //★スレッドの生成(アーキテクチャ依存の処理)★

	/* static threads overwrite it afterwards with real value */
	new_thread->init_data = NULL;
	new_thread->fn_abort = NULL;

#ifdef CONFIG_USE_SWITCH
	/* switch_handle must be non-null except when inside z_swap()
	 * for synchronization reasons.  Historically some notional
	 * USE_SWITCH architectures have actually ignored the field
	 */
	__ASSERT(new_thread->switch_handle != NULL,
		 "arch layer failed to initialize switch_handle");
#endif

...


void z_init_thread_base(struct _thread_base *thread_base, int priority,
		       uint32_t initial_state, unsigned int options)
{
	/* k_q_node is initialized upon first insertion in a list */

	thread_base->user_options = (uint8_t)options;    //★オプションはtest->thread_options (= 0) | K_INHERIT_PERMS (= 8) ★
	thread_base->thread_state = (uint8_t)initial_state;

	thread_base->prio = priority;

	thread_base->sched_locked = 0U;

#ifdef CONFIG_SMP
	thread_base->is_idle = 0;
#endif

	/* swap_data does not need to be initialized */

	z_init_thread_timeout(thread_base);
}


// zephyr/arch/riscv/core/thread.c

void arch_new_thread(struct k_thread *thread, k_thread_stack_t *stack,
		     char *stack_ptr, k_thread_entry_t entry,
		     void *p1, void *p2, void *p3)
{
	struct __esf *stack_init;

...

#if defined(CONFIG_FPU) && defined(CONFIG_FPU_SHARING)
	if ((thread->base.user_options & K_FP_REGS) != 0) {    //★この条件に引っかからず、浮動小数点数演算機能が有効にならない★
		stack_init->mstatus |= MSTATUS_FS_INIT;
	}
	stack_init->fp_state = 0;
#endif

	stack_init->mepc = (ulong_t)z_thread_entry_wrapper;

...

アーキテクチャ依存処理arch_new_thread() にやっとK_FP_REGSが出現します。user_optionsはz_init_thread_base() で設定しているとおり、APIの引数optionsの値そのものです。

引数optionsにK_FP_REGSを指定しない場合、mstatus CSRのFSフィールドが設定されず0のままになります。RISC-Vの仕様では、ハードウェアが浮動小数点数命令をサポートしていても、mstatusのFSフィールドが0だとIllegal Instruction例外を発生させます。QEMUも当然この仕様に習った実装になっています(2020年12月10日の日記参照)。


mstatus FSフィールドの意味

以上がztestで浮動小数点数命令を使うと例外が発生する原因です。原因はわかりましたが、スマートな直し方がわからないので、修正に関しては保留中です。

おまけ

最初の方でinit_testing(); //★何もしない★ とだけ書いてスルーしてしまった部分がありましたので、実装を載せておきます。

補足: init_testing() の実装

// zephyr/subsys/testsuite/ztest/src/ztest.c

#ifndef KERNEL

...

#else

...

static void init_testing(void)
{
	k_object_access_all_grant(&ztest_thread);
}

#endif /* !KERNEL */


// zephyr/(build_dir)/zephyr/misc/generated/syscalls_links/include/sys/kobject.h

static inline void k_object_access_all_grant(const void *object)
{
	ARG_UNUSED(object);
}

この通り、何もしていません。

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

コメント一覧

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



2020年12月12日

Zephyrと浮動小数点数命令のサポート その3 - ztestの仕組み、前編

目次: Zephyr

Zephyrのテストに用いられるztestというフレームワークがあります。使い方は、下記の通りで、テスト用の関数を1つずつztest_unit_test() というマクロに渡します。

最終的にtest_sprintfはstruct unit_testの配列になり、配列をztest_run_test_suite() に渡すと最初のテストから順番に実行してくれるという仕組みです。ztestの使い方は主題ではないのでこのくらいにしておきます。

ztestの使い方

// zephyr/tests/lib/sprint/src/main.c

void test_main(void)
{
	ztest_test_suite(test_sprintf,    //★変数名★
			 ztest_unit_test(test_sprintf_double),     //★テスト1つ目★
			 ztest_unit_test(test_sprintf_integer),    //★テスト2つ目★
			 ztest_unit_test(test_vsprintf),
			 ztest_unit_test(test_vsnprintf),
			 ztest_unit_test(test_sprintf_string),
			 ztest_unit_test(test_sprintf_misc));
	ztest_run_test_suite(test_sprintf);    
}

今回はztestがテスト実行時に何を行うのかを調べ、浮動小数点数命令で例外が発生する原因を探します。まずは前半のztest_test_suite() マクロから。

ztest_test_suiteマクロ

// zephyr/subsys/testsuite/ztest/include/ztest_test.h

/**
 * @brief Define a test suite
 *
 * This function should be called in the following fashion:.c
 *      ztest_test_suite(test_suite_name,
 *              ztest_unit_test(test_function),
 *              ztest_unit_test(test_other_function)
 *      );
 *
 *      ztest_run_test_suite(test_suite_name);
 * ```
 *
 * @param suite Name of the testing suite
 */
#define ztest_test_suite(suite, ...) \
	static ZTEST_DMEM struct unit_test _##suite[] = { \
		__VA_ARGS__, { 0 } \
	}

struct unit_test {
	const char *name;
	void (*test)(void);
	void (*setup)(void);
	void (*teardown)(void);
	uint32_t thread_options;
};

/**
 * @brief Define a test function
 *
 * This should be called as an argument to ztest_test_suite.
 *
 * @param fn Test function
 */

#define ztest_unit_test(fn) \
	ztest_unit_test_setup_teardown(fn, unit_test_noop, unit_test_noop)

/**
 * @brief Define a test with setup and teardown functions
 *
 * This should be called as an argument to ztest_test_suite. The test will
 * be run in the following order: @a setup, @a fn, @a teardown.
 *
 * @param fn Main test function
 * @param setup Setup function
 * @param teardown Teardown function
 */

#define ztest_unit_test_setup_teardown(fn, setup, teardown) { \
		STRINGIFY(fn), fn, setup, teardown, 0 \
}


//// 展開例: ztest_unit_test(test_sprintf_double)

// fn = test_sprintf_double
ztest_unit_test_setup_teardown(test_sprintf_double, unit_test_noop, unit_test_noop)

// fn = test_sprintf_double, setup = unit_test_noop, teardown = unit_test_noop
{
	name          : STRINGIFY(test_sprintf_double),
	test          : test_sprintf_double,
	setup         : unit_test_noop,
	teardown      : unit_test_noop,
	thread_options: 0
}

最初にちょこっと書いたとおり、ztest_test_suite() はstruct unit_testの配列宣言に展開され、ztest_unit_test() は配列の要素の初期化値に展開されます。

構造体unit_testにthread_optionsというメンバーがいます。このメンバーが浮動小数点数命令のサポートに重要な役割を果たします。ztest_unit_test() だと、常にthread_optionsは0になる、という点だけ覚えておいてくれればOKです。

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

コメント一覧

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



2020年12月11日

ZephyrのMemberになった

目次: Zephyr

GitHubを見ていたらZephyrのMemberに招待されていました。メーリングリストに来ていたメール(メールのアーカイブへのリンク)によると、現状MAINTAINERS.ymlに記載されているCollaboratorを全員招待したらしいです。

MemberといってもIssueのタグや担当者を付け外しできるようになる程度で、これといった権限はありません。

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

コメント一覧

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



2020年12月10日

QEMU RISC-Vエミュレーションで例外が起きるときの仕組み

目次: RISC-V

RISC-VではCPUが単精度浮動小数点F拡張や、倍精度浮動小数点D拡張の命令(flw, fldなど)に対応していても、mstatusのFSビットでFPUを有効にしていないと、命令実行時にIllegal Instruction例外が発生します。

QEMUはこのチェックをどこで行っているのかメモしておきます。

QEMU RISC-V例外発生部分

// qemu/target/riscv/translate.c

static void decode_opc(CPURISCVState *env, DisasContext *ctx, uint16_t opcode)
{
    /* check for compressed insn */
    if (extract16(opcode, 0, 2) != 3) {
        if (!has_ext(ctx, RVC)) {
            gen_exception_illegal(ctx);
        } else {
            ctx->pc_succ_insn = ctx->base.pc_next + 2;
            if (!decode_insn16(ctx, opcode)) {
                /* fall back to old decoder */
                decode_RV32_64C(ctx, opcode);
            }
        }
    } else {
        uint32_t opcode32 = opcode;
        opcode32 = deposit32(opcode32, 16, 16,
                             translator_lduw(env, ctx->base.pc_next + 2));
        ctx->pc_succ_insn = ctx->base.pc_next + 4;
        if (!decode_insn32(ctx, opcode32)) {    //★この関数がfalseを返す★
            gen_exception_illegal(ctx);
        }
    }
}

static void gen_exception_illegal(DisasContext *ctx)
{
    generate_exception(ctx, RISCV_EXCP_ILLEGAL_INST);    //★Illegal Instruction例外★
}

関数decode_insn32() で命令をデコードしfalseが返ってきたら、例外を発生させます。このdecode_insn32() という関数は自動生成されており、ビルドディレクトリの下にあります。RV64向けなら下記のようなパスです。

QEMU RISC-V命令デコード部分

// (build_dir)/libqemu-riscv64-softmmu.fa.p/decode-insn32.c.inc

static bool decode_insn32(DisasContext *ctx, uint32_t insn)
{
    switch (insn & 0x0000007f) {

    ...

    case 0x00000007:
        /* ........ ........ ........ .0000111 */
        switch ((insn >> 12) & 0x7) {

        ...

        case 0x3:
            /* ........ ........ .011.... .0000111 */
            /* ../target/riscv/insn32.decode:199 */
            decode_insn32_extract_i(ctx, &u.f_i, insn);
            if (trans_fld(ctx, &u.f_i)) return true;    //★このifが成立「しない」★
            break;

...


static void decode_insn32_extract_i(DisasContext *ctx, arg_i *a, uint32_t insn)
{
    a->imm = sextract32(insn, 20, 12);
    a->rs1 = extract32(insn, 15, 5);
    a->rd = extract32(insn, 7, 5);
}


// qemu/target/riscv/insn_trans/trans_rvd.c.inc

static bool trans_fld(DisasContext *ctx, arg_fld *a)
{
    REQUIRE_FPU;    //★このマクロ内でreturn false★
    REQUIRE_EXT(ctx, RVD);
    TCGv t0 = tcg_temp_new();
    gen_get_gpr(t0, a->rs1);
    tcg_gen_addi_tl(t0, t0, a->imm);

    tcg_gen_qemu_ld_i64(cpu_fpr[a->rd], t0, ctx->mem_idx, MO_TEQ);

    mark_fs_dirty(ctx);
    tcg_temp_free(t0);
    return true;
}


// qemu/target/riscv/insn_trans/trans_rvf.c.inc

#define REQUIRE_FPU do {\
    if (ctx->mstatus_fs == 0) \
        return false;                       \
} while (0)

大体の仕組みは把握できましたので、実際にこの部分を通るかどうか見ましょう。

実行して確かめる

Illegal Instruction例外は実行中に何度も発生する例外ではありません。カーネル内で1度でも発生するとZephyrはログを出して止まります。すなわちQEMUも例外を発生させるのは1度だけです。

何度も通る処理だとブレーク条件が必要でややこしいですが、1度しか通らない処理なら単純にREQUIRE_FPUの行でブレークすれば良いです。条件ctx->mstatus_fs == 0が成立するかどうか簡単に確認できます。

GDBでmstatus_fsの値を確認する
$ gdb qemu/build/qemu-system-riscv64

(gdb) b trans_rvd.c.inc:23

Breakpoint 1 at 0x555555a858b8: file ../target/riscv/insn_trans/trans_rvd.c.inc, line 23.

(gdb) r

[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[New Thread 0x7ffff440c700 (LWP 1675592)]
[New Thread 0x7ffff3a89700 (LWP 1675593)]
*** Booting Zephyr OS build zephyr-v2.4.0-2328-g4f33cf942643  ***
Running test suite test_sprintf
===================================================================
START - test_sprintf_double
[Switching to Thread 0x7ffff3a89700 (LWP 1675593)]

Thread 3 "qemu-system-ris" hit Breakpoint 1, trans_fld (ctx=0x7ffff3a883d0,
    a=0x7ffff3a88290) at ../target/riscv/insn_trans/trans_rvd.c.inc:23
23          REQUIRE_FPU;

(gdb) l

18       * this program.  If not, see <http://www.gnu.org/licenses/>.
19       */
20
21      static bool trans_fld(DisasContext *ctx, arg_fld *a)
22      {
23          REQUIRE_FPU;        //★ここで止めた★
24          REQUIRE_EXT(ctx, RVD);
25          TCGv t0 = tcg_temp_new();
26          gen_get_gpr(t0, a->rs1);
27          tcg_gen_addi_tl(t0, t0, a->imm);

(gdb) p ctx->mstatus_fs

$1 = 0

ちょっとしたメモのつもりが長くなってしまいました。まあいいや。

編集者:すずき(2021/09/21 22:59)

コメント一覧

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



2020年12月9日

Zephyrと浮動小数点数命令のサポート その2 - 実行

目次: Zephyr

ひとまずF拡張とD拡張の双方を有効にしRV64IMAFDC(省略形だとRV64GCともいう)でビルドして、実行します。使用するのはtests/lib/sprintfで、その名の通りsprintf() のテストです。しかし実行するやいなや、エラーで吹き飛んでしまいます。

D拡張を有効にしてsprintfのテストを実行するとクラッシュする
$ cmake -G Ninja -DBOARD=qemu_rv64_virt ../tests/lib/sprintf/
$ ninja
$ ninja run

[0/1] To exit from QEMU enter: 'CTRL+a, x'[QEMU] CPU: riscv64
*** Booting Zephyr OS build zephyr-v2.4.0-2328-g4f33cf942643  ***
Running test suite test_sprintf
===================================================================
START - test_sprintf_double
E: Exception cause Illegal instruction (2)
E: Faulting instruction address = 0x80000cba
E:   ra: 0x80003836  gp: 0x00000000  tp: 0x00000000  t0: 0x00000000
E:   t1: 0x00000000  t2: 0x00000000  t3: 0x00000000  t4: 0x00000000
E:   t5: 0x00000000  t6: 0x00000000  a0: 0x8000bdb0  a1: 0x00000000
E:   a2: 0x00000000  a3: 0x00000000  a4: 0x00000000  a5: 0x80006cb6
E:   a6: 0x00000000  a7: 0x00000000

E: >>> ZEPHYR FATAL ERROR 0: CPU exception on CPU 0
E: Current thread: 0x80009820 (unknown)
E: Halting system

ログには非常に役立つ情報が出ています。エラーの理由はIllegal Instruction例外、アドレスは0x80000cbaだと言っています。なるほど。この付近を逆アセンブルします。

例外が発生した命令を特定する
$ riscv64-zephyr-elf-objdump -drS build32/zephyr/zephyr.elf

0000000080000cb2 <test_sprintf_double>:
{
    80000cb2:   7121                    addi    sp,sp,-448
    80000cb4:   af22                    fsd     fs0,408(sp)
        sprintf(buffer, "%e", var.d);
    80000cb6:   00006797                auipc   a5,0x6
    ★fld命令でIllegal Instruction例外★
    80000cba:   ad27b407                fld     fs0,-1326(a5) # 80006788 <__font_entry_end>
    80000cbe:   e2040653                fmv.x.d a2,fs0
    80000cc2:   00007597                auipc   a1,0x7
    80000cc6:   ad658593                addi    a1,a1,-1322 # 80007798 <__clz_tab+0xf78>

...

QEMUのrv64 CPU指定は倍精度浮動小数D拡張命令に対応しているのに、なぜエラーになるのか?原因はztestというZephyrのテストフレームワークの仕組みによるものです。次回以降、エラーの原因を追います。

編集者:すずき(2023/09/24 11:59)

コメント一覧

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



2020年12月8日

Zephyrと浮動小数点数命令のサポート その1 - ボードの設定とビルド

目次: Zephyr

RISC-V向けZephyrには、既存のボード設定がいくつか含まれていますが、私が持っているボード(SiFive HiFive1 Rev.B)やQEMU用の設定では、浮動小数点数命令(単精度浮動小数点数命令 = F拡張、倍精度浮動小数点数命令 = D拡張)は未サポートです。

実はQEMUは浮動小数点数命令をサポートしているCPUタイプもあります(-cpu rv32もしくは -cpu rv64)。せっかくサポートしているのに使わないのは勿体無いですよね?

浮動小数点数命令のサポートを追加、ビルド

新たにqemu_rv64_virtボードを追加し、ハードウェア浮動小数点数命令をサポートします。Zephyrには既に仕組みがあるので、特に難しくはありません。CPU_HAS_FPUとCPU_HAS_FPU_DOUBLE_PRECISIONをselectするだけです。

D拡張を有効にしてsprintfのテストを実行するとクラッシュする

config BOARD_QEMU_RV64_VIRT
	bool "QEMU RV64 virt target"
	depends on SOC_QEMU_RV64_VIRT
	select QEMU_TARGET
	select 64BIT
	select CPU_HAS_FPU                     #★F拡張に対応★
	select CPU_HAS_FPU_DOUBLE_PRECISION    #★D拡張に対応★

この場合RV64IMAFDCの意味になりますが、select CPU_HAS_FPUだけ指定して、D拡張だけ外したRV64IMAFCにすることもできます。しかしZephyr SDKのツールチェーンが対応していないため、下記のようにリンク時に猛烈にエラーが出て怒られます。

F拡張だけ有効にしてビルドすると大量のエラー
[105/110] Linking C executable zephyr/zephyr_prebuilt.elf
FAILED: zephyr/zephyr_prebuilt.elf
: && ccache zephyr-sdk/riscv64-zephyr-elf/bin/riscv64-zephyr-elf-gcc   zephyr/CMakeFiles/zephyr_prebuilt.dir/misc/empty_file.c.obj -o zephyr/zephyr_prebuilt.elf  -Wl,-T  zephyr/linker.cmd  -Wl,-Map=zephyr/build32/zephyr/zephyr_prebuilt.map  -Wl,--whole-archive  app/libapp.a  zephyr/libzephyr.a  zephyr/arch/common/libarch__common.a  zephyr/arch/arch/riscv/core/libarch__riscv__core.a  zephyr/lib/libc/minimal/liblib__libc__minimal.a  zephyr/lib/posix/liblib__posix.a  zephyr/subsys/testsuite/ztest/libsubsys__testsuite__ztest.a  zephyr/drivers/serial/libdrivers__serial.a  -Wl,--no-whole-archive  zephyr/kernel/libkernel.a  zephyr/CMakeFiles/offsets.dir/./arch/riscv/core/offsets/offsets.c.obj  -L"zephyr-sdk/riscv64-zephyr-elf/bin/../lib/gcc/riscv64-zephyr-elf/10.2.0"  -Lzephyr/build32/zephyr  -lgcc  -Wl,--print-memory-usage  zephyr/arch/common/libisr_tables.a  -mabi=lp64f  -march=rv64imafc  -Wl,--gc-sections  -Wl,--build-id=none  -Wl,--sort-common=descending  -Wl,--sort-section=alignment  -Wl,-u,_OffsetAbsSyms  -Wl,-u,_ConfigAbsSyms  -nostdlib  -static  -no-pie  -Wl,-X  -Wl,-N  -Wl,--orphan-handling=warn && :
zephyr-sdk/riscv64-zephyr-elf/bin/../lib/gcc/riscv64-zephyr-elf/10.2.0/../../../../riscv64-zephyr-elf/bin/ld: zephyr-sdk/riscv64-zephyr-elf/bin/../lib/gcc/riscv64-zephyr-elf/10.2.0/libgcc.a(_clzdi2.o): ABI is incompatible with that of the selected emulation:
  target emulation `elf32-littleriscv' does not match `elf64-littleriscv'
zephyr-sdk/riscv64-zephyr-elf/bin/../lib/gcc/riscv64-zephyr-elf/10.2.0/../../../../riscv64-zephyr-elf/bin/ld: failed to merge target specific data of file zephyr-sdk/riscv64-zephyr-elf/bin/../lib/gcc/riscv64-zephyr-elf/10.2.0/libgcc.a(_clzdi2.o)
zephyr-sdk/riscv64-zephyr-elf/bin/../lib/gcc/riscv64-zephyr-elf/10.2.0/../../../../riscv64-zephyr-elf/bin/ld: zephyr-sdk/riscv64-zephyr-elf/bin/../lib/gcc/riscv64-zephyr-elf/10.2.0/libgcc.a(_ctzdi2.o): ABI is incompatible with that of the selected emulation:
  target emulation `elf32-littleriscv' does not match `elf64-littleriscv'
zephyr-sdk/riscv64-zephyr-elf/bin/../lib/gcc/riscv64-zephyr-elf/10.2.0/../../../../riscv64-zephyr-elf/bin/ld: failed to merge target specific data of file zephyr-sdk/riscv64-zephyr-elf/bin/../lib/gcc/riscv64-zephyr-elf/10.2.0/libgcc.a(_ctzdi2.o)
zephyr-sdk/riscv64-zephyr-elf/bin/../lib/gcc/riscv64-zephyr-elf/10.2.0/../../../../riscv64-zephyr-elf/bin/ld: zephyr-sdk/riscv64-zephyr-elf/bin/../lib/gcc/riscv64-zephyr-elf/10.2.0/libgcc.a(_clz.o): ABI is incompatible with that of the selected emulation:
  target emulation `elf32-littleriscv' does not match `elf64-littleriscv'
zephyr-sdk/riscv64-zephyr-elf/bin/../lib/gcc/riscv64-zephyr-elf/10.2.0/../../../../riscv64-zephyr-elf/bin/ld: failed to merge target specific data of file zephyr-sdk/riscv64-zephyr-elf/bin/../lib/gcc/riscv64-zephyr-elf/10.2.0/libgcc.a(_clz.o)
Memory region         Used Size  Region Size  %age Used
             RAM:       49220 B       256 KB     18.78%
        IDT_LIST:          57 B         2 KB      2.78%
collect2: error: ld returned 1 exit status
ninja: build stopped: subcommand failed.

上記のエラーについてZephyr SDKを作っている人に聞いてみたところ「RV64IMAFCは本当に必要?」と逆に聞かれてしまいました。うーん、現状そんな変なCPUはこの世にないし、対応する理由も思いつきません。要らないですね。

編集者:すずき(2023/09/24 11:59)

コメント一覧

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



2020年12月5日

東京オリンピックと祝日とカレンダー

日本政府は未だに東京オリンピックやる気満々らしく、内閣府の発表(国民の祝日について - 内閣府)によると、2020年に引き続いて来年2021年も、海の日、スポーツの日、山の日が本来と異なる日付に移動されるようです。

  • 海の日: 7月 第3月曜7/19 → 7/22
  • スポーツの日: 10月 第2月曜10/11 → 7/23
  • 山の日: 8/11 → 8/8(日曜日なので8/9が振替休日になる)

混乱しそうなのでこのサイトのカレンダー機能にも反映しておきました。カレンダー機能は便利で実装して良かったと思っている機能の一つですが、まさかこんなに東京オリンピック関係で祝日が蹂躙されるとは思っていませんでしたね。

編集者:すずき(2020/12/05 22:51)

コメント一覧

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



link もっと前
2020年12月17日 >>> 2020年12月4日
link もっと後

管理用メニュー

link 記事を新規作成

<2020>
<<<12>>>
--12345
6789101112
13141516171819
20212223242526
2728293031--

最近のコメント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)も...」

最近の記事20件

  • 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 19年3月28日
    すずき (11/02 13:27)
    「[マンガ紹介] 目次: マンガ紹介お気に入りのマンガ紹介シリーズ。こわもてかわもて(全2巻、2019年)(アマゾンへのリンク)...」
  • link 21年6月20日
    すずき (11/02 13:22)
    「[読書一生分が93万円?] 目次: マンガ紹介書籍通販のhontoがこんなキャンペーンをやっています。honto読書一生分プレ...」
  • link 17年10月27日
    すずき (11/02 13:11)
    「[異世界&最強系漫画の種類] 目次: マンガ紹介少し前にアニメ化されて盛り上がって(おそらく負の方向に…)いた「...」
  • link 24年10月28日
    すずき (10/30 23:49)
    「[Linuxからリモートデスクトップ] 目次: Linux開発用のLinuxマシンの画面を見るにはいろいろな手段がありますが、...」
  • link 23年4月10日
    すずき (10/30 23:46)
    「[Linux - まとめリンク] 目次: Linux関係の深いまとめリンク。目次: RISC-V目次: ROCK64/ROCK...」
  • link 24年10月24日
    すずき (10/25 02:35)
    「[ONKYOからM-AUDIOのUSB DACへ] 目次: PCかれこれ10年以上(2013年3月16日の日記参照)活躍してく...」
  • link 24年7月25日
    すずき (10/25 02:24)
    「[OpenSBIを調べる - デバイスツリーの扱い(別方法)] 目次: LinuxOpenSBIのブート部分を調べます。Ope...」
  • link 24年8月7日
    すずき (10/25 02:23)
    「[Debian独自の挙動をするQEMUとbinfmt_misc] 目次: Linux前回はbinfmt_miscの使い方や動作...」
  • link 24年9月9日
    すずき (10/25 02:22)
    「[GDBの便利コマンド] 目次: LinuxGDBは便利ですが、少し使わないでいるとあっという間にコマンドを忘れます。便利&使...」
  • link 24年10月20日
    すずき (10/25 02:22)
    「[ゲームを買ったら遊びましょう2] 目次: ゲーム前回の振り返り(2022年5月13日の日記参照)から2年半経ちました。所持し...」
  • link 24年8月2日
    すずき (10/25 02:21)
    「[Debian on RISC-V] 目次: LinuxOpenSBI + Linuxの環境まで動いたので、次はLinuxのデ...」
  • link 24年8月6日
    すずき (10/25 02:21)
    「[他アーキテクチャ向けバイナリを実行する仕組みbinfmt_misc] 目次: LinuxRISC-V 64bit用の実行ファ...」
  • link 24年8月27日
    すずき (10/25 02:20)
    「[Milk-V Jupiterが届いた] 目次: RISC-VMilk-V Jupiterが届きました。お値段が非常に安かった...」
  • link 24年9月13日
    すずき (10/25 02:20)
    「[OpenSBIを調べる - OpenSBIとRISC-V ISA extensions] 目次: Linux今回はOpenS...」
  • link 24年10月11日
    すずき (10/25 02:19)
    「[企業のドメイン] 今の企業は公式サイトを持っていなほうが珍しいと思いますが、ドメイン名の使い方は各社でバラバラで面白いです。...」
  • link 24年10月21日
    すずき (10/25 02:18)
    「[OpenPilotを調べる - プロセス間通信msgqの仕組み] 目次: OpenPilot最近はOSSの運転支援ソフトウェ...」
  • link 24年10月6日
    すずき (10/25 02:11)
    「[OpenPilotを調べる - ビルドと実行] 目次: OpenPilot最近はOSSの運転支援ソフトウェアOpenPilo...」
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