Skip to content

Loadable Module Development Kit - Standalone Kit using only Stable APIs.

License

Notifications You must be signed in to change notification settings

thesofproject/sof-lmdk

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

7 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Loadable Module Development Kit - Standalone Kit using only Stable APIs.

LMDK (Loadable Module Development Kit) is a standalone package that allows building a loadable module for the SOF. It operates independently from SOF FW, but it contains all necessary data structure definitions required for interaction with it.

Loadable modules constructed with LMDK provide functionality identical to that of IADK (Intel Audio Development Kit) modules. IADK modules are developed in C++, and their interface is grounded on classes with virtual methods. Given that SOF FW is written in C, an adapter is required for IADK modules to provide the required interfaces. Loadable modules developed using LMDK utilize a C language interface, which is the reason they are also referred to as native loadable modules.

Preparing a loadable module

All prepared module files should be situated in the modules/<module_name> directory. The most basic loadable module must encompass the following components:

API version definition

A declaration of the API version used is required. This is accomplished using the DECLARE_LOADABLE_MODULE_API_VERSION macro, which accepts the module name as a parameter.

DECLARE_LOADABLE_MODULE_API_VERSION(example);

It places at the beginning of the .text section a structure identifying the version of the API used by the module. During the module loading process, the SOF verifies if the declared API version is supported. The API version is declared in the header files and should not be altered independently.

Module interface

The struct module_interface structure represents the module interface. It contains pointers to functions provided by the module.

static struct module_interface example_module_interface = {
	.init = example_init,
	.process = example_process,
	.set_processing_mode = example_set_processing_mode,
 	.get_processing_mode = example_get_processing_mode,
	.set_configuration = example_set_configuration,
	.get_configuration = example_get_configuration,
	.reset = example_reset,
};

Currently, the module can provide one of three sample processing function. The process function is recommended and also allows processing in DP (Data Processing) mode. The functions process_audio_stream and process_raw_data are deprecated and will be phased out in the future. A comprehensive description of the function with parameters is situated in the module/interface.h header file. All functions provided by the module accepts as parameters a pointer to the struct processing_module structure, which represents the module. A module can store a pointer to its private data using the module_get_private_data function and retrieve it using the module_set_private_data function.

Module entry point

static const struct native_system_agent *system_agent;

static void* example_entry_point(const struct byte_array* mod_cfg,
				 void* reserved,
				 const struct native_system_agent* const* agent)
{
	system_agent = *agent;

	return &example_module_interface;
}

This function serves as the entry point for the module. It is invoked when each instance of the module is created. The mod_cfg parameter carries the module configuration passed via IPC. The agent parameter conveys a pointer to the system agent. The function yields a pointer to the module interface. Subsequently, the init function from the returned module interface is invoked.

Module manifest definition

__attribute__((section(".module")))
const struct sof_man_module_manifest example_module_manifest = {
    .module = {
        .name = "EXAMPLE",
        .uuid = {0x82, 0x68, 0x5a, 0x91, 0x0b, 0xda, 0x4b, 0x23,
		 0xa6, 0x2a, 0x4c, 0x2e, 0x77, 0xf7, 0xa9, 0x14},
        .entry_point = (uint32_t)example_entry_point,
        .type = {
            .load_type = SOF_MAN_MOD_TYPE_MODULE,
            .domain_ll = 1
        },
        .affinity_mask = 3,
    }
};

The structure of a module manifest specifies its name, GUID, entry point, and fundamental configuration. This structure is utilized by the rimage during the library preparation process. The rimage populates the remaining fields of this structure (offsets in the output file, hash), and in conjunction with the configuration from the toml file, incorporates them into the final image.

Preparing the module makefile

LMDK utilizes the CMake build tool. Consequently, the preparation of a CMakeLists.txt file for the module is necessary.

target_sources(example PRIVATE example.c)

set_target_properties(example PROPERTIES
    HPSRAM_ADDR "0xa06a1000"
)

The HPSRAM_ADDR property designates the base address where the module will be linked during the compilation process. The module will be situated at this address in the DSP memory during its loading phase. It is essential to specify a valid address that resides within the DSP address space. The address spaces of individual modules should not intersect. This is verified by rimage during the preparation of the final image.

Preparing a loadable library

The final step before initiating the compilation process is to prepare the CMakeLists.txt file for the library in the libraries/<lib_name> directory. This file dictates which modules will be incorporated into the resulting image. A library can comprise one or more modules. It is essential to specify the path to the toml file that contains the configuration of the prepared modules. The format of this file is identical to that utilized by SOF.

cmake_minimum_required(VERSION 3.20)
set(CMAKE_TOOLCHAIN_FILE "${CMAKE_CURRENT_LIST_DIR}/../../cmake/xtensa-toolchain.cmake")

project(example_lib)

# list of modules to be built and included into this loadable library
set(MODULES_LIST example)

# toml file for rimage to generate manifets
set(TOML "${CMAKE_CURRENT_LIST_DIR}/example.toml")

include(../../cmake/build.cmake)

Building of loadable library

To build dummy loadable library execute:

cd libraries/dummy
cmake -B build -G <Ninja/Makefile> -DRIMAGE_INSTALL_DIR="path/where/rimage/executable/is" -DSIGNING_KEY="path/to/key"
cd --build build

Here RIMAGE_INSTALL_DIR is a path to directory where rimage executable is, SIGNING_KEY is a path to signing key for rimage. Dummy module sets up toolchain file in the project file. However, in your library you can select toolchain file in the configure step command:

cmake -B build -G <Ninja/Makefile> --toolchain "../../cmake/xtensa-toolchain.cmake" -DSIGNING_KEY="path/to/key"

About

Loadable Module Development Kit - Standalone Kit using only Stable APIs.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published