Compatibility and versioning
heliaAOT follows semantic versioning from 1.0.0. This page says which parts of heliaAOT the version number covers. A part that is not listed under What a release promises can change in any release.
The promise starts at 1.0.0. A 0.x release is not bound by it: any 0.x release can change a promised surface, and Changes before 1.0 lists the changes that affect a configuration or a program written for an earlier one.
What a release promises
Section titled “What a release promises”Within one major version, each surface below keeps working as its reference page describes it.
- Most of those pages are rendered from a record exported from the code, named in the last column, and a test fails when a committed record no longer matches what the code exports.
- Some records are partly written by hand, such as the operator restrictions, and the plan and report rule is written on a page rather than exported.
| Surface | What is promised | Described by |
|---|---|---|
| Command line | Command names, flags, accepted values and exit codes | Command line (cli.json) |
| Configuration file | Keys, accepted values and defaults, and that an unknown key, an operator attribute the operator does not take, or an invalid attribute value is an error | Configuration (config-schema.json) |
| Python API | The symbols listed in the API manifest, each at the one import path listed for it, except those it lists as provisional | Python API (repo_tools/api_manifest.json) |
| Typed errors | The HeliaAotError classes, the standard-library type each one also is, and the exit code each class declares |
Errors (errors.json) |
| Generated module | The cmake, zephyr and nsx module types and their build manifests; the names of the other files module-layout.json marks public: the integration headers <prefix>_model.h, <prefix>_context.h, <prefix>_tensors.h, <prefix>_platform.h and <prefix>_test_case.h, and <prefix>_plan.json, <prefix>_report.json and <prefix>_residency.json; the declarations it marks public, the init and run signatures and their status codes; see the generated C API |
Generated module (module-layout.json) |
| Machine-readable files | What Machine-readable files says for each one: the schema_version rule for the plan and the report, and only that the residency report is written |
Plans and reports (plan-schema.json, report-schema.json) and Generated module (residency-example.json, residency-example-placed.json) |
| Targets | Registered platform names, matched in any case | Targets (targets.json) |
| Operators | Registered operator names; see the support matrix | Operator catalog (operators.json) |
| heliaCORE floor | The repository-wide minimum ns-cmsis-nn release; see the heliaCORE floor | Version requirements (versions.json) |
The support matrix
Section titled “The support matrix”The operator catalog, its float16 and float32 dtypes included, and the target list at the 1.0.0 tag are the frozen support matrix for 1.x. Minor releases can add operators, dtypes and targets; only a major release removes one.
- The
atomiq110target is experimental and outside that matrix: its name, memories and capabilities can change in any 1.x release until a board is available. - An operator’s dtypes and restrictions in the catalog describe what it accepts, and some are transcribed by hand. A minor or patch release can tighten an operator’s checks to reject a model its kernels cannot run correctly. That is a fix, not a breaking change: such a model never produced correct output.
The Python API
Section titled “The Python API”For a promised class, the promise covers the members its documentation names: the attributes and methods its docstring describes, and the members the guides and examples use.
- Any other public member of a promised class can change in a minor release, and a name with a leading underscore is private.
- An enum’s promise covers its members’ UPPER_CASE names and their values; the values are the configuration spellings.
The ConversionResult views and the helia_aot.views records are promised
like the rest of the manifest.
| View field | Meaning |
|---|---|
operator type |
The canonical type: the name of its operator in the model file, a LiteRT builtin operator name or a custom operator’s canonical key. An operator a transform rewrote reports the type the transform generates; a depthwise convolution that DEPTHWISE_TO_CONV rewrites is CONV_2D. The vocabulary of type names is stable through 1.x; which operators a transform rewrites can change in any release. |
operator source_indices |
Its operator in the model file. The provisional AirOperatorSource on the AIR operator records that operator’s name there. |
tensor kind |
input for a model input, otherwise output for a model output, otherwise its storage: constant, persistent or scratch. |
tensor input_index, output_index |
Its positions in the module’s input and output tables. |
A 1.x release can add a field at the end of a view, or define macs and
parameters for an operator type that reports None today; neither is a
breaking change. AirOperatorSource, the record the LiteRT loader puts on
each AIR operator, is provisional.
The configuration file
Section titled “The configuration file”The configuration reference marks each setting’s stability, and
config-schema.json records it per setting. A setting marked experimental is
outside the promise; a reserved one is not implemented yet.
- The run settings
verbose,force,keep_work_dirandlog_fileare part of the configuration schema, like every other setting: they are YAML keys as well as flags, and they are promised the same way. result_viewsis promised the same way, but it is a configuration key only. It changes whatAotConverter.convert()returns, and the command line returns nothing, so it has no flag.- A configuration file has no version key. A file written for an earlier 1.x release keeps working on a later one. A file written for a later release can use a key that an earlier release rejects as unknown, with an error that names it.
- The option keys of
transforms[].optionsandmemory.planner_optionsare described in the configuration guide and in Memory planners rather than exported as a schema. Exporting their schemas later adds a record and changes no key.
Machine-readable files
Section titled “Machine-readable files”These are the JSON documents heliaAOT writes for scripts. The last column is everything a release promises about each one.
| Output | Written | schema_version |
What is promised |
|---|---|---|---|
<prefix>_plan.json |
By every conversion | 1 | The schema_version rule below; helia_aot.optimization.OptimizationPlan reads it by that rule |
<prefix>_report.json |
By every conversion | 1 | The schema_version rule below for its fields; OptimizationReport reads it by that rule. Its figures, measurements and hints can change in any release |
<prefix>_residency.json |
When memory.dump_residency_json is set |
6 | Only that it is written, with a schema_version. Its fields and values can change in any release, so check schema_version before reading it |
platforms list --json, platforms show --json |
On request | 1 | Nothing: their fields and values can change in any release |
plan-schema.json, report-schema.json |
With each release’s documentation data | None | Exactly what that release writes in the plan and the report, as JSON schemas |
The offline model report’s download links save the same plan, report and residency files.
The plan and the report follow one rule:
- Within a
schema_version, a release can add an optional field, a knob, a knob value, or a member to the values a field takes. Renaming or removing a field, or changing what one means, incrementsschema_version, and only a major release does that. - A reader therefore ignores a field it does not know and keeps a knob, value
or member it does not know.
OptimizationPlanandOptimizationReportread that way: they read a file from any release with the sameschema_version, older or newer, and reject anotherschema_versionwith an error that names both. The plan’sschedulesection is the exception for a conversion that reads the plan as input (optimization.plan): heliaAOT applies it, so it rejects a field it does not know there, naming it. A release adds derived facts outsideschedule, and an applied field outside it only with a newschema_version. plan-schema.jsonandreport-schema.jsonhold, under theirjson_schemakey, a schema that lists every field, knob, value and member of the release that exported them and allows no other field (additionalProperties: false), except in a report hint’sfacts, which is a free-form object.- Validate against that
json_schemaobject, not the whole file. - A file validates against the schema of the release that wrote it, which the
plan names in
generator.helia_aot; a newer file need not validate against an older schema.
Three values in the plan are facts about the release that wrote it, not promises:
| Value | Fact |
|---|---|
kernel.provider |
Names the planning rules behind a kernel choice. An opaque string: compare it, but do not parse it. |
kernel.candidates |
The entries that release’s rules matched to the layer. A later release can add an entry or change a rule. |
platform.capabilities |
What that release knew of the target. A later release can remove a capability from a target as well as add one. |
Exit codes
Section titled “Exit codes”The exit codes and their meanings are part of the promise. They are listed on
the command line reference, which
renders them from the command line’s own definition in cli.json and says
which failures fall under each code. A typed error ends a command with the
exit_code its class declares, which the
Errors reference lists per class.
- The command line exits 2 when your input is rejected: the command line, the configuration, a file it names (the model, the golden data, the output location), the choice of model type or format, or the target. That holds whether the check runs before conversion or after the model has loaded.
- Among the rejections that exit 2: a missing or unreadable model
(
ModelLoadError), an existing output without--force(OutputExistsError), a missing golden archive (GoldenDataNotFoundError), out-of-range or duplicatetest.state_feedbackpairs, an out-of-rangemodel.subgraph, and a float16 model on a target without FP16 support. - It exits 1 when a valid request fails while converting, such as a model construct heliaAOT cannot lower or memory that does not fit, and 130 on an interrupt.
The generated C API
Section titled “The generated C API”module-layout.json marks each declaration in a module’s includes-api/
headers, and each struct field and enum member, with one of three stabilities.
The Generated module page shows them.
| Stability | What it covers | Promise |
|---|---|---|
| public | What an application uses to run a model: <prefix>_model_init and <prefix>_model_run, the context’s callback, user_data, inputs and outputs, the status codes, the input and output tables and macros, the <PREFIX>_PRINTF, <PREFIX>_PUT_IN_* and <PREFIX>_PUT_CODE_IN_ITCM hooks, the test-case entry points and the API version. Names built from the model, such as <prefix>_input_0_size, follow its inputs and outputs. |
Removed, renamed or changed incompatibly only in a major release of heliaAOT; a minor release can add one. |
| advanced | Context initialization (<prefix>_context_init, which <prefix>_model_init already calls; calling it after model_init clears the lifecycle latch), arena binding, constant hydration and the arena tables, for an application that manages a module’s memory itself. In a module with a stream pool, the pool region <prefix>_arena_pool_<memory> with its _size and _alignment, <prefix>_bind_arena in both arena modes (<prefix>_bind_arenas with caller-bound arenas only) and <prefix>_num_transfer_units. Such a module declares no <prefix>_arena_buffers table in either arena mode: its regions are reached through the bind table, which <prefix>_bind_arena writes. Which arenas a module has, and so the region names and sizes, follow the memory plan. |
Documented and versioned, not promised: a minor release can change one, and then the API version’s major part changes and the release notes say so. |
| internal | Everything else: the per-operator _init and _run functions, the kernel descriptors in <prefix>_kernels.h, the tensor enumerators the importer names and numbers, the tensor descriptor table, and the context’s _lifecycle_state, tensor_ptrs and _arena_bases. Custom-operator code reaches tensors through some of them, under the experimental plugin contract described below. |
Can change in any release. |
The operator callback’s op argument is the operator’s layer index for the
operator states: 0 for the first operator in execution order, counting up by
one. It is the index of the operator in ConversionResult.operators, and with
module.schedule: table the callback reports the operators in that order. For
the transfer states, which exist only in modules with a stream pool, it is the
unit id.
module-layout.json also marks each file’s name:
| Files | Stability |
|---|---|
| The names listed in What a release promises | public |
The constant sidecars a caller that binds its own arenas loads, <prefix>_arena_const_<memory>__blob.bin and __source.bin |
advanced |
Every other name: each operator’s <prefix>_<operator>_<n>.c and .h, <prefix>_operators.h, <prefix>_common.h, <prefix>_kernels.*, <prefix>_activations.*, the other sources and the CMSIS-Pack description |
internal, can change in any release; include <prefix>_model.h and build with the generated manifest rather than naming them |
Header includes and placement hooks
Section titled “Header includes and placement hooks”In 1.x, including <prefix>_model.h is sufficient for the public model
initialization and execution API, its context type, and its input and output
accessors and size macros. It includes <prefix>_tensors.h and
<prefix>_context.h, and applications may rely on their public declarations
being available through <prefix>_model.h; this does not promote advanced or
internal declarations in those headers to public.
<prefix>_context.hincludes the internal<prefix>_common.h, which includes<prefix>_platform.h, standard C headers and ns-cmsis-nn headers. That internal include chain is not a 1.x promise: include standard or kernel headers directly when using their declarations, and<prefix>_platform.hdirectly when using its hooks independently of the model API. Float kernel includes remain conditional on the generated model.- The public placement hooks are
<PREFIX>_PUT_IN_<MEMORY>,<PREFIX>_PUT_IN_<MEMORY>_INITand<PREFIX>_PUT_CODE_IN_ITCM, where<MEMORY>is PSRAM, DRAM, MRAM, SRAM, DTCM or ITCM. Their names and override points are part of the 1.x promise. - Specific linker section names and the platform’s default macro expansions are not promised: the application’s linker script and platform integration own the placement. Define overrides before the generated platform header is included, consistently for every generated translation unit.
What the hooks expand to today, which is not promised across 1.x:
| Module type | Data hooks | Code hook |
|---|---|---|
nsx |
Nothing | __attribute__((section(".itcm_text"))) with GCC, Clang or Arm compiler macros, otherwise nothing |
zephyr |
ITCM data __itcm_section; DTCM data __dtcm_bss_section or __dtcm_data_section on Apollo510 and Apollo330, __itcm_section on Apollo3 and Apollo4; the MRAM hook adds const; the other data hooks are empty |
__itcm_section |
cmake and the rest |
Nothing | Nothing |
Zephyr’s section tags, and any application overrides, choose the actual linker sections.
<prefix>_status_t divides its values into ranges:
| Range | Meaning |
|---|---|
| 0 | Success. |
| 1 to 999 | The generated module’s own codes. New codes take unused values there, and existing ones are never renumbered. |
1000 to 1999, <prefix>_status_custom_first to <prefix>_status_custom_last |
Custom operators. Generated code never returns one, and model_init and model_run forward a custom operator’s code unchanged. |
| Negative | A kernel’s failure: an arm_cmsis_nn_status from ns-cmsis-nn, a status from the Ethos-U driver, or -1 from the Ethos-U wrapper when it cannot reserve the driver. |
model_initalso forwards whatever an application’s override of the weak<prefix>_hydrate_constantsreturns.- Every entry point returns
int32_t, so hold a status in anint32_t, not in a<prefix>_status_t. The enum only names the values. It has no negative enumerator, so its type is unsigned, and its size can follow its range: under-fshort-enums, the Arm EABI default, the custom range makes it two bytes.
Names and units, final for C API 1.x:
| Name | Meaning |
|---|---|
<prefix>_input_<n>_size, <prefix>_output_<n>_size, <prefix>_inputs_len, <prefix>_outputs_len |
Element counts. |
<prefix>_input_<n>_nbytes, <prefix>_output_<n>_nbytes, <prefix>_tensor_io_t.size |
Byte counts. |
<prefix>_tensor_io_t.data |
An int8_t * for every element type; cast it to the element type to read or write elements. |
<PREFIX>_API_VERSION_*, <PREFIX>_PLAN_HASH, the platform hooks |
Spelled <PREFIX>_. |
Every other name, including <prefix>_num_inputs, the I/O size macros and the arena size and alignment macros |
Spelled <prefix>_. |
<prefix>_model.h defines <PREFIX>_API_VERSION_MAJOR,
<PREFIX>_API_VERSION_MINOR and <PREFIX>_API_VERSION_PATCH, integers an
application can test with #if. They version the public and advanced
declarations, separately from heliaAOT’s own version; the Generated module
page prints the version this release emits.
| Part | Changes when |
|---|---|
| major | A public or advanced declaration is removed or renamed, or its type, signature, value or documented behaviour changes, so that code written against it can stop compiling or working. From heliaAOT 1.0.0 a public declaration changes that way only in a major release of heliaAOT. Before 1.0, a change such as 0.32.0’s meaning of the callback’s op argument is recorded under Changes before 1.0 with a minor C API bump. |
| minor | One is added: a function, macro, type, status code, enum member, or a field at the end of a struct. |
| patch | One is fixed to work as documented without its declaration changing. |
Internal declarations, the bytes of the generated C, and the names and values
the model or the memory plan decide, such as sizes, region names and
<PREFIX>_PLAN_HASH, do not move the API version, and neither do the values of
the <PREFIX>_API_VERSION_* macros themselves.
#include "aot_model.h"
#if AOT_API_VERSION_MAJOR != 1#error "written against version 1 of the heliaAOT generated C API"#endifWhat is not promised
Section titled “What is not promised”These can change in any release, including a patch release:
- The code a module is built from. Kernel choice per layer, scratch sizes, arena layout and offsets, and the bytes of the generated C. A new release can emit different C for the same model and configuration.
- Internal C declarations, the ones
module-layout.jsonmarks internal; see the generated C API. - Numerical results. A different kernel, rounding mode or approximation can change the output values a model produces.
- Performance. Latency, image size and memory use. The benchmarks are dated records, not promises.
- Report contents. The figures and hints in
<prefix>_report.json. The plan records the choices. - The floor of a given model. See the heliaCORE floor.
- Text meant for people. Error messages and hints, help text, console and log output. Match on the error class and the exit code, not on the wording.
- CMSIS-Pack output, which is for evaluation until its packs pass full
packchkvalidation. See Firmware integration. - The
atomiq110target, which is experimental until a board is available; see the support matrix. - Experimental settings. The offline model report (
documentation.html), thegreedy_by_sizememory planner, thecmsis_packvalue ofmodule.type, the stream pool (memory.stream_pool), the plan input (optimization.plan), and any other setting or value the configuration reference marks experimental. - Python names outside the API manifest, including the lower-level helpers the custom operator starter uses, and any other import path for a name the manifest lists.
- Provisional Python names. The manifest lists some names as provisional;
see Provisional names.
helia_aot.air: everything but the core graph types and the operator-key helpers.helia_aot.optimization: the knob and setting registries and the measurement records.helia_aot.memory: the planner’s output types.helia_aot.aot.operators: the custom-operator plugin surface (AotOperator,CtxBufUsage,OpCapability,register_default_aot_operators).helia_aot.platforms: the platform model and its registration API (SocPlatform,get_platform,list_platforms,list_platform_names,register_platform,resolve_platform), while the registered platform names,platforms list,platforms showand theMemoryTypeandSocCapabilitynames stay promised.helia_aot.interpreters:AirInterpreterandcreate_interpreter.helia_aot.registry:RegistryBundle.
- The custom-operator plugin surface outside the API manifest.
Custom-operator plugins are experimental in 1.x: the parser signature, the
lower-level helpers and the C contract a custom operator’s code follows can
change;
AotOperatorand its companions are provisional, and the other manifest names a plugin uses keep their promise. A stable plugin API is tracked in #829. - Kernel behavior on hosts other than Linux, and host builds of a generated module on Windows or macOS. See Supported hosts.
How the version number changes
Section titled “How the version number changes”- A major release can make a breaking change to a promised surface: remove or rename something, change a default, change what a value means, or raise the repository-wide heliaCORE floor.
- A minor release adds to a surface: a command, flag, setting, value, operator, target, error class, optional file field or public C declaration.
- A patch release fixes defects and changes nothing promised, except to make it work as documented.
Deprecated and renamed settings are marked in the configuration reference, and the release notes list each deprecation and each breaking change.
- A deprecated setting or flag is still accepted, with a warning, for at least one minor release, and is removed only in a major release.
- A renamed setting keeps its former key and flag, with a warning, until the next major release.
- A removed setting or value is an error that names its replacement.
Deprecated in 1.x and removed in 2.0:
- the
list-targetsandtarget-infocommands, deprecated since 0.31.0. They print the same output asplatforms listandplatforms show NAME, and a warning on standard error. - the
platform.speedssetting, deprecated since 0.32.0, which no conversion reads; it is accepted with a warning. An empty list, which configurations written by 0.31.0 and earlier hold, does not warn, and a configuration 0.32.0 or later writes leaves an empty one out.
The heliaCORE floor
Section titled “The heliaCORE floor”A generated module calls heliaCORE, Ambiq’s neural-network kernel library,
which is released as the ns-cmsis-nn
package. Each module needs an ns-cmsis-nn release at or above its own floor.
The module enforces it with a #error in <prefix>_common.h, records it as
generator.cmsis_nn_required in <prefix>_plan.json and in its README, and a
CMSIS-Pack names it as its dependency. Upstream Arm CMSIS-NN is not supported.
The repository-wide floor, listed on Version requirements, is the lowest any module needs, and it changes only in a major release.
- A module’s own floor can be higher, because the kernels chosen for its layers can need a newer release. Kernel choice is not promised, so the floor a given model needs can rise in a minor release.
- Read it from the module, not from the operator catalog, whose floor column covers only the float path.
Supported hosts
Section titled “Supported hosts”heliaAOT runs on Python 3.11 to 3.14, on Linux, macOS and Windows, and the
package metadata says Operating System :: OS Independent.
- The promise on every host is the tool and the handoff:
helia-aotconverts a model, and the generated module builds for its target with the pinned Arm GNU Toolchain, in the cases continuous integration covers. - Kernel behavior is verified on Linux only: the tests that build and run a generated module against heliaCORE on the host, and the runs on the fixed virtual platform, are Linux jobs. Building a generated module for the host itself on Windows or macOS is not covered. Host coverage of the kernels themselves belongs to heliaCORE.
Continuous integration backs that promise this way:
- Linux runs the test suite on each of those Python versions; TensorFlow does
not install on 3.14, so the tests that need it skip there. It also installs
the built wheel on Python 3.11 with every direct dependency at the lowest
version
pyproject.tomlallows, and converts an example with it. - Windows, on every pull request marked ready, runs the unit suite with Python 3.11 without a heliaCORE checkout, so the tests that build against heliaCORE skip there, and cross-compiles the strict-compile cases with the Windows release of the pinned Arm GNU Toolchain against the released heliaCORE headers: the path from a conversion on Windows to a firmware build.
- macOS runs the same cross-compile for each release and nightly on
main, not on pull requests. - Each release installs the wheel and runs
helia-aot --helpon macOS and Windows, and builds a standalone binary on each of the three, which runsversion,--helpand one conversion with a generated test case on its own operating system before it is published.
A redirected stream on Windows uses the ANSI
code page, which lacks glyphs that the results output draws with; some code
pages, such as Japanese cp932, also lack glyphs in the help. Such a glyph
prints as ? on standard output, and as Python’s escape, such as \u2713, on
standard error, which carries progress, logs and errors.
The PyPI package is the supported install. The standalone Linux, macOS and
Windows binaries are convenience builds of the same release, smoke-tested by
build-binaries.yml and verified on publish; they are outside the 1.x
promise.
The modules heliaAOT generates are licensed for Ambiq CPUs only; see Support, licence and contributing. Which compilers and build systems have been used to build them is on Toolchains and build environments.
Changes before 1.0
Section titled “Changes before 1.0”The 0.x releases that changed a configuration, a program or a generated module are listed on Changes before 1.0.