Skip to content

Commit

Permalink
smart-amp-test: make a loadable module
Browse files Browse the repository at this point in the history
Convert the smart-amp-test in its IPC4 version to a loadable LLEXT
module. Use an overlay configuration to select between monolithic and
modular builds.

Signed-off-by: Guennadi Liakhovetski <[email protected]>
  • Loading branch information
lyakh committed Jan 16, 2024
1 parent 1b2a324 commit 40002f8
Show file tree
Hide file tree
Showing 7 changed files with 196 additions and 13 deletions.
1 change: 1 addition & 0 deletions app/overlays/mtl/module_overlay.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
CONFIG_SAMPLE_SMART_AMP=m
56 changes: 56 additions & 0 deletions scripts/llext_link_helper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: BSD-3-Clause

# We need to calculate ELF section addresses of an LLEXT module and use them to
# run the linker. We could just use Python to calculate addresses and pass them
# back to cmake to have it call the linker. However, there doesn't seem to be a
# portable way to do that. Therefore we pass the linker path and all the command
# line parameters to this script and call the linker directly.

import argparse
import subprocess
from elftools.elf.elffile import ELFFile

args = None
def parse_args():
global args

parser = argparse.ArgumentParser(description='Helper utility to run a linker command '
'with calculated ELF section addresses')

parser.add_argument('command', type=str, help='Linker command to execute')
parser.add_argument('params', nargs='+', help='Additional linker parameters')
parser.add_argument("-f", "--file", required=True, type=str,
help='Object file name')
parser.add_argument("-t", "--text-addr", required=True, type=str,
help='.text section address')

args = parser.parse_args()

def main():
parse_args()

elf = ELFFile(open(args.file, 'rb'))

text_addr = int(args.text_addr, 0)

text_offset = elf.get_section_by_name('.text').header.sh_offset
rodata_offset = elf.get_section_by_name('.rodata').header.sh_offset
data_offset = elf.get_section_by_name('.data').header.sh_offset

upper = rodata_offset - text_offset + text_addr + 0xfff
rodata_addr = upper - (upper % 0x1000)

upper = data_offset - rodata_offset + rodata_addr + 0xf
data_addr = upper - (upper % 0x10)

command = [args.command,
f'-Wl,-Ttext=0x{text_addr:x}',
f'-Wl,--section-start=.rodata=0x{rodata_addr:x}',
f'-Wl,-Tdata=0x{data_addr:x}']
command.extend(args.params)

subprocess.run(command)

if __name__ == "__main__":
main()
2 changes: 1 addition & 1 deletion src/samples/audio/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
menu "Audio component samples"

config SAMPLE_SMART_AMP
bool "Smart amplifier test component"
tristate "Smart amplifier test component"
default y
help
Select for test smart amplifier component
Expand Down
75 changes: 75 additions & 0 deletions src/samples/audio/smart_amp_llext/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Copyright (c) 2023 Intel Corporation.
# SPDX-License-Identifier: Apache-2.0

cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(smart_amp_test)

SET_PROPERTY(GLOBAL PROPERTY TARGET_SUPPORTS_SHARED_LIBS TRUE)

set(MODULE "smart_amp_test")
cmake_path(SET SOF_BASE NORMALIZE ${PROJECT_SOURCE_DIR}/../../../..)

add_library(${MODULE} SHARED)

target_sources(${MODULE} PRIVATE
${CMAKE_CURRENT_LIST_DIR}/../smart_amp_test_ipc4.c
)

sof_append_relative_path_definitions(${MODULE})

