Documentation · Build and contribute to the site
Ambiq's optimized neural-network kernel library for Ambiq silicon (package:
ns-cmsis-nn), built on the Arm CMSIS-NN and CMSIS-Pack ecosystem.
heliaCORE provides quantized neural-network kernels for Ambiq Apollo-class
Cortex-M DSP/MVE targets. It preserves CMSIS-NN-compatible APIs where that
surface applies, adds inherited CMSIS-NN float32 and float16
APIs, and adds Ambiq-tuned operators, kernel variants, and integration paths
for HELIA AI workflows.
Highlights
- CMSIS-NN-compatible public APIs where inherited and supported, with additional Ambiq/HELIA kernels for Ambiq silicon.
- int8 / int16 / int4-weight quantized kernels for Conv, Depthwise Conv, Transpose Conv, Fully Connected, LSTM, SVDF, Pooling, Softmax, elementwise math and more.
float32andfloat16CMSIS-NN APIs are available for selected operators when explicitly enabled.- Three backend paths selected automatically at build time from your toolchain CPU flags: pure C, DSP, and MVE / Helium. MVE is a primary optimization target where supported by the Ambiq device.
- Distributed as a CMSIS-Pack, a Zephyr module, CMake package/tarball, and HELIA/neuralSPOT-X integration point.
- No dynamic allocation; the caller owns every buffer.
- Apache-2.0 on upstream-derived files; Ambiq Apollo SDK License on Ambiq additions (see License).
Badges track main. Actively developed and released. Every change merges only through the
required CI Passed check: kernel numerics verified on the Corstone-300
FVP across cortex-m0/m4/m55 (the m4/m55 legs also at the shipped -Ofast build), a
five-toolchain build-and-link matrix, host sanitizers, and the packaging
contracts. Each release ships a CMSIS-Pack, per-core SDK tarballs and
static-library bundles, all verified after publication. See
Testing & verification below for the summary and
the Testing & Verification guide
for the full contract.
- Ambiq's foundation neural-network kernel layer for HELIA AI workflows on Ambiq silicon.
- Built on Arm CMSIS-NN concepts and compatible API surfaces where applicable, with attribution and license preservation for inherited sources.
- Optimized for Ambiq Apollo SoCs, taking advantage of M-Profile Vector Extensions (MVE / Helium) and DSP instructions where available.
- A kernel backend used by runtime and compiler flows on Ambiq parts. It follows the int8 and int16 quantization specifications used by TFLM.
- Not a vendor-neutral CMSIS-NN distribution. heliaCORE is intended for HELIA AI workflows on Ambiq silicon. For general Cortex-M kernel work, use upstream Arm CMSIS-NN.
- Not a model runtime. Pair it with TFLM (or your own runtime) to execute models.
heliaCORE ships under two license files:
| File | Applies to | SPDX |
|---|---|---|
LICENSE |
The fork as a whole — restricted to use on Ambiq CPUs | LicenseRef-Ambiq-Apollo-SDK |
LICENSES/Apache-2.0.txt |
Files originating from upstream Arm CMSIS-NN | Apache-2.0 |
Per-file copyright and license is declared via SPDX headers. The upstream CMSIS-NN copyright and Apache-2.0 license terms are preserved on every file that originated upstream; modifications are dual-attributed to Ambiq.
See NOTICE for the full attribution and licensing summary.
heliaCORE was forked from ARM-software/CMSIS-NN and is maintained
as an Ambiq silicon-focused kernel library. We:
- Preserve inherited CMSIS-NN-compatible public APIs where supported so existing
integrations can continue to use familiar
arm_*entry points. - Preserve upstream copyright, SPDX, and Apache-2.0 license notices on inherited files.
- Periodically review upstream for relevant kernels and improvements.
- Add Ambiq-specific kernels, variants, and Apollo optimizations for HELIA AI workflows.
If you find a kernel here that does not exist in upstream, that is intentional and part of Ambiq's HELIA-focused coverage for Ambiq devices.
Because heliaCORE ships as libcmsis-nn.a and exposes a cmsis-nn CMake
ALIAS for TFLM build compatibility, downstream code that calls Ambiq-only
kernels (e.g. arm_gather_s8) should guard against accidentally being built
against another CMSIS-NN implementation.
Include/arm_nn_types.h defines:
| Macro | Meaning |
|---|---|
NS_CMSIS_NN |
Always 1 when this header is from heliaCORE NN. |
NS_CMSIS_NN_VERSION_MAJOR / MINOR / PATCH |
Component version numbers. |
NS_CMSIS_NN_VERSION |
Packed as MAJOR * 1000000 + MINOR * 1000 + PATCH (3-digit fields, so semantic ordering is preserved for any reasonable version). |
Recommended guard:
#include "arm_nn_types.h"
#if !defined(NS_CMSIS_NN)
# error "this code requires ns-cmsis-nn"
#endif
#if NS_CMSIS_NN_VERSION < 7024000
# error "needs ns-cmsis-nn >= 7.24.0"
#endif| Name | Used for |
|---|---|
| heliaCORE | Product / brand name (prose, docs, marketing) |
ns-cmsis-nn |
Package id, repo name, CMake/Zephyr module name, file/path identifiers |
arm_* C symbols |
Public API — kept identical to upstream |
ns-cmsis-nn/
├── Include/ Public headers (C API)
│ ├── arm_nnfunctions.h Top-level kernel API
│ ├── arm_nnsupportfunctions.h Helpers shared between kernels
│ ├── arm_nn_types.h Shared types (dims, params, context)
│ ├── arm_nn_math_types.h Quantization / fixed-point types
│ ├── arm_nn_tables.h Lookup tables (e.g. for activations)
│ └── Internal/ Compiler / intrinsics compatibility shim
├── Source/ Kernel implementations (one .c per function)
│ ├── ActivationFunctions/
│ ├── BasicMathFunctions/
│ ├── ComparisonFunctions/
│ ├── ConcatenationFunctions/
│ ├── ConvolutionFunctions/
│ ├── FullyConnectedFunctions/
│ ├── GatherFunctions/
│ ├── LSTMFunctions/
│ ├── NNSupportFunctions/ Matmul kernels, requantize, ...
│ ├── PadFunctions/
│ ├── PoolingFunctions/
│ ├── QuantizationFunctions/
│ ├── ReshapeFunctions/
│ ├── SoftmaxFunctions/
│ ├── StridedSliceFunctions/
│ ├── SVDFunctions/
│ └── TransposeFunctions/
├── Tests/UnitTest/ Per-kernel unit tests (Unity + Python harness)
├── Documentation/ Doxygen sources + pre-built HTML
├── Examples/ Sample integrations
├── zephyr/ Zephyr module manifest + Kconfig + CMake glue
├── Ambiq.NS-CMSIS-NN.pdsc CMSIS-Pack description
├── CMakeLists.txt Top-level CMake (defines `cmsis-nn` static lib)
├── LICENSE Ambiq Apollo SDK License
├── LICENSES/ SPDX-referenced license texts (Apache-2.0, Ambiq)
└── NOTICE Attribution + dual-license summary
Optimizations are picked at compile time based on the architecture features the compiler reports.
| Backend | When it is used |
|---|---|
| Pure C | Always available; used on Cortex-M0 / M3 (no DSP, no MVE) |
| DSP | Cortex-M4, M7, M33 (with DSP extension) — uses SIMD intrinsics |
| MVE | Cortex-M55, M85 — uses Arm Helium / M-Profile Vector Extension |
heliaCORE NN includes the CMSIS-NN float32 and float16 APIs
inherited from upstream. They are disabled by default so integer-only builds do
not pay for extra code size or public API surface. Enable them only for
applications that strictly need floating-point kernels.
The floating-point API follows the same CMSIS-NN style as the integer API,
including the TFLM-shaped parameter structures. This keeps the public surface
consistent across data types. float16 APIs are included even though TFLM does
not define a float16 operator contract; they are intended for frameworks that
can carry float16 operator flows, such as ExecuTorch.
Floating-point support primarily targets Cortex-M CPUs with Helium/MVE and hardware floating-point support. Pure C scalar paths are present for correctness, bring-up, and fallback, but they are not the main performance target. On cores that only provide the classic DSP extension, float kernels may compile through the scalar C path but are not a performance target.
Cortex-M0 has no hardware floating point, so float32 there runs through the
pure C scalar path (soft-float). It is supported: the tester's float suite
runs it on the Corstone-300 FVP on every pull request, and the legacy Unity
float suites build and run it nightly and at release. float16 support
remains Cortex-M55 only.
float16_t is IEEE 754 binary16. The Arm alternative half-precision format
(GCC -mfp16-format=alternative) is not supported and is rejected at compile
time. A GCC source build that enables float16 without FP16 arithmetic (for
example Cortex-M4, or Cortex-M55 with -mfloat-abi=soft or +nofp) must pass
-mfp16-format=ieee; such a build compiles, but float16 is released and
CI-qualified only on Cortex-M55 with MVE.
No Ambiq product ships Cortex-M55 without MVE, and float16 without MVE is not
an expected configuration. As a safeguard for experimental or misconfigured
builds, the headers reject a GCC float16 build for Armv8.1-M without MVE (for
example cortex-m55+nomve) on GCC before 15.3: Arm GNU Toolchain 13.2 to 15.2
emit half-precision loads and stores there that are undefined on M-profile. With
GCC 15.3 or a Clang-based compiler such a build compiles, but it is not
runtime-qualified.
For float operators that support arm_nn_weight_format_flt, MVE performance is
generally better when constant weights are provided in the packed NTxN layout
instead of the standard NT x T layout. This avoids the gather-heavy RHS access
pattern of the standard formulation and is the preferred deployment format when
offline repacking is available.
The scalar floating-point code can also be compiled for Arm A-class CPUs with
float16 support and may benefit from NEON or SVE auto-vectorization. That is
not an intended deployment target for heliaCORE NN float support, and the
resulting performance is expected to be suboptimal compared with libraries
designed for that class of processor. For Arm A-class CPUs, prefer optimized
inference libraries such as Arm Compute Library or XNNPACK.
Yes = available; No = not implemented; N/A = does not apply to that dtype.
* int4 = int4 weights with int8 activations.
| Operator | C int8 | C int16 | C int4* | DSP int8 | DSP int16 | DSP int4* | MVE int8 | MVE int16 | MVE int4* |
|---|---|---|---|---|---|---|---|---|---|
| Conv2D | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
| DepthwiseConv2D | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
| TransposeConv2D | Yes | Yes | No | Yes | No | No | Yes | No | No |
| Fully Connected | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
| Batch MatMul | Yes | Yes | No | Yes | Yes | No | Yes | Yes | No |
| Add / Sub | Yes | Yes | N/A | Yes | Yes | N/A | Yes | Yes | N/A |
| Mul | Yes | Yes | N/A | Yes | Yes | N/A | Yes | Yes | N/A |
| Minimum / Maximum | Yes | Yes | N/A | Yes | Yes | N/A | Yes | Yes | N/A |
| Abs | Yes | Yes | N/A | Yes | Yes | N/A | Yes | Yes | N/A |
| Squared Difference | Yes | Yes | N/A | Yes | Yes | N/A | Yes | Yes | N/A |
| Sqrt / Rsqrt | Yes | Yes | N/A | Yes | Yes | N/A | Yes | Yes | N/A |
| Mean | Yes | Yes | N/A | Yes | Yes | N/A | Yes | Yes | N/A |
| Reduce Min / Reduce Max | Yes | Yes | N/A | Yes | Yes | N/A | Yes | Yes | N/A |
| ArgMin / ArgMax | Yes | Yes | N/A | Yes | Yes | N/A | Yes | Yes | N/A |
| Comparison (==, !=, <, >, ...) | Yes | Yes | N/A | Yes | Yes | N/A | Yes | Yes | N/A |
| ReLU / ReLU6 | Yes | Yes | N/A | Yes | Yes | N/A | Yes | Yes | N/A |
| Leaky ReLU | Yes | Yes | N/A | Yes | Yes | N/A | Yes | Yes | N/A |
| PReLU | Yes | Yes | N/A | Yes | Yes | N/A | Yes | Yes | N/A |
| Hard-Swish | Yes | Yes | N/A | Yes | Yes | N/A | Yes | Yes | N/A |
| Logistic | Yes | Yes | N/A | Yes | Yes | N/A | Yes | Yes | N/A |
| Tanh | Yes | Yes | N/A | Yes | Yes | N/A | Yes | Yes | N/A |
| Clamp | Yes | Yes | N/A | Yes | Yes | N/A | Yes | Yes | N/A |
| Softmax | Yes | Yes | N/A | Yes | Yes | N/A | Yes | Yes | N/A |
| MaxPool / AvgPool | Yes | Yes | N/A | Yes | Yes | N/A | Yes | Yes | N/A |
| Pad | Yes | Yes | N/A | Yes | Yes | N/A | Yes | Yes | N/A |
| Transpose | Yes | Yes | N/A | Yes | Yes | N/A | Yes | Yes | N/A |
| Reshape | Yes | N/A | N/A | Yes | N/A | N/A | Yes | N/A | N/A |
| Concatenation | Yes | Yes | N/A | Yes | Yes | N/A | Yes | Yes | N/A |
| Split | Yes | Yes | N/A | Yes | Yes | N/A | Yes | Yes | N/A |
| StridedSlice | Yes | Yes | N/A | Yes | Yes | N/A | Yes | Yes | N/A |
| Gather / GatherND | Yes | Yes | N/A | Yes | Yes | N/A | Yes | Yes | N/A |
| Quantize / Dequantize | Yes | Yes | N/A | Yes | Yes | N/A | Yes | Yes | N/A |
| Resize Nearest Neighbor | Yes | Yes | N/A | Yes | Yes | N/A | Yes | Yes | N/A |
| Space-to-Batch / Batch-to-Space | Yes | Yes | N/A | Yes | Yes | N/A | Yes | Yes | N/A |
| Space-to-Depth / Depth-to-Space | Yes | Yes | N/A | Yes | Yes | N/A | Yes | Yes | N/A |
| LSTM (unidirectional) | Yes | Yes | No | Yes | Yes | No | Yes | Yes | No |
| SVDF | Yes | No | No | Yes | No | No | Yes | No | No |
Coverage above reflects what is shipped in
Source/today. For exact dtype support per kernel, see the function prototypes inInclude/arm_nnfunctions.h.
The table below summarizes public float16 / float32 operator coverage. The
MVE column means the implementation contains explicit Helium-specialized paths
for at least the supported common cases; scalar C fallback remains available
unless the target or toolchain cannot provide the required floating-point type.
| Operator | C float16 | C float32 | MVE float16 | MVE float32 |
|---|---|---|---|---|
| Conv2D | Yes | Yes | Yes | Yes |
| DepthwiseConv2D | Yes | Yes | Yes | Yes |
| TransposeConv2D | Yes | Yes | Yes | Yes |
| Fully Connected | Yes | Yes | Yes | Yes |
| Batch MatMul | Yes | Yes | Yes | Yes |
| Activation | Yes | Yes | Yes | Yes |
| Add | Yes | Yes | Yes | Yes |
| Minimum / Maximum | Yes | Yes | Yes | Yes |
| Mul | Yes | Yes | Yes | Yes |
| MaxPool / AvgPool | Yes | Yes | Yes | Yes |
| Softmax | Yes | Yes | Yes | Yes |
| LSTM (unidirectional) | Yes | Yes | Yes | Yes |
| GRU (unidirectional) | Yes | Yes | Yes | Yes |
| SVDF | Yes | Yes | Yes | Yes |
| Batch Norm | Yes | Yes | Yes | Yes |
| Sub | Yes | Yes | Yes | Yes |
| Abs | Yes | Yes | Yes | Yes |
| PReLU | Yes | Yes | Yes | Yes |
| Reduce Sum | Yes | Yes | Yes | Yes |
| Pad | Yes | Yes | No | No |
| Transpose | Yes | Yes | Yes | Yes |
| Reshape | Yes | Yes | No | No |
| Concatenation | Yes | Yes | No | No |
| StridedSlice | Yes | Yes | No | No |
| Split | Yes | No | No | No |
| Dequantize | N/A | Yes | N/A | Yes |
-
Data layout. Activations are laid out NHWC (batch, height, width, channel). Weight layouts are kernel-specific and documented per function; most convolutions use HWIO with per-channel scaling.
-
Quantization. TFLM-style affine quantization. int8 or int16 activations, int8 (or int4-packed) weights, int32 bias. Conv-family kernels accept per-channel weight scales; fully-connected accepts per-tensor or per-channel depending on the variant. The exact scheme each kernel implements matches the TFLM int8 quantization spec.
-
Buffer convention. Every kernel takes a
cmsis_nn_contextwhosebufmust be sized via the matchingarm_*_get_buffer_size*query. If the query returns 0, you may pass{ NULL, 0 }— except for anyarm_svdf_*_ctx_get_buffer_sizequery (all eight of them, integer and float alike), where a 0 means a degenerate shape but the kernel still rejects a NULLbufwithARM_CMSIS_NN_ARG_ERROR. For the s8 and s16 integer sizers, a negative return (-1) means the dimensions are out of range — the required size does not fit in anint32_t, or a dimension is negative — and must never be used to size a buffer. The s4 convolution and depthwise sizers follow the same-1contract. One group does not fully follow that rule and needs the caller to range-check the shape itself:- most f32/f16 sizers (
arm_convolve_f32_get_buffer_sizeand siblings) report a size that does not fit in anint32_tas0, which is indistinguishable from "no buffer needed". Note this is a property of the individual sizer, not of the datatype:arm_svdf_f32_input_ctx_get_buffer_size,arm_svdf_f32_output_ctx_get_buffer_size,arm_svdf_f16_input_ctx_get_buffer_sizeandarm_svdf_f16_output_ctx_get_buffer_sizeare f32/f16 sizers that use-1, because their kernels readctx->sizeandsize == 0opts out of the scratch-size check. The LSTM/GRU temp-buffer queries (arm_lstm_unidirectional_*_temp1/temp2_get_buffer_size,arm_gru_unidirectional_f32/f16_temp1_get_buffer_size) all treat a negative return as an error, but what each range-checks follows its kernel: the s8/s16 and GRU queries fold dimensions and answer-1for negatives or overflow, while the f32/f16 LSTM queries have no dimensions to check (the buffers are unused) and answer-1only for NULL params,0otherwise. A generic float wrapper must branch per sizer, not on the datatype.
Every public
*_get_buffer_size_mveand*_get_buffer_size_dspvariant answers an out-of-range shape the same way as the dispatcher it belongs to —-1wherever that dispatcher diagnoses one — so a caller that tests only one leg is never told less than the dispatcher would say.Within the s4/s8/s16 family, a route that needs no scratch buffer returns 0 for an in-range shape, but any route — including one that needs no buffer — may return
-1when a dimension it inspects is negative or a product overflows, and which dimensions a route inspects is build-dependent. Always test for-1before using the value, and never read a 0 as a statement that the shape is valid — see the wrapper sizers'@returndocs inInclude/arm_nnfunctions.h.Sizing is not always sufficient: several kernels read a
cmsis_nn_contextas a precomputed input rather than as scratch — the int8 fully-connected family (arm_fully_connected_s8,arm_fully_connected_per_channel_s8and their wrapper),arm_svdf_s8(), and theweight_sum_ctxparameter of the int8 convolution and depthwise wrappers. Where the sizing query returns a non-zero size for these, the caller must also fill the buffer; passing a correctly sized but zeroed buffer produces wrong output while still returningARM_CMSIS_NN_SUCCESS. See each function'sctxdocumentation inInclude/arm_nnfunctions.hfor the exact contract. - most f32/f16 sizers (
-
Backend selection. Compile-time, driven by preprocessor defines:
ARM_MATH_MVEI→ Helium / MVE pathARM_MATH_DSP(without MVE) → DSP intrinsics path- neither → pure C
The CMake build sets these automatically from your toolchain CPU flags.
-
No dynamic allocation. Kernels never call
malloc/free. The caller owns all input, output, weight, bias and scratch buffers. -
Threading. Kernels are reentrant on disjoint buffers; there is no internal global state to protect.
heliaCORE NN can be consumed three ways. Pick whichever fits your build system.
The most common consumer of these kernels is TFLM. Because heliaCORE NN keeps
the upstream arm_* C ABI, TFLM's existing CMSIS-NN integration links against
it unchanged — point your build at this repository instead of
ARM-software/CMSIS-NN and rebuild. On Ambiq parts this is wired up for you
by the Ambiq SDK; you generally do not need to integrate it by hand.
The pack is published as a release asset on this repository:
Ambiq.NS-CMSIS-NN.<version>.pack.
Install via cpackget:
cpackget add Ambiq.NS-CMSIS-NN.<version>.packThen add the CMSIS:NN Lib component in your *.csolution.yml /
*.cproject.yml.
Add to your west.yml:
manifest:
projects:
- name: ns-cmsis-nn
url: https://github.com/AmbiqAI/ns-cmsis-nn
revision: main
path: modules/lib/ns-cmsis-nnThen enable in your project's prj.conf:
CONFIG_NS_CMSIS_NN=y
CONFIG_NS_CMSIS_NN_ALL=y # or pick individual kernel groups
Per-group CONFIG_NS_CMSIS_NN_* symbols are defined in
zephyr/Kconfig. The Zephyr module is gated by
CONFIG_CPU_CORTEX_M and is mutually exclusive with upstream CMSIS_NN.
add_subdirectory(third_party/ns-cmsis-nn)
target_link_libraries(my_app PRIVATE cmsis-nn)The CMake build picks up the right backend (C / DSP / MVE) from your
toolchain's CPU flags. See
CMakeLists.txt for the build target definition, and
Documentation/build.md for a full reference
of the SSoT CMake module — including how to opt in/out of operator
groups and how to add a new one.
Minimal example — a quantized 64→10 fully-connected layer:
#include "arm_nnfunctions.h"
static const int8_t weights[10 * 64]; /* trained, int8 */
static const int32_t bias[10]; /* int32 */
static int8_t output[10];
arm_cmsis_nn_status run_fc(const int8_t *input)
{
cmsis_nn_dims input_dims = { .n = 1, .h = 1, .w = 1, .c = 64 };
cmsis_nn_dims filter_dims = { .n = 10, .h = 1, .w = 1, .c = 64 };
cmsis_nn_dims bias_dims = { .n = 1, .h = 1, .w = 1, .c = 10 };
cmsis_nn_dims output_dims = { .n = 1, .h = 1, .w = 1, .c = 10 };
cmsis_nn_fc_params fc_params = {
.input_offset = 128, /* zero-points from your quantizer */
.filter_offset = 0,
.output_offset = -2,
.activation = { .min = INT8_MIN, .max = INT8_MAX },
};
cmsis_nn_per_tensor_quant_params q = {
.multiplier = 1073741824, /* M0 from your quantizer */
.shift = -7, /* shift from your quantizer */
};
int32_t buf_sz = arm_fully_connected_s8_get_buffer_size(&filter_dims);
if (buf_sz < 0) /* -1 => dims out of range; never size a buffer from it */
{
return ARM_CMSIS_NN_ARG_ERROR;
}
int8_t scratch[buf_sz]; /* or pool / static buffer */
cmsis_nn_context ctx = { .buf = scratch, .size = buf_sz };
return arm_fully_connected_s8(&ctx, &fc_params, &q,
&input_dims, input,
&filter_dims, weights,
&bias_dims, bias,
&output_dims, output);
}In practice, you don't write this by hand — TFLM (or another runtime) emits the parameter structs from the quantized model. The example shows the call shape every kernel in the library follows.
The repo ships a devcontainer with all required tooling
(see .devcontainer/). To build the library standalone:
mkdir build && cd build
cmake .. -DCMAKE_TOOLCHAIN_FILE=<your-toolchain.cmake> -DTARGET_CPU=cortex-m55
makeDefault optimization is -Ofast. Override with -DCMSIS_OPTIMIZATION_LEVEL=-O2.
Note that with -O0, you must define ARM_MATH_AUTOVECTORIZE for Helium parts.
Compile-time options that affect headers (set the same flag in TFLM):
| Option | Effect |
|---|---|
ARM_NN_ENABLE_F32 |
Enables float32 operator support. Leave disabled unless the application needs float32 kernels. |
ARM_NN_ENABLE_F16 |
Enables float16 operator support. Leave disabled unless the application needs float16 kernels and the toolchain/target support them. |
CMSIS_NN_USE_SINGLE_ROUNDING |
Use single instead of double rounding in requantization. May change outputs. |
CMSIS_NN_USE_REQUANTIZE_INLINE_ASSEMBLY |
Inline assembly for arm_nn_requantize. Faster on Cortex-M4, slower elsewhere. |
OPTIONAL_RESTRICT_KEYWORD=__restrict |
Enables restrict on int4/int8 conv outputs. Recommended on Cortex-M7. |
Additional implementation-selection options:
| Option | Effect |
|---|---|
NN_DISABLE_SPECIALIZATION |
Disables optional shape/layout-specific fast paths and forces the corresponding generic implementations. Useful for debugging or validating specialized kernels against generic paths. |
ARM_NN_USE_EXP_LUT |
Selects the LUT-based scalar float softmax exp approximation. This is the default if no scalar float softmax exp macro is defined. |
ARM_NN_WORD_COPY |
Makes arm_memcpy_s8/_s16/_s32/_q15, arm_memset_s8 and arm_memset_s16 copy and fill a word at a time instead of calling the C library (MVE builds keep their vector loops, except arm_memcpy_q15, which has none). Defined by default for clang (not armclang) with unaligned access on armv7-m, armv7e-m and armv8-m.main, where ATfE 22.1's C library moves one byte at a time. Requires unaligned word access. Without MVE it also makes arm_convolve_s8 fill the im2col taps of a filter depth of 1 (4 taps or more) with plain stores. |
ARM_NN_USE_EXP_TAYLOR |
Selects the Taylor/Estrin scalar float softmax exp approximation to avoid the extra lookup-table storage. Do not define this with ARM_NN_USE_EXP_LUT. |
ARM_NN_SKIP_GAS_F16_PROBE |
A CMake configure option, not a compile-time macro: pass -DARM_NN_SKIP_GAS_F16_PROBE=ON to cmake, defining it in your own compile flags does nothing. Default OFF. The MVE encoding probe is a hard error only when it cannot compile its witness with your flags at all; this downgrades that to a warning. The build then measures nothing and picks the conversion form from the compiler major instead, which is right for every Arm GNU release as shipped. See toolchains.md. |
Unit tests live in Tests/UnitTest/. See the
Tests README for the full workflow.
Every family below is built and strict-linked on every pull request, on
cortex-m55 (MVE + DSP + float) and cortex-m4 (DSP, no MVE). A strict link
resolves every object in the archive — no --gc-sections, no ignored
unresolved symbols — so a kernel that compiles but cannot link fails the gate.
| Toolchain | Version(s) gated per PR | Built | Linked | Functional tests |
|---|---|---|---|---|
Arm GNU Toolchain (arm-none-eabi-gcc) |
13.2.Rel1, 14.2.Rel1, 15.3.Rel1 | integer, float32 and, on cortex-m55, float16 on all three |
yes | partly. The numerics suite runs on 14.3.1, the CI container's toolchain, which this matrix does not gate; the float16 conversion suites run on 13.2.Rel1, the floor, which it does gate. Nothing executes on 14.2.Rel1 or 15.3.Rel1 |
Arm Compiler 6 (armclang) |
6.23.32 | yes | yes | no |
| LLVM Embedded Toolchain for Arm (ATfE) | 19.1.5 | yes | yes | no |
- Arm GNU Toolchain: GCC 13 through 15, one floor for everything:
13.2.Rel1, for the integer,
float32andfloat16kernels alike. One pinned release per major is built and strict-linked on every pull request. Versions below 13 are not supported and are not tested.- The
float16kernels convert between half and single precision, and assemblers before binutils 2.43 (Arm GNU 13.3.Rel1 and older) mis-encode the vector form of those conversions. The library does not use that form where it would be wrong: the wrappers inInclude/Internal/arm_nn_vcvt_f16.hfall back to scalar-form conversions, which every assembler encodes identically, and the CMake build picks between the two by compiling a witness at configure time with the flags the library target really compiles with. From 14.2.Rel1 up the vector form is used. A GCC 13.x compiler can have it too by borrowing a newer assembler with-B; see Toolchain pinning for that recipe, for what a build outside CMake gets, and for theARM_NN_SKIP_GAS_F16_PROBEoption (#427).
- The
- Arm Compiler 6 — built and strict-linked, not functionally tested. Until recently its release-asset check never invoked a linker at all, so armclang archives shipped without their symbols ever being resolved (#291); it now gets the same real bare-metal link as the others.
- LLVM Embedded Toolchain for Arm (ATfE) — built and strict-linked, not functionally tested.
The numerics suite (helia-core-tester, run under the Corstone-300 FVP)
executes only against GCC 14.3.1, the toolchain pinned in the CI container. A
separate leg runs the legacy Unity float16 suites on the floor
release under QEMU, because that is a class of defect a
build-and-link gate cannot see
(#427). So for armclang
and ATfE the guarantee is built and linked, not executed: they are
verified to compile and resolve, not to produce correct results. Kernel
logic is shared across all three, so the functional suite is not multiplied
across toolchains.
IAR is currently untested. Compiling for host is not supported out of the box.
Every pull request must pass the FVP numerics suite (int4/int8/int16 on
cortex-m0/m4/m55, float32/float16 where hardware support exists — the
m4/m55 legs also at the shipped -Ofast flags), a strict build-and-link
matrix across GCC 13/14/15, ATfE and armclang, the float16 conversion
suites executed on the float16 toolchain floor, the x86 host suites under
ASan/UBSan/LSan, and the packaging contracts. A nightly run adds the
legacy Unity suites on Arm, and every release re-runs the FVP numerics
and Unity suites and verifies its published assets.
cortex-m4 and cortex-m55 are the shipping targets; cortex-m0 is held to the same functional bar as a scalar baseline. The Corstone-300 FVP is the qualification vehicle — an instruction-accurate cortex-m55 model on which the m0/m4 images also execute — with EVB regression on Apollo parts as planned follow-on work. Known limits are tracked openly: armclang/ATfE are built and linked but not yet executed (#340), sanitizers cover the scalar paths only (helia-core-tester#68), and coverage is gated on a floor and no-regression per merged run, with per-kernel set-membership gates still open (helia-core-tester#73).
The full contract — per-leg matrices, the qualification model, coverage retrieval, release-asset inventory — is in the Testing & Verification guide. For the job-by-job lookup table, including which legs only build and what nothing executes at all, see the CI Matrix.
- Semantic versioning (
MAJOR.MINOR.PATCH). - Releases are cut from
mainby release-please driven by Conventional Commits. - Each release publishes:
- A GitHub Release with a CMSIS-Pack asset:
Ambiq.NS-CMSIS-NN.<version>.pack. - A new entry in
CHANGELOG.md. - (Optionally) refreshed Doxygen docs.
- A GitHub Release with a CMSIS-Pack asset:
The documentation site — guides plus the Doxygen-generated API reference —
is published at https://ambiqai.github.io/ns-cmsis-nn/ on every push to
main. To build it locally:
./Documentation/Doxygen/gen_doc.shOutput lands in Documentation/html/.
Public-launch readiness is tracked under the
GitHub milestones.
Issues blocking the public release carry the
public-launch
label.
Why not just use upstream Arm CMSIS-NN? Upstream Arm CMSIS-NN is the right choice for vendor-neutral Cortex-M work. Use heliaCORE when you are building HELIA AI workflows for Ambiq silicon and need Apollo-tuned kernels or Ambiq-specific operator coverage.
Are the arm_* symbols ABI-stable across heliaCORE NN versions?
Inherited symbols and signatures are kept compatible where supported so TFLM
and other consumers can continue to link through familiar arm_* entry points.
How do I enable the float APIs?
Enable ARM_NN_ENABLE_F32 and/or ARM_NN_ENABLE_F16 in the build. They are
off by default to keep integer-only builds small. If the enabled float headers
are consumed by TFLM or another downstream build, use matching definitions in
that build too.
The switches carry one name per entry path:
| Entry point | Set this |
|---|---|
| Standalone CMake | ARM_NN_ENABLE_F32 / ARM_NN_ENABLE_F16 |
NSX (nsx/CMakeLists.txt) |
ARM_NN_ENABLE_F32 / ARM_NN_ENABLE_F16, the same two options |
| Zephyr | CONFIG_NS_CMSIS_NN_ENABLE_F32 / _F16, the only writers of ARM_NN_ENABLE_* there |
find_package(ns-cmsis-nn) |
nothing; the archive's capabilities decide |
To learn what the library in scope was actually built with, call
ns_cmsis_nn_float_support(F32 <var> F16 <var>) rather than reading a cache
variable. It reads the library target, so the same call works in a source
build, against a prebuilt archive, and after find_package().
NSX_CMSIS_NN_ENABLE_F32/_F16 are removed; setting either is a configure
error that names every stale switch in the build and the recovery for all of
them at once:
$ cmake -S . -B build \
-UNSX_CMSIS_NN_ENABLE_F32 -UNSX_CMSIS_NN_ENABLE_F16 \
-DARM_NN_ENABLE_F16=ONDo floating-point kernels target all IEEE edge cases?
No. For performance reasons, the current floating-point kernels do not
specifically target IEEE edge cases such as NaN, Inf,
denormals/subnormals, or signed zero. The intended use is that pre-processing
provides finite, numerically safe input data and that model weights and biases
do not contain aberrant values.
What is the scalar float softmax storage cost?
The scalar floating-point softmax path uses the LUT-based exp approximation by
default for performance. This adds one 257-entry lookup table per enabled float
precision: about 514 bytes for float16 and about 1028 bytes for float32.
Define ARM_NN_USE_EXP_TAYLOR to avoid the lookup-table storage. Do not define
ARM_NN_USE_EXP_LUT and ARM_NN_USE_EXP_TAYLOR at the same time.
Can I build the library for the host (x86 / Mac) for testing?
Not directly — the kernels target Cortex-M. The Tests/UnitTest/ harness
builds for Cortex-M and runs the binaries under emulation.
How do I report a bug or request a kernel? Open an issue at https://github.com/AmbiqAI/ns-cmsis-nn/issues. Please include the target core (M4 / M55 / …), the toolchain, and a minimal reproducer if you can.
External contributions are welcome on additive kernels and bug fixes that do
not break inherited CMSIS-NN-compatible APIs or Ambiq HELIA integration paths. A
CONTRIBUTING.md with the full workflow is in progress (tracked separately).
In the meantime, the short version:
- One function per file; file name matches the function name.
- Variable and function names: lowercase with underscores.
- Add a Doxygen header to every public prototype.
- Every new kernel ships with a unit test under
Tests/UnitTest/.
Issues and PRs go to
AmbiqAI/ns-cmsis-nn.
For bug reports and questions, please open an issue: https://github.com/AmbiqAI/ns-cmsis-nn/issues.
For commercial / Apollo-platform support, contact Ambiq through your usual support channel.
heliaCORE NN is built on top of Arm CMSIS-NN (Apache-2.0). We are grateful to the Arm CMSIS-NN team for the foundational kernel work and to the TFLM community for the quantization specifications this library implements.