-
Notifications
You must be signed in to change notification settings - Fork 958
Zephyr: Add MobileNetV2 image classification sample with Ethos-U NPU #19131
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
782d064
8c6093e
eeb54c9
768a02e
a32b8d0
46092d6
8728a45
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,259 @@ | ||
| # Copyright (c) Meta Platforms, Inc. and affiliates. | ||
| # All rights reserved. | ||
| # Copyright 2025-2026 Arm Limited and/or its affiliates. | ||
| # | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| cmake_minimum_required(VERSION 3.24) | ||
|
|
||
| set(CMAKE_SKIP_INSTALL_RULES | ||
| ON | ||
| CACHE BOOL "" FORCE | ||
| ) | ||
|
|
||
| set(ET_PTE_FILE_PATH | ||
| "" | ||
| CACHE FILEPATH "Path to the ExecuTorch .pte (or .bpte) model to embed" | ||
| ) | ||
| set(ET_PTE_SECTION | ||
| "network_model_sec" | ||
| CACHE STRING "Section attribute used for the generated model data" | ||
| ) | ||
|
|
||
| if(NOT ET_PTE_FILE_PATH) | ||
| message( | ||
| FATAL_ERROR | ||
| "ET_PTE_FILE_PATH must point to the ExecuTorch .pte (or .bpte) model to embed." | ||
| ) | ||
| endif() | ||
|
|
||
| if(NOT IS_ABSOLUTE "${ET_PTE_FILE_PATH}") | ||
| get_filename_component( | ||
| ET_PTE_FILE_PATH "${ET_PTE_FILE_PATH}" ABSOLUTE BASE_DIR | ||
| "${CMAKE_CURRENT_SOURCE_DIR}" | ||
| ) | ||
| endif() | ||
|
|
||
| if(NOT EXISTS "${ET_PTE_FILE_PATH}") | ||
| message( | ||
| FATAL_ERROR | ||
| "Could not find ExecuTorch model at ET_PTE_FILE_PATH: ${ET_PTE_FILE_PATH}" | ||
| ) | ||
| endif() | ||
|
|
||
| set(ET_PTE_FILE_PATH | ||
| "${ET_PTE_FILE_PATH}" | ||
| CACHE FILEPATH "Path to the ExecuTorch .pte (or .bpte) model to embed" | ||
| FORCE | ||
| ) | ||
|
|
||
| find_package( | ||
| Python3 | ||
| COMPONENTS Interpreter | ||
| REQUIRED | ||
| ) | ||
|
|
||
| execute_process( | ||
| COMMAND | ||
| ${Python3_EXECUTABLE} | ||
| "${CMAKE_CURRENT_LIST_DIR}/../../../codegen/tools/gen_oplist.py" | ||
| --model_file_path=${ET_PTE_FILE_PATH} | ||
| --output_path=${CMAKE_CURRENT_BINARY_DIR}/temp.yaml | ||
| RESULT_VARIABLE GEN_OPLIST_RESULT | ||
| OUTPUT_VARIABLE CMD_RESULT | ||
| ERROR_VARIABLE GEN_OPLIST_ERROR | ||
| ) | ||
|
|
||
| if(NOT GEN_OPLIST_RESULT EQUAL 0) | ||
| message( | ||
| FATAL_ERROR | ||
| "gen_oplist.py failed with exit code ${GEN_OPLIST_RESULT}: ${GEN_OPLIST_ERROR}" | ||
| ) | ||
| endif() | ||
|
|
||
| if(CMD_RESULT MATCHES "aten::" OR CMD_RESULT MATCHES "dim_order_ops::") | ||
| set(FOUND_OPS_IN_FILE "true") | ||
| else() | ||
| set(FOUND_OPS_IN_FILE "false") | ||
| endif() | ||
|
|
||
| if(${FOUND_OPS_IN_FILE}) | ||
| set(EXECUTORCH_SELECT_OPS_LIST "") | ||
| set(EXECUTORCH_SELECT_OPS_MODEL | ||
| "${ET_PTE_FILE_PATH}" | ||
| CACHE STRING "Select operators from this ExecuTorch model" FORCE | ||
| ) | ||
| set(_EXECUTORCH_GEN_ZEPHYR_PORTABLE_OPS ON) | ||
| message( | ||
| "gen_oplist: EXECUTORCH_SELECT_OPS_MODEL=${ET_PTE_FILE_PATH} is used to auto generate ops from" | ||
| ) | ||
| else() | ||
| set(EXECUTORCH_SELECT_OPS_LIST "") | ||
| set(EXECUTORCH_SELECT_OPS_MODEL "") | ||
| set(_EXECUTORCH_GEN_ZEPHYR_PORTABLE_OPS OFF) | ||
| message( | ||
| "gen_oplist: No non-delegated ops were found in ${ET_PTE_FILE_PATH}; no ops added to build" | ||
| ) | ||
| endif() | ||
|
|
||
| find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) | ||
| project(executorch_mv2_ethosu) | ||
|
|
||
| set(CMAKE_CXX_FLAGS | ||
| "${CMAKE_CXX_FLAGS} -Wall -Wno-switch -Wno-float-conversion -Wno-double-promotion -ffunction-sections -fdata-sections" | ||
| ) | ||
|
|
||
| if(NOT DEFINED EXECUTORCH_DIR) | ||
| message( | ||
| STATUS | ||
| "ZEPHYR_EXECUTORCH_MODULE_DIR set to : ${ZEPHYR_EXECUTORCH_MODULE_DIR}" | ||
| ) | ||
| if(DEFINED ZEPHYR_EXECUTORCH_MODULE_DIR) | ||
| set(EXECUTORCH_DIR ${ZEPHYR_EXECUTORCH_MODULE_DIR}) | ||
| message( | ||
| STATUS "Using Zephyr module discovery: EXECUTORCH_DIR=${EXECUTORCH_DIR}" | ||
| ) | ||
| else() | ||
| message( | ||
| FATAL_ERROR | ||
| "ExecuTorch module not found. Ensure it's properly configured in your Zephyr workspace." | ||
| ) | ||
| endif() | ||
| else() | ||
| message(STATUS "Using predefined EXECUTORCH_DIR=${EXECUTORCH_DIR}") | ||
| endif() | ||
|
|
||
| set(EXECUTORCH_ROOT ${EXECUTORCH_DIR}) | ||
| include(${EXECUTORCH_DIR}/tools/cmake/Utils.cmake) | ||
|
|
||
| if(NOT TARGET portable_kernels) | ||
| set(EXECUTORCH_PORTABLE_BUILD_KERNELS_ONLY ON) | ||
| add_subdirectory( | ||
| ${EXECUTORCH_DIR}/kernels/portable | ||
| ${CMAKE_CURRENT_BINARY_DIR}/executorch/kernels/portable | ||
| ) | ||
| unset(EXECUTORCH_PORTABLE_BUILD_KERNELS_ONLY) | ||
| endif() | ||
| set(EXECUTORCH_OPS_LIB "") | ||
| if(_EXECUTORCH_GEN_ZEPHYR_PORTABLE_OPS) | ||
| include(${EXECUTORCH_DIR}/tools/cmake/Codegen.cmake) | ||
| if(NOT DEFINED EXECUTORCH_ENABLE_DTYPE_SELECTIVE_BUILD) | ||
| set(EXECUTORCH_ENABLE_DTYPE_SELECTIVE_BUILD "") | ||
| endif() | ||
| gen_selected_ops( | ||
| LIB_NAME | ||
| "cpu_portable_ops_lib" | ||
| OPS_SCHEMA_YAML | ||
| "" | ||
| ROOT_OPS | ||
| "${EXECUTORCH_SELECT_OPS_LIST}" | ||
| INCLUDE_ALL_OPS | ||
| "" | ||
| OPS_FROM_MODEL | ||
| "${EXECUTORCH_SELECT_OPS_MODEL}" | ||
| DTYPE_SELECTIVE_BUILD | ||
| "${EXECUTORCH_ENABLE_DTYPE_SELECTIVE_BUILD}" | ||
| ) | ||
| generate_bindings_for_kernels( | ||
| LIB_NAME "cpu_portable_ops_lib" FUNCTIONS_YAML | ||
| ${EXECUTORCH_DIR}/kernels/portable/functions.yaml DTYPE_SELECTIVE_BUILD | ||
| "${EXECUTORCH_ENABLE_DTYPE_SELECTIVE_BUILD}" | ||
| ) | ||
| gen_operators_lib( | ||
| LIB_NAME | ||
| "cpu_portable_ops_lib" | ||
| KERNEL_LIBS | ||
| portable_kernels | ||
| DEPS | ||
| executorch | ||
| DTYPE_SELECTIVE_BUILD | ||
| "${EXECUTORCH_ENABLE_DTYPE_SELECTIVE_BUILD}" | ||
| ) | ||
| set(EXECUTORCH_OPS_LIB "cpu_portable_ops_lib") | ||
| endif() | ||
|
|
||
| set(_local_flatcc_root ${CMAKE_BINARY_DIR}/flatcc_src) | ||
| if(NOT EXISTS ${_local_flatcc_root}/CMakeLists.txt) | ||
| file(MAKE_DIRECTORY ${_local_flatcc_root}) | ||
| execute_process( | ||
| COMMAND ${CMAKE_COMMAND} -E copy_directory | ||
| ${EXECUTORCH_DIR}/third-party/flatcc ${_local_flatcc_root} | ||
| ) | ||
| endif() | ||
| set(EXECUTORCH_FLATCC_SOURCE_ROOT | ||
| ${_local_flatcc_root} | ||
| CACHE PATH "" FORCE | ||
| ) | ||
| set(EXECUTORCH_FLATCC_INSTALL_ROOT | ||
| ${_local_flatcc_root} | ||
| CACHE PATH "" FORCE | ||
| ) | ||
|
|
||
| set(app_sources | ||
| src/main.cpp | ||
| ${EXECUTORCH_DIR}/examples/arm/executor_runner/arm_memory_allocator.cpp | ||
| ) | ||
| target_sources(app PRIVATE ${app_sources}) | ||
|
|
||
| set(_model_pte_header ${CMAKE_CURRENT_BINARY_DIR}/model_pte.h) | ||
| add_custom_command( | ||
| OUTPUT ${_model_pte_header} | ||
| COMMAND | ||
| ${Python3_EXECUTABLE} | ||
| ${EXECUTORCH_DIR}/examples/arm/executor_runner/pte_to_header.py --pte | ||
| ${ET_PTE_FILE_PATH} --outdir ${CMAKE_CURRENT_BINARY_DIR} --section | ||
| ${ET_PTE_SECTION} | ||
| DEPENDS ${ET_PTE_FILE_PATH} | ||
| ${EXECUTORCH_DIR}/examples/arm/executor_runner/pte_to_header.py | ||
| COMMENT "Converting ${ET_PTE_FILE_PATH} to model_pte.h" | ||
| ) | ||
| add_custom_target(gen_model_header DEPENDS ${_model_pte_header}) | ||
| add_dependencies(app gen_model_header) | ||
|
|
||
| if(DEFINED CONFIG_EXECUTORCH_METHOD_ALLOCATOR_POOL_SIZE) | ||
| target_compile_definitions( | ||
| app | ||
| PRIVATE | ||
| ET_ARM_METHOD_ALLOCATOR_POOL_SIZE=${CONFIG_EXECUTORCH_METHOD_ALLOCATOR_POOL_SIZE} | ||
| ) | ||
| endif() | ||
| if(DEFINED CONFIG_EXECUTORCH_TEMP_ALLOCATOR_POOL_SIZE) | ||
| target_compile_definitions( | ||
| app | ||
| PRIVATE | ||
| ET_ARM_BAREMETAL_SCRATCH_TEMP_ALLOCATOR_POOL_SIZE=${CONFIG_EXECUTORCH_TEMP_ALLOCATOR_POOL_SIZE} | ||
| ) | ||
| endif() | ||
|
|
||
| target_link_libraries(app PRIVATE libexecutorch) | ||
| if(EXECUTORCH_OPS_LIB) | ||
| target_link_libraries(app PRIVATE ${EXECUTORCH_OPS_LIB}) | ||
| endif() | ||
| if(CONFIG_CPU_CORTEX_M) | ||
| if(TARGET cortex_m_ops_lib) | ||
| target_link_libraries(app PRIVATE cortex_m_ops_lib) | ||
| endif() | ||
| if(TARGET cortex_m_kernels) | ||
| executorch_target_link_options_shared_lib(cortex_m_kernels) | ||
| target_link_libraries(app PRIVATE cortex_m_kernels) | ||
| endif() | ||
| endif() | ||
| if(TARGET quantized_kernels) | ||
| executorch_target_link_options_shared_lib(quantized_kernels) | ||
| target_link_libraries(app PRIVATE quantized_kernels) | ||
| endif() | ||
| if(TARGET portable_kernels) | ||
| executorch_target_link_options_shared_lib(portable_kernels) | ||
| target_link_libraries(app PRIVATE portable_kernels) | ||
| endif() | ||
| if(TARGET executorch_delegate_ethos_u) | ||
| executorch_target_link_options_shared_lib(executorch_delegate_ethos_u) | ||
| target_link_libraries(app PRIVATE executorch_delegate_ethos_u) | ||
| endif() | ||
| if(TARGET ethosu_core_driver) | ||
| target_link_libraries(app PRIVATE ethosu_core_driver) | ||
| endif() | ||
|
|
||
| target_include_directories(app PRIVATE src ${CMAKE_CURRENT_BINARY_DIR}) | ||
| get_target_property(OUT app LINK_LIBRARIES) | ||
| message(STATUS ${OUT}) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| # Copyright (c) Meta Platforms, Inc. and affiliates. | ||
| # All rights reserved. | ||
| # Copyright 2025-2026 Arm Limited and/or its affiliates. | ||
| # | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| source "Kconfig.zephyr" | ||
|
|
||
| menu "ExecuTorch MobileNetV2 sample configuration" | ||
|
|
||
| config EXECUTORCH_METHOD_ALLOCATOR_POOL_SIZE | ||
| int "Method allocator pool size in bytes" | ||
| default 1572864 | ||
| depends on EXECUTORCH | ||
| help | ||
| Size of the method allocator pool in bytes. MobileNetV2 requires | ||
| more memory than simple models. Default is 1.5MB which is sufficient | ||
| for a fully NPU-delegated INT8 MobileNetV2 model. | ||
|
|
||
| config EXECUTORCH_TEMP_ALLOCATOR_POOL_SIZE | ||
| int "Temporary allocator pool size in bytes" | ||
| default 1572864 | ||
| depends on EXECUTORCH | ||
| help | ||
| Size of the temporary allocator pool in bytes. Default is 1.5MB | ||
| which provides sufficient scratch space for MobileNetV2 inference | ||
| on Ethos-U NPU. | ||
|
|
||
| endmenu |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,82 @@ | ||
| # MobileNetV2 Image Classification with Ethos-U NPU | ||
|
|
||
| This sample demonstrates running a quantized MobileNetV2 image classification | ||
| model on the Arm Ethos-U NPU using ExecuTorch within a Zephyr RTOS application. | ||
|
|
||
| The model classifies a static 224x224x3 RGB test image into one of 1000 | ||
| ImageNet classes and prints the top-5 predictions. | ||
|
|
||
| ## Prerequisites | ||
|
|
||
| - Zephyr SDK with ExecuTorch module enabled | ||
| - Python 3.10+ with ExecuTorch, torchvision, and ethos-u-vela installed | ||
| - A board with Arm Ethos-U NPU (e.g., Corstone-300 FVP, Alif E7/E8 DevKit) | ||
|
|
||
| ## Export the model | ||
|
|
||
| Export a quantized INT8 MobileNetV2 model with Ethos-U delegation: | ||
|
|
||
| ```bash | ||
| python -m modules.lib.executorch.backends.arm.scripts.aot_arm_compiler \ | ||
| --model_name=mv2_untrained \ | ||
| --quantize \ | ||
| --delegate \ | ||
| --target=ethos-u55-128 \ | ||
| --output=mv2_ethosu.pte | ||
|
Comment on lines
+19
to
+25
|
||
| ``` | ||
|
|
||
| For boards with Ethos-U55-256 (e.g., Alif E8 HP core), use `--target=ethos-u55-256`. | ||
|
|
||
| ## Build | ||
|
|
||
| ### Corstone-300 FVP | ||
|
|
||
| ```bash | ||
| west build -b mps3/corstone300/fvp \ | ||
| modules/lib/executorch/zephyr/samples/mv2-ethosu \ | ||
| -t run -- \ | ||
| -DET_PTE_FILE_PATH=mv2_ethosu.pte | ||
| ``` | ||
|
|
||
| ### Alif Ensemble E8 DevKit | ||
|
|
||
| ```bash | ||
| west build -b alif_e8_dk/ae822fa0e5597xx0/rtss_hp \ | ||
| -S ethos-u55-enable \ | ||
| modules/lib/executorch/zephyr/samples/mv2-ethosu -- \ | ||
| -DET_PTE_FILE_PATH=mv2_ethosu.pte | ||
| ``` | ||
|
|
||
| ## Expected output | ||
|
|
||
| ``` | ||
| ======================================== | ||
| ExecuTorch MobileNetV2 Classification Demo | ||
| ======================================== | ||
|
|
||
| Ethos-U backend registered successfully | ||
| Model loaded, has 1 methods | ||
| Inference completed in <N> ms | ||
|
|
||
| --- Classification Results --- | ||
| Top-5 predictions: | ||
| [1] class <id>: <score> | ||
| [2] class <id>: <score> | ||
| ... | ||
|
|
||
| MobileNetV2 Demo Complete | ||
| Inference time: <N> ms | ||
| ======================================== | ||
| ``` | ||
|
|
||
| When using `mv2_untrained`, the output class IDs will be arbitrary since the | ||
| model has no trained weights. Use `mv2` (requires torchvision pretrained | ||
| weights) for meaningful predictions. | ||
|
|
||
| ## Memory requirements | ||
|
|
||
| The default configuration allocates 1.5 MB each for the method and temporary | ||
| allocator pools. These defaults are sufficient for a fully NPU-delegated INT8 | ||
| MobileNetV2. Adjust `CONFIG_EXECUTORCH_METHOD_ALLOCATOR_POOL_SIZE` and | ||
| `CONFIG_EXECUTORCH_TEMP_ALLOCATOR_POOL_SIZE` in `prj.conf` or via board-specific | ||
| overlay files for different model configurations. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| # Copyright 2026 Arm Limited and/or its affiliates. | ||
| # | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
| # | ||
| # Enable the Zephyr Ethos-U driver so executorch_delegate_ethos_u can reserve | ||
| # and use the hardware instance exposed by the board DTS. | ||
| CONFIG_ETHOS_U=y | ||
|
|
||
| # Corstone-300 has 2 MiB ISRAM. Reduce pool sizes to fit within budget | ||
| # alongside stack, heap, model data, and runtime buffers. | ||
| CONFIG_EXECUTORCH_METHOD_ALLOCATOR_POOL_SIZE=786432 | ||
| CONFIG_EXECUTORCH_TEMP_ALLOCATOR_POOL_SIZE=786432 |
Uh oh!
There was an error while loading. Please reload this page.