target_include_directories(${MODULE} PRIVATE
"${ZEPHYR_BASE}/include"
"${ZEPHYR_BASE}/soc/xtensa/intel_adsp/common/include"
"${ZEPHYR_BASE}/soc/xtensa/intel_adsp/ace/include/intel_ace15_mtpm"
"${ZEPHYR_BASE}/../modules/hal/xtensa/include"
"${ZEPHYR_BASE}/../modules/hal/xtensa/zephyr/soc/intel_ace15_mtpm"
"${SOF_BASE}/src/include"
"${SOF_BASE}/src/arch/xtensa/include"
"${SOF_BASE}/src/platform/meteorlake/include"
"${SOF_BASE}/src/platform/intel/ace/include"
"${SOF_BASE}/src/include/sof/audio/module_adapter/iadk"
"${SOF_BASE}/zephyr/include"
"${SOF_BASE}/xtos/include"
"${SOF_BASE}/tools/rimage/src/include"
"${PROJECT_BINARY_DIR}/../include/generated"
)

set(MODULE_PROPERTIES HPSRAM_ADDR "0xa06c1000")
set_target_properties(${MODULE} PROPERTIES ${MODULE_PROPERTIES})

set(MODULE_COMPILE_DEF
__ZEPHYR__=1
__XTENSA__
KERNEL
MAJOR_IADSP_API_VERSION=5
MIDDLE_IADSP_API_VERSION=0
MINOR_IADSP_API_VERSION=0
)
target_compile_definitions(${MODULE} PRIVATE ${MODULE_COMPILE_DEF})

target_compile_options(${MODULE} PRIVATE
-imacros${PROJECT_BINARY_DIR}/../include/generated/autoconf.h
-save-temps -O2
)

set(MODULE_LINKER_PARAMS -nostdlib -nodefaultlibs)
target_link_options(${MODULE} PRIVATE
${MODULE_LINKER_PARAMS}
)

add_custom_command(OUTPUT lib${MODULE}_out.so
DEPENDS ${MODULE}
COMMAND ${SOF_BASE}scripts/llext_link_helper.py
-f lib${MODULE}.so -t "0xa06ca000" ${CMAKE_C_COMPILER} --
${MODULE_LINKER_PARAMS} -shared -fPIC
-o lib${MODULE}_llext.so $<TARGET_OBJECTS:${MODULE}>
COMMAND ${CMAKE_STRIP} -R .xt.* -o lib${MODULE}_out.so lib${MODULE}_llext.so
COMMAND_EXPAND_LISTS
)

add_custom_target(${MODULE}_llext ALL
DEPENDS lib${MODULE}_out.so
)

add_dependencies(${MODULE} zephyr_interface)
5 changes: 5 additions & 0 deletions src/samples/audio/smart_amp_llext/llext.toml.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#include <tools/rimage/config/platform.toml>
#include "../smart_amp_test.toml"

