Zephyr + heliaRT
This example shows the minimum process to create, build, and run a Zephyr application that uses heliaRT.
Use one integration path or the other:
- source modules:
helia-rt+ opencmsis-nn - source modules:
helia-rt+ns-cmsis-nn - prebuilt release module: a single
helia-rtarchive with heliaRT +ns-cmsis-nnalready linked in
This guide assumes a workspace like:
<ws>/
├── zephyr/
├── modules/
└── app/
└── helia_rt_app/
├── CMakeLists.txt
├── prj.conf
└── src/
└── main.cpp
Known-good versions:
- Zephyr: 4.3
- Zephyr SDK: zephyr-sdk-0.17.4
Note
Do not add both the source-module and prebuilt-release variants to the same app.
1. Fetch the default cmsis-nn module
In a standard Zephyr west workspace, open cmsis-nn is already provided by the upstream manifest at modules/lib/cmsis-nn.
Fetch it with west:
2. Add the local helia-rt source copy
Download the raw helia-rt source archive from the Ambiq content portal and copy it into modules/:
Result:
<ws>/
├── zephyr/
├── modules/
│ ├── helia-rt/
│ └── lib/
│ └── cmsis-nn/
└── app/
└── helia_rt_app/
3. Create the application
CMakeLists.txt
cmake_minimum_required(VERSION 3.20.0)
list(APPEND ZEPHYR_EXTRA_MODULES
${CMAKE_CURRENT_SOURCE_DIR}/../../modules/helia-rt
)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(helia_rt_app)
target_sources(app PRIVATE src/main.cpp)
prj.conf
CONFIG_CPP=y
CONFIG_STD_CPP17=y
CONFIG_FPU=y
CONFIG_PRINTK=y
CONFIG_CONSOLE=y
CONFIG_UART_CONSOLE=y
CONFIG_CMSIS_NN=y
CONFIG_CMSIS_NN_CONVOLUTION=y
CONFIG_CMSIS_NN_FULLYCONNECTED=y
CONFIG_HELIA_RT=y
CONFIG_HELIA_RT_BACKEND_CMSIS_NN=y
Required heliaRT-specific settings:
CONFIG_CMSIS_NN=yCONFIG_HELIA_RT=yCONFIG_HELIA_RT_BACKEND_CMSIS_NN=y
Notes:
- Add only
helia-rttoZEPHYR_EXTRA_MODULESon this path. - Do not add open
cmsis-nntoZEPHYR_EXTRA_MODULESwhen it already comes from the standard west workspace. - In the default Zephyr workspace layout, open
cmsis-nnis discovered atmodules/lib/cmsis-nn. - Open
cmsis-nndoes not provide anALLKconfig switch in Zephyr. You must enable the CMSIS-NN kernel groups your model needs.
Do not enable HELIA-only settings on this path:
CONFIG_NS_CMSIS_NNCONFIG_HELIA_RT_BACKEND_HELIA
1. Download the sources
Download the raw source archives for both helia-rt and ns-cmsis-nn from the Ambiq content portal.
After extracting them, copy the repositories into modules/:
Result:
2. Create the application
CMakeLists.txt
cmake_minimum_required(VERSION 3.20.0)
list(APPEND ZEPHYR_EXTRA_MODULES
${CMAKE_CURRENT_SOURCE_DIR}/../../modules/helia-rt
${CMAKE_CURRENT_SOURCE_DIR}/../../modules/ns-cmsis-nn
)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(helia_rt_app)
target_sources(app PRIVATE src/main.cpp)
prj.conf
CONFIG_CPP=y
CONFIG_STD_CPP17=y
CONFIG_FPU=y
CONFIG_PRINTK=y
CONFIG_CONSOLE=y
CONFIG_UART_CONSOLE=y
CONFIG_HELIA_RT=y
CONFIG_NS_CMSIS_NN=y
CONFIG_HELIA_RT_BACKEND_HELIA=y
Required heliaRT-specific settings:
CONFIG_HELIA_RT=yCONFIG_NS_CMSIS_NN=yCONFIG_HELIA_RT_BACKEND_HELIA=y
1. Download the prebuilt release
Download the prebuilt heliaRT release archive from the Ambiq content portal.
This bundle already contains the HELIA runtime and the ns-cmsis-nn kernel implementation inside the static archive.
After extracting it, copy the bundle into modules/:
Result:
2. Create the application
CMakeLists.txt
Only add the prebuilt bundle as a Zephyr module:
cmake_minimum_required(VERSION 3.20.0)
list(APPEND ZEPHYR_EXTRA_MODULES
${CMAKE_CURRENT_SOURCE_DIR}/../../modules/helia-rt-m55-release
)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(helia_rt_app)
target_sources(app PRIVATE src/main.cpp)
prj.conf
CONFIG_CPP=y
CONFIG_STD_CPP17=y
CONFIG_FPU=y
CONFIG_PRINTK=y
CONFIG_CONSOLE=y
CONFIG_UART_CONSOLE=y
CONFIG_HELIA_RT=y
Optional prebuilt flavor selection:
CONFIG_HELIA_RT_PREBUILT_BUILD_RELEASE=yThis is the default.CONFIG_HELIA_RT_PREBUILT_BUILD_DEBUG=yCONFIG_HELIA_RT_PREBUILT_BUILD_RELEASE_WITH_LOGS=y
Do not enable these source-module-only settings with the prebuilt bundle:
CONFIG_NS_CMSIS_NNCONFIG_HELIA_RT_BACKEND_HELIA
Notes:
- Do not add
modules/ns-cmsis-nntoZEPHYR_EXTRA_MODULESfor the prebuilt bundle. - Enable
CONFIG_FPU=ywhen using the prebuiltcm55archive. The published Cortex-M55 prebuilts use hard-float calling conventions. - The prebuilt Zephyr module also forces
TF_LITE_STATIC_MEMORYfor the application build. That is required so your app sees the sameTfLiteTensorlayout as the prebuilt archive. - The prebuilt Zephyr module supports Cortex-M55, and Cortex-M4 with FPU.
- The prebuilt archive selection is automatic from board CPU, toolchain, and selected flavor.
3. Minimal bring-up
After wiring the module and prj.conf, the smallest useful app flow is:
- embed a
.tfliteflatbuffer as a C array - map it with
tflite::GetModel() - register the ops your model uses
- allocate tensors
- write input data
- call
Invoke() - read the output tensor
The example below is intentionally small, but it is a real inference flow. It assumes:
- one embedded model in
g_model - one
int8input tensor - one
int8output tensor - a model that uses
FULLY_CONNECTED
If your model uses different operators or tensor types, change the resolver and tensor access accordingly.
src/model_data.h
src/model_data.cpp
src/main.cpp
#include <cstdint>
#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>
#include <tensorflow/lite/micro/micro_interpreter.h>
#include <tensorflow/lite/micro/micro_mutable_op_resolver.h>
#include <tensorflow/lite/schema/schema_generated.h>
#include "model_data.h"
namespace {
constexpr int kTensorArenaSize = 96 * 1024;
alignas(16) uint8_t tensor_arena[kTensorArenaSize];
int TensorElementCount(const TfLiteTensor* tensor) {
int count = 1;
for (int i = 0; i < tensor->dims->size; ++i) {
count *= tensor->dims->data[i];
}
return count;
}
} // namespace
int main() {
const tflite::Model* model = tflite::GetModel(g_model);
if (model->version() != TFLITE_SCHEMA_VERSION) {
printk("Model schema mismatch: %d != %d\n",
model->version(), TFLITE_SCHEMA_VERSION);
return 1;
}
tflite::MicroMutableOpResolver<1> resolver;
resolver.AddFullyConnected();
tflite::MicroInterpreter interpreter(
model, resolver, tensor_arena, kTensorArenaSize);
if (interpreter.AllocateTensors() != kTfLiteOk) {
printk("AllocateTensors failed\n");
return 1;
}
TfLiteTensor* input = interpreter.input(0);
TfLiteTensor* output = interpreter.output(0);
if (input == nullptr || output == nullptr) {
printk("Missing input or output tensor\n");
return 1;
}
const int input_count = TensorElementCount(input);
for (int i = 0; i < input_count; ++i) {
input->data.int8[i] = static_cast<int8_t>(input->params.zero_point);
}
if (interpreter.Invoke() != kTfLiteOk) {
printk("Invoke failed\n");
return 1;
}
const int output_count = TensorElementCount(output);
const int preview = output_count < 8 ? output_count : 8;
for (int i = 0; i < preview; ++i) {
printk("out[%d] = %d\n", i, output->data.int8[i]);
}
while (true) {
k_msleep(1000);
}
return 0;
}
4. Build
Example build for Apollo510 EVB:
5. Flash
6. View logs
If UART console is enabled, open the board serial port at 115200 8N1.
Example:
7. Checklist
Source modules + CMSIS-NN:
modules/helia-rtexistsmodules/lib/cmsis-nnexistsmodules/helia-rtis listed inZEPHYR_EXTRA_MODULESCONFIG_CMSIS_NN=yis enabledCONFIG_HELIA_RT=yis enabledCONFIG_HELIA_RT_BACKEND_CMSIS_NN=yis enabled- no HELIA-only Kconfig options are enabled
Source modules + HELIA:
modules/helia-rtexistsmodules/ns-cmsis-nnexists- both module paths are listed in
ZEPHYR_EXTRA_MODULES CONFIG_HELIA_RT=yis enabledCONFIG_NS_CMSIS_NN=yis enabledCONFIG_HELIA_RT_BACKEND_HELIA=yis enabled
Prebuilt release module:
modules/helia-rt-heliaRT-v1.10.1exists- only that path is listed in
ZEPHYR_EXTRA_MODULES CONFIG_HELIA_RT=yis enabledCONFIG_FPU=yis enabled for Cortex-M55 builds- no source-backend Kconfig options are enabled
For the broader setup guide, including Reference, open CMSIS-NN, and prebuilt flows, see Zephyr setup.