diff --git a/Readme.md b/Readme.md index 5fb49a30cf506b90d270e2b31af9c0f2ba7e83ef..1a938fb01bebff160a19d2eb021a6849920fa8e8 100644 --- a/Readme.md +++ b/Readme.md @@ -220,11 +220,8 @@ with the following limitations: * Arm® Compiler is not supported * Runtime memory usage is higher than we would like -* `Dedicated_Sram` mode is not supported on any NPU -* Arm® Ethos™-U65 NPU is not supported - -The last two limitations are expected to be addressed with an upcoming update to a newer version of the -ExecuTorch source tree. +* Arm® Ethos™-U65 NPU is not supported; this limitation could be resolved with minor tweaks within + Executorch tree. ## Licenses diff --git a/dependencies/executorch b/dependencies/executorch index ff9fcaa9863f476ef61b391bd219fb95c5756e0e..d6e25e26bf27660ea937cff68cb4c49dbb041d5d 160000 --- a/dependencies/executorch +++ b/dependencies/executorch @@ -1 +1 @@ -Subproject commit ff9fcaa9863f476ef61b391bd219fb95c5756e0e +Subproject commit d6e25e26bf27660ea937cff68cb4c49dbb041d5d diff --git a/docs/sections/troubleshooting.md b/docs/sections/troubleshooting.md index 0f410d84ae4b1a16a0b5d87669549a55389288d0..c3ea98fce860ecd5116da96b6bc3ceeebe2cf71a 100644 --- a/docs/sections/troubleshooting.md +++ b/docs/sections/troubleshooting.md @@ -29,7 +29,7 @@ For TensorFlow Lite Micro, ensure that your model is in a fully quantized `.tfli `int8`, and that it has successfully been run through the Vela compiler. For ExecuTorch ensure the model has been generated using the Arm AOT compiler (which calls Vela) with `--quantize` and `--delegate` options passed to it and the right target selected with `--target `. See the AOT compiler script -[aot_arm_compiler.py](https://github.com/pytorch/executorch/blob/v0.6.0-rc2/examples/arm/aot_arm_compiler.py) +[aot_arm_compiler.py](https://github.com/pytorch/executorch/blob/d6e25e26bf27660ea937cff68cb4c49dbb041d5d/examples/arm/aot_arm_compiler.py) in ExecuTorch source tree for more details. Also, please check that the CMake parameters used match the input requirements of your new model. diff --git a/scripts/cmake/executorch.cmake b/scripts/cmake/executorch.cmake index da8304e2a602e4bbdeb7d1923bddf3b8841a4646..8ca2ab7fdc1a3ad9218b997e94993fcd1fb43fe0 100644 --- a/scripts/cmake/executorch.cmake +++ b/scripts/cmake/executorch.cmake @@ -33,11 +33,6 @@ if (ETHOS_U_NPU_ENABLED) message(FATAL_ERROR "Support for Arm Ethos-U65 is currently disabled in this " "experimental branch. Use Arm Ethos-U55 or Arm Ethos-U85") endif() - if (ETHOS_U_NPU_MEMORY_MODE STREQUAL Dedicated_Sram) - message(FATAL_ERROR "`Dedicated_Sram` memory mode is not supported " - "by current rev of ExecuTorch." - "Use `Sram_Only` or `Shared_Sram` modes") - endif() endif() # Validate pre-requisites. @@ -50,22 +45,27 @@ set(EXECUTORCH_BUILD_EXECUTOR_RUNNER OFF) set(EXECUTORCH_BUILD_KERNELS_QUANTIZED ON) set(EXECUTORCH_BUILD_EXTENSION_RUNNER_UTIL ON) set(EXECUTORCH_ENABLE_LOGGING ON) +set(EXECUTORCH_BUILD_DEVTOOLS OFF) +set(EXECUTORCH_ENABLE_EVENT_TRACER OFF) +set(GFLAGS_INTTYPES_FORMAT C99) if(TARGET_PLATFORM STREQUAL native) set(EXECUTORCH_BUILD_ARM_BAREMETAL OFF) + set(EXECUTORCH_BUILD_CORTEX_M OFF) set(EXECUTORCH_BUILD_CPUINFO ON) else() set(EXECUTORCH_BUILD_ARM_BAREMETAL ON) + set(EXECUTORCH_BUILD_CORTEX_M ON) set(EXECUTORCH_BUILD_HOST_TARGETS OFF) endif() set(EXECUTORCH_PAL_DEFAULT minimal) # Map ExecuTorch supported log levels -if (${LOG_LEVEL} STREQUAL MLEK_LOG_LEVEL_TRACE OR - ${LOG_LEVEL} STREQUAL MLEK_LOG_LEVEL_DEBUG) +if (${MLEK_LOG_LEVEL} STREQUAL MLEK_LOG_LEVEL_TRACE OR + ${MLEK_LOG_LEVEL} STREQUAL MLEK_LOG_LEVEL_DEBUG) set(EXECUTORCH_LOG_LEVEL "Debug") -elseif(${LOG_LEVEL} STREQUAL MLEK_LOG_LEVEL_INFO) +elseif(${MLEK_LOG_LEVEL} STREQUAL MLEK_LOG_LEVEL_INFO) set(EXECUTORCH_LOG_LEVEL "Info") else() set(EXECUTORCH_LOG_LEVEL "Error") @@ -91,7 +91,8 @@ block(SCOPE_FOR VARIABLES) endif() # Add ET main subdirectory - add_subdirectory(${EXECUTORCH_SRC_PATH} ${CMAKE_BINARY_DIR}/executorch) + add_subdirectory(${EXECUTORCH_SRC_PATH} + ${CMAKE_BINARY_DIR}/executorch EXCLUDE_FROM_ALL) # Generate C++ bindings to register kernels into both PyTorch (for AOT) and # Executorch (for runtime). Here select all ops in functions.yaml @@ -127,20 +128,35 @@ block(SCOPE_FOR VARIABLES) endif() endblock() -set(MLEK_EXECUTORCH_LINK_STR) -list(APPEND MLEK_EXECUTORCH_LINK_STR - "-Wl,--whole-archive" - $<$:executorch_delegate_ethos_u> - "-Wl,--no-whole-archive" - quantized_ops_lib - quantized_kernels - portable_kernels - executorch +# Collate the targets for easily linking against. +add_library(mlek_executorch INTERFACE) + +target_link_libraries(mlek_executorch INTERFACE extension_runner_util -) + quantized_ops_lib) +# Based on target, link to the correct portable ops library if (TARGET_PLATFORM STREQUAL native) - list(APPEND MLEK_EXECUTORCH_LINK_STR portable_ops_lib) + target_link_libraries(mlek_executorch INTERFACE + portable_ops_lib) else() - list(APPEND MLEK_EXECUTORCH_LINK_STR arm_portable_ops_lib) + target_link_libraries(mlek_executorch INTERFACE + arm_portable_ops_lib + cortex_m_ops_lib) + + if (TARGET executorch_delegate_ethos_u) + # Supress warnings from Arm Ethos-U delegate library + target_compile_options(executorch_delegate_ethos_u PRIVATE + -Wno-error=deprecated-declarations + -Wno-error=unused-parameter) + + # Whole archive needs to be included for the delegate. + target_link_libraries(mlek_executorch INTERFACE + "-Wl,--whole-archive" + $ + "-Wl,--no-whole-archive") + endif() endif() + +# Provide alias target for rest of projects to use +add_library(meta::executorch ALIAS mlek_executorch) diff --git a/scripts/py/setup/setup_config.py b/scripts/py/setup/setup_config.py index 811b6eddc8e55c3be4e8358e7c119daceb5cfe26..ed645eeae295264cc84a5a22408f9d6faad3c6b8 100644 --- a/scripts/py/setup/setup_config.py +++ b/scripts/py/setup/setup_config.py @@ -100,8 +100,6 @@ class SetupContext: self.env_path: Path = Path("/") self.env_activate_cmd: str = "" - self.quantized_ops_lib_path: Path = Path("/") - @property def setup_config(self): """ diff --git a/set_up_default_resources.py b/set_up_default_resources.py index a172809bf12f9d5bb234fce147010595b25cdac5..888a802f908ef00cc293510757c201b6318c773b 100755 --- a/set_up_default_resources.py +++ b/set_up_default_resources.py @@ -26,7 +26,6 @@ import json import logging import os import re -import shutil import sys import textwrap import typing @@ -34,6 +33,7 @@ from argparse import ArgumentParser from argparse import ArgumentTypeError from pathlib import Path from enum import Enum +from tempfile import TemporaryDirectory from scripts.py.check_update_resources_downloaded import get_md5sum_for_file from scripts.py.setup.npu_config import NpuConfigs, NpuConfig @@ -386,38 +386,6 @@ def install_executorch(executorch_path: Path, env_activate_cmd: str) -> None: verbose=True ) - -def install_executorch_quantized_ops_lib( - executorch_path: Path, - env_activate_cmd: str -) -> typing.List[Path]: - """ - Builds the quantized ops shared library for host. The path to this lib - is needed for generating PTE files. - :param executorch_path: Root of Executorch source tree - :param env_activate_cmd: Command to activate the Python virtual - environment where we need to install. - returns List of paths to generated shared_lib files. - """ - if not executorch_path.is_dir(): - raise NotADirectoryError(f'Invalid dir {executorch_path}') - - if len(env_activate_cmd.strip()) == 0: - raise ValueError('venv activation command cannot be empty.') - - script_dir = executorch_path / 'backends' / 'arm' / 'scripts' - install_script = script_dir / 'build_quantized_ops_aot_lib.sh' - expected_lib_path = executorch_path / 'cmake-out-aot-lib' / 'kernels' / 'quantized' - expected_ext = '.so' - - call_command( - command=f'{env_activate_cmd} && {install_script}', - verbose=True - ) - - return list(expected_lib_path.glob('*' + expected_ext)) - - def optimize_executorch_model( model_name: str, npu_config: NpuConfig, @@ -434,16 +402,15 @@ def optimize_executorch_model( :return: True if optimization was skipped, False otherwise """ if npu_config is not None: - # Should be --memory_mode {npu_config.memory_mode} but Dedicated_Sram mode - # doesn't work with current rev of ExecuTorch. cfg = (f" --target {npu_config.config_name}" f" --system_config {npu_config.system_config}" - " --delegate --quantize" - " --memory_mode Shared_Sram") + f" --memory_mode {npu_config.memory_mode}" + f" --config {vela_config_file}" + " --delegate --quantize") optimized_model_name = f"{model_name}_arm_delegate_{npu_config.config_name}.pte" else: - cfg = " --quantize --target TOSA" - optimized_model_name = output_dir / f"{model_name}_arm_TOSA.pte" + cfg = " --target TOSA-1.0+INT" + optimized_model_name = output_dir / f"{model_name}_arm_TOSA-1.0+INT.pte" optimized_model_path = output_dir / optimized_model_name if optimized_model_path.is_file(): @@ -456,57 +423,40 @@ def optimize_executorch_model( call_command( command=(f"{setup_context.env_activate_cmd} && python3 -m examples.arm.aot_arm_compiler" f" --model_name={model_name}" - f"{cfg}" - f" --so_library={setup_context.quantized_ops_lib_path}" + f" {cfg}" f" --output {output_dir}"), cwd=setup_context.paths_config.executorch_path) return False -def setup_executorch(setup_context: SetupContext) -> typing.List[Path]: +def setup_executorch(setup_context: SetupContext): """ - Installs Tosa tools and ExecuTorch in the Python virtual environment and builds the - quantized ops shared libraries. + Installs TOSA tools and ExecuTorch in the Python virtual environment. + Note: ExecuTorch setup currently not supported on Microsoft Windows based systems. :param setup_context: SetupContext :return: list of shared library files needed for generating PTE files. """ + if sys.platform not in ['linux', 'darwin']: + raise EnvironmentError(f'{sys.platform} does not support ExecuTorch set up.') + # Install TOSA tools: - install_pip_package_if_needed( - f"git+{TOSA_URL}@{TOSA_VER}", - setup_context.env_activate_cmd, - installed_package_name="tosa-tools", - environment={ - "CMAKE_POLICY_VERSION_MINIMUM": 3.5 - }, - no_deps=True - ) + executorch_path = setup_context.paths_config.executorch_path + if not is_pip_package_installed('tosa-tools', setup_context.env_activate_cmd): + with TemporaryDirectory() as tmpdir: + tosa_tools_install_script = (executorch_path / + 'backends' / 'arm' / 'scripts' / + 'install_reference_model.sh') + logging.info('Installing TOSA tools using %s', tosa_tools_install_script) + call_command((f'{setup_context.env_activate_cmd} && ' + f'{tosa_tools_install_script} {tmpdir}'), + cwd=executorch_path) + else: + logging.info('tosa-tools package is already installed.') # Install ExecuTorch package if not is_pip_package_installed("executorch", setup_context.env_activate_cmd): - install_executorch(default_executorch_path, setup_context.env_activate_cmd) - - # Build and install quantized ops libraries: - lib_aot_quantized_ops_path = setup_context.env_path / "libquantized_ops_aot_lib.so" - if lib_aot_quantized_ops_path.exists(): - et_ops_lib_list = [lib_aot_quantized_ops_path] - else: - et_ops_lib_list = install_executorch_quantized_ops_lib( - default_executorch_path, - setup_context.env_activate_cmd - ) - if not et_ops_lib_list: - raise FileNotFoundError('No shared libraries found for ExecuTorch quantized ops') - - logging.info('Libs required for pte file generation: %s,', et_ops_lib_list) - for file in et_ops_lib_list: - if file.is_file(): - shutil.copy(file, setup_context.env_path / file.name) - - et_ops_lib_list = [setup_context.env_path / file.name for file in et_ops_lib_list] - logging.info('Libs copied here: %s', et_ops_lib_list) - - return et_ops_lib_list + install_executorch(executorch_path, setup_context.env_activate_cmd) def setup_vela(env_activate_cmd: str): @@ -819,14 +769,9 @@ def set_up_resources( setup_vela(context.env_activate_cmd) if setup_config.set_up_executorch: - et_ops_lib_list = setup_executorch(context) - context.quantized_ops_lib_path = [ - lib for lib in et_ops_lib_list - if lib.name == "libquantized_ops_aot_lib.so" - ][0] + setup_executorch(context) # Download models - npu_configs = [ get_default_npu_config_from_name(npu_config_name, optimization_config.arena_cache_size) for npu_config_name in set( @@ -835,6 +780,8 @@ def set_up_resources( ) ] + # Arm AOT compiler (helper script within ExecuTorch) does not list support + # for Arm Ethos-U65 yet. executorch_npu_configs = [config for config in npu_configs if config.processor_id != "U65"] # For ExecuTorch, we need to generate TOSA PTE files for native host as well. diff --git a/source/application/api/fwk/executorch/CMakeLists.txt b/source/application/api/fwk/executorch/CMakeLists.txt index eb8b626ecd79faa26c24a8632fdf8b08bbf62480..c784099a855a8f8c1e253df6347edac866953bf4 100644 --- a/source/application/api/fwk/executorch/CMakeLists.txt +++ b/source/application/api/fwk/executorch/CMakeLists.txt @@ -46,12 +46,14 @@ target_include_directories(${ML_FWK_ET_TARGET} PUBLIC include) # Link time library targets: -target_link_libraries(${ML_FWK_ET_TARGET} PUBLIC - mlek_log # Logging functions - ml-framework-iface # ML framework interface lib - ${MLEK_EXECUTORCH_LINK_STR}) # ExecuTorch libraries - -target_compile_definitions(${ML_FWK_ET_TARGET} INTERFACE MLEK_FWK_EXECUTORCH=1) +target_link_libraries(${ML_FWK_ET_TARGET} + PUBLIC + mlek_log # Logging functions + ml-framework-iface # ML framework interface lib + meta::executorch) # ExecuTorch libraries + +target_compile_definitions(${ML_FWK_ET_TARGET} INTERFACE + MLEK_FWK_EXECUTORCH=1) # Display status: message(STATUS "*******************************************************") diff --git a/source/use_case/img_class/usecase.cmake b/source/use_case/img_class/usecase.cmake index d21b9397dd408fc82a3d38ef89a9a72690d466c6..c13972e5942d1d948930a3da44f2305e115d4c6f 100644 --- a/source/use_case/img_class/usecase.cmake +++ b/source/use_case/img_class/usecase.cmake @@ -53,7 +53,7 @@ elseif(${ML_FRAMEWORK} STREQUAL "ExecuTorch") set(DEFAULT_MODEL_PATH ${DEFAULT_MODEL_DIR}/mv2_arm_delegate_${_NPU_CFG_ID}.pte) set(DEFAULT_ACTIVATION_BUF_SZ 0x00200000) else() - set(DEFAULT_MODEL_PATH ${DEFAULT_MODEL_DIR}/mv2_arm_TOSA.pte) + set(DEFAULT_MODEL_PATH ${DEFAULT_MODEL_DIR}/mv2_arm_TOSA-1.0+INT.pte) set(DEFAULT_ACTIVATION_BUF_SZ 0x00C00000) endif() endif()