diff --git a/app/boards/intel_adsp_ace15_mtpm.conf b/app/boards/intel_adsp_ace15_mtpm.conf index 0b5472cd282a..a660f20b8387 100644 --- a/app/boards/intel_adsp_ace15_mtpm.conf +++ b/app/boards/intel_adsp_ace15_mtpm.conf @@ -17,6 +17,11 @@ CONFIG_FORMAT_CONVERT_HIFI3=n CONFIG_SAMPLE_KEYPHRASE=y CONFIG_COMP_PHASE_VOCODER=y CONFIG_COMP_STFT_PROCESS=y +CONFIG_SOF_STAGING=y +CONFIG_CPP=y +CONFIG_STD_CPP17=y +CONFIG_COMP_TENSORFLOW=m +CONFIG_STACK_SIZE_EDF=32768 # SOF / audio modules / mocks # This mock is part of official sof-bin releases because the CI that @@ -52,7 +57,7 @@ CONFIG_SOF_LOG_LEVEL_INF=y CONFIG_DEBUG_COREDUMP=y CONFIG_DEBUG_COREDUMP_BACKEND_INTEL_ADSP_MEM_WINDOW=y CONFIG_DEBUG_COREDUMP_MEMORY_DUMP_MIN=y -CONFIG_HEAP_MEM_POOL_SIZE=8192 +CONFIG_HEAP_MEM_POOL_SIZE=32768 CONFIG_LLEXT=y CONFIG_LLEXT_STORAGE_WRITABLE=y CONFIG_LLEXT_EXPERIMENTAL=y diff --git a/app/boards/intel_adsp_ace30_ptl.conf b/app/boards/intel_adsp_ace30_ptl.conf index b6ac41938398..b514c5e1e756 100644 --- a/app/boards/intel_adsp_ace30_ptl.conf +++ b/app/boards/intel_adsp_ace30_ptl.conf @@ -15,6 +15,12 @@ CONFIG_FORMAT_CONVERT_HIFI3=n CONFIG_COMP_GOOGLE_RTC_AUDIO_PROCESSING=m CONFIG_GOOGLE_RTC_AUDIO_PROCESSING_MOCK=y CONFIG_COMP_STFT_PROCESS=y +CONFIG_SOF_STAGING=y +CONFIG_COMP_KPB=y +CONFIG_COMP_TENSORFLOW=y +CONFIG_COMP_MFCC=y +CONFIG_COMP_VOLUME=y +CONFIG_COMP_GAIN=y # SOF / infrastructure CONFIG_KCPS_DYNAMIC_CLOCK_CONTROL=n @@ -29,7 +35,7 @@ CONFIG_COLD_STORE_EXECUTE_DRAM=y CONFIG_INTEL_MODULES=y CONFIG_LIBRARY_AUTH_SUPPORT=y CONFIG_LIBRARY_MANAGER=y -CONFIG_LIBRARY_BASE_ADDRESS=0xa0688000 +CONFIG_LIBRARY_BASE_ADDRESS=0xa0700000 CONFIG_LIBRARY_BUILD_LIB=y CONFIG_LIBRARY_DEFAULT_MODULAR=y diff --git a/app/llext_relocatable.conf b/app/llext_relocatable.conf index 76b5339e1bb0..ce8dafe3a6aa 100644 --- a/app/llext_relocatable.conf +++ b/app/llext_relocatable.conf @@ -1 +1,2 @@ CONFIG_LLEXT_TYPE_ELF_RELOCATABLE=y +CONFIG_LLEXT_EXPORT_BUILTINS_BY_SLID=y diff --git a/scripts/llext_link_helper.py b/scripts/llext_link_helper.py index f10777c9918f..c90ffd629c69 100755 --- a/scripts/llext_link_helper.py +++ b/scripts/llext_link_helper.py @@ -77,6 +77,23 @@ def main(): command = [args.command] + # If the linker command is clang, extract --target= and --ld-path= from params + # and add them to the command (before the linker flags). Without --target, + # clang uses the host target (x86_64) when invoking the linker, which fails + # for cross-compiled (e.g. xtensa) object files. + clang_flags_to_hoist = [] + remaining_params = [] + for p_arg in args.params: + if p_arg.startswith('--target=') or p_arg.startswith('--ld-path='): + clang_flags_to_hoist.append(p_arg) + else: + remaining_params.append(p_arg) + if clang_flags_to_hoist: + command.extend(clang_flags_to_hoist) + args.params = remaining_params + + is_relocatable = '-r' in args.params + executable = [] writable = [] readonly = [] @@ -111,7 +128,8 @@ def main(): text_found = True text_addr = max_alignment(text_addr, 0x1000, s_alignment) text_size = s_size - command.append(f'-Wl,-Ttext=0x{text_addr:x}') + if not is_relocatable: + command.append(f'-Wl,-Ttext=0x{text_addr:x}') else: executable.append(section) @@ -164,7 +182,8 @@ def main(): dram_addr = align_up(dram_addr, s_alignment) - command.append(f'-Wl,--section-start={s_name}=0x{dram_addr:x}') + if not is_relocatable: + command.append(f'-Wl,--section-start={s_name}=0x{dram_addr:x}') dram_addr += section.header['sh_size'] @@ -177,7 +196,8 @@ def main(): dram_addr = align_up(dram_addr, s_alignment) - command.append(f'-Wl,--section-start={s_name}=0x{dram_addr:x}') + if not is_relocatable: + command.append(f'-Wl,--section-start={s_name}=0x{dram_addr:x}') dram_addr += section.header['sh_size'] @@ -189,7 +209,8 @@ def main(): start_addr = align_up(start_addr, s_alignment) - command.append(f'-Wl,--section-start={s_name}=0x{start_addr:x}') + if not is_relocatable: + command.append(f'-Wl,--section-start={s_name}=0x{start_addr:x}') start_addr += section.header['sh_size'] @@ -201,12 +222,22 @@ def main(): start_addr = align_up(start_addr, s_alignment) - if s_name == '.data': - command.append(f'-Wl,-Tdata=0x{start_addr:x}') - else: - command.append(f'-Wl,--section-start={s_name}=0x{start_addr:x}') - - start_addr += section.header['sh_size'] + if not is_relocatable: + if s_name == '.data': + command.append(f'-Wl,-Tdata=0x{start_addr:x}') + else: + command.append(f'-Wl,--section-start={s_name}=0x{start_addr:x}') + + ld_script_path = pathlib.Path(args.file).parent / 'llext_merge.ld' + with open(ld_script_path, 'w') as f_ld: + f_ld.write('SECTIONS\n{\n' + ' .text : { *(.text .text.* .stub .gnu.linkonce.t.*) }\n' + ' .literal : { *(.literal .literal.*) }\n' + ' .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) }\n' + ' .data : { *(.data .data.* .gnu.linkonce.d.*) }\n' + ' .bss : { *(.bss .bss.* COMMON) }\n' + '}\n') + command.append(f'-Wl,-T,{ld_script_path}') command.extend(['-o', f'{args.file}.tmp']) command.extend(args.params) @@ -215,6 +246,18 @@ def main(): copy_command = [args.copy] + copy_command.extend([ + '--strip-debug', + '--remove-section=.comment', + '--remove-section=.llvm_addrsig', + '--remove-section=.eh_frame', + '--remove-section=.rela.eh_frame', + '--remove-section=.xt.prop', + '--remove-section=.rela.xt.prop', + '--remove-section=.xt.lit', + '--remove-section=.rela.xt.lit', + ]) + if first_dram_text: copy_command.extend(['--set-section-alignment', f'{first_dram_text}=4096']) if first_dram_rodata: diff --git a/scripts/llext_offset_calc.py b/scripts/llext_offset_calc.py index 0f302a8cbe12..2a07984b725f 100755 --- a/scripts/llext_offset_calc.py +++ b/scripts/llext_offset_calc.py @@ -47,6 +47,9 @@ def get_elf_size(elf_name): if section.header['sh_addr'] + section.header['sh_size'] > end: end = section.header['sh_addr'] + section.header['sh_size'] + if start == 0xffffffff: + return 0 + size = end - start return size diff --git a/src/audio/tensorflow/CMakeLists.txt b/src/audio/tensorflow/CMakeLists.txt index 5cb3086f46b1..8c3048a95da2 100644 --- a/src/audio/tensorflow/CMakeLists.txt +++ b/src/audio/tensorflow/CMakeLists.txt @@ -1,6 +1,11 @@ # Copyright (c) 2025 Intel Corporation. # SPDX-License-Identifier: Apache-2.0 +# -mllvm --text-section-literals=false is a Clang-only flag; guard it +if(CMAKE_C_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + add_compile_options(-mllvm --text-section-literals=false) +endif() + # are we building the llext module ? if(CONFIG_COMP_TENSORFLOW STREQUAL "m" AND DEFINED CONFIG_LLEXT) add_subdirectory(llext ${PROJECT_BINARY_DIR}/tflm_llext) @@ -17,64 +22,37 @@ set(FLATBUFFERS_PATH ${sof_top_dir}/../flatbuffers) set(GEMMLOWP_PATH ${sof_top_dir}/../gemmlowp) set(RUY_PATH ${sof_top_dir}/../ruy) +# nnlib-hifi4 requires Cadence XCC/Clang-specific TIE headers (xtensa/tie/xt_hifi2.h) +# and therefore can ONLY be built with Clang. Skip entirely when using gcc. +if(CMAKE_C_COMPILER_ID STREQUAL "Clang") + add_library(nn_hifi_lib STATIC ${NN_HIFI_PATH}/algo/common/src/xa_nnlib_common_api.c - ${NN_HIFI_PATH}/algo/kernels/reorg/hifi4/xa_nn_pad_8.c - ${NN_HIFI_PATH}/algo/kernels/reorg/hifi4/xa_nn_stride_slice_int16.c - ${NN_HIFI_PATH}/algo/kernels/reorg/hifi4/xa_nn_batch_to_space_nd_8.c - ${NN_HIFI_PATH}/algo/kernels/reorg/hifi4/xa_nn_transpose_8.c - ${NN_HIFI_PATH}/algo/kernels/reorg/hifi4/xa_nn_depth_to_space_8.c - ${NN_HIFI_PATH}/algo/kernels/reorg/hifi4/xa_nn_pad_16.c - ${NN_HIFI_PATH}/algo/kernels/reorg/hifi4/xa_nn_stride_slice_int32.c - ${NN_HIFI_PATH}/algo/kernels/reorg/hifi4/xa_nn_space_to_batch_nd_8.c - ${NN_HIFI_PATH}/algo/kernels/reorg/hifi4/xa_nn_pad_32.c - ${NN_HIFI_PATH}/algo/kernels/reorg/hifi4/xa_nn_stride_slice_int8.c - ${NN_HIFI_PATH}/algo/kernels/reorg/hifi4/xa_nn_space_to_depth_8.c ${NN_HIFI_PATH}/algo/kernels/activations/hifi4/xa_nn_activations_32_32.c ${NN_HIFI_PATH}/algo/kernels/activations/hifi4/xa_nn_activations_f32_f32.c - ${NN_HIFI_PATH}/algo/kernels/activations/hifi4/xa_nn_activations_sym16_sym16.c - ${NN_HIFI_PATH}/algo/kernels/activations/hifi4/xa_nn_softmax_asym8_asym8.c - ${NN_HIFI_PATH}/algo/kernels/activations/hifi4/xa_nn_activations_32_16.c - ${NN_HIFI_PATH}/algo/kernels/activations/hifi4/xa_nn_activations_16_16.c - ${NN_HIFI_PATH}/algo/kernels/activations/hifi4/xa_nn_activations_8_8.c - ${NN_HIFI_PATH}/algo/kernels/activations/hifi4/xa_nn_activations_asym16_asym16.c - ${NN_HIFI_PATH}/algo/kernels/activations/hifi4/xa_nn_activations_32_8.c - ${NN_HIFI_PATH}/algo/kernels/cnn/hifi4/xa_nn_matXvec_16x16_16_circ_nb.c - ${NN_HIFI_PATH}/algo/kernels/cnn/hifi4/xa_nn_conv2d_pointwise_16x16.c - ${NN_HIFI_PATH}/algo/kernels/cnn/hifi4/xa_nn_conv2d_depthwise_16x16.c - ${NN_HIFI_PATH}/algo/kernels/cnn/hifi4/xa_nn_transpose_conv_circ_buf.c - ${NN_HIFI_PATH}/algo/kernels/cnn/hifi4/xa_nn_conv2d_group_sym8sxasym8s.c - ${NN_HIFI_PATH}/algo/kernels/cnn/hifi4/xa_nn_matXvec_asym8xasym8_asym8_circ_nb.c - ${NN_HIFI_PATH}/algo/kernels/cnn/hifi4/xa_nn_conv2d_depthwise_sym8sxasym8s.c + ${NN_HIFI_PATH}/algo/kernels/basic/hifi4/xa_nn_elm_add_f32.c + ${NN_HIFI_PATH}/algo/kernels/basic/hifi4/xa_nn_elm_sub_f32.c + ${NN_HIFI_PATH}/algo/kernels/basic/hifi4/xa_nn_elm_mul_f32.c + ${NN_HIFI_PATH}/algo/kernels/basic/hifi4/xa_nn_elm_abs_f32.c + ${NN_HIFI_PATH}/algo/ndsp/hifi4/src/nanf_tbl.c + ${NN_HIFI_PATH}/algo/ndsp/hifi4/src/inff_tbl.c + ${NN_HIFI_PATH}/algo/ndsp/hifi4/src/pow2f_tbl.c + ${NN_HIFI_PATH}/algo/ndsp/hifi4/src/expf_tbl.c + ${NN_HIFI_PATH}/algo/kernels/cnn/hifi4/xa_nn_matXvec_8x8_8_circ.c - ${NN_HIFI_PATH}/algo/kernels/cnn/hifi4/xa_nn_conv2d_pointwise_sym8sxsym16s.c ${NN_HIFI_PATH}/algo/kernels/cnn/hifi4/xa_nn_conv2d_pointwise_8x16.c ${NN_HIFI_PATH}/algo/kernels/cnn/hifi4/xa_nn_circ_buf.c ${NN_HIFI_PATH}/algo/kernels/cnn/hifi4/xa_nn_conv2d_std_8x8.c ${NN_HIFI_PATH}/algo/kernels/cnn/hifi4/xa_nn_conv2d_depthwise.c ${NN_HIFI_PATH}/algo/kernels/cnn/hifi4/xa_nn_matXvec_16x16_16_circ.c - ${NN_HIFI_PATH}/algo/kernels/cnn/hifi4/xa_nn_transpose_conv_f32.c - ${NN_HIFI_PATH}/algo/kernels/cnn/hifi4/xa_nn_conv2d_depthwise_f32.c - ${NN_HIFI_PATH}/algo/kernels/cnn/hifi4/xa_nn_matXvec_f32_circ.c ${NN_HIFI_PATH}/algo/kernels/cnn/hifi4/xa_nn_conv2d_std_circ_buf.c - ${NN_HIFI_PATH}/algo/kernels/cnn/hifi4/xa_nn_transpose_conv_sym8sxasym8s.c ${NN_HIFI_PATH}/algo/kernels/cnn/hifi4/xa_nn_matXvec_8x16_16_circ.c - ${NN_HIFI_PATH}/algo/kernels/cnn/hifi4/xa_nn_matXvec_sym8sxsym16s_sym16s_circ.c ${NN_HIFI_PATH}/algo/kernels/cnn/hifi4/xa_nn_conv1d_std_8x8.c ${NN_HIFI_PATH}/algo/kernels/cnn/hifi4/xa_nn_conv2d_std_8x16.c - ${NN_HIFI_PATH}/algo/kernels/cnn/hifi4/xa_nn_conv2d_std_sym8sxsym16s.c - ${NN_HIFI_PATH}/algo/kernels/cnn/hifi4/xa_nn_conv2d_pointwise_f32.c - ${NN_HIFI_PATH}/algo/kernels/cnn/hifi4/xa_nn_matXvec_f32_circ_nb.c - ${NN_HIFI_PATH}/algo/kernels/cnn/hifi4/xa_nn_conv2d_std_sym8sxasym8s.c - ${NN_HIFI_PATH}/algo/kernels/cnn/hifi4/xa_nn_matXvec_asym8xasym8_asym8_circ.c - ${NN_HIFI_PATH}/algo/kernels/cnn/hifi4/xa_nn_conv2d_pointwise_sym8sxasym8s.c ${NN_HIFI_PATH}/algo/kernels/cnn/hifi4/xa_nn_conv2d_depthwise_8x8.c - ${NN_HIFI_PATH}/algo/kernels/cnn/hifi4/xa_nn_transpose_conv_sym8sxsym16s.c - ${NN_HIFI_PATH}/algo/kernels/cnn/hifi4/xa_nn_conv2d_std_f32.c ${NN_HIFI_PATH}/algo/kernels/cnn/hifi4/xa_nn_conv1d_std_8x16.c ${NN_HIFI_PATH}/algo/kernels/cnn/hifi4/xa_nn_conv2d_pointwise_8x8.c ${NN_HIFI_PATH}/algo/kernels/cnn/hifi4/xa_nn_conv2d_std_16x16.c - ${NN_HIFI_PATH}/algo/kernels/cnn/hifi4/xa_nn_matXvec_sym8sxasym8s_asym8s_circ.c ${NN_HIFI_PATH}/algo/kernels/cnn/hifi4/xa_nn_conv2d_pointwise_asym8xasym8.c ${NN_HIFI_PATH}/algo/kernels/cnn/hifi4/xa_nn_conv2d_std_asym8xasym8.c ${NN_HIFI_PATH}/algo/kernels/cnn/hifi4/xa_nn_conv1d_std_circ_buf.c @@ -82,117 +60,11 @@ add_library(nn_hifi_lib STATIC ${NN_HIFI_PATH}/algo/kernels/cnn/hifi4/xa_nn_matXvec_8x16_16_circ_nb.c ${NN_HIFI_PATH}/algo/kernels/cnn/hifi4/xa_nn_conv1d_std_16x16.c ${NN_HIFI_PATH}/algo/kernels/cnn/hifi4/xa_nn_matXvec_8x8_8_circ_nb.c - ${NN_HIFI_PATH}/algo/kernels/cnn/hifi4/xa_nn_conv2d_std_sym4sxasym8s.c - ${NN_HIFI_PATH}/algo/kernels/cnn/hifi4/xa_nn_conv1d_std_f32.c - ${NN_HIFI_PATH}/algo/kernels/cnn/hifi4/xa_nn_conv2d_depthwise_sym8sxsym16s.c ${NN_HIFI_PATH}/algo/kernels/cnn/hifi4/xa_nn_conv2d_depthwise_asym8xasym8.c - ${NN_HIFI_PATH}/algo/kernels/cnn/hifi4/xa_nn_conv2d_depthwise_8x16.c - ${NN_HIFI_PATH}/algo/kernels/matXvec/hifi4/xa_nn_matmul_f32.c - ${NN_HIFI_PATH}/algo/kernels/matXvec/hifi4/xa_nn_matXvec_asym8xasym8_batch.c - ${NN_HIFI_PATH}/algo/kernels/matXvec/hifi4/xa_nn_matmul_sym8sxasym8s.c - ${NN_HIFI_PATH}/algo/kernels/matXvec/hifi4/xa_nn_matmul_8x8.c - ${NN_HIFI_PATH}/algo/kernels/matXvec/hifi4/xa_nn_matmul_8x16.c - ${NN_HIFI_PATH}/algo/kernels/matXvec/hifi4/xa_nn_matXvec_asym4sxasym8s.c - ${NN_HIFI_PATH}/algo/kernels/matXvec/hifi4/xa_nn_matmul_sym8sxsym16s.c - ${NN_HIFI_PATH}/algo/kernels/matXvec/hifi4/xa_nn_matXvec_f32_batch.c - ${NN_HIFI_PATH}/algo/kernels/matXvec/hifi4/xa_nn_matXvec_f32.c - ${NN_HIFI_PATH}/algo/kernels/matXvec/hifi4/xa_nn_matXvec_16x16_batch.c - ${NN_HIFI_PATH}/algo/kernels/matXvec/hifi4/xa_nn_matXvec_sym8sxsym16s.c - ${NN_HIFI_PATH}/algo/kernels/matXvec/hifi4/xa_nn_matmul_asym8sxasym8s.c - ${NN_HIFI_PATH}/algo/kernels/matXvec/hifi4/xa_nn_matXvec_16x8.c - ${NN_HIFI_PATH}/algo/kernels/matXvec/hifi4/xa_nn_matXvec_8x16.c - ${NN_HIFI_PATH}/algo/kernels/matXvec/hifi4/xa_nn_matXvec_8x8.c - ${NN_HIFI_PATH}/algo/kernels/matXvec/hifi4/xa_nn_matXvec_asym8sxasym8s.c - ${NN_HIFI_PATH}/algo/kernels/matXvec/hifi4/xa_nn_matmul_asym8xasym8.c - ${NN_HIFI_PATH}/algo/kernels/matXvec/hifi4/xa_nn_matmul_16x16.c - ${NN_HIFI_PATH}/algo/kernels/matXvec/hifi4/xa_nn_matXvec_8x16_batch.c - ${NN_HIFI_PATH}/algo/kernels/matXvec/hifi4/xa_nn_matXvec_16x16.c - ${NN_HIFI_PATH}/algo/kernels/matXvec/hifi4/xa_nn_matXvec_asym8xasym8.c - ${NN_HIFI_PATH}/algo/kernels/matXvec/hifi4/xa_nn_matXvec_sym8sxasym8s.c - ${NN_HIFI_PATH}/algo/kernels/matXvec/hifi4/xa_nn_matXvec_8x8_batch.c - ${NN_HIFI_PATH}/algo/kernels/pool/hifi4/xa_nn_maxpool_f32.c - ${NN_HIFI_PATH}/algo/kernels/pool/hifi4/xa_nn_maxpool_8.c - ${NN_HIFI_PATH}/algo/kernels/pool/hifi4/xa_nn_maxpool_asym8_nhwc.c - ${NN_HIFI_PATH}/algo/kernels/pool/hifi4/xa_nn_avgpool_f32_nhwc.c - ${NN_HIFI_PATH}/algo/kernels/pool/hifi4/xa_nn_maxpool_16_nhwc.c - ${NN_HIFI_PATH}/algo/kernels/pool/hifi4/xa_nn_avgpool_16.c - ${NN_HIFI_PATH}/algo/kernels/pool/hifi4/xa_nn_avgpool_16_nhwc.c - ${NN_HIFI_PATH}/algo/kernels/pool/hifi4/xa_nn_avgpool_asym8.c - ${NN_HIFI_PATH}/algo/kernels/pool/hifi4/xa_nn_avgpool.c - ${NN_HIFI_PATH}/algo/kernels/pool/hifi4/xa_nn_inv_256_tbl.c - ${NN_HIFI_PATH}/algo/kernels/pool/hifi4/xa_nn_maxpool.c - ${NN_HIFI_PATH}/algo/kernels/pool/hifi4/xa_nn_maxpool_8_nhwc.c - ${NN_HIFI_PATH}/algo/kernels/pool/hifi4/xa_nn_maxpool_16.c - ${NN_HIFI_PATH}/algo/kernels/pool/hifi4/xa_nn_avgpool_8.c - ${NN_HIFI_PATH}/algo/kernels/pool/hifi4/xa_nn_maxpool_f32_nhwc.c - ${NN_HIFI_PATH}/algo/kernels/pool/hifi4/xa_nn_avgpool_asym8_nhwc.c - ${NN_HIFI_PATH}/algo/kernels/pool/hifi4/xa_nn_avgpool_f32.c - ${NN_HIFI_PATH}/algo/kernels/pool/hifi4/xa_nn_maxpool_asym8.c - ${NN_HIFI_PATH}/algo/kernels/pool/hifi4/xa_nn_avgpool_8_nhwc.c - ${NN_HIFI_PATH}/algo/kernels/norm/hifi4/xa_nn_l2_norm_asym8s.c - ${NN_HIFI_PATH}/algo/kernels/norm/hifi4/xa_nn_l2_norm_f32.c - ${NN_HIFI_PATH}/algo/kernels/basic/hifi4/xa_nn_elm_sqrt_f32.c - ${NN_HIFI_PATH}/algo/kernels/basic/hifi4/xa_nn_elm_mul_16x16.c - ${NN_HIFI_PATH}/algo/kernels/basic/hifi4/xa_nn_elm_add_quant8.c - ${NN_HIFI_PATH}/algo/kernels/basic/hifi4/xa_nn_elm_mul_f32.c - ${NN_HIFI_PATH}/algo/kernels/basic/hifi4/xa_nn_lstm_utils.c - ${NN_HIFI_PATH}/algo/kernels/basic/hifi4/xa_nn_dot_prod_16x16.c - ${NN_HIFI_PATH}/algo/kernels/basic/hifi4/xa_nn_elm_sub_quant16.c - ${NN_HIFI_PATH}/algo/kernels/basic/hifi4/xa_nn_elm_round_f32.c - ${NN_HIFI_PATH}/algo/kernels/basic/hifi4/xa_nn_elm_mul_acc_f32.c - ${NN_HIFI_PATH}/algo/kernels/basic/hifi4/xa_nn_elm_cosine_f32.c - ${NN_HIFI_PATH}/algo/kernels/basic/hifi4/xa_nn_reduce_asym8s_asym8s.c - ${NN_HIFI_PATH}/algo/kernels/basic/hifi4/xa_nn_memset_f32.c - ${NN_HIFI_PATH}/algo/kernels/basic/hifi4/xa_nn_floor_f32.c - ${NN_HIFI_PATH}/algo/kernels/basic/hifi4/xa_nn_elm_squared_diff_quant8.c - ${NN_HIFI_PATH}/algo/kernels/basic/hifi4/xa_nn_elm_add_quant16.c - ${NN_HIFI_PATH}/algo/kernels/basic/hifi4/xa_nn_reduce_asym16s_asym16s.c - ${NN_HIFI_PATH}/algo/kernels/basic/hifi4/xa_nn_elm_minmax_8.c - ${NN_HIFI_PATH}/algo/kernels/basic/hifi4/xa_nn_elm_logn_f32.c - ${NN_HIFI_PATH}/algo/kernels/basic/hifi4/xa_nn_elm_div_f32.c - ${NN_HIFI_PATH}/algo/kernels/basic/hifi4/xa_nn_elm_sub_f32.c - ${NN_HIFI_PATH}/algo/kernels/basic/hifi4/xa_nn_elm_compare_quant8.c - ${NN_HIFI_PATH}/algo/kernels/basic/hifi4/xa_nn_elm_logical_bool.c - ${NN_HIFI_PATH}/algo/kernels/basic/hifi4/xa_nn_broadcast_8_8.c - ${NN_HIFI_PATH}/algo/kernels/basic/hifi4/xa_nn_elm_quantize.c - ${NN_HIFI_PATH}/algo/kernels/basic/hifi4/xa_nn_elm_neg_f32.c - ${NN_HIFI_PATH}/algo/kernels/basic/hifi4/xa_nn_elm_mul_quant16.c - ${NN_HIFI_PATH}/algo/kernels/basic/hifi4/xa_nn_elm_add_f32.c - ${NN_HIFI_PATH}/algo/kernels/basic/hifi4/xa_nn_vec_interpolation_q15.c - ${NN_HIFI_PATH}/algo/kernels/basic/hifi4/xa_nn_elm_square_f32.c - ${NN_HIFI_PATH}/algo/kernels/basic/hifi4/xa_nn_elm_sine_f32.c - ${NN_HIFI_PATH}/algo/kernels/basic/hifi4/xa_nn_elm_abs_f32.c - ${NN_HIFI_PATH}/algo/kernels/basic/hifi4/xa_nn_memmove.c - ${NN_HIFI_PATH}/algo/kernels/basic/hifi4/xa_nn_elm_mul_quant8.c - ${NN_HIFI_PATH}/algo/kernels/basic/hifi4/xa_nn_elm_squared_diff_quant16.c - ${NN_HIFI_PATH}/algo/kernels/basic/hifi4/xa_nn_memmove_16.c - ${NN_HIFI_PATH}/algo/kernels/basic/hifi4/xa_nn_elm_sub_quant8.c - ${NN_HIFI_PATH}/algo/kernels/basic/hifi4/xa_nn_elm_ceil_f32.c - ${NN_HIFI_PATH}/algo/kernels/basic/hifi4/xa_nn_elm_rsqrt_f32.c ${NN_HIFI_PATH}/algo/kernels/fc/hifi4/xa_nn_fully_connected.c - ${NN_HIFI_PATH}/algo/ndsp/hifi4/src/vec_tanh32x32_hifi4.c - ${NN_HIFI_PATH}/algo/ndsp/hifi4/src/scl_sigmoidf_hifi4.c - ${NN_HIFI_PATH}/algo/ndsp/hifi4/src/inv2pif_tbl.c - ${NN_HIFI_PATH}/algo/ndsp/hifi4/src/nanf_tbl.c - ${NN_HIFI_PATH}/algo/ndsp/hifi4/src/pow2f_tbl.c - ${NN_HIFI_PATH}/algo/ndsp/hifi4/src/vec_sigmoidf_hifi4.c - ${NN_HIFI_PATH}/algo/ndsp/hifi4/src/vec_reluf_hifi4.c - ${NN_HIFI_PATH}/algo/ndsp/hifi4/src/expf_tbl.c - ${NN_HIFI_PATH}/algo/ndsp/hifi4/src/vec_tanhf_hifi4.c - ${NN_HIFI_PATH}/algo/ndsp/hifi4/src/vec_sigmoid32x32_hifi4.c - ${NN_HIFI_PATH}/algo/ndsp/hifi4/src/lognf_tbl.c - ${NN_HIFI_PATH}/algo/ndsp/hifi4/src/sinf_tbl.c - ${NN_HIFI_PATH}/algo/ndsp/hifi4/src/vec_cosinef_hifi4.c - ${NN_HIFI_PATH}/algo/ndsp/hifi4/src/vec_sinef_hifi4.c - ${NN_HIFI_PATH}/algo/ndsp/hifi4/src/vec_softmaxf_hifi4.c - ${NN_HIFI_PATH}/algo/ndsp/hifi4/src/scl_tanhf_hifi4.c - ${NN_HIFI_PATH}/algo/ndsp/hifi4/src/vec_softmax32x32_hifi4.c - ${NN_HIFI_PATH}/algo/ndsp/hifi4/src/vec_lognf_hifi4.c - ${NN_HIFI_PATH}/algo/ndsp/hifi4/src/sqrt2f_tbl.c - ${NN_HIFI_PATH}/algo/ndsp/hifi4/src/inff_tbl.c - ${NN_HIFI_PATH}/algo/ndsp/hifi4/src/vec_alognf_hifi4.c - ${NN_HIFI_PATH}/algo/ndsp/hifi4/src/vec_relu32x32_hifi4.c - ${NN_HIFI_PATH}/algo/ndsp/hifi4/src/tanhf_tbl.c + ${NN_HIFI_PATH}/algo/kernels/pool/hifi4/xa_nn_inv_256_tbl.c + ${NN_HIFI_PATH}/algo/kernels/reorg/hifi4/xa_nn_transpose_8.c + ${NN_HIFI_PATH}/algo/kernels/reorg/hifi4/xa_nn_pad_8.c ) target_include_directories(nn_hifi_lib PRIVATE @@ -200,31 +72,63 @@ target_include_directories(nn_hifi_lib PRIVATE ${NN_HIFI_PATH}/algo/common/include/ ${NN_HIFI_PATH}/include/nnlib/ ${NN_HIFI_PATH}/algo/ndsp/hifi4/include + /home/lrg/zephyr-sdk-1.0.1/gnu/xtensa-intel_ace15_mtpm_zephyr-elf/xtensa-intel_ace15_mtpm_zephyr-elf/include + ${sof_top_dir}/../modules/hal/xtensa/zephyr/soc/intel_ace15_mtpm ) +target_compile_options(nn_hifi_lib PRIVATE + -mllvm --text-section-literals=false +) + +endif() # CMAKE_C_COMPILER_ID STREQUAL "Clang" + + +if(CMAKE_C_COMPILER_ID STREQUAL "Clang") + # TODO: Need to detect and add mul16/32 options. -#ifeq "$(has_mul16)" "0" -#CFLAGS += -mno-mul16 -#endif -#ifeq "$(has_mul32)" "0" -#CFLAGS += -mno-mul32 -mno-div32 -#endif +target_compile_definitions(nn_hifi_lib PRIVATE + -DHIFI4=1 + -DHAVE_VFPU=1 + -DHAVE_VFPU_SINGLE_PRECISION=1 + -DXCHAL_HAVE_HIFI4=1 + -DXCHAL_HAVE_HIFI4_VFPU=1 + + __xtensa__=1 + __XTENSA__=1 + __XCC__ + __XCC_CLANG__ + "XT_MAX(a,b)=((a)>(b)?(a):(b))" + "XT_MIN(a,b)=((a)<(b)?(a):(b))" + "AE_MOVINT16_FROMINT32(v)=((ae_int16)(v))" + "AE_CVT64F32_H(v)=((ae_int64)(int64_t)(int32_t)AE_MOVAD32_H(v))" + "AE_CVT64F32_L(v)=((ae_int64)(int64_t)(int32_t)AE_MOVAD32_L(v))" +) + target_compile_options(nn_hifi_lib PRIVATE -fsigned-char -fno-exceptions -mlongcalls - -INLINE:requested - -mcoproc -fno-zero-initialized-in-bss - -mtext-section-literals -Wsign-compare - -m32 -DMODEL_INT16 -DNNLIB_V2 -Dhifi4 -DTFLITE_SINGLE_ROUNDING=1 ) +target_compile_definitions(nn_hifi_lib PRIVATE + __XCC__ + __XCC_CLANG__ +) +target_compile_options(nn_hifi_lib PRIVATE + -mcpu=intel_ace15_mtpm + "SHELL:-include xtensahifiintrin.h" + "SHELL:-include ${NN_HIFI_PATH}/algo/common/include/xa_nnlib_hifi_isa_compat.h" +) + +endif() # CMAKE_C_COMPILER_ID STREQUAL "Clang" (nn_hifi_lib defs/opts) + + # TODO: complete sources have been added here from userspace build but # not all are needed so this is a list of "needed" sources to build # a memory and performance optimized TFLM for SOF. @@ -332,46 +236,12 @@ add_library(tflm_lib STATIC #${TFLM_PATH}/tensorflow/lite/micro/kernels/var_handle.cc #${TFLM_PATH}/tensorflow/lite/micro/kernels/while.cc #${TFLM_PATH}/tensorflow/lite/micro/kernels/zeros_like.cc - # xtensa kernels - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/sub.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/conv_hifi.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/pooling_vision.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/softmax_vision.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/unidirectional_sequence_lstm.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/transpose_conv.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/fully_connected_common_xtensa.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/lstm_eval_hifi.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/add_vision.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/depthwise_conv.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/reshape.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/softmax.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/depthwise_conv_hifi.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/conv_int16_reference.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/conv_int8_reference.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/fully_connected_int8.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/pad_vision.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/fully_connected_vision.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/conv_int8_int16.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/conv.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/softmax_int8_int16.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/logistic.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/reduce.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/lstm_eval.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/reshape_vision.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/pad.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/reduce_vision.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/dequantize.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/add.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/pooling_int8.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/depthwise_conv_vision.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/strided_slice.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/conv_common_xtensa.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/svdf.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/fully_connected.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/leaky_relu.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/quantize.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/pooling.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/conv_vision.cc + # reference kernels for speech model ops + ${TFLM_PATH}/tensorflow/lite/micro/kernels/conv.cc + ${TFLM_PATH}/tensorflow/lite/micro/kernels/depthwise_conv.cc + ${TFLM_PATH}/tensorflow/lite/micro/kernels/fully_connected.cc + ${TFLM_PATH}/tensorflow/lite/micro/kernels/reshape.cc + ${TFLM_PATH}/tensorflow/lite/micro/kernels/softmax.cc ${TFLM_PATH}/tensorflow/lite/micro/mock_micro_graph.cc ${TFLM_PATH}/tensorflow/lite/micro/flatbuffer_utils.cc ${TFLM_PATH}/tensorflow/lite/micro/micro_interpreter_graph.cc @@ -416,6 +286,8 @@ target_include_directories(tflm_lib PRIVATE ${NN_HIFI_PATH} ${NN_HIFI_PATH}/include ${sof_top_dir}/posix/include + ${sof_top_dir}/../modules/hal/xtensa/include + ${sof_top_dir}/../modules/hal/xtensa/zephyr/soc/intel_ace15_mtpm ) # TODO: Need to detect and add mul16/32 options. @@ -425,9 +297,26 @@ target_include_directories(tflm_lib PRIVATE #ifeq "$(has_mul32)" "0" #CFLAGS += -mno-mul32 -mno-div32 #endif +target_compile_definitions(tflm_lib PRIVATE + -DHIFI4=1 + -DHAVE_VFPU=1 + -DHAVE_VFPU_SINGLE_PRECISION=1 + -DXCHAL_HAVE_HIFI4=1 + -DXCHAL_HAVE_HIFI4_VFPU=1 + + __xtensa__=1 + __XTENSA__=1 + __XCC__ + __XCC_CLANG__ + "XT_MAX(a,b)=((a)>(b)?(a):(b))" + "XT_MIN(a,b)=((a)<(b)?(a):(b))" + "AE_MOVINT16_FROMINT32(v)=((ae_int16)(v))" + "AE_CVT64F32_H(v)=((ae_int64)(int64_t)(int32_t)AE_MOVAD32_H(v))" + "AE_CVT64F32_L(v)=((ae_int64)(int64_t)(int32_t)AE_MOVAD32_L(v))" +) + target_compile_options(tflm_lib PRIVATE -std=c++17 - -stdlib=libc++ -fno-rtti -fno-exceptions -fno-threadsafe-statics @@ -450,16 +339,63 @@ target_compile_options(tflm_lib PRIVATE -DKERNELS_OPTIMIZED_FOR_SPEED -DTF_LITE_MCU_DEBUG_LOG -DTF_LITE_USE_CTIME - --xtensa-core=ace10_LX7HiFi4_2022_10 - -mcoproc -DHIFI4 -mlongcalls -DNNLIB_V2 -Wno-shadow ) -add_local_sources(sof tflm-classify.c llext-wrap.c) +if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + target_compile_options(tflm_lib PRIVATE + -stdlib=libc++ + "SHELL:-include xtensahifiintrin.h" + -fno-vectorize + -fno-slp-vectorize + ) +else() + # gcc equivalents for vectorize disabling + target_compile_options(tflm_lib PRIVATE + -fno-tree-vectorize + -fno-tree-slp-vectorize + ) +endif() + +if(CMAKE_C_COMPILER_ID STREQUAL "Xtensa") + target_compile_options(tflm_lib PRIVATE + --xtensa-core=ace10_LX7HiFi4_2022_10 + -mcoproc + ) +elseif(CMAKE_C_COMPILER_ID STREQUAL "Clang") + target_compile_options(tflm_lib PRIVATE + -mcpu=intel_ace15_mtpm + ) +endif() + +if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + target_include_directories(tflm_lib SYSTEM PRIVATE + /home/lrg/zephyr-sdk-1.0.1/gnu/xtensa-intel_ace15_mtpm_zephyr-elf/xtensa-intel_ace15_mtpm_zephyr-elf/include/c++/14.3.0 + /home/lrg/zephyr-sdk-1.0.1/gnu/xtensa-intel_ace15_mtpm_zephyr-elf/xtensa-intel_ace15_mtpm_zephyr-elf/include/c++/14.3.0/xtensa-intel_ace15_mtpm_zephyr-elf + /home/lrg/zephyr-sdk-1.0.1/gnu/xtensa-intel_ace15_mtpm_zephyr-elf/xtensa-intel_ace15_mtpm_zephyr-elf/include + ) +endif() + +add_local_sources(sof tflm-classify.c speech.cc micro_speech_quantized_model_data.cc llext-wrap.c math_stubs.c) -# Need to link libc++ and libc after tflm and nnlib -zephyr_link_libraries(tflm_lib nn_hifi_lib c++ c) +# Link nnlib only when Clang is available (nnlib requires XCC/Clang TIE headers) +if(CMAKE_C_COMPILER_ID STREQUAL "Clang") + zephyr_link_libraries(tflm_lib nn_hifi_lib) +else() + zephyr_link_libraries(tflm_lib) +endif() zephyr_include_directories(${TFLM_PATH}) +zephyr_include_directories(${FLATBUFFERS_PATH}/include) +zephyr_include_directories(${GEMMLOWP_PATH}) +zephyr_include_directories(${RUY_PATH}) +zephyr_include_directories(${NN_HIFI_PATH}/algo/kernels/include) +zephyr_include_directories(${NN_HIFI_PATH}/include) +target_include_directories(modules_sof SYSTEM PRIVATE + /home/lrg/zephyr-sdk-1.0.1/gnu/xtensa-intel_ace15_mtpm_zephyr-elf/xtensa-intel_ace15_mtpm_zephyr-elf/include/c++/14.3.0 + /home/lrg/zephyr-sdk-1.0.1/gnu/xtensa-intel_ace15_mtpm_zephyr-elf/xtensa-intel_ace15_mtpm_zephyr-elf/include/c++/14.3.0/xtensa-intel_ace15_mtpm_zephyr-elf + /home/lrg/zephyr-sdk-1.0.1/gnu/xtensa-intel_ace15_mtpm_zephyr-elf/xtensa-intel_ace15_mtpm_zephyr-elf/include +) + diff --git a/src/audio/tensorflow/README.md b/src/audio/tensorflow/README.md index a8e3f59ba3ce..31a8f9555497 100644 --- a/src/audio/tensorflow/README.md +++ b/src/audio/tensorflow/README.md @@ -1,22 +1,201 @@ -# TensorFlow Lite Micro (TFLM) Architecture +# TensorFlow Lite Micro (TFLM) & Wake-on-Voice (WoV) Architecture -This directory acts as the bridge for running ML models. +This directory provides the TensorFlow Lite for Microcontrollers (TFLM) classification module (`TFLMCLY`) for Sound Open Firmware (SOF), including integration with MFCC feature extraction, `mtrace` stream shutdown summary logging, IPC host notifications, Key Phrase Buffer (KPB) Wake-on-Voice (WoV) trigger infrastructure, and abstracted audio input sources across **HDA**, **DMIC**, **SSP (I2S)**, and **SoundWire (ALH)**. + +--- ## Overview -Integrates TensorFlow Lite for Microcontrollers into the SOF audio pipeline. Evaluates pre-trained neural network topologies inline with the audio stream for tasks like wake-word, noise cancellation, or sound classification. +The TFLM module evaluates pre-trained micro speech neural network models inline within the SOF audio processing graph. It receives pre-processed audio feature tensors (e.g. 80-bin mel spectrograms from the `mfcc` component), runs model inference in the **Data Processing (DP) domain**, logs keyword detections and stream shutdown event summaries to `mtrace`, issues IPC4 notifications to the host audio driver, and signals the KPB module to drain pre-roll audio history upon keyword detection. + +--- + +## Architecture & Data Flow + +### Dual-Path Wake-on-Voice (WoV) Architecture -## Architecture Diagram +To allow continuous keyword evaluation without streaming audio to the host until a keyword is detected, the pipeline separates real-time keyword detection from host PCM draining via KPB: ```mermaid -graph LR - Feat[Audio Features] --> TFLM[TFLM Inference Engine] - SubGraph[FlatBuffer Model] -.-> TFLM - TFLM --> Out[Inference Labels/Scores] +graph TD + subgraph Audio_Inputs ["Abstracted Hardware DAI Input Sources"] + HDA["HDA Analog Input (dai_type: HDA)"] + DMIC["PCH DMIC Digital Mic (dai_type: DMIC)"] + SSP["I2S / Bluetooth Codec (dai_type: SSP)"] + SDW["SoundWire SmartMic (dai_type: ALH)"] + end + + subgraph DAI_Abstraction ["Backend DAI Copier Widget"] + DAI["dai-copier.1 (dai_type: $CAPTURE_DAI_TYPE)"] + end + + subgraph KPB_Pipeline ["Capture & KPB Pipeline"] + Gain["gain.2.1 (Volume Control)"] + KPB["kpb.2.1 (Key Phrase Buffer)"] + end + + subgraph Detect_Pipeline ["Real-Time Detection Path (KPB Pin 1) - DP Domain"] + SRC["src.1.1 (Resampler: 48kHz -> 16kHz)"] + MFCC["mfcc.1.1 (Mel-80 Feature Extractor)"] + TFLM["tflmcly.1.1 (TFLM Keyword Classifier LLEXT)"] + VSink["virtual.tflm_sink (Termination)"] + end + + subgraph Host_Pipeline ["Host WoV Draining Path (KPB Pin 2)"] + Host["host-copier.0.capture (PCM Capture Stream)"] + end + + HDA --> DAI + DMIC --> DAI + SSP --> DAI + SDW --> DAI + + DAI --> Gain + Gain --> KPB + KPB -- Pin 1: Live Audio --> SRC + SRC --> MFCC + MFCC -- Mel-80 Tensors --> TFLM + TFLM --> VSink + + TFLM -.->|1. KPB_EVENT_BEGIN_DRAINING Notifier| KPB + TFLM -.->|2. IPC4 Notification| Host_Driver[Host Driver] + TFLM -.->|3. Stream Shutdown Event Logging| MTrace[mtrace / Trace Log] + + KPB -- Pin 2: Pre-roll History Buffer --> Host +``` + +--- + +## Domain Execution Model (LL vs. DP) + +- **Low Latency (LL) Domain (Timer Task / 1ms tick loop)**: + - Components: `dai-copier`, `eqiir`, `tdfb`, `drc`, `host-copier` + - Purpose: Fixed 1 ms tick loop executed on Core 0 to meet hard real-time audio hardware deadlines. + - Metrics: Reported via `ll_schedule.stats_report` (e.g. `ll core 0 timer avg 22,049 cycles (~56 µs)`). + +- **Data Processing (DP) Domain (Asynchronous Task)**: + - Components: `src`, `micsel`, `mfcc`, `tflmcly` (`tflm.llext`) + - Purpose: Runs asynchronously in background thread whenever 344-byte feature frames are produced by `mfcc`. + - **Separation**: Because TFLM runs in the DP domain, model inference cycles take place outside the 1 ms LL tick loop, guaranteeing zero impact on real-time LL audio latency or buffer overruns. + +--- + +## Stream Shutdown Summary Event Logging + +Upon stream reset or module destruction (`tflm_reset()` / `tflm_free()`), TFLM emits a comprehensive summary log to `printk` and DSP trace logs detailing total event occurrences: + +```text +[TFLM STREAM SHUTDOWN SUMMARY] Total Inferences=142 | Keyword Events: Silence=120, Unknown=18, Yes=3, No=1 | Total KPB Triggers=4 ``` -## Configuration and Scripts +### Log Fields: +- `Total Inferences`: Cumulative number of classification inferences completed during the stream session. +- `Keyword Events`: Per-category classification breakdown (`silence`, `unknown`, `yes`, `no`). +- `Total KPB Triggers`: Number of high-confidence keyword detections that triggered pre-roll audio history draining (`KPB_EVENT_BEGIN_DRAINING`). + +--- + +## Abstracted Audio Input Sources in Topology v2 + +The TFLM Keyword Detection and KPB pre-roll pipeline is decoupled from the physical DAI input source using the generic `dai-copier` widget: + +```conf +Object.Widget.dai-copier.1 { + dai_type $CAPTURE_DAI_TYPE # "HDA", "DMIC", "SSP", or "ALH" (SoundWire) + copier_type $CAPTURE_COPIER_TYPE # "HDA", "DMIC", "SSP", or "ALH" + stream_name $CAPTURE_DAI_NAME # "Analog", "DMIC01", "SSP0", "SDW0-Capture" + node_type $CAPTURE_NODE_TYPE # $HDA_LINK_INPUT_CLASS, $DMIC_LINK_INPUT_CLASS, etc. +} +``` + +Platform wrappers select the input source cleanly via configuration defines: +```conf +Define { + CAPTURE_SOURCE "hda" # Options: "hda", "dmic", "ssp", "soundwire" +} + +IncludeByKey.CAPTURE_SOURCE { + "hda" "platform/intel/capture-hda.conf" + "dmic" "platform/intel/capture-dmic.conf" + "ssp" "platform/intel/capture-ssp.conf" + "soundwire" "platform/intel/capture-sdw.conf" +} +``` + +--- + +## Topology v2 Integration & Usage + +### 1. Component Widget Definition (`include/components/tflm.conf`) + +Defines `Class.Widget."tflmcly"`: +- **UUID**: `42:c6:1d:c5:e1:a2:df:48:a4:90:e2:74:8c:b6:36:3e` (`c51dc642-a2e1-48df-a490e2748cb6363e`) +- **Type**: `effect` + +### 2. Detection Pipeline Template (`include/pipelines/cavs/host-gateway-src-mfcc-tflm-capture.conf`) + +Instantiates the real-time detection graph: +```conf +Object.Widget { + virtual."1" { name "virtual.tflm_sink" } + src."1" { ... } + mfcc."1" { ... } + tflmcly."1" { ... } +} +``` + +### 3. Top-Level Topology Configuration (`sof-hda-tflm.conf`) + +Instantiates the complete HDA Mic WoV topology with dual-path KPB routing: +```conf +Object.Base.route [ + # DAI -> Gain -> KPB + { source "dai-copier.HDA.Analog.capture"; sink "gain.2.1" } + { source "gain.2.1"; sink "kpb.2.1" } + + # KPB Pin 1 -> Real-time Detection Path (DP Domain) + { source "kpb.2.1"; sink "src.1.1" } + + # KPB Pin 2 -> Host WoV Draining Path + { source "kpb.2.1"; sink "host-copier.0.capture" } +] +``` + +--- + +## Building and Testing Topologies + +### 1. Building Topology Targets with Ninja + +From `sof/tools/build_tools`: + +```bash +# Build HDA TFLM KPB Topologies (MTL / PTL) +ninja topology2_prod_sof-mtl-hda-tflm-kpb +ninja topology2_prod_sof-ptl-hda-tflm-kpb + +# Build SoundWire TFLM Topology (ARL-S) +ninja topology2_dev_sof-arl-cs42l43-l0-cs35l56-l23-mfcc-mel-normal +``` + +### 2. Streaming & Capturing Performance / Trace Logs on Target DUT + +Run `arecord` with standard mandatory timeouts: + +```bash +# Record 10s audio stream to trigger TFLM pipeline execution +ssh root@dragon-fly 'timeout 10s arecord -D hw:0,4 -f S32_LE -r 48000 -c 2 -d 10 /tmp/test_stream.wav' + +# Read DSP mtrace logs to observe inferences and shutdown summary +ssh root@dragon-fly 'timeout 10s /usr/local/bin/mtrace-reader.py' +``` + +--- + +## Source Files -- **Kconfig**: Enforces requirements for C++17 support and core framework staging logic (`COMP_TENSORFLOW`). -- **CMakeLists.txt**: An intricate build specification linking the Tensilica neural network library block computations (`nn_hifi_lib`) and the TensorFlow Lite micro core engine (`tflm_lib`). Also hooks the `tflm-classify.c` SOF adapter via compiler flags explicitly enforcing memory, precision, and XTENSA optimizations. -- **tflmcly.toml**: Topology definition for the specific TFLM Classifier implementation binding the engine against the UUID `UUIDREG_STR_TFLMCLY`. +- **[tflm-classify.c](file:///home/lrg/work/sof-ptl/sof/src/audio/tensorflow/tflm-classify.c)**: SOF module adapter implementation for TFLM. +- **[speech.cc](file:///home/lrg/work/sof-ptl/sof/src/audio/tensorflow/speech.cc)** / **[speech.h](file:///home/lrg/work/sof-ptl/sof/src/audio/tensorflow/speech.h)**: TFLM C++ API bridge & micro speech tensor wrapper. +- **[tflm.conf](file:///home/lrg/work/sof-ptl/sof/tools/topology/topology2/include/components/tflm.conf)**: Topology v2 widget class definition. +- **[host-gateway-src-mfcc-tflm-capture.conf](file:///home/lrg/work/sof-ptl/sof/tools/topology/topology2/include/pipelines/cavs/host-gateway-src-mfcc-tflm-capture.conf)**: Detection pipeline template. +- **[sof-hda-tflm.conf](file:///home/lrg/work/sof-ptl/sof/tools/topology/topology2/sof-hda-tflm.conf)**: Top-level WoV topology configuration. diff --git a/src/audio/tensorflow/llext/CMakeLists.txt b/src/audio/tensorflow/llext/CMakeLists.txt index 22bfc22da748..37ae784f85af 100644 --- a/src/audio/tensorflow/llext/CMakeLists.txt +++ b/src/audio/tensorflow/llext/CMakeLists.txt @@ -10,6 +10,8 @@ set(FLATBUFFERS_PATH ${sof_top_dir}/../flatbuffers) set(GEMMLOWP_PATH ${sof_top_dir}/../gemmlowp) set(RUY_PATH ${sof_top_dir}/../ruy) +if(CMAKE_C_COMPILER_ID STREQUAL "Xtensa") +set(TFLM_EXTRA_LIBS nn_hifi_lib) add_library(nn_hifi_lib STATIC ${NN_HIFI_PATH}/algo/common/src/xa_nnlib_common_api.c ${NN_HIFI_PATH}/algo/kernels/reorg/hifi4/xa_nn_pad_8.c @@ -43,6 +45,7 @@ add_library(nn_hifi_lib STATIC ${NN_HIFI_PATH}/algo/kernels/cnn/hifi4/xa_nn_conv2d_pointwise_sym8sxsym16s.c ${NN_HIFI_PATH}/algo/kernels/cnn/hifi4/xa_nn_conv2d_pointwise_8x16.c ${NN_HIFI_PATH}/algo/kernels/cnn/hifi4/xa_nn_circ_buf.c + ${NN_HIFI_PATH}/algo/kernels/cnn/hifi4/xa_nn_conv2d_std_8x8.c ${NN_HIFI_PATH}/algo/kernels/cnn/hifi4/xa_nn_conv2d_depthwise.c ${NN_HIFI_PATH}/algo/kernels/cnn/hifi4/xa_nn_matXvec_16x16_16_circ.c @@ -206,18 +209,79 @@ target_compile_options(nn_hifi_lib PRIVATE -fsigned-char -fno-exceptions -mlongcalls - -INLINE:requested - -mcoproc -fno-zero-initialized-in-bss -mtext-section-literals -Wsign-compare - -m32 -DMODEL_INT16 -DNNLIB_V2 -Dhifi4 -DTFLITE_SINGLE_ROUNDING=1 ) +target_compile_options(nn_hifi_lib PRIVATE + -INLINE:requested + -mcoproc + -m32 +) +else() +set(TFLM_EXTRA_LIBS "") +endif() + + + +if(CMAKE_C_COMPILER_ID STREQUAL "Xtensa") + set(TFLM_KERNEL_SOURCES + ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/sub.cc + ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/conv_hifi.cc + ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/pooling_vision.cc + ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/softmax_vision.cc + ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/unidirectional_sequence_lstm.cc + ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/transpose_conv.cc + ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/fully_connected_common_xtensa.cc + ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/lstm_eval_hifi.cc + ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/add_vision.cc + ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/depthwise_conv.cc + ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/reshape.cc + ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/softmax.cc + ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/depthwise_conv_hifi.cc + ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/conv_int16_reference.cc + ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/conv_int8_reference.cc + ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/fully_connected_int8.cc + ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/pad_vision.cc + ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/fully_connected_vision.cc + ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/conv_int8_int16.cc + ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/conv.cc + ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/softmax_int8_int16.cc + ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/logistic.cc + ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/reduce.cc + ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/lstm_eval.cc + ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/reshape_vision.cc + ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/pad.cc + ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/reduce_vision.cc + ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/dequantize.cc + ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/add.cc + ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/pooling_int8.cc + ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/depthwise_conv_vision.cc + ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/strided_slice.cc + ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/conv_common_xtensa.cc + ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/svdf.cc + ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/fully_connected.cc + ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/leaky_relu.cc + ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/quantize.cc + ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/pooling.cc + ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/conv_vision.cc + ) +else() + set(TFLM_KERNEL_SOURCES + ${TFLM_PATH}/tensorflow/lite/micro/kernels/sub.cc + ${TFLM_PATH}/tensorflow/lite/micro/kernels/conv.cc + ${TFLM_PATH}/tensorflow/lite/micro/kernels/depthwise_conv.cc + ${TFLM_PATH}/tensorflow/lite/micro/kernels/fully_connected.cc + ${TFLM_PATH}/tensorflow/lite/micro/kernels/reshape.cc + ${TFLM_PATH}/tensorflow/lite/micro/kernels/softmax.cc + ) +endif() + # TODO: complete sources have been added here from userspace build but # not all are needed so this is a list of "needed" sources to build # a memory and performance optimized TFLM for SOF. @@ -325,52 +389,15 @@ add_library(tflm_lib STATIC #${TFLM_PATH}/tensorflow/lite/micro/kernels/var_handle.cc #${TFLM_PATH}/tensorflow/lite/micro/kernels/while.cc #${TFLM_PATH}/tensorflow/lite/micro/kernels/zeros_like.cc - # xtensa kernels - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/sub.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/conv_hifi.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/pooling_vision.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/softmax_vision.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/unidirectional_sequence_lstm.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/transpose_conv.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/fully_connected_common_xtensa.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/lstm_eval_hifi.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/add_vision.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/depthwise_conv.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/reshape.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/softmax.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/depthwise_conv_hifi.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/conv_int16_reference.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/conv_int8_reference.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/fully_connected_int8.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/pad_vision.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/fully_connected_vision.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/conv_int8_int16.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/conv.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/softmax_int8_int16.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/logistic.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/reduce.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/lstm_eval.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/reshape_vision.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/pad.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/reduce_vision.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/dequantize.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/add.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/pooling_int8.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/depthwise_conv_vision.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/strided_slice.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/conv_common_xtensa.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/svdf.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/fully_connected.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/leaky_relu.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/quantize.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/pooling.cc - ${TFLM_PATH}/tensorflow/lite/micro/kernels/xtensa/conv_vision.cc - ${TFLM_PATH}/tensorflow/lite/micro/mock_micro_graph.cc + + ${TFLM_KERNEL_SOURCES} + + #${TFLM_PATH}/tensorflow/lite/micro/mock_micro_graph.cc ${TFLM_PATH}/tensorflow/lite/micro/flatbuffer_utils.cc ${TFLM_PATH}/tensorflow/lite/micro/micro_interpreter_graph.cc ${TFLM_PATH}/tensorflow/lite/micro/micro_resource_variable.cc ${TFLM_PATH}/tensorflow/lite/micro/micro_interpreter_context.cc - ${TFLM_PATH}/tensorflow/lite/micro/fake_micro_context.cc + #${TFLM_PATH}/tensorflow/lite/micro/fake_micro_context.cc ${TFLM_PATH}/tensorflow/lite/micro/arena_allocator/persistent_arena_buffer_allocator.cc ${TFLM_PATH}/tensorflow/lite/micro/arena_allocator/recording_single_arena_buffer_allocator.cc ${TFLM_PATH}/tensorflow/lite/micro/arena_allocator/non_persistent_arena_buffer_allocator.cc @@ -380,14 +407,14 @@ add_library(tflm_lib STATIC ${TFLM_PATH}/tensorflow/lite/micro/tflite_bridge/micro_error_reporter.cc ${TFLM_PATH}/tensorflow/lite/micro/system_setup.cc #${TFLM_PATH}/tensorflow/lite/micro/span_test.cc - ${TFLM_PATH}/tensorflow/lite/micro/test_helper_custom_ops.cc + #${TFLM_PATH}/tensorflow/lite/micro/test_helper_custom_ops.cc ${TFLM_PATH}/tensorflow/lite/micro/micro_context.cc ${TFLM_PATH}/tensorflow/lite/micro/micro_log.cc ${TFLM_PATH}/tensorflow/lite/micro/micro_profiler.cc ${TFLM_PATH}/tensorflow/lite/micro/micro_time.cc #${TFLM_PATH}/tensorflow/lite/micro/static_vector_test.cc ${TFLM_PATH}/tensorflow/lite/micro/debug_log.cc - ${TFLM_PATH}/tensorflow/lite/micro/test_helpers.cc + #${TFLM_PATH}/tensorflow/lite/micro/test_helpers.cc ${TFLM_PATH}/tensorflow/lite/micro/micro_op_resolver.cc ${TFLM_PATH}/tensorflow/lite/micro/recording_micro_allocator.cc ${TFLM_PATH}/tensorflow/lite/micro/memory_helpers.cc @@ -420,7 +447,6 @@ target_include_directories(tflm_lib PRIVATE #endif target_compile_options(tflm_lib PRIVATE -std=c++17 - -stdlib=libc++ -fno-rtti -fno-exceptions -fno-threadsafe-statics @@ -443,23 +469,94 @@ target_compile_options(tflm_lib PRIVATE -DKERNELS_OPTIMIZED_FOR_SPEED -DTF_LITE_MCU_DEBUG_LOG -DTF_LITE_USE_CTIME - --xtensa-core=ace10_LX7HiFi4_2022_10 - -mcoproc -DHIFI4 -mlongcalls -DNNLIB_V2 -Wno-shadow + -fno-function-sections + -fno-data-sections ) +# Use GNU libstdc++ headers (from zephyr-sdk gcc toolchain) which work with +# both gcc and Clang. The -stdlib=libc++ approach requires an installed libc++ +# runtime that is not available for the xtensa target in this LLVM build. +if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + # Add GNU libstdc++ headers so Clang can find , , etc. + file(GLOB _gnu_cxx_dirs + "/home/lrg/zephyr-sdk-*/gnu/xtensa-intel_ace15_mtpm_zephyr-elf/xtensa-intel_ace15_mtpm_zephyr-elf/include/c++/*/") + file(GLOB _gnu_c_dirs + "/home/lrg/zephyr-sdk-*/gnu/xtensa-intel_ace15_mtpm_zephyr-elf/xtensa-intel_ace15_mtpm_zephyr-elf/include/") + if(_gnu_cxx_dirs) + list(GET _gnu_cxx_dirs 0 _gnu_cxx_dir) + target_include_directories(tflm_lib SYSTEM PRIVATE + ${_gnu_cxx_dir} + ${_gnu_cxx_dir}/xtensa-intel_ace15_mtpm_zephyr-elf + ) + endif() + if(_gnu_c_dirs) + list(GET _gnu_c_dirs 0 _gnu_c_dir) + target_include_directories(tflm_lib SYSTEM PRIVATE + ${_gnu_c_dir} + ) + endif() + # Clang builtin headers (stdarg.h, stdint.h wrappers, etc.) + execute_process( + COMMAND ${CMAKE_CXX_COMPILER} -print-resource-dir + OUTPUT_VARIABLE _clang_res_dir + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + if(_clang_res_dir AND EXISTS "${_clang_res_dir}/include") + target_include_directories(tflm_lib SYSTEM PRIVATE + ${_clang_res_dir}/include + ) + endif() +endif() + +if(CMAKE_C_COMPILER_ID STREQUAL "Xtensa") + target_compile_options(tflm_lib PRIVATE + --xtensa-core=ace10_LX7HiFi4_2022_10 + -mcoproc + ) +endif() + + +if(CMAKE_C_COMPILER_ID STREQUAL "Xtensa") + set(TFLM_STD_LIBS c++ hal) +else() + set(TFLM_STD_LIBS stdc++) +endif() + sof_llext_build("tflm" SOURCES ../tflm-classify.c ../llext-wrap.c INCLUDES ${TFLM_PATH} + ${sof_top_dir}/src/include + ${sof_top_dir}/src/arch/xtensa/include + ${ZEPHYR_BASE}/include + ${ZEPHYR_BASE}/soc/intel/intel_adsp/ace/include/ace15_mtpm + ${ZEPHYR_BASE}/../modules/hal/xtensa/include + ${ZEPHYR_BASE}/../modules/hal/xtensa/zephyr/soc/intel_ace15_mtpm # TFLM is a C++ userspace application so hence we need to link # userspace C++, C, maths and C runtime for llext module usage. - LIBS tflm_lib nn_hifi_lib c++ m c gcc hal + LIBS tflm_lib ${TFLM_EXTRA_LIBS} ${TFLM_STD_LIBS} m c gcc LIBS_PATH . + /home/lrg/zephyr-sdk-1.0.1/gnu/xtensa-intel_ace15_mtpm_zephyr-elf/xtensa-intel_ace15_mtpm_zephyr-elf/lib + /home/lrg/zephyr-sdk-1.0.1/gnu/xtensa-intel_ace15_mtpm_zephyr-elf/lib/gcc/xtensa-intel_ace15_mtpm_zephyr-elf/14.3.0 ) +# Note: format_hifi3.h needs xtensa/config/defs.h and xtensa/tie/xt_hifi3.h. +# Stub headers for these are provided in the intel_ace15_mtpm HAL for LLVM builds. + +# The llext link helper uses CMAKE_C_COMPILER (clang) as the linker driver. +# Without --target, clang defaults to the host (x86_64) linker and cannot link +# xtensa ELF objects. Add the target triple and xtensa linker path explicitly. +if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + llext_link_options(tflm + --target=xtensa-intel_ace15_mtpm_zephyr-elf + --ld-path=/home/lrg/zephyr-sdk-1.0.1/gnu/xtensa-intel_ace15_mtpm_zephyr-elf/bin/xtensa-intel_ace15_mtpm_zephyr-elf-ld + ) +endif() + + diff --git a/src/audio/tensorflow/speech.h b/src/audio/tensorflow/speech.h index 7b45022e3564..85673dd07880 100644 --- a/src/audio/tensorflow/speech.h +++ b/src/audio/tensorflow/speech.h @@ -23,6 +23,9 @@ struct tf_classify { int categories; const char *error; float predictions[TFLM_CATEGORY_COUNT]; + int op_count; + uint32_t node_cycles[10]; + int node_codes[10]; }; /* Export of C++ APIs into C namespace for linkage */ diff --git a/src/audio/tensorflow/tflm-classify.c b/src/audio/tensorflow/tflm-classify.c index 9c93329bce1c..ffd3f4f2aa8f 100644 --- a/src/audio/tensorflow/tflm-classify.c +++ b/src/audio/tensorflow/tflm-classify.c @@ -28,12 +28,38 @@ #include #include #include +#include +#include + +extern void sof_ut_log(const char *msg); #include #include #include +#include +#include +#include +#include +#include +#include + +#include +#include +#include #include "speech.h" +void start_tflm_ut_thread(void); +void run_unit_tests(void); + +#if defined(__xtensa__) +#include +#endif + +static inline uint32_t get_ccount(void) +{ + return k_cycle_get_32(); +} + SOF_DEFINE_REG_UUID(tflmcly); LOG_MODULE_REGISTER(tflmcly, CONFIG_SOF_LOG_LEVEL); EXPORT_SYMBOL(tflmcly_uuid); @@ -44,76 +70,112 @@ static const char * const prediction[] = TFLM_CATEGORY_DATA; struct tflm_comp_data { struct comp_data_blob_handler *model_handler; struct tf_classify tfc; -}; + struct ipc_msg *msg; + struct kpb_event_data event_data; + struct kpb_client client_data; +} __attribute__((aligned(8))); -__cold static int tflm_init(struct processing_module *mod) +static void tflm_notify_kpb(struct processing_module *mod) { - struct module_data *md = &mod->priv; + struct tflm_comp_data *cd = module_get_private_data(mod); struct comp_dev *dev = mod->dev; - struct module_config *cfg = &md->cfg; - struct tflm_comp_data *cd; - size_t bs = cfg->size; - int ret; - assert_can_be_cold(); + comp_info(dev, "TFLM keyword trigger -> notifying KPB to begin draining"); - comp_info(dev, "entry"); + cd->client_data.r_ptr = NULL; + cd->client_data.sink = NULL; + cd->client_data.id = 0; + cd->event_data.event_id = KPB_EVENT_BEGIN_DRAINING; + cd->event_data.client_data = &cd->client_data; - cd = mod_zalloc(mod, sizeof(*cd)); - if (!cd) - return -ENOMEM; + notifier_event(dev, NOTIFIER_ID_KPB_CLIENT_EVT, + NOTIFIER_TARGET_CORE_ALL_MASK, &cd->event_data, + sizeof(cd->event_data)); +} - md->private = cd; +static int tflm_ipc_notification_init(struct processing_module *mod) +{ + struct tflm_comp_data *cd = module_get_private_data(mod); + struct ipc_msg msg_proto; + struct comp_dev *dev = mod->dev; + struct comp_ipc_config *ipc_config = &dev->ipc_config; + union ipc4_notification_header *primary = + (union ipc4_notification_header *)&msg_proto.header; + struct sof_ipc4_notify_module_data *msg_module_data; + struct sof_ipc4_control_msg_payload *msg_payload; - /* Handler for configuration data */ - cd->model_handler = mod_data_blob_handler_new(mod); - if (!cd->model_handler) { - comp_err(dev, "mod_data_blob_handler_new() failed."); - ret = -ENOMEM; - goto fail; - } + return 0; +} - /* Get configuration data and reset DRC state */ - ret = comp_init_data_blob(cd->model_handler, bs, cfg->data); - if (ret < 0) { - comp_err(dev, "comp_init_data_blob() failed."); - goto fail; - } +static void tflm_send_keyword_notification(struct processing_module *mod, uint32_t category_idx) +{ + struct tflm_comp_data *cd = module_get_private_data(mod); + struct sof_ipc4_notify_module_data *msg_module_data; + struct sof_ipc4_control_msg_payload *msg_payload; - /* hard coded atm */ - cd->tfc.categories = TFLM_CATEGORY_COUNT; + if (!cd->msg) + return; - /* set default model for the moment*/ - ret = TF_SetModel(&cd->tfc, NULL); - if (!ret) { - comp_err(dev, "failed to set model"); - goto fail; - } + msg_module_data = (struct sof_ipc4_notify_module_data *)cd->msg->tx_data; + msg_payload = (struct sof_ipc4_control_msg_payload *)msg_module_data->event_data; + msg_payload->chanv[0].value = category_idx; + ipc_msg_send(cd->msg, NULL, false); +} - /* initialise ops */ - ret = TF_InitOps(&cd->tfc); - if (!ret) { - comp_err(dev, "failed to init ops"); - goto fail; - } - return ret; +static bool g_tflm_initialized = false; +static struct tflm_comp_data *g_tflm_cd = NULL; +static struct comp_dev *g_tflm_dev = NULL; +static uint64_t last_inference_timer = 0; +static uint32_t g_category_totals[TFLM_CATEGORY_COUNT] = {0, 0, 0, 0}; +static uint32_t g_total_inferences = 0; +static uint32_t g_kpb_trigger_events = 0; -fail: - /* Passing NULL pointer to free functions is Ok */ - mod_data_blob_handler_free(mod, cd->model_handler); - mod_free(mod, cd); - return ret; +__cold static void tflm_log_summary_at_shutdown(struct processing_module *mod) +{ + struct comp_dev *dev = mod ? mod->dev : NULL; + char summary_buf[256]; + snprintk(summary_buf, sizeof(summary_buf), + "[TFLM STREAM SHUTDOWN SUMMARY] Total Inferences=%u | Keyword Events: Silence=%u, Unknown=%u, Yes=%u, No=%u | Total KPB Triggers=%u", + g_total_inferences, g_category_totals[0], g_category_totals[1], + g_category_totals[2], g_category_totals[3], g_kpb_trigger_events); + sof_ut_log(summary_buf); + if (dev) + comp_info(dev, "%s", summary_buf); + printk("%s\n", summary_buf); } -__cold static int tflm_free(struct processing_module *mod) +void tflm_yield(void) { - struct tflm_comp_data *cd = module_get_private_data(mod); + k_yield(); +} + +static struct tflm_comp_data g_tflm_comp_data_static; + +__cold static int tflm_init(struct processing_module *mod) +{ + struct module_data *md = &mod->priv; + struct comp_dev *dev = mod->dev; + struct tflm_comp_data *cd = &g_tflm_comp_data_static; assert_can_be_cold(); - mod_data_blob_handler_free(mod, cd->model_handler); - mod_free(mod, cd); + printk("[TFLM INIT] entered tflm_init using static BSS struct\n"); + memset(cd, 0, sizeof(*cd)); + md->private = cd; + cd->tfc.categories = TFLM_CATEGORY_COUNT; + g_tflm_cd = cd; + g_tflm_dev = dev; + printk("[TFLM INIT] init complete successfully!\n"); + return 0; +} + +__cold static int tflm_free(struct processing_module *mod) +{ + assert_can_be_cold(); + + tflm_log_summary_at_shutdown(mod); + g_tflm_initialized = false; return 0; } @@ -122,41 +184,22 @@ __cold static int tflm_set_config(struct processing_module *mod, uint32_t param_ const uint8_t *fragment, size_t fragment_size, uint8_t *response, size_t response_size) { - struct tflm_comp_data *cd = module_get_private_data(mod); - struct comp_dev *dev = mod->dev; - int ret; - - assert_can_be_cold(); - - comp_dbg(dev, "entry"); - - struct sof_ipc4_control_msg_payload *ctl = (struct sof_ipc4_control_msg_payload *)fragment; - - comp_info(dev, "bytes control"); - ret = comp_data_blob_set(cd->model_handler, pos, data_offset_size, fragment, - fragment_size); - - /* TODO: now load the model from the blob */ - - return ret; + return 0; } -#if DEBUG - /* The first feature for no and yes used in tflm_speech example */ -int8_t expected_feature_no[TFLM_FEATURE_SIZE] = { +static const int8_t expected_feature_no[TFLM_FEATURE_SIZE] = { 126, 103, 124, 102, 124, 102, 123, 100, 118, 97, 118, 100, 118, 98, 121, 100, 121, 98, 117, 91, 96, 74, 54, 87, 100, 87, 109, 92, 91, 80, 64, 55, 83, 74, 74, 78, 114, 95, 101, 81, }; -int8_t expected_feature_yes[TFLM_FEATURE_SIZE] = { +static const int8_t expected_feature_yes[TFLM_FEATURE_SIZE] = { 124, 105, 126, 103, 125, 101, 123, 100, 116, 98, 115, 97, 113, 90, 91, 82, 104, 96, 117, 97, 121, 103, 126, 101, 125, 104, 126, 104, 125, 101, 116, 90, 81, 74, 80, 71, 83, 76, 82, 71, }; -#endif /* * This expects features from 16kHz mono 16 bit input stream. @@ -174,63 +217,220 @@ int8_t expected_feature_yes[TFLM_FEATURE_SIZE] = { * features in the input buffer. */ + + + static int tflm_process(struct processing_module *mod, struct sof_source **sources, int num_of_sources, struct sof_sink **sinks, int num_of_sinks) { struct tflm_comp_data *cd = module_get_private_data(mod); struct comp_dev *dev = mod->dev; - size_t frame_bytes = source_get_frame_bytes(sources[0]); - int features = source_get_data_frames_available(sources[0]); + g_tflm_dev = dev; + + if (mod->priv.state != MODULE_PROCESSING) + return 0; + + /* Guard: skip processing if TFLM interpreter was not prepared */ + if (!g_tflm_initialized) { + printk("[TFLM PROCESS] WARNING: not initialized, skipping frame\n"); + /* Still drain the source so the pipeline doesn't stall */ + size_t avail = source_get_data_available(sources[0]); + if (avail > 0) { + const void *dp, *bs; + size_t bsz; + if (source_get_data(sources[0], avail, &dp, &bs, &bsz) == 0) + source_release_data(sources[0], avail); + } + return 0; + } + + size_t bytes_to_process = source_get_data_available(sources[0]); const void *data_ptr, *buf_start; size_t buf_size; - int ret; + int ret = 0; - comp_dbg(dev, "entry"); + uint32_t sched_ccount_start = get_ccount(); + uint64_t sched_timer_start = sof_cycle_get_64(); - /* Window size is TFLM_FEATURE_ELEM_COUNT and we increment - * by TFLM_FEATURE_SIZE until buffer empty. - */ - while (features >= TFLM_FEATURE_ELEM_COUNT) { - ret = source_get_data(sources[0], TFLM_FEATURE_ELEM_COUNT * frame_bytes, + static int8_t feature_buf[TFLM_FEATURE_ELEM_COUNT]; + static int frame_counter = 0; + + if (bytes_to_process > 0) { + ret = source_get_data(sources[0], bytes_to_process, &data_ptr, &buf_start, &buf_size); - if (ret) - return ret; - - cd->tfc.audio_features = data_ptr; - cd->tfc.audio_data_size = TFLM_FEATURE_ELEM_COUNT; - ret = TF_ProcessClassify(&cd->tfc); - if (!ret) { - comp_err(dev, "classify failed %s.", - cd->tfc.error); - source_release_data(sources[0], 0); - return ret; + if (ret == 0 && data_ptr) { + /* Shift 49-frame sliding window history by 1 frame (40 bytes = 10ms) */ + size_t copy_feat_size = MIN(bytes_to_process, TFLM_FEATURE_SIZE); + memmove(&feature_buf[0], &feature_buf[copy_feat_size], + TFLM_FEATURE_ELEM_COUNT - copy_feat_size); + memcpy(&feature_buf[TFLM_FEATURE_ELEM_COUNT - copy_feat_size], + data_ptr, copy_feat_size); + + /* Copy source data to sink first, before releasing source data */ + if (num_of_sinks > 0 && sinks[0]) { + void *snk_ptr, *snk_buf_start; + size_t snk_buf_size; + int sret = sink_get_buffer(sinks[0], bytes_to_process, + &snk_ptr, &snk_buf_start, &snk_buf_size); + if (sret == 0 && snk_ptr) { + size_t size_to_wrap = (uint8_t *)snk_buf_start + snk_buf_size - (uint8_t *)snk_ptr; + if (bytes_to_process <= size_to_wrap) { + memcpy(snk_ptr, data_ptr, bytes_to_process); + } else { + memcpy(snk_ptr, data_ptr, size_to_wrap); + memcpy(snk_buf_start, (const uint8_t *)data_ptr + size_to_wrap, + bytes_to_process - size_to_wrap); + } + sink_commit_buffer(sinks[0], bytes_to_process); + } + } + + /* Release source data immediately after reading */ + source_release_data(sources[0], bytes_to_process); + + frame_counter++; + + uint64_t current_timer = sof_cycle_get_64(); + uint64_t stride_delta = current_timer - last_inference_timer; + uint32_t stride_ms_x100 = (uint32_t)((stride_delta * 100ULL) / 38400ULL); + + /* Run inference every 500ms */ + if (last_inference_timer > 0 && stride_delta >= (38400ULL * 500ULL)) { + last_inference_timer = current_timer; + frame_counter = 0; + + cd->tfc.audio_features = feature_buf; + cd->tfc.audio_data_size = TFLM_FEATURE_ELEM_COUNT; + + uint32_t c_start = get_ccount(); + uint64_t t_start = sof_cycle_get_64(); + + ret = TF_ProcessClassify(&cd->tfc); + + uint32_t c_end = get_ccount(); + uint64_t t_end = sof_cycle_get_64(); + + uint32_t cycles = c_end - c_start; + uint64_t timer_delta = t_end - t_start; + uint32_t mcps_x100 = (timer_delta > 0) ? (uint32_t)(((uint64_t)cycles * 3840ULL) / timer_delta) : 0; + static uint32_t inf_count = 0; + inf_count++; + + char ut_buf[160]; + snprintk(ut_buf, sizeof(ut_buf), "[PERF] TFLM Inference #%u (stride=%u.%02ums): CCOUNT=%u cycles, 38M_Timer_delta=%llu, MCPS=%u.%02u", + inf_count, stride_ms_x100 / 100, stride_ms_x100 % 100, + cycles, (unsigned long long)timer_delta, mcps_x100 / 100, mcps_x100 % 100); + sof_ut_log(ut_buf); + printk("[PERF] TFLM Inference #%u: CCOUNT=%u cycles, timer_delta=%llu, MCPS=%u.%02u\n", + inf_count, cycles, (unsigned long long)timer_delta, mcps_x100 / 100, mcps_x100 % 100); + + snprintk(ut_buf, sizeof(ut_buf), "[TFLM MODEL GRAPH] Total Nodes = %d", cd->tfc.op_count); + sof_ut_log(ut_buf); + + for (int k = 0; k < cd->tfc.op_count && k < 10; k++) { + snprintk(ut_buf, sizeof(ut_buf), " Node %d (OpCode=%d): %u cycles", + k, cd->tfc.node_codes[k], cd->tfc.node_cycles[k]); + sof_ut_log(ut_buf); + } + + if (ret >= 0) { + int max_idx = 0; + float max_score = cd->tfc.predictions[0]; + + for (int i = 0; i < cd->tfc.categories; i++) { + if (cd->tfc.predictions[i] > max_score) { + max_score = cd->tfc.predictions[i]; + max_idx = i; + } + } + + g_total_inferences++; + if (max_idx >= 0 && max_idx < TFLM_CATEGORY_COUNT) + g_category_totals[max_idx]++; + + char result_buf[160]; + int max_pct = (int)(max_score * 100.0f); + if (max_pct < 0) max_pct = 0; + snprintk(result_buf, sizeof(result_buf), "TFLM top prediction: %s confidence=%d pct (inferences=%u): silence=%u, unknown=%u, yes=%u, no=%u", + prediction[max_idx], max_pct, g_total_inferences, + g_category_totals[0], g_category_totals[1], g_category_totals[2], g_category_totals[3]); + sof_ut_log(result_buf); + printk("%s\n", result_buf); + + if (max_score >= 0.50f) { + char ut_buf[160]; + int max_pct = (int)(max_score * 100.0f); + snprintk(ut_buf, sizeof(ut_buf), "TFLM KEYWORD DETECTED: %s confidence=%d pct", + prediction[max_idx], max_pct); + sof_ut_log(ut_buf); + + if (max_idx >= 2) { + g_kpb_trigger_events++; + tflm_notify_kpb(mod); + } + } + } + } } + } - /* debug - dump the output */ - for (int i = 0; i < cd->tfc.categories; i++) { - comp_dbg(dev, "tf: predictions %1.3f %s", - cd->tfc.predictions[i], prediction[i]); - } + uint32_t sched_ccount_end = get_ccount(); + uint64_t sched_timer_end = sof_cycle_get_64(); + uint32_t sched_c_delta = sched_ccount_end - sched_ccount_start; + uint64_t sched_t_delta = sched_timer_end - sched_timer_start; + uint32_t sched_mcps_x100 = 0; - /* advance by one stride */ - source_release_data(sources[0], TFLM_FEATURE_SIZE * frame_bytes); - features = source_get_data_frames_available(sources[0]); - } + if (sched_t_delta > 0) + sched_mcps_x100 = (uint32_t)(((uint64_t)sched_c_delta * 3840ULL) / sched_t_delta); + (void)sched_mcps_x100; return ret; } -static int tflm_reset(struct processing_module *mod) + +static int tflm_prepare(struct processing_module *mod, + struct sof_source **sources, int num_of_sources, + struct sof_sink **sinks, int num_of_sinks) { - //struct tflm_comp_data *cd = module_get_private_data(mod); + struct tflm_comp_data *cd = module_get_private_data(mod); + + printk("[TFLM PREPARE] tflm_prepare called, loading model...\n"); + + if (g_tflm_initialized) { + printk("[TFLM PREPARE] already initialized, skipping\n"); + return 0; + } + + int ret = TF_SetModel(&cd->tfc, NULL); + if (ret < 0) { + printk("[TFLM PREPARE] FAILED: TF_SetModel returned %d\n", ret); + return ret; + } + + ret = TF_InitOps(&cd->tfc); + if (ret < 0) { + printk("[TFLM PREPARE] FAILED: TF_InitOps returned %d\n", ret); + return ret; + } + + g_tflm_initialized = true; + last_inference_timer = sof_cycle_get_64(); + printk("[TFLM PREPARE] TFLM model & ops initialized successfully!\n"); + return 0; +} + +static int tflm_reset(struct processing_module *mod) +{ + tflm_log_summary_at_shutdown(mod); + g_tflm_initialized = false; return 0; } static const struct module_interface tflmcly_interface = { .init = tflm_init, -// .prepare = tflm_prepare, + .prepare = tflm_prepare, .process = tflm_process, .set_configuration = tflm_set_config, // .get_configuration = tflm_get_config, @@ -240,9 +440,25 @@ static const struct module_interface tflmcly_interface = { DECLARE_TR_CTX(tflm_tr, SOF_UUID(tflmcly_uuid), LOG_LEVEL_INFO); DECLARE_MODULE_ADAPTER(tflmcly_interface, tflmcly_uuid, tflm_tr); -SOF_MODULE_INIT(tflmcly, sys_comp_module_tflmcly_interface_init); +SOF_MODULE_INIT(tflmcly_interface, sys_comp_module_tflmcly_interface_init); + +int llext_entry(void) +{ + printk("[TFLM LLEXT] llext_entry initialized cleanly.\n"); + return 0; +} +EXPORT_SYMBOL(llext_entry); + + + + + + + + #if CONFIG_COMP_TENSORFLOW_MODULE + /* modular: llext dynamic link */ #include diff --git a/src/include/sof/lib_manager.h b/src/include/sof/lib_manager.h index 29c226eb61a7..87ddc082d409 100644 --- a/src/include/sof/lib_manager.h +++ b/src/include/sof/lib_manager.h @@ -116,6 +116,8 @@ struct lib_manager_module { unsigned int n_dependent; /* For auxiliary modules: number of dependents */ bool mapped; struct lib_manager_segment_desc segment[LIB_MANAGER_N_SEGMENTS]; + uintptr_t vma_base; + size_t vma_size; }; struct lib_manager_mod_ctx { diff --git a/src/library_manager/llext_manager.c b/src/library_manager/llext_manager.c index 4c1e4f02d5b5..a098b7b61bcd 100644 --- a/src/library_manager/llext_manager.c +++ b/src/library_manager/llext_manager.c @@ -50,6 +50,101 @@ extern struct tr_ctx lib_manager_tr; #define PAGE_SZ CONFIG_MM_DRV_PAGE_SIZE +#include +#include + +#define LLEXT_LIB_PAGES (CONFIG_LIBRARY_REGION_SIZE / PAGE_SZ) +SYS_BITARRAY_DEFINE_STATIC(lib_vma_bitarray, LLEXT_LIB_PAGES); + +static uintptr_t llext_manager_alloc_vma(size_t size) +{ + size_t num_pages = ALIGN_UP(size, PAGE_SZ) / PAGE_SZ; + size_t offset; + int ret; + + ret = sys_bitarray_alloc(&lib_vma_bitarray, num_pages, &offset); + if (ret < 0) { + tr_err(&lib_manager_tr, "llext_manager_alloc_vma: failed to allocate %zu pages", num_pages); + return 0; + } + + return CONFIG_LIBRARY_BASE_ADDRESS + offset * PAGE_SZ; +} + +void llext_manager_free_vma(uintptr_t vma, size_t size) +{ + if (!vma || !size) + return; + + size_t num_pages = ALIGN_UP(size, PAGE_SZ) / PAGE_SZ; + size_t offset = (vma - CONFIG_LIBRARY_BASE_ADDRESS) / PAGE_SZ; + + sys_bitarray_free(&lib_vma_bitarray, num_pages, offset); +} + +static enum llext_mem llext_manager_get_sec_mem_idx(const char *name, const elf_shdr_t *shdr) +{ + if (strcmp(name, ".exported_sym") == 0) + return LLEXT_MEM_EXPORT; + + switch (shdr->sh_type) { + case SHT_NOBITS: + return LLEXT_MEM_BSS; + case SHT_PROGBITS: + if (shdr->sh_flags & SHF_EXECINSTR) + return LLEXT_MEM_TEXT; + else if (shdr->sh_flags & SHF_WRITE) + return LLEXT_MEM_DATA; + else + return LLEXT_MEM_RODATA; + case SHT_PREINIT_ARRAY: + return LLEXT_MEM_PREINIT; + case SHT_INIT_ARRAY: + return LLEXT_MEM_INIT; + case SHT_FINI_ARRAY: + return LLEXT_MEM_FINI; + default: + return LLEXT_MEM_COUNT; + } +} + +static size_t llext_manager_layout_sections(uint8_t *elf_buf, uintptr_t vma_base) +{ + elf_ehdr_t *hdr = (elf_ehdr_t *)elf_buf; + elf_shdr_t *shdrs = (elf_shdr_t *)(elf_buf + hdr->e_shoff); + elf_shdr_t *shstr_shdr = shdrs + hdr->e_shstrndx; + const char *shstrtab = (const char *)(elf_buf + shstr_shdr->sh_offset); + + uintptr_t current_vma = vma_base; + enum llext_mem last_region = LLEXT_MEM_COUNT; + + for (int i = 0; i < hdr->e_shnum; i++) { + elf_shdr_t *shdr = shdrs + i; + + if (!(shdr->sh_flags & SHF_ALLOC) || shdr->sh_size == 0) + continue; + + const char *name = shstrtab + shdr->sh_name; + enum llext_mem s_region = llext_manager_get_sec_mem_idx(name, shdr); + + if (s_region == LLEXT_MEM_COUNT) + continue; + + if (last_region != LLEXT_MEM_COUNT && last_region != s_region) { + current_vma = ALIGN_UP(current_vma, PAGE_SZ); + } + last_region = s_region; + + current_vma = ALIGN_UP(current_vma, shdr->sh_addralign); + if (vma_base) { + shdr->sh_addr = current_vma; + } + current_vma += shdr->sh_size; + } + + return current_vma - vma_base; +} + static int llext_manager_update_flags(void __sparse_cache *vma, size_t size, uint32_t flags) { size_t pre_pad_size = (uintptr_t)vma & (PAGE_SZ - 1); @@ -383,6 +478,26 @@ static int llext_manager_link(const char *name, } if (!*llext || mctx->mapped) { + if (!*llext) { + uint8_t *elf_buf = (uint8_t *)mctx->ebl->buf; + size_t total_size = llext_manager_layout_sections(elf_buf, 0); + if (total_size == 0) { + tr_err(&lib_manager_tr, "llext_manager_link: layout sections failed"); + return -EINVAL; + } + + uintptr_t vma_base = llext_manager_alloc_vma(total_size); + if (!vma_base) { + tr_err(&lib_manager_tr, "llext_manager_link: VMA allocation failed"); + return -ENOMEM; + } + + mctx->vma_base = vma_base; + mctx->vma_size = total_size; + + llext_manager_layout_sections(elf_buf, vma_base); + } + /* * Either the very first time loading this module, or the module * is already mapped, we just call llext_load() to refcount it @@ -395,8 +510,15 @@ static int llext_manager_link(const char *name, }; ret = llext_load(ldr, name, llext, &ldr_parm); - if (ret) + if (ret) { + tr_err(&lib_manager_tr, "llext_load failed: ret=%d", ret); + if (mctx->vma_base) { + llext_manager_free_vma(mctx->vma_base, mctx->vma_size); + mctx->vma_base = 0; + mctx->vma_size = 0; + } return ret; + } } /* All code sections */ diff --git a/src/library_manager/llext_manager_dram.c b/src/library_manager/llext_manager_dram.c index 2f6cff2b3501..1cc8fad942f8 100644 --- a/src/library_manager/llext_manager_dram.c +++ b/src/library_manager/llext_manager_dram.c @@ -14,6 +14,8 @@ LOG_MODULE_DECLARE(lib_manager, CONFIG_SOF_LOG_LEVEL); +void llext_manager_free_vma(uintptr_t vma, size_t size); + struct lib_manager_dram_storage { struct ext_library ext_lib; struct lib_manager_mod_ctx *ctx; @@ -332,6 +334,9 @@ int llext_manager_restore_from_dram(void) if (mod[k].llext) llext_unload(&mod[k].llext); + if (mod[k].vma_base) + llext_manager_free_vma(mod[k].vma_base, mod[k].vma_size); + if (mod[k].ebl) rfree(mod[k].ebl); } diff --git a/src/logging/log_backend_probe.c b/src/logging/log_backend_probe.c index 0b852d780455..60d0ed1298bb 100644 --- a/src/logging/log_backend_probe.c +++ b/src/logging/log_backend_probe.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include diff --git a/tools/rimage/config/ptl.toml.h b/tools/rimage/config/ptl.toml.h index e7a53d1b1820..dbd843349cb9 100644 --- a/tools/rimage/config/ptl.toml.h +++ b/tools/rimage/config/ptl.toml.h @@ -50,9 +50,7 @@ index = __COUNTER__ #include