diff --git a/app/boards/intel_adsp/Kconfig.defconfig b/app/boards/intel_adsp/Kconfig.defconfig index f705960eee1a..d7010bdfe165 100644 --- a/app/boards/intel_adsp/Kconfig.defconfig +++ b/app/boards/intel_adsp/Kconfig.defconfig @@ -123,8 +123,8 @@ config INTEL_ADSP_TIMER config MM_DRV default y -config UAOL - default y if ACE +# config UAOL +# default y if ACE # # Zephyr / power settings @@ -165,8 +165,8 @@ config LOG_FUNC_NAME_PREFIX_INF config LOG_FUNC_NAME_PREFIX_DBG default y -config LOG_CORE_ID_PREFIX - default y +# config LOG_CORE_ID_PREFIX +# default y config LOG_TIMESTAMP_64BIT default y diff --git a/app/boards/intel_adsp_ace30_ptl_llvm_qemu.conf b/app/boards/intel_adsp_ace30_ptl_llvm_qemu.conf new file mode 100644 index 000000000000..5a0371bd4a3c --- /dev/null +++ b/app/boards/intel_adsp_ace30_ptl_llvm_qemu.conf @@ -0,0 +1,40 @@ +# LLVM Single-Core QEMU Testing Configuration +# Base: intel_adsp_ace30_ptl.conf (applied automatically by board name) +# Purpose: Override settings for single-core LLVM QEMU testing +# +# This config is applied ON TOP of the base intel_adsp_ace30_ptl.conf +# via -DEXTRA_CONF_FILE= to the build system. + +# ---- Single core for QEMU (no SMP hangs) ---- +CONFIG_CORE_COUNT=1 +CONFIG_SMP=n +CONFIG_EVENTS=y + +# ---- Disable LLEXT/loadable modules (missing TIE headers in LLVM) ---- +CONFIG_LLEXT=n +CONFIG_LIBRARY_MANAGER=n +CONFIG_INTEL_MODULES=n +CONFIG_LIBRARY_AUTH_SUPPORT=n +CONFIG_LIBRARY_BUILD_LIB=n +CONFIG_LIBRARY_DEFAULT_MODULAR=n + +# ---- Disable HiFi3-optimized code (missing xt_FP.h in LLVM) ---- +CONFIG_FORMAT_CONVERT_HIFI3=n +CONFIG_PCM_CONVERTER_FORMAT_CONVERT_HIFI3=n + +# ---- Make all modules static (since LLEXT is off) ---- +CONFIG_COMP_TESTER=y +CONFIG_COMP_GOOGLE_RTC_AUDIO_PROCESSING=y + +# ---- Logging: use deferred mode matching working build-ptl ---- +CONFIG_LOG_MODE_DEFERRED=y + +# ---- Disable userspace for simpler single-core boot ---- +CONFIG_USERSPACE=n +CONFIG_DYNAMIC_THREAD=n +CONFIG_DYNAMIC_THREAD_ALLOC=n +CONFIG_SOF_USERSPACE_PROXY=n +CONFIG_MAX_THREAD_BYTES=1 +CONFIG_SCHED_CPU_MASK=n +CONFIG_PM=n +CONFIG_ADSP_IMR_CONTEXT_SAVE=n diff --git a/scripts/llext_link_helper.py b/scripts/llext_link_helper.py index f10777c9918f..ebd059c96e4e 100755 --- a/scripts/llext_link_helper.py +++ b/scripts/llext_link_helper.py @@ -119,6 +119,11 @@ def main(): if (s_flags & (SH_FLAGS.SHF_WRITE | SH_FLAGS.SHF_ALLOC) == SH_FLAGS.SHF_WRITE | SH_FLAGS.SHF_ALLOC): + # .rodata may be marked writable by the Clang/objcopy LLEXT + # pipeline but should still be placed near .text for l32r reach + if s_name == '.rodata' or s_name.startswith('.rodata.'): + readonly.append(section) + continue # .data, .bss or other writable sections writable.append(section) continue @@ -181,17 +186,30 @@ def main(): dram_addr += section.header['sh_size'] - start_addr = align_up(text_addr + text_size, 0x1000) - + # Place readonly sections BEFORE .text so that Xtensa l32r instructions + # (which can only reach backwards up to 256KB) can access .rodata literals. + readonly_size = 0 + for section in readonly: + readonly_size += align_up(section.header['sh_size'], section.header['sh_addralign']) + # Reserve space for readonly before .text + readonly_start = max_alignment(text_addr, 0x1000, 4) + text_addr = align_up(readonly_start + readonly_size, 0x1000) + # Now re-place .text at the updated address + command = [c for c in command if not c.startswith('-Wl,-Ttext=')] + command.append(f'-Wl,-Ttext=0x{text_addr:x}') + + ro_addr = readonly_start for section in readonly: s_alignment = section.header['sh_addralign'] s_name = section.name - start_addr = align_up(start_addr, s_alignment) + ro_addr = align_up(ro_addr, s_alignment) - command.append(f'-Wl,--section-start={s_name}=0x{start_addr:x}') + command.append(f'-Wl,--section-start={s_name}=0x{ro_addr:x}') - start_addr += section.header['sh_size'] + ro_addr += section.header['sh_size'] + + start_addr = align_up(text_addr + text_size, 0x1000) start_addr = align_up(start_addr, 0x1000) diff --git a/scripts/xtensa-build-zephyr.py b/scripts/xtensa-build-zephyr.py index 8a7a866d7aad..c8dcded81ace 100755 --- a/scripts/xtensa-build-zephyr.py +++ b/scripts/xtensa-build-zephyr.py @@ -412,6 +412,13 @@ def parse_args(): help="Build menuconfig for target") parser.add_argument("-z", "--zephyrsdk", required=False, action="store_true", help="Force Build using Zephyr SDK for target") + parser.add_argument("--llvm-clang", required=False, type=pathlib.Path, default=None, + metavar="LLVM_BUILD_DIR", + help="""Use a custom LLVM/Clang build for C/ASM compilation instead of +xt-clang or GCC. Specify the LLVM build directory containing bin/clang. +A wrapper script is auto-generated to handle GCC flag translation, +external assembler usage, and Xtensa target configuration. +Example: --llvm-clang /home/user/llvm-project/build""") args = parser.parse_args() @@ -805,6 +812,166 @@ def rimage_options(platform_dict): return opts +def generate_clang_wrapper(llvm_build_dir, platform, plat_config, top_dir): + """Generate a clang wrapper script that translates GCC-only flags and + configures the external assembler for Xtensa targets. + + The Zephyr build system passes several GCC-specific flags that clang + does not support. This wrapper filters those out, ensures the external + GCC assembler is used (clang's integrated asm doesn't support the full + Xtensa ISA), and adds the necessary -B/-I paths. + + Returns the path to the generated wrapper script. + """ + llvm_build_dir = pathlib.Path(llvm_build_dir).resolve() + clang_bin = llvm_build_dir / "bin" / "clang" + if not clang_bin.exists(): + raise FileNotFoundError(f"Clang not found at {clang_bin}") + + # Derive the Zephyr SDK cross-compiler name from the board config + # e.g. "intel_adsp/ace30/ptl" -> "xtensa-intel_ace30_ptl_zephyr-elf" + # The Zephyr SDK naming uses the SoC name with underscores + board_parts = plat_config.replace("/", "_") + # Try to find the actual SDK cross-compiler directory + sdk_dir = os.environ.get("ZEPHYR_SDK_INSTALL_DIR", "") + cross_prefix = f"xtensa-{platform_configs[platform].XTENSA_CORE.split('_')[0]}" + + # Find the GNU toolchain directory in the Zephyr SDK + gnu_dir = None + if sdk_dir: + sdk_path = pathlib.Path(sdk_dir) + # Look for the xtensa-*_zephyr-elf directory + for d in sdk_path.glob("gnu/xtensa-*_zephyr-elf"): + # Match based on platform name in the directory + if platform in str(d) or board_parts.split("_")[2] in str(d): + gnu_dir = d + break + # Fallback: try exact board-based name + if gnu_dir is None: + # Convert board config to SDK name + # intel_adsp/ace30/ptl -> intel_ace30_ptl + parts = plat_config.split("/") + if len(parts) >= 3: + sdk_name = f"{parts[1]}_{parts[2]}" + else: + sdk_name = parts[-1] + for d in sdk_path.glob(f"gnu/xtensa-*{sdk_name}*"): + gnu_dir = d + break + + # Find the HAL SoC directory for core-isa.h + hal_soc_dir = None + modules_hal = top_dir / "modules" / "hal" / "xtensa" + if modules_hal.exists(): + parts = plat_config.split("/") + if len(parts) >= 3: + soc_name = f"{parts[0].replace('intel_adsp', 'intel')}_{parts[1]}_{parts[2]}" + else: + soc_name = parts[-1] + for d in modules_hal.glob(f"zephyr/soc/*{parts[1]}*{parts[-1]}*"): + hal_soc_dir = d + break + # Fallback: try matching just the last part + if hal_soc_dir is None: + for d in modules_hal.glob(f"zephyr/soc/*{parts[-1]}*"): + hal_soc_dir = d + break + + # Build the wrapper script + wrapper_dir = llvm_build_dir / "bin" + wrapper_path = wrapper_dir / f"clang-sof-wrapper-{platform}.sh" + + # Determine target triple from GNU dir name + target_triple = "" + if gnu_dir: + # e.g. "xtensa-intel_ace30_ptl_zephyr-elf" from the dir name + target_triple = gnu_dir.name + + # GCC compatibility queries must come before arg processing + gcc_compat = "" + if gnu_dir: + gcc_bin = gnu_dir / "bin" / f"{target_triple}-gcc" + gcc_compat = f""" +# GCC compatibility: forward GCC-specific queries to the real GCC. +# Zephyr's cmake toolchain detection calls -dumpfullversion, +# --print-file-name, --print-multi-directory etc. which clang doesn't support. +case "$1" in + -dumpversion|-dumpfullversion|-dumpmachine|\ + --print-file-name=*|--print-multi-directory|--print-multi-lib|\ + --print-libgcc-file-name|--print-search-dirs|-print-search-dirs|\ + -print-multi-directory) + exec {gcc_bin} "$@" + ;; +esac +""" + + wrapper_content = f"""#!/bin/bash +# Auto-generated clang wrapper for SOF/{platform} builds +# Generated by xtensa-build-zephyr.py --llvm-clang +# Clang: {clang_bin} +# Target: {target_triple} +{gcc_compat} +ARGS=() +HAS_NO_INTEGRATED_AS=0 +HAS_TARGET=0 +for arg in "$@"; do + case "$arg" in + -fno-reorder-functions) ;; # GCC only + -fno-defer-pop) ;; # GCC only + --param=*) ;; # GCC only + -fstrict-overflow) ;; # deprecated GCC flag + -mlongcalls) ARGS+=("-mlong-calls") ;; # translate + -fno-integrated-as) HAS_NO_INTEGRATED_AS=1; ARGS+=("$arg") ;; + --target=*) HAS_TARGET=1; ARGS+=("$arg") ;; + *) ARGS+=("$arg") ;; + esac +done + +# Always use external assembler (clang integrated asm lacks full Xtensa ISA) +if [ "$HAS_NO_INTEGRATED_AS" -eq 0 ]; then + ARGS+=("-fno-integrated-as") +fi +""" + + if target_triple: + wrapper_content += f""" +# Set Xtensa target if not already specified +if [ "$HAS_TARGET" -eq 0 ]; then + ARGS+=("--target={target_triple}") +fi +""" + + if hal_soc_dir: + core_isa_dir = hal_soc_dir / "xtensa" / "config" + if core_isa_dir.exists(): + wrapper_content += f""" +# core-isa.h lives in the SoC-specific HAL overlay, not alongside core.h +ARGS+=("-I{core_isa_dir}") +""" + + if gnu_dir: + sysroot_bin = gnu_dir / target_triple / "bin" + gnu_bin = gnu_dir / "bin" + wrapper_content += f""" +# Paths so clang can find the GCC external assembler and linker +ARGS+=("-B{sysroot_bin}") +ARGS+=("-B{gnu_bin}") +""" + + wrapper_content += f""" +# Enable FLIX VLIW instruction bundling +ARGS+=("-Xclang" "-target-feature" "-Xclang" "+flix") + +exec {clang_bin} "${{ARGS[@]}}" +""" + + with open(wrapper_path, "w") as f: + f.write(wrapper_content) + os.chmod(wrapper_path, 0o755) + print(f"Generated clang wrapper: {wrapper_path}") + return str(wrapper_path) + + STAGING_DIR = None def build_platforms(): global west_top, SOF_TOP @@ -854,7 +1021,13 @@ def build_platforms(): _dict = dataclasses.asdict(platform_configs[platform]) platform_dict = { k:v for (k,v) in _dict.items() if _dict[k] is not None } - if args.zephyrsdk: + if args.llvm_clang: + print(f"Using LLVM/Clang from {args.llvm_clang}") + xtensa_tools_root_dir = None + # Use llvm toolchain variant for native Clang support with GCC assembler/linker + platf_build_environ["ZEPHYR_TOOLCHAIN_VARIANT"] = "llvm" + platf_build_environ["LLVM_TOOLCHAIN_PATH"] = str(pathlib.Path(args.llvm_clang).resolve()) + elif args.zephyrsdk: print("Using Zephyr SDK for building") xtensa_tools_root_dir = None else: @@ -920,16 +1093,39 @@ def build_platforms(): if args.cmake_args: build_cmd += args.cmake_args + # When using LLVM/Clang, the llvm toolchain variant handles: + # - Compiler detection via LLVM_TOOLCHAIN_PATH + # - GCC assembler/linker integration via CROSS_COMPILE + # - FLIX VLIW bundling via TOOLCHAIN_C_FLAGS + # - LLEXT shared library link wrapper + # No wrapper script or CMAKE_C_COMPILER override needed. + if args.llvm_clang: + # Set XTENSA_CORE_ID so cmake/toolchain/llvm/target.cmake + # selects the correct -mcpu for the LLVM backend. + # Platforms that share an SDK toolchain (e.g. NVL uses PTL's + # assembler) need an explicit CPU override for HiFi5 support. + parts = PLAT_CONFIG.split("/") + if len(parts) >= 3: + core_id = f"{parts[1]}_{parts[2]}" # e.g. "ace40_nvl" -> "intel_ace40" + # Map board-level names to LLVM processor names + core_id_map = { + "ace30_ptl": "intel_ace30_ptl", + "ace40_nvl": "intel_ace40", + "ace40_nvls": "intel_ace40", + } + platf_build_environ["XTENSA_CORE_ID"] = core_id_map.get(core_id, "intel_ace30_ptl") + extra_conf_files = [str(item.resolve(True)) for item in args.overlay] # The '-d' option is a shortcut for '-o path_to_debug_overlay', we are good # if both are provided, because it's no harm to merge the same overlay twice. if args.debug: extra_conf_files.append(str(pathlib.Path(SOF_TOP, "app", "debug_overlay.conf"))) - # The xt-cland Cadence toolchain currently cannot link shared + # The xt-clang Cadence toolchain currently cannot link shared # libraries for Xtensa. Therefore when it's used we switch to - # building relocatable ELF objects. - if platf_build_environ.get("ZEPHYR_TOOLCHAIN_VARIANT") == 'xt-clang': + # building relocatable ELF objects. We do the same for LLVM/Clang + # to match the xt-clang LLEXT format and avoid GOT/PLT overhead. + if platf_build_environ.get("ZEPHYR_TOOLCHAIN_VARIANT") == 'xt-clang' or args.llvm_clang: extra_conf_files.append(str(pathlib.Path(SOF_TOP, "app", "llext_relocatable.conf"))) if extra_conf_files: diff --git a/src/audio/dai-zephyr.c b/src/audio/dai-zephyr.c index 80cba02eb8fc..e81a1a7eadbf 100644 --- a/src/audio/dai-zephyr.c +++ b/src/audio/dai-zephyr.c @@ -192,6 +192,7 @@ __cold int dai_set_config(struct dai *dai, struct ipc_config_dai *common_config, cfg.type = DAI_IMX_MICFIL; cfg_params = &sof_cfg->micfil; break; +#if 0 case SOF_DAI_INTEL_UAOL: cfg.type = DAI_INTEL_UAOL; cfg.channels = common_config->gtw_fmt->channels_count; @@ -204,6 +205,7 @@ __cold int dai_set_config(struct dai *dai, struct ipc_config_dai *common_config, cfg_params = spec_config; dai_set_link_hda_config(&cfg.link_config, common_config, spec_config); break; +#endif default: return -EINVAL; } diff --git a/src/lib/dai.c b/src/lib/dai.c index 58ff68a2e57e..8b8f47e0c6c0 100644 --- a/src/lib/dai.c +++ b/src/lib/dai.c @@ -210,8 +210,10 @@ static int sof_dai_type_to_zephyr(uint32_t type) return DAI_INTEL_HDA; case SOF_DAI_INTEL_ALH: return DAI_INTEL_ALH; +#if 0 case SOF_DAI_INTEL_UAOL: return DAI_INTEL_UAOL; +#endif case SOF_DAI_IMX_SAI: return DAI_IMX_SAI; case SOF_DAI_IMX_ESAI: diff --git a/zephyr/CMakeLists.txt b/zephyr/CMakeLists.txt index 9a4737858910..11664a327c12 100644 --- a/zephyr/CMakeLists.txt +++ b/zephyr/CMakeLists.txt @@ -63,7 +63,11 @@ function(sof_llext_build module) # the L32R instruction. Usually this is guaranteed by the linker script. # However, since a linker script is not used for LLEXT modules builds, # correct literal placement is ensured with a compilation option here. - set(SOF_LLEXT_CFLAGS ${SOF_LLEXT_CFLAGS} -mtext-section-literals) + # Note: -mtext-section-literals is GCC-specific; Clang doesn't support it. + check_c_compiler_flag(-mtext-section-literals HAS_MTEXT_SECTION_LITERALS) + if(HAS_MTEXT_SECTION_LITERALS) + set(SOF_LLEXT_CFLAGS ${SOF_LLEXT_CFLAGS} -mtext-section-literals) + endif() endif() target_include_directories(${module}_llext_lib PRIVATE @@ -132,8 +136,9 @@ function(sof_llext_build module) COMMAND ${PYTHON_EXECUTABLE} ${SOF_BASE}scripts/llext_link_helper.py -s ${size_file} --text-addr=${CONFIG_LIBRARY_BASE_ADDRESS} -c $ - -f ${proc_in_file} -o ${proc_out_file} ${CMAKE_C_COMPILER} -- - ${EXTRA_LINKER_PARAMS} $ ${EXTRA_LIBS} + -f ${proc_in_file} -o ${proc_out_file} + $ENV{ZEPHYR_SDK_INSTALL_DIR}/gnu/${CROSS_COMPILE_TARGET}/bin/${CROSS_COMPILE_TARGET}-gcc -- + -mtext-section-literals ${EXTRA_LINKER_PARAMS} $ ${EXTRA_LIBS} ) add_llext_command(TARGET ${module}