Skip to content

xtensa: support for C library provided by Xtensa toolchain #90351

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions arch/xtensa/core/thread.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ Z_THREAD_LOCAL uint32_t is_user_mode;

#endif /* CONFIG_USERSPACE */

#if defined(CONFIG_XTENSA_LIBC)
#include <sys/reent.h>
#endif

/**
* Initializes a stack area such that it can be "restored" later and
* begin running with the specified function and three arguments. The
Expand Down Expand Up @@ -125,6 +129,9 @@ void arch_new_thread(struct k_thread *thread, k_thread_stack_t *stack,
__ASSERT((((size_t)stack_ptr) % XCHAL_DCACHE_LINESIZE) == 0, "");
sys_cache_data_flush_and_invd_range(stack, (char *)stack_ptr - (char *)stack);
#endif
#if defined(CONFIG_XTENSA_LIBC)
_init_reent(&thread->arch.reent);
#endif
}

#if defined(CONFIG_FPU) && defined(CONFIG_FPU_SHARING)
Expand Down
2 changes: 2 additions & 0 deletions boards/cdns/xt-sim/xt-sim_defconfig
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@ CONFIG_GEN_IRQ_VECTOR_TABLE=n
CONFIG_SIMULATOR_XTENSA=y

CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC=2000000

CONFIG_XTENSA_LIBC=y
2 changes: 1 addition & 1 deletion cmake/compiler/xt-clang/compiler_flags.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ set_compiler_property(PROPERTY nostdinc_include)
# For C++ code, re-add the standard includes directory which was
# cleared up from nostdinc_inlcude in above lines with no
# "include-fixed" this time"
if(CONFIG_CPP)
if(CONFIG_CPP AND NOT CONFIG_XTENSA_LIBC)
execute_process(
COMMAND ${CMAKE_C_COMPILER} --print-file-name=include/stddef.h
OUTPUT_VARIABLE _OUTPUT
Expand Down
6 changes: 5 additions & 1 deletion cmake/linker/xt-ld/linker_flags.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ check_set_linker_property(TARGET linker PROPERTY base
)

if(NOT CONFIG_NATIVE_LIBRARY AND NOT CONFIG_EXTERNAL_MODULE_LIBCPP)
set_property(TARGET linker PROPERTY cpp_base -lstdc++)
if (CONFIG_XTENSA_LIBC)
set_property(TARGET linker PROPERTY cpp_base -lstdc++11)
else()
set_property(TARGET linker PROPERTY cpp_base -lstdc++)
endif()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not relevant to this PR, but note that once this lands SOF will want to use it instead of its current integration for a handful of third party C++ modules, which add the libraries manually. (@lyakh @kv2019i et. al.)

endif()

check_set_linker_property(TARGET linker PROPERTY baremetal
Expand Down
8 changes: 7 additions & 1 deletion cmake/linker/xt-ld/linker_libraries.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,10 @@ set_linker_property(NO_CREATE PROPERTY c_library "-lc")
set_linker_property(NO_CREATE PROPERTY rt_library "-lgcc")
set_linker_property(NO_CREATE PROPERTY c++_library "-lstdc++")
set_linker_property(NO_CREATE PROPERTY hal_library "-lhal")
set_linker_property(PROPERTY link_order_library "c;rt;hal")
if(CONFIG_XTENSA_LIBC)
set_linker_property(NO_CREATE PROPERTY m_library "-lm")
set_linker_property(NO_CREATE PROPERTY sim_library "-lsim")
set_linker_property(PROPERTY link_order_library "m;c;rt;sim;hal")
else()
set_linker_property(PROPERTY link_order_library "c;rt;hal")
endif()
7 changes: 7 additions & 0 deletions include/zephyr/arch/xtensa/thread.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@
#include <zephyr/arch/xtensa/mpu.h>
#endif

#ifdef CONFIG_XTENSA_LIBC
#include <sys/reent.h>
#endif

/* Xtensa doesn't use these structs, but Zephyr core requires they be
* defined so they can be included in struct _thread_base. Dummy
* field exists for sizeof compatibility with C++.
Expand Down Expand Up @@ -48,6 +52,9 @@ struct _thread_arch {
*/
uint32_t return_ps;
#endif
#ifdef CONFIG_XTENSA_LIBC
struct _reent reent;
#endif
};

