Bare-metal software for the case studies in our EMSOFT 2026 paper, "A Centralized Performance Monitoring Architecture for Heterogeneous Multicore SoCs." These programs run on the he-soc SoC — a quad-core CVA6 platform extended with our APMU / EVU / SPU monitoring infrastructure — on a Xilinx VCU118 board, and were used to produce the paper's results.
Artifact evaluation (EMSOFT 2026). Active development of this artifact continues in this GitHub repository: https://github.com/Rodolfo-Pellizzoni-Lab/apmu-software.
| Folder | Experiment |
|---|---|
mempol/ |
Memory-bandwidth policing: the APMU core interrupts and halts a CVA6 core when it exceeds its bandwidth budget, and resumes it when the budget refills |
sad_profile/ |
EVU PC-milestone profiling of function entry & exit: per-call cycle/event profiles of padarray4 inside the SD-VBS disparity (SAD) benchmark |
evu_test/ |
Self-checking EVU smoke test (core 0 only, ~6 ms of simulated time): PC-track milestones on a small function, counted by APMU counters 20/21 and logged by the sad_profile firmware; exit code 0 = PASS |
Each experiment's main code is in the following files:
APMU: pmu_firmware/main.c, RV32IM C compiled for the Ibex PMU core.
CVA6 side: pmu_bench.c, RV64 bare-metal program for the CVA6 cores.
They compile together as one binary: the firmware is compiled first, its
.text/.data are turned into raw bins, and pmu_text_wrapper.S /
pmu_data_wrapper.S embed them (.incbin) into the CVA6 ELF. At boot the
CVA6 program copies the firmware into the APMU's ISPM (0x10427000) and DSPM
(0x10428000).
- RV64 bare-metal toolchain (
riscv64-unknown-elf-gcc) for the CVA6 side. - RV32 bare-metal toolchain (
riscv32-unknown-elf-gcc, zicsr support) for the Ibex firmware. python3(sad_profile's two-pass address extraction).
Both toolchains are picked up from PATH: override with
RISCV_PREFIX= (RV64 side) and RV32_PREFIX= (firmware) on the make
command line if yours live elsewhere.
cd mempol
make clean
make pmu_bench fpga=1Produces pmu_bench.riscv
cd sad_profile
make clean
make two-pass fpga=1Load pmu_bench.riscv over JTAG and run hart 0 — e.g. with OpenOCD and GDB:
(gdb) target extended-remote :<openocd-port>
(gdb) monitor reset halt
(gdb) load pmu_bench.riscv
(gdb) continue
Then watch the serial console:
picocom -b 115200 --imap lfcrlf /dev/ttyUSB1Both programs set the UART to 115200 baud (50 MHz UART clock). Multi-hart bring-up is done inside pmu_bench.c.
The APMU firmware runs a token-bucket regulator over the CVA6 cores' memory traffic. When a core spends its budget, the APMU pauses it by injecting an interrupt; when the bucket refills, it injects another interupt to trigger resume.
Per-call profiling of padarray4 (one of the disparity/SAD kernel functions)
without instrumenting the benchmark: the EVU watches the CVA6 commit PC, and
the APMU firmware records cycles and event counts between each entry and exit.
- Edit
extract_addrs.py/ theDYNAMIC_PROFILE_FUNC_*defines inpmu_bench.cto point at the new function (entry symbol + offset of its firstretfrom the disassembly). - Rebuild with
make two-pass fpga=1— it re-extracts addresses and rebuilds the firmware to match.
From an alsaqr-software checkout (toolchains, OpenOCD, GDB and the bitstreams live there):
APMU=$(pwd) # this checkout, with the three programs built (fpga=1)
cd ~/alsaqr-software && source source.sh
make load_bitstream BITSTREAM=bitstreams/alsaqr_xilinx_50MHz.bit HW_TARGET=xilinx_tcf/Digilent/210308B3AC77
# wait ~30 s for DDR calibration; if the first attach fails with "IR capture error", run it again
./scripts/load.sh $APMU/evu_test/evu_test.riscv --time 3000 --batch --serial-log evu.log
./scripts/load.sh $APMU/sad_profile/pmu_bench.riscv --time 60000 --batch --serial-log sad.log
./scripts/load.sh $APMU/mempol/pmu_bench.riscv --time 120000 --batch --serial-log mempol.log--serial-log records /dev/ttyUSB3 at 38400 (the UART is hardwired to that rate in the bitstream; the
programs only set 8N1 and never touch the divisor). What to expect: === EVU TEST PASS ===; sad_profile
prints 8 padarray4 calls per pass with milestone counters 20/21 at 8 and a per-call table, the interference
pass several times slower; mempol prints the stage timings, the pause/resume interrupt counts and the mempol
log as CSV. Reference results (50 MHz bitstream, 2026-09-18): sad_profile solo about 113k cycles per call,
about 446k under interference; mempol about 900 pause/resume interrupts over 30 iterations.
A quick check that the CVA6 EVU, the APMU counters and the sad_profile firmware still work
together, small enough for the he-soc xsim RTL simulation (about 6 ms of simulated time; on the
board it runs in an instant). It arms PCTRACK0/1 on the entry and the first ret of evu_probe(),
counts the milestone hits in counters 20/21, calls the function N_CALLS (16) times and checks
the two counters and the firmware's per-call log against that number. The verdict goes to the
UART and to tohost (exit code 0 = PASS).
cd evu_test
make clean
make two-pass # or fpga=1 for the board; N_CALLS=<n> to change the call countFirmware loading note (all three programs): the firmware image is copied into the ISPM with 32-bit
word stores, and evu_test/sad_profile read it back and report mismatches. memcpy may finish an
image whose size is not a multiple of 8 (the sad_profile firmware is 2292 bytes) with byte stores,
and the ISPM/DSPM SRAMs index by byte address without honouring byte strobes, so those bytes land
in the wrong entries: the last instruction of the firmware, the polling loop's back-edge, became
j . and the firmware never logged a call. This was reproduced both in the he-soc xsim simulation
and on the 50 MHz bitstream; the RTL fix belongs in ip_list/apmu/src/pmu_ispm.sv.
Per-call cycle counts from the polling firmware have a resolution of roughly one poll iteration (about 80 cycles, two AXI-Lite counter reads); calls shorter than that, or several calls within one iteration, are reported as the poll overhead. The milestone counters themselves are exact.
This repository is part of the EMSOFT 2026 artifact (we apply for the Artifacts
Available badge). The artifact-evaluation documents are under docs/:
docs/STATUS.md— the badge we apply for and the reasons why.docs/REQUIREMENTS.md— hardware and software needed, including the GCC 15.1.0 board toolchain used for the paper's experiments.docs/INSTALL.md— clone → build → load on the board → expected output.docs/Supplementary_Material_EMSOFT.pdf— supplementary material: the design specification for our monitoring IPs (APMU / SPU / CVA6-EVU).
The software we wrote for this artifact — the APMU firmware (*/pmu_firmware/), the
CVA6-side harnesses (pmu_bench.c, pmu_test_func.c), the profiling glue, and the build
scripts — is released under the MIT License (see LICENSE).
Vendored third-party components keep their own upstream licenses:
inc/— PULP / AlSaqr platform drivers and headers, © ETH Zurich / University of Bologna, under the Apache License 2.0 (see the per-file headers).sad_profile/sdvb/— the SD-VBS (San Diego Vision Benchmark Suite) disparity kernel, © UC San Diego, under its original SD-VBS license.- The
mempol/policing firmware adapts the published mempol memory-bandwidth regulation approach; the implementation in this repository is our own.
If you use this software, please cite the paper:
@inproceedings{jafri2026centralized,
author = {Jafri, Mohammed Sajjad and Rahman, Abdur and Sarkar, Emon and Hassen, Mahdi and Thayyil, Gopishankar and Mani, Ashwin Krishna and Pellizzoni, Rodolfo},
title = {A Centralized Performance Monitoring Architecture for Heterogeneous Multicore SoCs},
booktitle = {Proc. ACM SIGBED International Conference on Embedded Software (EMSOFT)},
year = {2026}
}