Metadata-Version: 2.4
Name: helia-profiler
Version: 0.2.2
Summary: Profile LiteRT and ExecuTorch models on Ambiq Apollo hardware
License-Expression: Apache-2.0
Project-URL: Documentation, https://ambiqai.github.io/helia-profiler/
Project-URL: Issues, https://github.com/AmbiqAI/helia-profiler/issues
Project-URL: Repository, https://github.com/AmbiqAI/helia-profiler
Keywords: ambiq,apollo,embedded,joulescope,litert,profiling
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Software Development :: Embedded Systems
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
License-File: THIRD_PARTY_NOTICES.md
License-File: src/helia_profiler/vendor/segger_rtt/LICENSE.md
Requires-Dist: Jinja2>=3.1
Requires-Dist: PyYAML>=6.0
Requires-Dist: rich>=13.0
Requires-Dist: pyserial>=3.5
Requires-Dist: pylink-square>=0.14.0
Requires-Dist: neuralspotx==0.8.1
Requires-Dist: numpy<3,>=2.1
Requires-Dist: pyjoulescope-driver<2.3.0,>=2.2.12; python_version < "3.12"
Requires-Dist: pyjoulescope-driver<3,>=2.3.4; python_version >= "3.12"
Requires-Dist: pydantic<3,>=2.7
Requires-Dist: typer>=0.16
Requires-Dist: click>=8.2
Provides-Extra: aot
Requires-Dist: helia-aot<0.31.0,>=0.23.0; extra == "aot"
Requires-Dist: ai-edge-litert<2.3,>=2.1.6; extra == "aot"
Requires-Dist: backports-strenum<1.3; extra == "aot"
Provides-Extra: analysis
Requires-Dist: ai-edge-litert<2.3,>=2.1.6; extra == "analysis"
Requires-Dist: backports-strenum<1.3; extra == "analysis"
Requires-Dist: flatbuffers>=24.0; extra == "analysis"
Dynamic: license-file

# heliaPROFILER

**`hpx`** profiles LiteRT (`.tflite`) and ExecuTorch (`.pte`) models on real
Ambiq Apollo silicon.
One command resolves an inference engine, builds temporary NSX firmware,
flashes the target, captures measurements, and writes a portable result bundle.

> **Alpha.** heliaPROFILER is pre-1.0. Breaking changes may land on **minor**
> versions until v1.0. Pin an exact version in production pipelines.

📖 Full docs: **https://ambiqai.github.io/helia-profiler/**

## Why hpx

- **Per-layer PMU breakdowns** — cycles, instructions, cache, and (on
  Cortex-M55 boards) MVE and memory counter groups, one row per layer.
- **Power & energy per inference** — GPIO-gated Joulescope capture
  (JS110/JS220/JS320) uses a dedicated transport-free firmware image to
  isolate the inference window from setup/teardown and capture traffic.
- **Four engines, one per run** — `tflm` (vanilla TFLM baseline),
  `helia-rt` (Ambiq's optimized TFLM interpreter), `helia-aot` (Ambiq's
  ahead-of-time model compiler), and `executorch` (Cortex-M ExecuTorch
  programs, on the `arm` or `ns` CMSIS-NN provider). Selected explicitly —
  never auto-detected.
- **Multiple toolchains** — `arm-none-eabi-gcc`, `armclang`, and ATfE, so you
  can compare build/runtime trade-offs without changing your model.
- **Memory placement control** — place the heliaRT tensor arena in TCM, SRAM,
  or PSRAM and weights in TCM, SRAM, MRAM, or PSRAM; heliaAOT exposes
  per-tensor placement through its engine configuration, and ExecuTorch
  places each of its five runtime buffers independently.
- **Four capture transports** — lossless RTT by default, plus USB CDC, UART,
  and diagnostic SWO, with probe, port, and target-reset helpers for bring-up.
- **Host-only model analysis** — inspect MACs, parameters, tensor sizes, and
  heliaAOT graph transforms without connecting hardware.
- **Run comparison and regression policy** — compare two result bundles,
  inspect the largest layer deltas, and apply versioned metric thresholds.