typedef struct _thread_arch _thread_arch_t;
Expand Down
1 change: 1 addition & 0 deletions lib/libc/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@ add_subdirectory_ifdef(CONFIG_IAR_LIBC iar)
add_subdirectory_ifdef(CONFIG_MINIMAL_LIBC minimal)
add_subdirectory_ifdef(CONFIG_NEWLIB_LIBC newlib)
add_subdirectory_ifdef(CONFIG_PICOLIBC picolibc)
add_subdirectory_ifdef(CONFIG_XTENSA_LIBC xclib)

add_subdirectory(common)
7 changes: 7 additions & 0 deletions lib/libc/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,13 @@ config IAR_LIBC
A reduced Zephyr minimal libc will be used for library functionality
not provided by the IAR C Runtime Library.

config XTENSA_LIBC
bool "Xtensa C library"
depends on !NATIVE_APPLICATION
depends on "$(ZEPHYR_TOOLCHAIN_VARIANT)" = "xt-clang"
help
C library provided by Xtensa toolchain.

endchoice # LIBC_IMPLEMENTATION

config HAS_NEWLIB_LIBC_NANO
Expand Down
8 changes: 8 additions & 0 deletions lib/libc/xclib/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# SPDX-License-Identifier: Apache-2.0

zephyr_library()

zephyr_library_sources(
libc-hooks.c
multithreading.c
)
15 changes: 15 additions & 0 deletions lib/libc/xclib/libc-hooks.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*
* Copyright (c) 2025 Cadence Design Systems, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/

char __os_flag __attribute__((retain)); /* Needed by xclib to indicate the presense of an OS */

int arch_printk_char_out(int c);
static int (*_stdout_hook)(int c) = arch_printk_char_out;

void __stdout_hook_install(int (*hook)(int))
{
_stdout_hook = hook;
}
67 changes: 67 additions & 0 deletions lib/libc/xclib/multithreading.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Copyright (c) 2025 Cadence Design Systems, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <zephyr/kernel.h>
#include <stdlib.h>
#include <sys/reent.h>

int32_t _xclib_use_mt = 1; /* Enables xclib multithread support */

typedef struct k_mutex *_Rmtx;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pet peeve: you probably want a spinlock or a k_sem here and not a mutex. k_mutex is very heavyweight and the only feature it brings is priority inheritance. Semaphores are a better general purpose blocking IPC lock for almost all purposes. But given the use case, I'm thinking that the libc is probably using this to protect tight critical sections, where spinlocks are much lighter still and objectively better choices.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I initially used k_sem, but a recursive locking mechanism is needed to avoid hangs.


/* Number of Locks needed by _Initlocks() without calling malloc()
* which would infinitely call _Initlocks() recursively.
*/
#define NLOCKS (_MAX_LOCK + 1)

static struct k_mutex _Mtxinit_mtx[NLOCKS];

void _Mtxinit(_Rmtx *mtx)
{
static int i; /* static variable initially 0 */

if (i < NLOCKS) {
*mtx = &_Mtxinit_mtx[i];
++i;
} else {
*mtx = malloc(sizeof(struct k_mutex));
}
if (!*mtx) {
k_panic();
}
k_mutex_init(*mtx);
}

void _Mtxdst(_Rmtx *mtx)
{
if (mtx != NULL && *mtx != NULL &&
(*mtx > &_Mtxinit_mtx[NLOCKS] || *mtx < &_Mtxinit_mtx[0])) {
free(*mtx);
}
}

void _Mtxlock(_Rmtx *mtx)
{
if ((mtx != NULL) && (*mtx != NULL)) {
k_mutex_lock(*mtx, K_FOREVER);
}
}

void _Mtxunlock(_Rmtx *mtx)
{
if ((mtx != NULL) && (*mtx != NULL)) {
k_mutex_unlock(*mtx);
}
}

#if defined(__DYNAMIC_REENT__)
struct _reent *__getreent(void)
{
return &k_current_get()->arch.reent;
}
#else
#error __DYNAMIC_REENT__ support missing
#endif