The Arm ExecuTorch example, on its hackathon branch: a generative model on
a microcontroller. pico-faces by
cpldcpu, a latent diffusion transformer (2.5M parameters) with a small
decoder, generates 128x128 faces; exported through ExecuTorch, every layer of
it runs on the Ethos-U85 NPU while the Cortex-M drives the sampling loop. One
CMSIS solution runs it on the Alif Ensemble E8 DevKit or the Ensemble E8
AI/ML AppKit (Cortex-M55 HP core; the faces appear on the board's LCD, a new
one per tap on the screen's OK badge) and on the Corstone-320 FVP; you switch between
them by target-type. The model is
exported from PyTorch in three steps: the CMSIS-Toolbox describes the target,
create_ai_layer.py turns that into the AI layer, the toolbox builds the
application. This page takes you from an empty machine to a debug session on
the board. Everything about the example itself is in
documentation/example.md.
- Install VS Code.
- Install the extensions Keil Studio Pack (
Arm.keil-studio-pack, which brings the CMSIS Solution extension 1.72.0 or newer with CMSIS-Toolbox 2.15) and Python (ms-python.python). Sign in with an Arm account when Keil Studio asks; the free Keil MDK Community license is enough. - Nothing else by hand: when you open the project, the Arm Tools Environment
Manager offers to install the tools pinned in
vcpkg-configuration.json(CMSIS-Toolbox, Arm Compiler 6, GCC, CMake, Ninja, and on Linux and Windows the Corstone-320 FVP; macOS runs it in Docker, see step 6). Accept. - Optional: the CMSIS Developer Assistant extension lets an AI agent
(Claude Code or GitHub Copilot Chat) build, flash and debug the board
through an MCP server. Install it, install one of the agents, run
CMSIS Developer Assistant: Configure Agents and Skills from the command
palette and pick at least the
cmsis-debug-liveandcmsis-helpskills.
-
Alif SETOOLS V1.110.00 or later (V1.112.00 is current) from the Alif software and tools page (login required). Unpack it; on Linux and macOS make the tools executable and install the Python packages its README lists. Add the root directory (the one with
app-gen-tocandapp-write-mram) to your VS Code user settings:"alif.setools.root": "/absolute/path/to/setools"
-
SEGGER J-Link Software V8.42 or later from segger.com. The board has an on-board J-Link.
-
Connect a USB cable to PRG USB (USB-C in the corner of the DevKit, Micro-USB on the AppKit). It powers the board and carries the J-Link and a USB-to-UART bridge. Leave the other USB device connector unconnected.
-
DevKit: jumpers at their defaults, JP5 on 1-2, JP7 on 3-4. Never move jumpers with power applied.
-
SW4 (on both boards) selects what the UART bridge is connected to:
SW4 Connected to Used for SEUART(default)Secure Enclave UART SETOOLS (step 5) UART2(AppKit only, middle position)Application UART2 Not used here UART4Application UART4, 115200 8N1 The example's console (step 6) -
With the board attached, run these once: in the SETOOLS directory
updateSystemPackage -d(SW4 onSEUART; picks the serial port, checks the system firmware, offers to make the E8 the default target: answer yes), and J-Link Commander (JLinkExe,JLink.exeon Windows), which updates the on-board J-Link firmware and installs its serial-port drivers.
git clone https://github.com/Arm-Examples/CMSIS-Executorch.git
cd CMSIS-Executorch
git checkout hackathonOpen the folder in VS Code and accept the tool activation and the pack
installation (PyTorch::ExecuTorch, AlifSemiconductor::Ensemble, CMSIS).
In the CMSIS view open Manage Solution, choose the target-type
of your board, DevKit-E8 or AppKit-E8 (SSE-320-U85 for the FVP),
and click Apply.
The model data is generated rather than committed, so a fresh checkout needs Python once before the first build:
- Terminal > Run Task > Setup Python virtual environment (several GB of
PyTorch, takes a while; the (uv) variant of the task uses uv and can
download the Python version it asks for). It also downloads the
pico-faces checkpoints (about 60 MB, checked by SHA-256) into
model/pico_faces/. - Terminal > Run Task > Create AI layer exports the model for the NPU
and writes
ai_layer/(a few minutes). It readscmsis-executorch.cbuild-mlops.yml, which the CMSIS Solution extension writes when it loads the solution (click Build once if the file is missing). Run the task again after changingmodel/model.py.
The default variant is now m3_decD_deep_full. Its exported program is
4,454,368 bytes. The DevKit-E8 HP image now starts at 0x80000000, using the
lower MRAM formerly reserved for HE. The AC6 load image is 4,750,736 bytes
against a 5,701,632-byte HP MRAM region, leaving 950,896 bytes free. The
DevKit boot-table entry and debug stub are relocated to match. The AppKit-E8
still starts at 0x80200000; its smaller MRAM region cannot hold this default
model. The successful console transcript below is from the smaller
m3_long_cfg variant; the full model has only been checked by building.
The Secure Enclave boots the M55 cores from a table of contents in MRAM; the debugger needs that table to point at a debug stub.
- SW4 to SEUART, PRG USB attached.
- Terminal > Run Task > Alif: Install M55_HP debug stubs (DevKit-E8, single core configuration),
or the (AppKit-E8, ...) task for the AppKit. Choose COM port
discovery (
-d) the first time; SETOOLS remembers the port. The task selects the board's part in SETOOLS (tools-config -p 'E8 (AE822FA0E5597LS0) ...' -r A0for the DevKit,AE822FA0E5597BS0for the AppKit: the part goes into the table of contents, and the Secure Enclave does not boot a table built for another part), copies the configuration and stub from.alif/into the SETOOLS tree and runsapp-gen-tocandapp-write-mram. Ifapp-write-mramreports a different revision of the board, answery: only the part number decides whether the table boots. - SW4 to UART4.
Repeat this after another project has reprogrammed the table.
-
With SW4 on UART4, open the Serial Monitor panel on the PRG USB port, 115200 baud.
-
In the CMSIS view click Build, then Debug (or Run). Keil Studio starts the J-Link GDB server over SWD, loads the image into MRAM and stops at
main; continue with F5. The console shows the Ethos-U banner, the program and its two methods, the timings of the boot demo, the CRC of the image, an ASCII preview of the face and the pass marker:Ethos-U version info: Arch: v2.0.0 MACs/cc: 256 Cmd stream: v1 ExecuTorch pico-faces (m3_long_cfg, a16w8/a8w8): 2796016 byte program Methods: dit_step 2 input(s), 1 output(s), 16384 planned byte(s) decode 1 input(s), 1 output(s), 245760 planned byte(s) Generating: seed 3, 4 steps, class 1, w 4.0 Display: 480x800 RGB888 panel started dit_step: 8 call(s), 68 ms total (8 ms each) decode: 3 ms total: 78 ms at 400 MHz (wall clock; not meaningful on the FVP) dit_step: NPU 27132 kcycles, active 95%, MAC active 39%, 32 MAC/cycle, read 21279 kB on AXI0 + 0 kB on AXI1 decode: NPU 1303 kcycles, active 50%, MAC active 37%, 83 MAC/cycle, read 434 kB on AXI0 + 0 kB on AXI1 Image: 128x128x3, CRC32 6b938c66 |::::::::....... . ..........| |::::::::........... ..... | ... Test_result: PASS Interactive: send "G <seed> [k_steps] [class] [w]" (viewer/view_serial.py) or "I" Touch: tap OK for a new imageA face with guidance takes 78 ms: eight
dit_stepcalls of 8.5 ms on the NPU and a 3 ms decode. The same face appears on the LCD, scaled to 384 x 384 in the centre of the screen; it is bit for bit the image the FVP produces. (The CRC depends on the exported program, which can differ slightly between the machines that run Create AI layer.) -
Tap the green OK badge at the bottom of the touchscreen for a new face (the next seed; class and guidance follow from it). Each one is reported on the console.
-
Or request faces from the host with pico-faces' viewer (close the Serial Monitor first, the viewer needs the port; in a clone of pico-faces,
pip install pyserial pillow):python viewer/view_serial.py --port /dev/tty.usbmodemXXXX --seed 3 --steps 4 --class 1 --cfg 4 --show
Classes are 0 (female, neutral), 1 (female, smiling), 2 (male, neutral), 3 (male, smiling) and 4 (unconditional);
--cfgis the guidance strength (0 = plain),--steps8, 4, 2 or 1. -
Set a breakpoint after
generate()insrc/app_main.cppand inspect the timings intm, or ask the CMSIS Developer Assistant to do it: "Build for the DevKit-E8, load it, break after the first generate() and show me the NPU cycles per method."
FVP instead of the board: choose the SSE-320-U85 target-type and click
Run or Debug; the same output appears in the terminal (a run takes a
few minutes), and the image lands in out/fvp_image.bin.
python3 model/verify_export.py --compare out/fvp_image.bin <reference.png>
compares it with the host's rendering of the same seed (see
documentation/example.md). On macOS, where Arm
ships no FVP build, the FVP runs in Docker: set model: in the csolution's
SSE-320-U85 target-set to ${workspaceFolder}/.vscode/fvp.sh, copy
.vscode/launch.json.mac to .vscode/launch.json, and keep Docker Desktop
running (the first run builds the image, about 100 MB).
- J-Link connects but never stops at
main: either the table of contents does not point at the debug stub or was built for another part, which the Secure Enclave skips (repeat step 5), or the image cannot boot. Look before reprogramming: in the debugger, read the vector table at0x80000000on DevKit-E8 (0x80200000on AppKit-E8) and the fault registers (CFSR/HFSR); a PC of0xEFFFFFFEis a lockup at reset, which an image linked to run from ITCM produces when the code that copies it there is itself in ITCM. - Debug hangs at "Connecting": the target-set was switched to
protocol: jtag. Keep SWD: the generated load task blocks on JLinkExe's JTAG-chain prompt, and the device stays in SWD mode after any SWD use until it is power-cycled. - No console output: SW4 is still on
SEUART, or the port was opened before the switch was moved. SetUART4and reopen the port. - Every SETOOLS tool prints
Revision is invalid!: SETOOLS stores the selected part and revision inutils/global-cfg.db, and atools-config -pwithout-r(another project switching to an E7, for example) keeps a revision the new part does not have. From then on eventools-configrefuses to run. The stub tasks of step 5 write their board's part and revision into that file before they calltools-config, so they recover by themselves; for SETOOLS commands of your own, set"Part#"to"E8 (AE822FA0E5597LS0) - 5.5 MRAM / 9.75 SRAM"(DevKit) or"E8 (AE822FA0E5597BS0) - 5.5 MRAM / 9.75 SRAM"(AppKit) and"Revision"to"A0"there. app-write-mramgets no answer: press reset while it waits, check SW4 is onSEUART, close any terminal holding the port.- The build stops at
model_pte_data.inc is missing: the model data is not committed; run the tasks Setup Python virtual environment and Create AI layer (step 4). - "
.venvdoes not exist" or "model/pico_faces/...is missing": run the task Setup Python virtual environment first; it creates the environment and downloads the checkpoints. .vscode/launch.jsonis generated by the extension for the target-types and is not committed; after Apply it has a J-Link entry for the DevKit-E8 or the AppKit-E8. That is expected.
- documentation/example.md: the example, the command-line flow, how the model is generated.
- board/DevKit-E8/README.md: the board layer, memory map and RTE configuration.
- board/AppKit-E8/README.md: the AppKit-E8 layer and what differs from the DevKit.
- documentation/mlops-flow.md: the
mlops:node and*.cbuild-mlops.ymlin detail. - documentation/pack-provenance.md: where
the
PyTorch::ExecuTorchpack comes from and how to move to a new version.