# Profile a model

## Goal

Get one valid result bundle from a board and know what every line of the
terminal panel means. You will learn the shape of a run, the difference
between the two cycle totals it prints, and where the arena and binary
numbers come from.

## Hardware and wiring

An Apollo510 EVB connected to the host over its J-Link USB port. Nothing
else: the default transport is RTT over the debug connection, so no
serial cable and no power instrument. [Validate your setup](https://ambiqai.github.io/helia-profiler/getting-started/validate-your-setup/)
confirms the probe sees the board.

## Software prerequisites

hpx (`pip install helia-profiler`) and a clone of the repository for the
quickstart config and model, the GNU Arm toolchain, CMake and Ninja
on `PATH`, SEGGER J-Link software, and on Linux the udev rule
([Install](https://ambiqai.github.io/helia-profiler/getting-started/install/)). `hpx doctor` must report the
toolchain and J-Link as found.

## Config file

[`examples/quickstart/hpx_rt.yml`](https://github.com/AmbiqAI/helia-profiler/blob/main/examples/quickstart/hpx_rt.yml):

```yaml
model:
  path: kws_model.tflite
  arena_size: 131072
engine:
  type: helia-rt
  config:
    variant: release-with-logs
target:
  board: apollo510_evb
  toolchain: arm-none-eabi-gcc
profiling:
  pmu_counters:
    cpu: default
    memory: default
  per_layer: true
  iterations: 3
  warmup: 1
power:
  enabled: false
output:
  format: csv
  dir: ./results/results_rt
```

The model path is relative to the config file, so run from
`examples/quickstart/` or pass the model on the command line. Every key is
on the generated [configuration reference](https://ambiqai.github.io/helia-profiler/reference/configuration/).

## Run

```bash
cd examples/quickstart
hpx profile --config hpx_rt.yml
```

Or without a file, from anywhere:

```bash
hpx profile examples/quickstart/kws_model.tflite --board apollo510_evb --engine helia-rt
```

The first run resolves and builds the NSX dependency workspace, which
takes minutes; later runs reuse it.

## Expected output

The results panel of the nightly case named under Validated on (the
`Memory` and `Plan vs measured` tables that follow it are on
[Memory placement](https://ambiqai.github.io/helia-profiler/guide/memory/); the arena progress bar and the
host wall-clock subtitle are omitted):

```
─────────────────────────────────── Results ────────────────────────────────────
  Engine              helia-rt
  Compatibility       qualified
  Board               apollo510_evb
  Layers              13
  Total cycles        2,060,239
  Clean E2E cycles    2,051,232  (-0.4% vs per-layer sum)
  Total MACs          2,656,768
  Total OPS           5,321,596
  Cycles/MAC          0.78
  Parameters          22,604
Top Layers by Cycles
   #   Operator                     Cycles         %           MACs    Cyc/MAC
   1   CONV_2D                     337,348     16.4%        320,000        1.1
   2   DEPTHWISE_CONV_2D           216,207     10.5%         72,000        3.0
   3   DEPTHWISE_CONV_2D           216,205     10.5%         72,000        3.0
   4   DEPTHWISE_CONV_2D           216,194     10.5%         72,000        3.0
   5   DEPTHWISE_CONV_2D           216,192     10.5%         72,000        3.0
  Arena      29,780 / 32,768 bytes  91%
  Model      53,744 bytes
  Binary Sections
  Section        Size
  text        278,596
  data        154,796
  bss          86,260
  reserved    365,404
  total       885,056
  Validity: VALID
╭─ Output → results/… ───────────╮
│    profile_results.csv         │
│    summary.json                │
│    run_metadata.json           │
│    nsx.lock                    │
│    result_manifest.json        │
╰────────────────────────────────╯
```

## How to read it

- `Clean E2E cycles` is the inference cost: the firmware ran the model
  warm, back to back, with no per-layer instrumentation, and averaged 46
  inferences. `Total cycles` is the sum of the instrumented per-layer rows
  and is 0.4% higher because every operator was bracketed by a counter
  read. Publish the clean number ([Latency and per-layer](https://ambiqai.github.io/helia-profiler/guide/latency/)).
- `Top Layers by Cycles` ranks the per-layer rows; `Cyc/MAC` divides by
  the operator's multiply-accumulates from the host-side model analysis.
  A depthwise convolution at 3.0 cycles per MAC next to a full convolution
  at 1.1 is the usual shape: depthwise has less arithmetic per byte moved.
- `Arena 29,780 / 32,768` is the interpreter's allocation against the
  configured `arena_size`; a run that reports the arena does not fit stops
  before flashing. The quickstart config asks for 131,072 bytes, which is
  what this model needs with headroom; the nightly case used 32,768.
- `Validity: VALID` and an empty issue list mean the run is a measurement.
  A `DEGRADED` or `INVALID` verdict lists codes to look up on the
  [issue codes](https://ambiqai.github.io/helia-profiler/reference/issue-codes/) page.
- The five files are described on [Output files](https://ambiqai.github.io/helia-profiler/guide/output/);
  `summary.json` has every number above in machine-readable form.

## Variations

- Other boards, same config with `--board`: the same nightly run profiled
  this model on `apollo330mP_evb` (2,056,992 clean cycles, Cortex-M55),
  `apollo3p_evb` (10,186,141, Cortex-M4, DWT cycle counter only) and
  `apollo4l_blue_evb` (8,683,558, Cortex-M4); those bundles ship beside
  the Apollo510 one. Cortex-M4 boards capture cycles only, not the PMU
  groups ([Boards and targets](https://ambiqai.github.io/helia-profiler/guide/boards/)).
- More iterations: `--iterations 20 --warmup 5` tightens the per-layer
  median; the clean window is sized separately by `window_target_ms`.
- Another engine or toolchain: [Compare engines](https://ambiqai.github.io/helia-profiler/examples/engine-comparison/),
  [Compare toolchains](https://ambiqai.github.io/helia-profiler/examples/toolchain-comparison/).
- A JSON bundle: `--output-format json` writes `profile_results.json`
  instead of the CSV.

## Validated on

| | |
|---|---|
| Board | `apollo510_evb` (Apollo510, Cortex-M55, `lp` clock tier, 96 MHz as recorded in the bundle's `run_metadata.json`) |
| Run | nightly hardware validation [run 35078330526](https://github.com/AmbiqAI/helia-profiler/actions/runs/35078330526), 2026-09-16 09:15 UTC, suite `complete`, hpx 0.1.6 at source commit `0714893` |
| Case | `apollo510_evb-kws-rt-ns-arm-none-eabi-gcc-rtt-auto`, verdict `valid`, no issues |
| Model | `kws_model.tflite`, SHA-256 `935c513a…bdf038`, the same bytes as the quickstart file |
| nsx modules | baseline `hpx-neuralspotx-0.8.1-2026-09`: `nsx-helia-rt` helia-rt-v1.20.0 (edb3a25), `nsx-cmsis-nn` aaeb145, `nsx-ambiq-sdk` a9f4ec2, `nsx-pmu-armv8m` 5725c06, `nsx-board-apollo510-evb` packaged with neuralspotx 0.8.1 |
| Toolchain | arm-none-eabi-gcc 15.2.1 (Arm GNU Toolchain 15.2.Rel1), CMake 4.1.2 |
| Differences from the config above | the nightly case used `arena_size: 32768`, `pmu_counters: {cpu: default}` and `output.format: csv`; the quickstart config adds the `memory` group and a larger arena |
| Bundle | [`examples/results/hardware-validation-2026-09-16/apollo510_evb-kws-rt-ns-arm-none-eabi-gcc-rtt-auto/`](https://github.com/AmbiqAI/helia-profiler/tree/main/examples/results/hardware-validation-2026-09-16/apollo510_evb-kws-rt-ns-arm-none-eabi-gcc-rtt-auto) |
