Skip to content
heliaAOT
HELIA HUB

Firmware integration

Build the out/kws_ref module from the previous step into a small application. The main path below uses Zephyr on Apollo510 EVB and calls the generated test case. You can finish the build without connecting a board.

Every format needs ns-cmsis-nn meeting the version guard in aot_common.h (at least 7.35.0 for this release). Changing module.type changes build files, platform headers and hooks, so regenerate for the environment you use.

Follow Zephyr to complete this walkthrough, or use the recipe matching your existing firmware project. Each recipe starts from the same saved kws.yaml.

Environment Integration files Recipe
Zephyr Module manifest and Kconfig Build the walkthrough application
neuralSPOT Make fragment Add to a neuralSPOT application
CMake CMake target Add to a CMake project
NSX Module manifest and CMake target Use an NSX workspace
CMSIS-Pack Pack description and optional archive Package for a pack-aware tool

Start with a working west workspace and Zephyr SDK supporting apollo510_evb. The repository’s Zephyr compile test pins Zephyr v4.3.0, west 1.5.0 and SDK 0.17.4 with the arm-zephyr-eabi toolchain. Its workspace includes cmsis_6, hal_ambiq and cmsis-dsp. The current kernel dependency used by that test is listed on Versions.

If Zephyr is new to you, complete its environment setup and build an Apollo510 Hello World application first. The application below assumes that SDK/toolchain setup is already working; it supplies all the additional application files for this model.

From the kws-first directory used for conversion, create:

kws-first/
├── out/kws_ref/ # Already generated
└── app/
├── CMakeLists.txt
├── prj.conf
├── src/main.c
└── modules/ns-cmsis-nn/ # Kernel library checkout
Terminal window
mkdir -p app/src app/modules
cp -R /path/to/ns-cmsis-nn app/modules/ns-cmsis-nn

Replace /path/to/ns-cmsis-nn with your pinned kernel-library checkout. Record its revision. Register it and the generated module before Zephyr configures the application:

app/CMakeLists.txt
cmake_minimum_required(VERSION 3.20.0)
list(APPEND ZEPHYR_EXTRA_MODULES
${CMAKE_CURRENT_SOURCE_DIR}/modules/ns-cmsis-nn
${CMAKE_CURRENT_SOURCE_DIR}/../out/kws_ref
)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(kws_first)
target_sources(app PRIVATE src/main.c)

Enable the generated module, kernels and serial logging:

app/prj.conf
CONFIG_FPU=y
CONFIG_NS_CMSIS_NN=y
CONFIG_NS_CMSIS_NN_ALL=y
CONFIG_KWS_REF=y
CONFIG_PRINTK=y
CONFIG_CONSOLE=y
CONFIG_UART_CONSOLE=y

CONFIG_KWS_REF comes from module.name, uppercased with hyphens changed to underscores. CONFIG_NS_CMSIS_NN_ALL includes all kernel families; the generated README lists individual family switches for later build-size tuning.

Save this complete entry point:

app/src/main.c
#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>
#include "aot_test_case.h"
int main(void)
{
printk("KWS golden check: starting\n");
int32_t status = aot_test_case_init();
printk("KWS init status=%ld\n", (long)status);
if (status != 0) {
return (int)status;
}
status = aot_test_case_run();
printk("KWS test status=%ld\n", (long)status);
return (int)status;
}

The test owns its context, copies golden.npz input data from generated constant arrays, runs the model and compares the output. It does not read files from the board. Zephyr’s CONFIG_PRINTK also enables logging from the generated module; without it, those comparison messages are discarded.

Using your Zephyr environment, build from kws-first:

Terminal window
west build -p always -b apollo510_evb -s app -d build/kws_ref

Check for build/kws_ref/zephyr/zephyr.elf. Inspect the build’s linker map when confirming memory placement: Apollo510 Zephyr hooks distinguish DTCM data from ITCM, and the rest of your firmware also consumes memory. A successful link establishes the build contract, not numerical correctness.

