# Every counter, every layer

## Goal

Get the complete counter picture of a model rather than the default four.
You will learn how hpx schedules more counters than the hardware holds at
once, what the merged CSV looks like, and which derived ratios to compute.

## Hardware and wiring

An Apollo510 EVB (Cortex-M55 with the Armv8-M PMU and MVE) on its J-Link
USB port. A Cortex-M4 board cannot run this recipe: it exposes the DWT
cycle counter only ([Boards and targets](https://ambiqai.github.io/helia-profiler/guide/boards/)).

## Software prerequisites

hpx with the `aot` extra (the config uses heliaAOT), the GNU Arm
toolchain, CMake, Ninja and J-Link software.

## Config file

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

```yaml
model:
  path: kws_model.tflite
  arena_size: 131072
engine:
  type: helia-aot
  config:
    prefix: hpx
    module_name: hpx_model
target:
  board: apollo510_evb
  toolchain: arm-none-eabi-gcc
profiling:
  pmu_counters:
    cpu: all
    memory: all
    mve: all
  per_layer: true
  iterations: 5
  warmup: 2
power:
  enabled: false
output:
  format: csv
  dir: ./results/results_full_sweep
  model_explorer: true
  detailed: true
```

`all` on three groups asks for every counter the SoC's catalogue lists for
that group; the [PMU counters](https://ambiqai.github.io/helia-profiler/guide/pmu-counters/) page carries the
catalogue per SoC.

## Run

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

## Expected output

Not yet captured for this config. The shape of the per-layer file is the
same as the default-counter bundle shipped with
[Profile a model](https://ambiqai.github.io/helia-profiler/examples/basic-profiling/), which has four CPU counters:

```
id,op,ARM_PMU_CPU_CYCLES,ARM_PMU_INST_RETIRED,ARM_PMU_STALL_FRONTEND,ARM_PMU_STALL_BACKEND,cycles,cycles_pct,overflow,macs,ops,cycles_per_mac
0,CONV_2D,337348.0,262360.0,1907.0,39945.0,337348.0,16.4,False,320000,640000,1.05
1,DEPTHWISE_CONV_2D,216205.0,155556.0,1358.0,48006.0,216205.0,10.5,False,72000,144000,3.0
2,CONV_2D,209934.0,164568.0,7.0,24863.0,209934.0,10.2,False,512000,1024000,0.41
```

With the three groups on `all`, the same rows carry one column per
counter in the catalogue, and `output.detailed: true` adds
`detailed/profile_<preset>.csv` per firmware pass and
`detailed/profile_<group>.csv` per group, plus `detailed/memory.json`.

## How to read it

- The SoC holds a fixed number of PMU counters at once, so the firmware
  runs the model once per counter preset (`<group>_<index>`, for example
  `cpu_0`, `cpu_1`, `memory_0`) and the host merges the presets by layer
  id. Every row is therefore the same operator measured in several passes;
  `cycles` comes from the pass that carried `ARM_PMU_CPU_CYCLES`
  ([Data capture](https://ambiqai.github.io/helia-profiler/guide/concepts/capture/)).
- Ratios are more useful than raw counts: `ARM_PMU_L1D_CACHE_REFILL /
  ARM_PMU_L1D_CACHE` is the data-cache miss rate, `ARM_PMU_MVE_INST_RETIRED
  / ARM_PMU_INST_RETIRED` the vector share, `STALL_BACKEND / CPU_CYCLES`
  the fraction of cycles waiting on memory or the pipeline. The
  [PMU counters](https://ambiqai.github.io/helia-profiler/guide/pmu-counters/#5-read-the-results) page lists
  the derived metrics hpx computes and the ones to compute yourself.
- `overflow` is `True` when a 32-bit counter saturated in any pass; the
  run is then `invalid` for that counter and fewer iterations or fewer
  counters per pass fix it.
- `macs`, `ops` and `cycles_per_mac` join the host-side model analysis to
  the measured row by the operator's original index, so heliaAOT rows
  line up with the model even where the compiler skipped operators.

## Variations

- One group at a time: `pmu_counters: {memory: all}` keeps the run short
  and the passes few.
- Explicit lists: `memory: [ARM_PMU_L1D_CACHE, ARM_PMU_L1D_CACHE_MISS_RD]`
  names counters under the group that owns them; an unsupported name or
  group for the SoC is rejected at preflight.
- heliaRT instead of heliaAOT: the counters are engine-independent; only
  the operator rows differ.
- Overlays: `output.model_explorer: true` writes one overlay per counter
  for [Model Explorer](https://ambiqai.github.io/helia-profiler/examples/model-explorer-overlay/).

## Validated on

Not yet validated. The nightly suite captures the default `cpu` group
only, so no bundle with the `memory` and `mve` groups exists; the CSV
excerpt above is from `apollo510_evb-kws-rt-ns-arm-none-eabi-gcc-rtt-auto`
(nightly [run 35078330526](https://github.com/AmbiqAI/helia-profiler/actions/runs/35078330526),
2026-09-16, hpx 0.1.6 at `0714893`), which ran the default four counters.