[module]
count = __COUNTER__
58 changes: 49 additions & 9 deletions src/samples/audio/smart_amp_test_ipc4.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,16 @@ DECLARE_TR_CTX(smart_amp_test_comp_tr, SOF_UUID(smart_amp_test_comp_uuid),
#include <stddef.h>

#include <module/base.h>
#include <module/api_ver.h>
#include <module/interface.h>
#include <iadk/adsp_error_code.h>
#include <rimage/sof/user/manifest.h>
#include <audio/source_api.h>
#include <audio/sink_api.h>
#include <ipc4/module.h>
#include <sof/math/numbers.h>
#endif
#include <sof/samples/audio/smart_amp_test.h>
#include <module/module/api_ver.h>
#include <rimage/sof/user/manifest.h>

typedef void (*smart_amp_proc)(int8_t const *src_ptr,
int8_t const *src_begin,
Expand All @@ -63,13 +63,21 @@ struct smart_amp_data {
static struct smart_amp_data smart_amp_priv;
#endif

/* When building as a loadable module, we need .bss to avoid rimage errors */
static int keep_bss __attribute__((used));

static int smart_amp_init(struct processing_module *mod)
{
struct smart_amp_data *sad;
struct module_data *mod_data = &mod->priv;
int ret;
const struct ipc4_base_module_extended_cfg *base_cfg = mod_data->cfg.init_data;

if (!base_cfg) {
LOG_ERR("smart_amp_init(): no module configuration");
return -EINVAL;
}

LOG_DBG("smart_amp_init()");

#ifndef __SOF_MODULE_SERVICE_BUILD__
Expand Down Expand Up @@ -360,32 +368,64 @@ static const struct module_interface smart_amp_test_interface = {
.free = smart_amp_free
};

/*
* We have to distinguish between 3 kinds of builds:
* 1. built-in: MAJOR_IADSP_API_VERSION isn't defined
* 2. system-service API: MAJOR_IADSP_API_VERSION and
* __SOF_MODULE_SERVICE_BUILD__ are defined
* 3. dynamic linking: only MAJOR_IADSP_API_VERSION is defined
*/

#ifndef __SOF_MODULE_SERVICE_BUILD__
/* All builds except system-service API */

DECLARE_MODULE_ADAPTER(smart_amp_test_interface, smart_amp_test_comp_uuid, smart_amp_test_comp_tr);
/* DECLARE_MODULE_ADAPTER() creates
* "sys_comp_module_<smart_amp_test_interface>_init()" (and a lot more)
*/
SOF_MODULE_INIT(smart_amp_test, sys_comp_module_smart_amp_test_interface_init);
#else

#endif

#ifdef MAJOR_IADSP_API_VERSION
/* modular: system-services or dynamic link */

static const struct module_interface *loadable_module_main(void *mod_cfg,
void *parent_ppl,
void **mod_ptr)
{
return &smart_amp_test_interface;
}

DECLARE_LOADABLE_MODULE_API_VERSION(smart_amp_test);

__attribute__((section(".module")))
const struct sof_man_module_manifest main_manifest = {
static const struct sof_man_module_manifest main_manifest __section(".module") __attribute__((used)) = {
.module = {
.name = "SMATEST",
.uuid = {0x1E, 0x96, 0x7A, 0x16, 0xE4, 0x8A, 0xEA, 0x11,
0x89, 0xF1, 0x00, 0x0C, 0x29, 0xCE, 0x16, 0x35},
.entry_point = (uint32_t)loadable_module_main,
.type = { .load_type = SOF_MAN_MOD_TYPE_MODULE,
.domain_ll = 1 },
.type = {
.load_type = SOF_MAN_MOD_TYPE_MODULE,
.domain_ll = 1,
},
.affinity_mask = 1,
}
};

#ifdef __SOF_MODULE_SERVICE_BUILD__
/* system services */

DECLARE_LOADABLE_MODULE_API_VERSION(smart_amp_test);

#else
/* dynamic link */

static const struct sof_module_api_build_info buildinfo __section(".mod_buildinfo") __attribute__((used)) = {
.format = SOF_MODULE_API_BUILD_INFO_FORMAT,
.api_version_number = {
.fields.major = SOF_MODULE_API_MAJOR_VERSION,
},
};

#endif

#endif
12 changes: 9 additions & 3 deletions zephyr/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -800,9 +800,15 @@ if(CONFIG_IPC_MAJOR_3)
${SOF_AUDIO_PATH}/tdfb/tdfb_ipc3.c
)
elseif(CONFIG_IPC_MAJOR_4)
zephyr_library_sources_ifdef(CONFIG_SAMPLE_SMART_AMP
${SOF_SAMPLES_PATH}/audio/smart_amp_test_ipc4.c
)
if(CONFIG_SAMPLE_SMART_AMP STREQUAL "m")
add_subdirectory(${SOF_SAMPLES_PATH}/audio/smart_amp_llext
${PROJECT_BINARY_DIR}/smart_amp_llext)
add_dependencies(app smart_amp_test_llext)
elseif(CONFIG_SAMPLE_SMART_AMP)
zephyr_library_sources(
${SOF_SAMPLES_PATH}/audio/smart_amp_test_ipc4.c
)
endif()

zephyr_library_sources_ifdef(CONFIG_COMP_TDFB
${SOF_AUDIO_PATH}/tdfb/tdfb_ipc4.c
Expand Down

0 comments on commit 40002f8

Please sign in to comment.