The repository’s Zephyr job compiles and links a small add_scalar_s8 model with verification disabled against a real Zephyr/CMSIS 6 tree. That exercises the integration mechanism; it is not a recorded KWS execution or a board test. Continue to On-device validation to program and observe this application.

For an existing neuralSPOT application, regenerate into a separate directory:

Terminal window
helia-aot convert --path kws.yaml --module.type neuralspot --module.path ./out-neuralspot

Copy out-neuralspot/kws_ref and ns-cmsis-nn into the application’s modules directory, then add both to its makefile:

modules += modules/ns-cmsis-nn
modules += modules/kws_ref

The generated module.mk uses neuralSPOT’s make-library helper. Call aot_test_case_init and, only after it succeeds, aot_test_case_run from the application after its board and logging initialization. Check both statuses. Generated logging uses ns_lp_printf, so use the application’s configured console. Build with the neuralSPOT project’s normal make target; its startup, linker script and programming procedure remain part of that project.

For a firmware project that already supplies the cross-toolchain, startup and linker script:

Terminal window
helia-aot convert --path kws.yaml --module.type cmake --module.path ./out-cmake

In the parent project, add the kernel library before the generated module:

add_subdirectory(modules/ns-cmsis-nn)
add_subdirectory(/absolute/path/to/out-cmake/kws_ref ${CMAKE_BINARY_DIR}/kws_ref)
target_link_libraries(app PRIVATE kws_ref)

The generated static library exposes includes-api, requests c_std_99, links m for GCC/Clang and links ns::cmsis-nn if that target already exists. Without it, the consumer must supply equivalent compatible kernel linkage. Merely configuring this library does not create a complete firmware executable.

CMake output defaults AOT_PRINTF and placement hooks to no-ops. Supply logging and section definitions to all generated translation units, for example through a compiler-preincluded application header configured on the module target. A macro defined only in main.c does not affect separately compiled aot_test_case.c. Confirm actual placement in the linker map.

Terminal window
helia-aot convert --path kws.yaml --module.type nsx --module.path ./out-nsx

This emits CMakeLists.txt and nsx-module.yaml. Use them inside a neuralSPOT-X project whose board layer defines NSX_BOARD_FLAGS_TARGET. Add the NSX kernel module before the generated module; the latter requires nsx::cmsis_nn and exports nsx::kws_ref.

Set an optional attributes header before adding the generated directory:

add_subdirectory(modules/nsx-cmsis-nn)
set(KWS_REF_ATTRIBUTES_HEADER "${CMAKE_CURRENT_SOURCE_DIR}/my_attributes.h")
add_subdirectory(/absolute/path/to/out-nsx/kws_ref ${CMAKE_BINARY_DIR}/kws_ref)
target_link_libraries(app PRIVATE nsx::kws_ref)

The header is force-preincluded into generated sources. Use it to define the application’s placement and AOT_PRINTF hooks, which otherwise default to no-ops. Float models additionally need the kernel-library switches described in Precision.

Terminal window
helia-aot convert --path kws.yaml --module.type cmsis_pack --module.path ./Ambiq.kws_ref.0.23.0.pack

The .pack contains a .pdsc at its root, C sources and public headers. Its core condition follows the selected platform’s CPU, and its dependency names the required Ambiq.NS-CMSIS-NN component/version. A directory output leaves an unpacked tree; .zip is a plain archive, not an installable pack.

Install the pack and its kernel dependency through your pack-aware build tool, select its Machine Learning / AOT Model component, and supply the same checked application calls and logging/placement hooks. One generated pack targets one platform configuration.

The converter does not invoke packchk itself. Repository tests run it with XSD validation disabled and allow specified unresolved-dependency warnings; they do not compile a consuming pack project. Validate your installed dependency set and build in the actual consumer tool.