目次: Zephyr
前半ではztest_test_suite() の実装を見ました。戻って見るのは面倒だと思うので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() マクロの実装を見ます。
// 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日の日記参照)。
以上がztestで浮動小数点数命令を使うと例外が発生する原因です。原因はわかりましたが、スマートな直し方がわからないので、修正に関しては保留中です。
最初の方で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);
}
この通り、何もしていません。
 この記事にコメントする
 この記事にコメントする
目次: Zephyr
Zephyrのテストに用いられるztestというフレームワークがあります。使い方は、下記の通りで、テスト用の関数を1つずつztest_unit_test() というマクロに渡します。
最終的にtest_sprintfはstruct unit_testの配列になり、配列をztest_run_test_suite() に渡すと最初のテストから順番に実行してくれるという仕組みです。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() マクロから。
// 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です。
 この記事にコメントする
 この記事にコメントする
目次: Zephyr
GitHubを見ていたらZephyrのMemberに招待されていました。メーリングリストに来ていたメール(メールのアーカイブへのリンク)によると、現状MAINTAINERS.ymlに記載されているCollaboratorを全員招待したらしいです。
MemberといってもIssueのタグや担当者を付け外しできるようになる程度で、これといった権限はありません。
 この記事にコメントする
 この記事にコメントする
目次: RISC-V
RISC-VではCPUが単精度浮動小数点F拡張や、倍精度浮動小数点D拡張の命令(flw, fldなど)に対応していても、mstatusのFSビットでFPUを有効にしていないと、命令実行時にIllegal Instruction例外が発生します。
QEMUはこのチェックをどこで行っているのかメモしておきます。
// 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向けなら下記のようなパスです。
// (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 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
ちょっとしたメモのつもりが長くなってしまいました。まあいいや。
 この記事にコメントする
 この記事にコメントする
目次: Zephyr
ひとまずF拡張とD拡張の双方を有効にしRV64IMAFDC(省略形だとRV64GCともいう)でビルドして、実行します。使用するのはtests/lib/sprintfで、その名の通り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のテストフレームワークの仕組みによるものです。次回以降、エラーの原因を追います。
 この記事にコメントする
 この記事にコメントする
目次: 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するだけです。
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のツールチェーンが対応していないため、下記のようにリンク時に猛烈にエラーが出て怒られます。
[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はこの世にないし、対応する理由も思いつきません。要らないですね。
 この記事にコメントする
 この記事にコメントする
日本政府は未だに東京オリンピックやる気満々らしく、内閣府の発表(国民の祝日について - 内閣府)によると、2020年に引き続いて来年2021年も、海の日、スポーツの日、山の日が本来と異なる日付に移動されるようです。
混乱しそうなのでこのサイトのカレンダー機能にも反映しておきました。カレンダー機能は便利で実装して良かったと思っている機能の一つですが、まさかこんなに東京オリンピック関係で祝日が蹂躙されるとは思っていませんでしたね。
 この記事にコメントする
 この記事にコメントする
目次: Zephyr
ZephyrのWatchdogのCollaboratorになりました。先日(2020年11月17日の日記参照)CollaboratorになったRISC-VもWatchdogも、元々メンテナーが不在の領域です。
発端はSiFive HiFive1を買った記念にWatchdogドライバを書いたら、割とあっさり動いたので、PRを送ったことです。送ったまでは良かったのですが、メンテナーが不在でした。またこのパターン……。
Zephyrは周辺ドライバが充実しているのが売りですが、メンテナーは不足気味です。今ならサブシステムのCollaboratorくらいなら、何の実績もなくても気前良く追加してくれます。
RTOS興味ある人はいかがですか?
 この記事にコメントする
 この記事にコメントする
| < | 2020 | > | ||||
| << | < | 12 | > | >> | ||
| 日 | 月 | 火 | 水 | 木 | 金 | 土 | 
| - | - | 1 | 2 | 3 | 4 | 5 | 
| 6 | 7 | 8 | 9 | 10 | 11 | 12 | 
| 13 | 14 | 15 | 16 | 17 | 18 | 19 | 
| 20 | 21 | 22 | 23 | 24 | 25 | 26 | 
| 27 | 28 | 29 | 30 | 31 | - | - | 
 wiki
 wiki Linux JM
 Linux JM Java API
 Java API 2002年
 2002年 2003年
 2003年 2004年
 2004年 2005年
 2005年 2006年
 2006年 2007年
 2007年 2008年
 2008年 2009年
 2009年 2010年
 2010年 2011年
 2011年 2012年
 2012年 2013年
 2013年 2014年
 2014年 2015年
 2015年 2016年
 2016年 2017年
 2017年 2018年
 2018年 2019年
 2019年 2020年
 2020年 2021年
 2021年 2022年
 2022年 2023年
 2023年 2024年
 2024年 2025年
 2025年 過去日記について
 過去日記について アクセス統計
 アクセス統計 サーバ一覧
 サーバ一覧 サイトの情報
 サイトの情報合計: 
本日: