目次: 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 | - | - |
合計:
本日: