Build the classification app runner
Build the patched runner into a separate directory:
~/executorch/backends/arm/scripts/build_executor_runner.sh \
--pte="$PWD/module-4-ethos-npu-accelerated-inference/artifacts/mobilenetv2/mobilenetv2_int8_ethosu.pte" \
--target=ethos-u85-256 \
--system_config=Ethos_U85_SYS_DRAM_Mid \
--memory_mode=Shared_Sram \
--extra_build_flags="-DSEMIHOSTING=ON -DET_ARM_BAREMETAL_SEMIHOSTING_FILE_ALLOCATOR_POOL_SIZE=0x01000000" \
--output="$PWD/module-4-ethos-npu-accelerated-inference/artifacts/fvp_runner/mobilenetv2_classifier"
The same linker warnings described for the generic runner can appear here.
Continue if the build reports Built target arm_executor_runner and prints the
generated ELF path.
This build uses the same MobileNetV2 .pte artifact as the FVP runner. The
difference is the C++ application code around the model call, so no separate Python decoding script is needed afterward.
Run the classification app
Create the same MobileNetV2 input tensor in a separate run folder:
python module-4-ethos-npu-accelerated-inference/scripts/fvp/create_mobilenetv2_input.py \
--spec module-4-ethos-npu-accelerated-inference/artifacts/mobilenetv2/model_spec.json \
--image module-4-ethos-npu-accelerated-inference/artifacts/mobilenetv2/sample.jpg \
--out module-4-ethos-npu-accelerated-inference/artifacts/fvp_runs/mobilenetv2_classifier/i0.bin
Run the patched app runner with a separate semihosting folder:
FVP_Corstone_SSE-320 \
-C mps4_board.subsystem.ethosu.num_macs=256 \
-C mps4_board.visualisation.disable-visualisation=1 \
-C vis_hdlcd.disable_visualisation=1 \
-C mps4_board.telnetterminal0.start_telnet=0 \
-C mps4_board.uart0.out_file='-' \
-C mps4_board.uart0.unbuffered_output=1 \
-C mps4_board.uart0.shutdown_on_eot=1 \
-C mps4_board.subsystem.cpu0.semihosting-enable=1 \
-C mps4_board.subsystem.cpu0.semihosting-stack_base=0 \
-C mps4_board.subsystem.cpu0.semihosting-heap_limit=0 \
-C "mps4_board.subsystem.cpu0.semihosting-cwd=$(realpath module-4-ethos-npu-accelerated-inference/artifacts/fvp_runs/mobilenetv2_classifier)" \
-C "mps4_board.subsystem.cpu0.semihosting-cmd_line=executor_runner -o out -i i0.bin" \
-a "$(realpath module-4-ethos-npu-accelerated-inference/artifacts/fvp_runner/mobilenetv2_classifier/arm_executor_runner)" \
--timelimit 600 \
2>&1 | tee module-4-ethos-npu-accelerated-inference/artifacts/fvp_runs/mobilenetv2_classifier/mobilenetv2_classifier.log
The UART log should now include lines similar to:
MobileNetV2 output tensor: scalar_type=Float, numel=1000
MobileNetV2 top-5 predictions:
1. Labrador retriever (class 208): <logit>
2. golden retriever (class 207): <logit>
3. <another ImageNet label> (class <id>): <logit>
4. <another ImageNet label> (class <id>): <logit>
5. <another ImageNet label> (class <id>): <logit>
Consider the following questions:
- What application behavior is still missing before this could become a production application?
- What would need validation on physical hardware?
Optional next steps
Build on a C++ Reference Application
The classification patch shows the small change needed to add MobileNetV2 postprocessing to the upstream runner. For a new application, it is often easier to start from source files that separate the ExecuTorch runtime calls from the model-specific output handling.
An Ethos-U C++ reference application provides that initial structure:
| File | Responsibility |
|---|---|
app_main.cpp | Initialize ExecuTorch and connect model outputs to the application |
model_runner.cpp | Load the .pte, allocate method memory, set the input, execute the method, and retrieve outputs |
classification_postprocess.cpp | Convert the MobileNetV2 output tensor into top-five labels and logits |
app_config.h | Define the input count, output index, top-k value, and allocator sizes |
CMakeLists.txt | Link the application with ExecuTorch, the Ethos-U delegate, Arm kernels, and Corstone-320 startup code |
The runtime path is visible in model_runner.cpp:
Result<Program> program = Program::load(&loader);
Result<MethodMeta> method_meta = program->method_meta(*method_name);
MemoryManager memory_manager(
&method_allocator, &planned_memory, &temporary_allocator);
Result<Method> method =
program->load_method(*method_name, &memory_manager);
method->set_input(input_tensor, 0);
method->execute();
method->get_outputs(outputs.data(), outputs.size());
Program::load() reads the serialized ExecuTorch program. method_meta()
provides the tensor and memory-plan information needed before the method is
loaded. load_method() prepares the executable method with the supplied
allocators. The application then sets the prepared input tensor, calls
execute(), and retrieves the output values.
The reference build embeds the .pte, i0.bin, and ImageNet labels in the
application ELF instead of reading them through semihosting. This makes the
application structure closer to one that could be moved to a physical board.
Follow the build and FVP commands in its README after completing the main Module 4 workflow.
To adapt it, keep model_runner.cpp and replace the input preparation,
classification_postprocess.cpp, and handle_output() with code for the new
model and application. Update the visible allocator sizes after checking the
new model’s memory plan and the target memory map.
Run the One-Command Flow
ExecuTorch provides examples/arm/run.sh, which combines model export,
quantization, lowering, runner compilation, and FVP execution. Use it to see
the upstream end-to-end flow after completing the individual stages above:
cd ~/executorch
source examples/arm/arm-scratch/setup_path.sh
./examples/arm/run.sh \
--output=mv2_u85 \
--target=ethos-u85-256 \
--system_config=Ethos_U85_SYS_DRAM_Mid \
--memory_mode=Shared_Sram \
--model_name=mv2
To inspect the exported and quantized graphs, add
--aot_arm_compiler_flags="--debug". This produces a large amount of terminal
output.
The wrapper enables delegation and quantization by default.
This command uses
the upstream built-in mv2 model. The FVP runs it with an all-ones dummy
tensor rather than an image, so any printed class labels are not meaningful
predictions.
The command also uses the model’s example tensor for calibration; it does not reproduce the course’s 100-image Open Images calibration method or artifact names.
Course complete
Nice work
You completed Optimize and deploy AI inference with ExecuTorch on Arm.