# 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.

## Choose your build environment

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](https://ambiqai.github.io/helia-aot/getting-started/integrate/#zephyr) |
| neuralSPOT | Make fragment | [Add to a neuralSPOT application](https://ambiqai.github.io/helia-aot/getting-started/integrate/#neuralspot) |
| CMake | CMake target | [Add to a CMake project](https://ambiqai.github.io/helia-aot/getting-started/integrate/#cmake) |
| NSX | Module manifest and CMake target | [Use an NSX workspace](https://ambiqai.github.io/helia-aot/getting-started/integrate/#nsx) |
| CMSIS-Pack | Pack description and optional archive | [Package for a pack-aware tool](https://ambiqai.github.io/helia-aot/getting-started/integrate/#cmsis-pack) |

## Zephyr

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](https://ambiqai.github.io/helia-aot/reference/versions/).

If Zephyr is new to you, complete its
[environment setup](https://docs.zephyrproject.org/latest/develop/getting_started/index.html)
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:

```text
kws-first/
├── out/kws_ref/                 # Already generated
└── app/
    ├── CMakeLists.txt
    ├── prj.conf
    ├── src/main.c
    └── modules/ns-cmsis-nn/     # Kernel library checkout
```

```sh
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:

```cmake title="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:

```ini title="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:

```c title="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`:

```sh
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](https://ambiqai.github.io/helia-aot/getting-started/validate/) to
program and observe this application.

## neuralSPOT

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

```sh
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:

```make
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.

## CMake

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

```sh
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:

```cmake
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.

## NSX

```sh
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:

```cmake
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](https://ambiqai.github.io/helia-aot/guide/precision/#the-library-build-switch).

## CMSIS-Pack

```sh
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.
