Skip to content
neuralSPOT-X
User guide
HELIA HUB

PMU Profiling

Cortex-M55 PMU cycle counting, cache hits, and branch mispredicts.

Declared by the example author Value
Tier Capabilities
Status tested
Capabilities pmu, profiling
Boards tested apollo510_evb

Build it from a source checkout by name, because the positional app argument resolves under ./examples:

Terminal window
nsx build pmu_profiling --board apollo510_evb

The rest of this page is the example’s own README, from examples/pmu_profiling/.

Demonstrates the nsx-pmu-armv8m module on the Apollo510 EVB. Configures the Arm Cortex-M55 Performance Monitoring Unit (PMU) to track CPU cycles and retired instructions for a tight integer workload, printing a minimal summary over SWO every 2 seconds.

The Cortex-M55 has a hardware PMU with 8 configurable event counters plus a dedicated cycle counter. Each counter can be assigned to any of ~70 hardware events (cache misses, stalls, MVE instructions, bus accesses, etc.). The standalone nsx-pmu-armv8m module provides the shared PMU API, while the selected SDK supplies the local nsx_ambiq_pmu backend shim.

You can also configure individual events from any of the 70+ Cortex-M55 PMU events (see ns_pmu_map[] in ns_pmu_utils.c for the full list).

Terminal window
nsx configure --app-dir .
nsx build --app-dir .
nsx flash --app-dir .
nsx view --app-dir . # opens SWO viewer

The SWO viewer will print a compact PMU summary every ~2 seconds:

--- PMU after 100 iterations ---
cycles=230563 inst=130093
--- PMU after 100 iterations ---
cycles=230563 inst=130093

The workload is a fixed 256 × i² accumulation loop repeated 100 times, so counter values are usually stable across runs. Occasional small outliers are expected from normal runtime noise such as interrupts or SWO activity.

// Configure the two counters this example prints
g_pmu.api = &nsx_pmu_V1_0_0;
nsx_pmu_reset_config(&g_pmu);
nsx_pmu_event_create(&g_pmu.events[0], ARM_PMU_CPU_CYCLES, NSX_PMU_EVENT_COUNTER_SIZE_32);
nsx_pmu_event_create(&g_pmu.events[1], ARM_PMU_INST_RETIRED, NSX_PMU_EVENT_COUNTER_SIZE_32);
nsx_pmu_init(&g_pmu);
// Reset, run workload, read
nsx_pmu_reset_counters();
workload();
nsx_pmu_get_counters(&g_pmu);
nsx_printf("cycles=%lu inst=%lu\r\n",
(unsigned long)g_pmu.counter[0].counterValue,
(unsigned long)g_pmu.counter[1].counterValue);

To profile different events, change the two nsx_pmu_event_create() calls in src/main.c:

nsx_pmu_event_create(&cfg.events[0], 0x0011, NSX_PMU_EVENT_COUNTER_SIZE_32); // CPU_CYCLES
nsx_pmu_event_create(&cfg.events[1], 0x0039, NSX_PMU_EVENT_COUNTER_SIZE_32); // L1D_CACHE_MISS_RD