- **Model Explorer overlays** — export per-layer metrics as JSON overlays
  for [Model Explorer](https://github.com/google-ai-edge/model-explorer).
- **Config-file driven** — a frozen, immutable `hpx.yml` schema merges with
  CLI flags, with strict validation and did-you-mean suggestions for typos.
- **Multi-board** — Apollo3, Apollo4, and Apollo5-family EVBs. Run
  `hpx boards` for the exact list your install supports.
- **Verifiable result bundles** — summaries, per-layer data, provenance,
  validity/comparability issues, and a SHA-256 manifest designed for scripts
  as well as people.
- **Hardware-in-the-loop validation** — `hpx validate` runs canonical MLPerf
  Tiny models end-to-end across engines, toolchains, transports, and memory
  placements, and emits a portable bundle two runs can be compared from.
- **Typed Python API** — call `profile()` directly or use immutable,
  branchable `Session` workflows in notebooks and automation.

## Install

See the
[Quick install](https://ambiqai.github.io/helia-profiler/getting-started/install/#quick-install)
for Nix, uv, and pip setup.

## Quick taste

```bash
hpx doctor                                   # check toolchain + dependencies
hpx profile model.tflite                     # profile with defaults
hpx profile model.tflite --power             # add Joulescope power capture
hpx profile --config hpx.yml                 # reproducible, config-driven run
hpx analyze model.tflite                     # inspect the model without hardware
hpx compare results/baseline results/change  # compare two completed runs
hpx validate --suite smoke                   # prove the whole bench works
```

`hpx profile` defaults to heliaRT, GNU Arm, RTT, `apollo510_evb`, per-layer
CPU counters, 100 measured iterations, and 5 warmups. Select a different
engine, board, toolchain, transport, memory plan, PMU selection, or output
directory explicitly on the command line or in YAML.

```text
  Layer  Op                  ARM_PMU_CPU_CYCLES  ARM_PMU_INST_RETIRED
  0      CONV_2D                        123,456                98,765
  1      DEPTHWISE_CONV_2D               45,678                34,567
  ...
  Power:  1.234 mA avg   12.345 mW avg   x.xxx µJ / inference
```

*(Illustrative sample only — see
[Getting Started](https://ambiqai.github.io/helia-profiler/getting-started/)
for a real walkthrough.)*

## Development

```bash
uv sync --locked --all-groups --extra aot --extra analysis
uv run ruff check .
uv run ty check src/helia_profiler tests
uv run pytest -q
```

`pytest` deselects the `hardware` marker by default, so the whole unit suite
runs with no board attached. Hardware cases run through `hpx validate` (or
`pytest -m hardware`).

For software-only capture tests or standalone review probes, install the device
guard **before** importing HPX:

```bash
uv run python tools/software_only.py pytest -m 'not hardware and not compile_hw and not host_integration' -q
uv run pytest -m 'host_integration and not hardware and not compile_hw' -q
uv run python tools/software_only.py python /path/to/probe.py
```

Standalone probes can import sibling helpers; the checkout `src` directory
retains precedence over the probe directory.

The launcher blocks Joulescope, J-Link and USB driver imports and provides inert
serial constructors and port discovery that raise until replaced with fakes.
Callbacks copied after startup retain those inert operations even if a later
monkeypatch changes the original module. Installation rejects already imported
HPX/device modules. Selecting a pytest marker alone does not provide this guard.

Python children must use `sys.executable` and retain the launcher's bootstrap and
checkout `src` prefix in `PYTHONPATH`, plus `PYTHONSAFEPATH=1`;
a `sitecustomize` hook guards children and grandchildren before
their scripts run. Stripped environments, startup flags that suppress the hook,
shells, `preexec_fn` callbacks and external commands are rejected. Mock external
tool calls in these tests. Pure statistics tests supply timebase metadata without
loading the vendor package.

`host_integration` identifies tests whose assertions require real host compilers,
Git, packaging, shell scripts, or interpreter isolation. These still run in the
ordinary CI suite, separately from hardware tests. Their fixtures use temporary
files, inert device sentinels and fake external commands where needed; they do
not contact boards. The guard's own negative controls belong here because they
must compare guarded and unguarded child interpreters. Compiler discovery happens
at test setup, so collecting or deselecting these tests does not launch a compiler.
To inspect either selection, add `--collect-only -q` to its command above.

This is an accidental-access guard for trusted Python tests, not an OS sandbox:
trusted interpreter startup/site files are assumed, and direct native device I/O
or deliberate removal of the guard is outside its boundary. Run hardware tests
separately through the existing explicit hardware workflow with board ownership.
The guard does not alter production capture behavior or claim hardware validation.

The launcher's own tests run fresh interpreters against inert vendor sentinels,
including unguarded negative controls; run them outside the launcher:

```bash
uv run pytest --confcutdir=tools/tests tools/tests/test_software_only.py -q
```

### Pre-commit hooks

One-time setup after cloning:

```bash
uv tool install pre-commit==4.6.0
pre-commit install
```

Nix contributors can run `nix develop .#contrib` to get pre-commit 4.6.0
alongside the HPX tools, then run `pre-commit install` once. The default
`nix develop` environment is unchanged; entering either shell does not install hooks.

This installs both hook stages the repo uses: `pre-commit` (formatting,
whitespace/YAML/JSON/TOML checks, ruff, and a gate requiring every
`TODO(...)`/`FIXME(...)`/`HACK(...)` marker to carry a reference) and
`prepare-commit-msg` (strips AI-tool attribution trailers from the commit
message). `pre-push` is left untouched, so git-lfs's own `pre-push` hook
keeps working. Secret scanning is server-side: GitHub secret scanning and
push protection are enabled on the repository, so a pushed secret is
rejected regardless of local hooks.

Run every hook against the whole tree with `pre-commit run --all-files`.
CI runs the identical `.pre-commit-config.yaml`, so a clean local run means
a clean CI run. Hook revisions are bumped deliberately via `pre-commit
autoupdate` in its own reviewed PR, not ad hoc.

The docs site lives in `astro-site/` (Astro and Starlight on `@ambiqai/helia-ui`);
`npm ci && npm run build` there builds it, `npm run dev` serves it, and the
check chain in `.github/workflows/docs.yml` is what CI runs. It deploys to
GitHub Pages on a release and on docs-only pushes to `main`.
See [Documentation release checks](maintainers/documentation.md) for browser
validation, manual publishing and post-deployment verification.

Repository workflows use the committed `uv.lock` for reproducibility. PyPI
installations continue to resolve the compatible dependency ranges published in
`pyproject.toml`.

Architectural rules, module responsibilities, and the repo workflows that
should stay stable are documented in [`AGENTS.md`](AGENTS.md) — worth reading
before a first change, whether you are a person or an agent.

## License

Apache-2.0

### Fixed-fixture PSRAM weights

The fixture API supports SRAM or DTCM working memory with PSRAM weights for heliaAOT
and upstream LiteRT Micro on Apollo510 EVB. AOT uses externally bound arenas;
Micro constructs its interpreter from the PSRAM flatbuffer. Both load complete
weights once from MRAM staging before model initialization and timing. MRAM
staging remains part of the firmware image. Internal state policy and input
restoration are unchanged.

Pass the compiled build's `psram_weights` descriptor to
`FixtureCaptureRequest.psram_weights`. The descriptor binds expected payloads
and PSRAM clock to the exact image. Capture requires the bounded placement
terminal and verifies ready/XIP status, active ranges and payload checksums.
A linker map alone cannot establish runtime PSRAM placement. A build rendered
without compilation has an unbound descriptor and cannot authorize capture.
This additive interface is fixture API 3.5; it does not include a streaming or
numerical qualification claim. Placement validation uses the canonical target
bank extents and implemented backing paths rather than benchmark recipe pairs.
Static working arenas currently use SRAM or DTCM; dynamic PSRAM working arenas,
other fixture reset targets, and NOR/RRAM loader declarations are separate
adapter gaps. External AOT fixture binding currently requires PSRAM weights.
Operator timing binding compares the effective working and weight banks recorded
in the build receipt; legacy receipts without those banks are refused.
Ordinary profile operator timings cannot be bound to PSRAM fixture builds: their
active payload, external arena layout and loader configuration are not recorded
by that profile binding.
