diff --git a/.github/workflows/trunk.yml b/.github/workflows/trunk.yml index 04a6c96f3ec..dff2b400ee6 100644 --- a/.github/workflows/trunk.yml +++ b/.github/workflows/trunk.yml @@ -159,7 +159,7 @@ jobs: sudo sysctl fs.inotify.max_user_watches=1048576 # 1024 * 1024 # Test ethos-u delegate examples with run.sh - backends/arm/test/test_arm_baremetal.sh test_run_ethosu_fvp + backends/arm/test/test_arm_baremetal.sh test_full_ethosu_fvp test-arm-reference-delegation: diff --git a/backends/arm/README.md b/backends/arm/README.md index 9a5a6f94085..04815bf23d2 100644 --- a/backends/arm/README.md +++ b/backends/arm/README.md @@ -55,10 +55,10 @@ To run the unit test suite with Corstone3x0 FVP simulator support use backends/arm/test/test_arm_baremetal.sh test_pytest_ethosu_fvp ``` -You can test to run some models with the run.sh flow +You can test to run some models with the full fvp test flow ``` -backends/arm/test/test_arm_baremetal.sh test_run_ethosu_fvp +backends/arm/test/test_arm_baremetal.sh test_full_ethosu_fvp ``` ## Unit tests diff --git a/backends/arm/scripts/build_executorch.sh b/backends/arm/scripts/build_executorch.sh new file mode 100755 index 00000000000..f868d264f48 --- /dev/null +++ b/backends/arm/scripts/build_executorch.sh @@ -0,0 +1,123 @@ +#!/usr/bin/env bash +# Copyright 2025 Arm Limited and/or its affiliates. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. + +# Optional parameter: +# --build_type= "Release" | "Debug" | "RelWithDebInfo" +# --etdump build with devtools-etdump support + +set -eu + +script_dir=$(cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd) +et_root_dir=$(cd ${script_dir}/../../.. && pwd) +et_root_dir=$(realpath ${et_root_dir}) +toolchain_cmake=${script_dir}/../../../examples/arm/ethos-u-setup/arm-none-eabi-gcc.cmake +toolchain_cmake=$(realpath ${toolchain_cmake}) + + + +et_build_root="${et_root_dir}/arm_test" +build_type="Release" +build_with_etdump=false + + +help() { + echo "Usage: $(basename $0) [options]" + echo "Options:" + echo " --et_build_root= Build output root folder to use, defaults to ${et_build_root}" + echo " --build_type= Build with Release, Debug or RelWithDebInfo, default is ${build_type}" + echo " --etdump Adds Devtools etdump support to track timing, etdump area will be base64 encoded in the log" + exit 0 +} + +for arg in "$@"; do + case $arg in + -h|--help) help ;; + --et_build_root=*) et_build_root="${arg#*=}";; + --build_type=*) build_type="${arg#*=}";; + --etdump) build_with_etdump=true ;; + *) + ;; + esac +done + +et_build_dir="${et_build_root}/cmake-out" +et_build_host_dir=${et_build_root}/cmake-out-host-tools + +set -x +cd "${et_root_dir}" + +build_with_etdump_flags="" +if [ "$build_with_etdump" = true ] ; then + ( set +x ; + echo "--------------------------------------------------------------------------------" ; + echo "Build ExecuTorch Libraries host flatcc bin ${build_type} into ${et_build_host_dir} - ${et_build_host_dir}/bin/flatcc" ; + echo "--------------------------------------------------------------------------------" ) + + + # Build host flatcc bin + # This is a way to work around that the flatcc executable get build for target (e.g. Arm) later + # and get replaced. flatcc is a tool used on the host for etdump and BundleIO handling. + # The way to solve this is to generate it once for the host, then copy it to ${et_build_host_dir}/bin + # and later point that out with -DFLATCC_EXECUTABLE=${et_build_host_dir}/bin/flatcc later. + mkdir -p ${et_build_host_dir} + cmake \ + -DCMAKE_INSTALL_PREFIX=${et_build_host_dir} \ + -DCMAKE_BUILD_TYPE=${build_type} \ + -DEXECUTORCH_BUILD_EXECUTOR_RUNNER=OFF \ + -DEXECUTORCH_ENABLE_LOGGING=ON \ + -DEXECUTORCH_BUILD_ARM_BAREMETAL=ON \ + -DEXECUTORCH_BUILD_KERNELS_QUANTIZED=ON \ + -DEXECUTORCH_BUILD_EXTENSION_RUNNER_UTIL=ON \ + -DEXECUTORCH_BUILD_DEVTOOLS=ON \ + -DEXECUTORCH_ENABLE_EVENT_TRACER=ON \ + -DEXECUTORCH_SEPARATE_FLATCC_HOST_PROJECT=ON \ + -DFLATCC_ALLOW_WERROR=OFF \ + -DFLATC_EXECUTABLE="$(which flatc)" \ + -B"${et_build_host_dir}" \ + "${et_root_dir}" + + # Copy host flatcc excutable to it's saved when we build for target (Arm) later + mkdir -p ${et_build_host_dir}/bin + cp third-party/flatcc/bin/flatcc ${et_build_host_dir}/bin + + # Add DevTools flags use in the Target build below + build_with_etdump_flags="-DEXECUTORCH_BUILD_DEVTOOLS=ON \ + -DEXECUTORCH_ENABLE_EVENT_TRACER=ON \ + -DEXECUTORCH_SEPARATE_FLATCC_HOST_PROJECT=OFF \ + -DEXECUTORCH_BUILD_EXTENSION_DATA_LOADER=OFF \ + -DFLATCC_ALLOW_WERROR=OFF \ + -DFLATCC_EXECUTABLE=${et_build_host_dir}/bin/flatcc " + echo "build_with_etdump_flags=$build_with_etdump_flags" +fi + +( set +x ; + echo "--------------------------------------------------------------------------------" ; + echo "Build ExecuTorch target libs ${build_type} into '${et_build_dir}'" ; + echo "--------------------------------------------------------------------------------" ) + +# Build +cmake \ + -DCMAKE_INSTALL_PREFIX=${et_build_dir} \ + -DCMAKE_BUILD_TYPE=${build_type} \ + -DCMAKE_TOOLCHAIN_FILE="${toolchain_cmake}" \ + -DEXECUTORCH_BUILD_EXECUTOR_RUNNER=OFF \ + -DEXECUTORCH_BUILD_ARM_BAREMETAL=ON \ + -DEXECUTORCH_BUILD_KERNELS_QUANTIZED=ON \ + -DEXECUTORCH_BUILD_EXTENSION_RUNNER_UTIL=ON \ + -DEXECUTORCH_ENABLE_LOGGING=ON \ + ${build_with_etdump_flags} \ + -DFLATC_EXECUTABLE="$(which flatc)" \ + -B"${et_build_dir}" \ + "${et_root_dir}" + +echo "[$(basename $0)] Configured CMAKE" + +cmake --build ${et_build_dir} --parallel --target install --config ${build_type} -- + +set +x + +echo "[$(basename $0)] Generated static libraries for ExecuTorch:" +find ${et_build_dir} -name "*.a" -exec ls -al {} \; diff --git a/backends/arm/scripts/build_executorch_runner.sh b/backends/arm/scripts/build_executorch_runner.sh new file mode 100755 index 00000000000..afa8f27bdff --- /dev/null +++ b/backends/arm/scripts/build_executorch_runner.sh @@ -0,0 +1,125 @@ +#!/usr/bin/env bash +# Copyright 2025 Arm Limited and/or its affiliates. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. + +set -eu + +script_dir=$(cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd) +et_root_dir=$(cd ${script_dir}/../../.. && pwd) +et_root_dir=$(realpath ${et_root_dir}) +toolchain_cmake=${et_root_dir}/examples/arm/ethos-u-setup/arm-none-eabi-gcc.cmake + +pte_file="" +target="ethos-u55-128" +build_type="Release" +system_config="" +build_with_etdump=false +extra_build_flags="" +output_folder_set=false +output_folder="." +et_build_root="${et_root_dir}/arm_test" +ethosu_tools_dir=${et_root_dir}/examples/arm/ethos-u-scratch + +help() { + echo "Usage: $(basename $0) [options]" + echo "Options:" + echo " --pte= pte file (genrated by the aot_arm_compier from the model to include in the elf" + echo " --target= Target to build and run for Default: ${target}" + echo " --build_type= Build with Release, Debug or RelWithDebInfo, default is ${build_type}" + echo " --system_config= System configuration to select from the Vela configuration file (see vela.ini). Default: Ethos_U55_High_End_Embedded for EthosU55 targets, Ethos_U85_SYS_DRAM_Mid for EthosU85 targets." + echo " NOTE: If given, this option must match the given target. This option also sets timing adapter values customized for specific hardware, see ./executor_runner/CMakeLists.txt." + echo " --etdump Adds Devtools etdump support to track timing, etdump area will be base64 encoded in the log" + echo " --extra_build_flags= Extra flags to pass to cmake like -DET_ARM_BAREMETAL_METHOD_ALLOCATOR_POOL_SIZE=60000 Default: none " + echo " --output= Output folder Default: /_.pte" + echo " --et_build_root= Build output root folder to use, defaults to ${et_build_root}" + echo " --ethosu_tools_dir= Path to your Ethos-U tools dir if you not using default: ${ethosu_tools_dir}" + exit 0 +} + +for arg in "$@"; do + case $arg in + -h|--help) help ;; + --pte=*) pte_file="${arg#*=}";; + --target=*) target="${arg#*=}";; + --build_type=*) build_type="${arg#*=}";; + --system_config=*) system_config="${arg#*=}";; + --etdump) build_with_etdump=true ;; + --extra_build_flags=*) extra_build_flags="${arg#*=}";; + --output=*) output_folder="${arg#*=}" ; output_folder_set=true ;; + --et_build_root=*) et_build_root="${arg#*=}";; + --ethosu_tools_dir=*) ethosu_tools_dir="${arg#*=}";; + *) + ;; + esac +done + +pte_file=$(realpath ${pte_file}) +ethosu_tools_dir=$(realpath ${ethosu_tools_dir}) +ethos_u_root_dir="$ethosu_tools_dir/ethos-u" +ethosu_tools_dir=$(realpath ${ethos_u_root_dir}) + +et_build_dir=${et_build_root}/cmake-out +et_build_dir=$(realpath ${et_build_dir}) + +if [ "$output_folder_set" = false ] ; then + pte_folder=$(cd -- "$( dirname -- "${pte_file}" )" &> /dev/null && pwd) + pte_short_name=$(basename -- "${pte_file}" ".pte") + output_folder="$pte_folder/$pte_short_name" +fi + +if [[ ${system_config} == "" ]] +then + system_config="Ethos_U55_High_End_Embedded" + if [[ ${target} =~ "ethos-u85" ]] + then + system_config="Ethos_U85_SYS_DRAM_Mid" + fi +fi + +output_folder=$(realpath ${output_folder}) + +if [[ ${target} == *"ethos-u55"* ]]; then + target_cpu=cortex-m55 +else + target_cpu=cortex-m85 +fi +echo "--------------------------------------------------------------------------------" +echo "Build Arm Baremetal executor_runner for ${target} with ${pte_file} using ${system_config} to '${output_folder}/cmake-out'" +echo "--------------------------------------------------------------------------------" + +cd ${et_root_dir}/examples/arm/executor_runner + +build_with_etdump_flags="" +if [ "$build_with_etdump" = true ] ; then + echo "Building with etdump e.g. -DEXECUTORCH_ENABLE_EVENT_TRACER=ON" + build_with_etdump_flags=" -DEXECUTORCH_ENABLE_EVENT_TRACER=ON " +fi + +mkdir -p "$output_folder" + +cmake \ + -DCMAKE_BUILD_TYPE=${build_type} \ + -DCMAKE_TOOLCHAIN_FILE=${toolchain_cmake} \ + -DTARGET_CPU=${target_cpu} \ + -DET_DIR_PATH:PATH=${et_root_dir} \ + -DET_BUILD_DIR_PATH:PATH=${et_build_dir} \ + -DET_PTE_FILE_PATH:PATH="${pte_file}" \ + -DETHOS_SDK_PATH:PATH=${ethos_u_root_dir} \ + -DETHOSU_TARGET_NPU_CONFIG=${target} \ + ${build_with_etdump_flags} \ + -DPYTHON_EXECUTABLE=$(which python3) \ + -DSYSTEM_CONFIG=${system_config} \ + ${extra_build_flags} \ + -B ${output_folder}/cmake-out + +echo "[${BASH_SOURCE[0]}] Configured CMAKE" + +cmake --build ${output_folder}/cmake-out --parallel -- arm_executor_runner + +echo "[${BASH_SOURCE[0]}] Generated baremetal elf file:" +find ${output_folder}/cmake-out -name "arm_executor_runner" +echo "executable_text: $(find ${output_folder}/cmake-out -name arm_executor_runner -exec arm-none-eabi-size {} \; | grep -v filename | awk '{print $1}') bytes" +echo "executable_data: $(find ${output_folder}/cmake-out -name arm_executor_runner -exec arm-none-eabi-size {} \; | grep -v filename | awk '{print $2}') bytes" +echo "executable_bss: $(find ${output_folder}/cmake-out -name arm_executor_runner -exec arm-none-eabi-size {} \; | grep -v filename | awk '{print $3}') bytes" diff --git a/backends/arm/scripts/build_portable_kernels.sh b/backends/arm/scripts/build_portable_kernels.sh new file mode 100755 index 00000000000..afdccd79cfd --- /dev/null +++ b/backends/arm/scripts/build_portable_kernels.sh @@ -0,0 +1,74 @@ +#!/usr/bin/env bash +# Copyright 2025 Arm Limited and/or its affiliates. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. + +# Optional parameter: +# --build_type= "Release" | "Debug" | "RelWithDebInfo" +# --etdump build with devtools-etdump support + +set -eu + +script_dir=$(cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd) +et_root_dir=$(cd ${script_dir}/../../.. && pwd) +et_root_dir=$(realpath ${et_root_dir}) +toolchain_cmake=${script_dir}/../../../examples/arm/ethos-u-setup/arm-none-eabi-gcc.cmake +toolchain_cmake=$(realpath ${toolchain_cmake}) + + +et_build_root="${et_root_dir}/arm_test" +build_type="Release" +portable_kernels="aten::_softmax.out" + +help() { + echo "Usage: $(basename $0) [options]" + echo "Options:" + echo " --et_build_root= Build output root folder to use, defaults to ${et_build_root}" + echo " --build_type= Build with Release, Debug or RelWithDebInfo, default is ${build_type}" + echo " --portable_kernels= Comma separated list of portable (non delagated) kernels to include Default: ${portable_kernels}" + exit 0 +} + +for arg in "$@"; do + case $arg in + -h|--help) help ;; + --et_build_root=*) et_build_root="${arg#*=}";; + --build_type=*) build_type="${arg#*=}";; + --portable_kernels=*) portable_kernels="${arg#*=}";; + *) + ;; + esac +done + +et_build_dir=${et_build_root}/cmake-out + +cd "${et_root_dir}" + +echo "--------------------------------------------------------------------------------" ; +echo "Build ExecuTorch Libraries ${build_type} portable kernels: ${portable_kernels} into '${et_build_dir}'" ; +echo "--------------------------------------------------------------------------------" + +if ! [[ $portable_kernels =~ ^((^|,)aten::[a-zA-Z0-9_]+\.[a-zA-Z0-9_]*out)*$ ]]; then + echo " ERROR: specified argument --portable_kernels=${portable_kernels}" + echo " is in the wrong format please use \"aten::.out,aten::.out,...\"" + echo " e.g. \"aten::_softmax.out,aten::add.out\"" + exit 1 +fi + +set -x + +cmake \ + -DCMAKE_INSTALL_PREFIX=${et_build_dir} \ + -DCMAKE_BUILD_TYPE=${build_type} \ + -DCMAKE_TOOLCHAIN_FILE="${toolchain_cmake}" \ + -DEXECUTORCH_SELECT_OPS_LIST=${portable_kernels} \ + -B"${et_build_dir}/examples/arm" \ + "${et_root_dir}/examples/arm" + +cmake --build "${et_build_dir}/examples/arm" --parallel --config ${build_type} -- + +set +x + +echo "[$(basename $0)] Generated static libraries for ExecuTorch:" +find "${et_build_dir}/examples/arm" -name "*.a" -exec ls -al {} \; diff --git a/backends/arm/scripts/build_quantized_ops_aot_lib.sh b/backends/arm/scripts/build_quantized_ops_aot_lib.sh index 3c70b48a5dc..ad6fad9c122 100755 --- a/backends/arm/scripts/build_quantized_ops_aot_lib.sh +++ b/backends/arm/scripts/build_quantized_ops_aot_lib.sh @@ -4,26 +4,51 @@ # This source code is licensed under the BSD-style license found in the # LICENSE file in the root directory of this source tree. -# Needs to be run from exeuctorch root. # Optional parameter: 1: build_type= "Release" | "Debug" | "RelWithDebInfo" +set -eu +script_dir=$(cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd) +et_root_dir=$(cd ${script_dir}/../../.. && pwd) +et_root_dir=$(realpath ${et_root_dir}) + build_type="Release" +et_build_root="${et_root_dir}" + +help() { + echo "Usage: $(basename $0) [options]" + echo "Options:" + echo " --et_build_root= Build output root folder to use, defaults to ${et_build_root}" + echo " --build_type= Build with Release, Debug or RelWithDebInfo, default is ${build_type}" + exit 0 +} + +for arg in "$@"; do + case $arg in + -h|--help) help ;; + --et_build_root=*) et_build_root="${arg#*=}";; + --build_type=*) build_type="${arg#*=}";; + *) + ;; + esac +done + +et_build_dir=${et_build_root}/cmake-out-aot-lib -build_type=${1:-$build_type} +cd "${et_root_dir}" echo "--------------------------------------------------------------------------------" -echo "Build .so library to register quant ops with AoT flow ${build_type} into '$(echo $(pwd))/cmake-out-aot-lib'" +echo "Build quantized_ops_aot_lib library to register quant ops with AoT flow ${build_type} into '${et_build_dir}'" echo "--------------------------------------------------------------------------------" # Since we only want to build the quantized_aot lib in the specified folder, # we want exactly the configuration set below and deleting the cache is OK. -rm -f cmake-out-aot-lib/CMakeCache.txt +rm -f ${et_build_dir}/CMakeCache.txt CXXFLAGS="-fno-exceptions -fno-rtti" cmake \ -DCMAKE_BUILD_TYPE=${build_type} \ -DEXECUTORCH_BUILD_KERNELS_QUANTIZED=ON \ -DEXECUTORCH_BUILD_KERNELS_QUANTIZED_AOT=ON \ - -Bcmake-out-aot-lib \ + -B${et_build_dir} \ . -cmake --build cmake-out-aot-lib --parallel -- quantized_ops_aot_lib +cmake --build ${et_build_dir} --parallel -- quantized_ops_aot_lib diff --git a/backends/arm/scripts/run_fvp.sh b/backends/arm/scripts/run_fvp.sh new file mode 100755 index 00000000000..568f07011f2 --- /dev/null +++ b/backends/arm/scripts/run_fvp.sh @@ -0,0 +1,104 @@ +#!/usr/bin/env bash +# Copyright 2025 Arm Limited and/or its affiliates. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. + +# Optional parameter: +# --build_type= "Release" | "Debug" | "RelWithDebInfo" +# --etdump build with devtools-etdump support + +set -eu + +script_dir=$(cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd) +et_root_dir=$(cd ${script_dir}/../../.. && pwd) +et_root_dir=$(realpath ${et_root_dir}) +setup_path_script=${et_root_dir}/examples/arm/ethos-u-scratch/setup_path.sh +_setup_msg="please refer to ${et_root_dir}/examples/arm/setup.sh to properly install necessary tools." + + +elf_file="" +target="ethos-u55-128" + +help() { + echo "Usage: $(basename $0) [options]" + echo "Options:" + echo " --elf= elf file to run" + echo " --target= Target to build and run for Default: ${target}" + exit 0 +} + +for arg in "$@"; do + case $arg in + -h|--help) help ;; + --elf=*) elf_file="${arg#*=}";; + --target=*) target="${arg#*=}";; + *) + ;; + esac +done + +elf_file=$(realpath ${elf_file}) + +if [[ ${target} == *"ethos-u55"* ]]; then + fvp_model=FVP_Corstone_SSE-300_Ethos-U55 +else + fvp_model=FVP_Corstone_SSE-320 +fi + +# Source the tools +# This should be prepared by the setup.sh +[[ -f ${setup_path_script} ]] \ + || { echo "Missing ${setup_path_script}. ${_setup_msg}"; exit 1; } + +source ${setup_path_script} + +# basic checks before we get started +hash ${fvp_model} \ + || { echo "Could not find ${fvp_model} on PATH, ${_setup_msg}"; exit 1; } + + +[[ ! -f $elf_file ]] && { echo "[${BASH_SOURCE[0]}]: Unable to find executor_runner elf: ${elf_file}"; exit 1; } +num_macs=$(echo ${target} | cut -d - -f 3) + +echo "--------------------------------------------------------------------------------" +echo "Running ${elf_file} for ${target} run with FVP:${fvp_model} num_macs:${num_macs}" +echo "--------------------------------------------------------------------------------" + +log_file=$(mktemp) + +if [[ ${target} == *"ethos-u55"* ]]; then + ${fvp_model} \ + -C ethosu.num_macs=${num_macs} \ + -C mps3_board.visualisation.disable-visualisation=1 \ + -C mps3_board.telnetterminal0.start_telnet=0 \ + -C mps3_board.uart0.out_file='-' \ + -C mps3_board.uart0.shutdown_on_eot=1 \ + -a "${elf_file}" \ + --timelimit 220 2>&1 | tee ${log_file} || true # seconds + echo "[${BASH_SOURCE[0]}] Simulation complete, $?" +elif [[ ${target} == *"ethos-u85"* ]]; then + ${fvp_model} \ + -C mps4_board.subsystem.ethosu.num_macs=${num_macs} \ + -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.shutdown_on_eot=1 \ + -a "${elf_file}" \ + --timelimit 220 2>&1 | tee ${log_file} || true # seconds + echo "[${BASH_SOURCE[0]}] Simulation complete, $?" +else + echo "Running ${elf_file} for ${target} is not supported" + exit 1 +fi + +echo "Checking for problems in log:" +! grep -E "^(F|E|\\[critical\\]|Hard fault.|Info: Simulation is stopping. Reason: CPU time has been exceeded.).*$" ${log_file} +if [ $? != 0 ]; then + echo "Found ERROR" + rm "${log_file}" + exit 1 +fi +echo "No problems found!" +rm "${log_file}" diff --git a/backends/arm/test/runner_utils.py b/backends/arm/test/runner_utils.py index 65be0b88f7b..2d182b4a410 100644 --- a/backends/arm/test/runner_utils.py +++ b/backends/arm/test/runner_utils.py @@ -525,7 +525,7 @@ def corstone320_installed() -> bool: def get_elf_path(target_board): elf_path = os.path.join( - "cmake-out", + "arm_test", f"arm_semihosting_executor_runner_{target_board}", "arm_executor_runner", ) diff --git a/backends/arm/test/setup_testing.sh b/backends/arm/test/setup_testing.sh index ebf9d799677..b9f8fc454ee 100755 --- a/backends/arm/test/setup_testing.sh +++ b/backends/arm/test/setup_testing.sh @@ -12,8 +12,8 @@ et_root_dir=$(cd ${script_dir}/../../.. && pwd) ethos_u_root_dir=${et_root_dir}/examples/arm/ethos-u-scratch/ethos-u toolchain_cmake=${et_root_dir}/examples/arm/ethos-u-setup/arm-none-eabi-gcc.cmake -et_build_dir=${et_root_dir}/cmake-out -build_root_test_dir=${et_build_dir}/arm_semihosting_executor_runner +et_build_dir=${et_root_dir}/arm_test/cmake-out +build_root_test_dir=${et_root_dir}/arm_test/arm_semihosting_executor_runner # Build Arm Baremetal executor_runner in semihosting mode. # Put in backends/arm/test/res to be used by unit tests. @@ -38,12 +38,12 @@ function build_semihosting_executorch_runner() { -DTARGET_CPU=${target_cpu} \ -DSEMIHOSTING=ON \ -DCMAKE_RUNTIME_OUTPUT_DIRECTORY=${build_test_dir} \ - -B ${build_test_dir} \ -DETHOS_SDK_PATH:PATH=${ethos_u_root_dir} \ -DET_DIR_PATH:PATH=${et_root_dir} \ -DET_BUILD_DIR_PATH:PATH=${et_build_dir} \ -DPYTHON_EXECUTABLE=$(which python3) \ - -DSYSTEM_CONFIG=${system_config} + -DSYSTEM_CONFIG=${system_config} \ + -B ${build_test_dir} echo "[${FUNCNAME[0]}] Configured CMAKE" n=$(nproc) diff --git a/backends/arm/test/test_arm_baremetal.sh b/backends/arm/test/test_arm_baremetal.sh index 9f2fa4c17d0..6c2784501b0 100755 --- a/backends/arm/test/test_arm_baremetal.sh +++ b/backends/arm/test/test_arm_baremetal.sh @@ -17,46 +17,47 @@ pwd TEST_SUITE=$1 help() { - echo "Usage:" - echo " $0 " - echo " where can be any of:" - # This will list all lines in this file that is starting with test_ remove () { and print it as a list. - # e,g, "test_pytest() { # Test ops and other things" -> test_pytest # Test ops and other things - echo "all # run all tests" - grep "^test_" $0 | sed 's/([^)]*)[[:space:]]*{*//g' - exit + echo "Usage:" + echo " $0 " + echo " where can be any of:" + # This will list all lines in this file that is starting with test_ remove () { and print it as a list. + # e,g, "test_pytest() { # Test ops and other things" -> test_pytest # Test ops and other things + echo "all # run all tests" + grep "^test_" $0 | sed 's/([^)]*)[[:space:]]*{*//g' + exit } if [[ -z "${TEST_SUITE:-}" ]]; then - echo "Missing test suite name, exiting..." - help + echo "Missing test suite name, exiting..." + help else - echo "Run Arm baremetal test suite ${TEST_SUITE}" + echo "Run Arm baremetal test suite ${TEST_SUITE}" fi TEST_SUITE_NAME="$(basename "$0") ${TEST_SUITE}" all() { # Run all tests - # This will list all lines in this file that is starting with test_ remove () { and add this script name in - # front of it and execute it in a sub shell - # e.g. from this file: - # - # test_pytest() { # Test ops and other things - # bla bla bla - # } - # test_pytest_ethosu_fvp() { # Same as test_pytest but ... - # bla bla bla - # } - #... - # become a small script: - # ---- - # backends/arm/test/test_arm_baremetal.sh test_pytest # Test ops and other things - # backends/arm/test/test_arm_baremetal.sh test_pytest_ethosu_fvp # Same as test_pytest but ... - # ... - # ---- - # That is executed - echo "${TEST_SUITE_NAME}: Run all tests" - grep "^test_" backends/arm/test/test_arm_baremetal.sh | sed 's/([^)]*)[[:space:]]*{*//g' | sed "s|^|$0 |" | sh + # This will list all lines in this file that is starting with test_ remove () { and add this script name in + # front of it and execute it in a sub shell + # e.g. from this file: + # + # test_pytest() { # Test ops and other things + # bla bla bla + # } + # test_pytest_ethosu_fvp() { # Same as test_pytest but ... + # bla bla bla + # } + #... + # become a small script: + # ---- + # backends/arm/test/test_arm_baremetal.sh test_pytest # Test ops and other things + # backends/arm/test/test_arm_baremetal.sh test_pytest_ethosu_fvp # Same as test_pytest but ... + # ... + # ---- + # That is executed + echo "${TEST_SUITE_NAME}: Run all tests" + grep "^test_" backends/arm/test/test_arm_baremetal.sh | sed 's/([^)]*)[[:space:]]*{*//g' | sed "s|^|$0 |" | sh + echo "${TEST_SUITE_NAME}: PASS" } test_pytest() { # Test ops and other things @@ -67,6 +68,7 @@ test_pytest() { # Test ops and other things # Run arm baremetal pytest tests without FVP pytest --verbose --color=yes --numprocesses=auto backends/arm/test/ + echo "${TEST_SUITE_NAME}: PASS" } test_pytest_ethosu_fvp() { # Same as test_pytest but also sometime verify using Corstone FVP @@ -80,28 +82,68 @@ test_pytest_ethosu_fvp() { # Same as test_pytest but also sometime verify using # Run arm baremetal pytest tests with FVP pytest --verbose --color=yes --numprocesses=auto backends/arm/test/ --arm_run_corstoneFVP + echo "${TEST_SUITE_NAME}: PASS" } -test_run_ethosu_fvp() { # End to End model tests +test_run_ethosu_fvp() { # End to End model tests using run.sh echo "${TEST_SUITE_NAME}: Test ethos-u delegate examples with run.sh" source examples/arm/ethos-u-scratch/setup_path.sh # TOSA quantized echo "${TEST_SUITE_NAME}: Test ethos-u target TOSA" - examples/arm/run.sh --target=TOSA --model_name=mv2 - examples/arm/run.sh --target=TOSA --model_name=lstm - examples/arm/run.sh --target=TOSA --model_name=edsr + examples/arm/run.sh --target=TOSA --model_name=add + examples/arm/run.sh --target=TOSA --model_name=mul # Ethos-U55 echo "${TEST_SUITE_NAME}: Test ethos-u target Ethos-U55" - examples/arm/run.sh --target=ethos-u55-128 --model_name=mv2 - examples/arm/run.sh --target=ethos-u55-128 --model_name=lstm + examples/arm/run.sh --target=ethos-u55-128 --model_name=add + examples/arm/run.sh --target=ethos-u55-128 --model_name=mul # Ethos-U85 echo "${TEST_SUITE_NAME}: Test ethos-u target Ethos-U85" - examples/arm/run.sh --target=ethos-u85-128 --model_name=mv2 - examples/arm/run.sh --target=ethos-u85-128 --model_name=lstm + examples/arm/run.sh --target=ethos-u85-128 --model_name=add + examples/arm/run.sh --target=ethos-u85-128 --model_name=mul + echo "${TEST_SUITE_NAME}: PASS" } +test_models_ethosu_fvp() { # End to End model tests using model_test.py + echo "${TEST_SUITE_NAME}: Test ethos-u delegate models with test_model.py" + + source examples/arm/ethos-u-scratch/setup_path.sh + + # Build common libs once + python3 backends/arm/test/test_model.py --build_libs + + # TOSA quantized + echo "${TEST_SUITE_NAME}: Test ethos-u target TOSA" + python3 backends/arm/test/test_model.py --target=TOSA --model=mv2 + python3 backends/arm/test/test_model.py --target=TOSA --model=mv3 + python3 backends/arm/test/test_model.py --target=TOSA --model=lstm + python3 backends/arm/test/test_model.py --target=TOSA --model=edsr + + # Ethos-U55 + echo "${TEST_SUITE_NAME}: Test ethos-u target Ethos-U55" + python3 backends/arm/test/test_model.py --target=ethos-u55-128 --model=mv2 + python3 backends/arm/test/test_model.py --target=ethos-u55-64 --model=mv3 + python3 backends/arm/test/test_model.py --target=ethos-u55-256 --model=lstm + + # Ethos-U85 + echo "${TEST_SUITE_NAME}: Test ethos-u target Ethos-U85" + python3 backends/arm/test/test_model.py --target=ethos-u85-256 --model=mv2 + python3 backends/arm/test/test_model.py --target=ethos-u85-1024 --model=mv3 + python3 backends/arm/test/test_model.py --target=ethos-u85-128 --model=lstm + echo "${TEST_SUITE_NAME}: PASS" + } + +test_full_ethosu_fvp() { # All End to End model tests + echo "${TEST_SUITE_NAME}: Test ethos-u delegate models and examples on fvp" + + test_models_ethosu_fvp + test_run_ethosu_fvp + echo "${TEST_SUITE_NAME}: PASS" + } + + + ${TEST_SUITE} \ No newline at end of file diff --git a/backends/arm/test/test_model.py b/backends/arm/test/test_model.py new file mode 100755 index 00000000000..990b9e5f70b --- /dev/null +++ b/backends/arm/test/test_model.py @@ -0,0 +1,247 @@ +# Copyright 2025 Arm Limited and/or its affiliates. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. + +import argparse +import os +import platform +import subprocess +import sys + + +def get_args(): + parser = argparse.ArgumentParser() + parser.add_argument( + "--build_libs", + action="store_true", + required=False, + default=False, + help="Flag for building executorch libs needed for this testing", + ) + parser.add_argument( + "--model", + required=False, + default=None, + help="Model to use that aot_arm_compiler.py can handle, can be a builtin, examples/models or a filename.", + ) + parser.add_argument( + "--target", + required=False, + default=None, + help="Target name", + ) + parser.add_argument( + "--test_output", + required=False, + default="arm_test", + help="Output folder used for build and test defults to arm_test", + ) + parser.add_argument( + "--system_config", + required=False, + default=None, + help="Target specific system_config (See Vela compiler)", + ) + parser.add_argument( + "--memory_mode", + required=False, + default=None, + help="Target specific memory_mode (See Vela compiler)", + ) + parser.add_argument( + "--no_intermediate", + action="store_true", + required=False, + default=False, + help="Don't save temporary files during compilation", + ) + + args = parser.parse_args() + + if args.model and "ethos-u" in args.target and args.system_config is None: + if "u55" in args.target: + args.system_config = "Ethos_U55_High_End_Embedded" + elif "u85" in args.target: + args.system_config = "Ethos_U85_SYS_DRAM_Mid" + else: + raise RuntimeError(f"Invalid target name {args.target}") + + if args.model and "ethos-u" in args.target and args.memory_mode is None: + if "u55" in args.target: + args.memory_mode = "Shared_Sram" + elif "u85" in args.target: + args.memory_mode = "Sram_Only" + else: + raise RuntimeError(f"Invalid target name {args.target}") + + return args + + +def run_external_cmd(cmd: []): + print("CALL:", *cmd, sep=" ") + try: + subprocess.check_call(cmd) + except subprocess.CalledProcessError as err: + print("ERROR called: ", *cmd, sep=" ") + print(f"Failed with: {err.returncode}") + sys.exit(err.returncode) + + +def build_libs(et_build_root: str, script_path: str): + run_external_cmd( + [ + "bash", + os.path.join(script_path, "build_executorch.sh"), + f"--et_build_root={et_build_root}", + "--build_type=Release", + ] + ) + run_external_cmd( + [ + "bash", + os.path.join(script_path, "build_portable_kernels.sh"), + f"--et_build_root={et_build_root}", + "--build_type=Release", + "--portable_kernels=aten::_softmax.out", + ] + ) + run_external_cmd( + [ + "bash", + os.path.join(script_path, "build_quantized_ops_aot_lib.sh"), + f"--et_build_root={et_build_root}", + "--build_type=Release", + ] + ) + + +def build_pte( + et_build_root: str, + model_name: str, + target: str, + system_config: str, + memory_mode: str, + build_output: str, + no_intermediate: bool, +): + soext = {"Darwin": "dylib", "Linux": "so", "Windows": "dll"}.get( + platform.system(), None + ) + solibs_path = os.path.join( + et_build_root, + "cmake-out-aot-lib", + "kernels", + "quantized", + f"libquantized_ops_aot_lib.{soext}", + ) + solibs = f"--so_library={solibs_path}" + + intermediate = "" + if not no_intermediate: + intermediate = f"--intermediate={output}" + + run_external_cmd( + [ + "python3", + "-m", + "examples.arm.aot_arm_compiler", + "--delegate", + "--quantize", + intermediate, + f"--model_name={model_name}", + f"--target={target}", + f"--output={build_output}", + f"--system_config={system_config}", + f"--memory_mode={memory_mode}", + solibs, + ] + ) + + pte_file = os.path.join(output, f"{model_name}_arm_delegate_{args.target}.pte") + return pte_file + + +def build_ethosu_runtime( + et_build_root: str, + script_path: str, + pte_file: str, + target: str, + system_config: str, + elf_build_path: str, +): + run_external_cmd( + [ + "bash", + os.path.join(script_path, "build_executorch_runner.sh"), + f"--et_build_root={et_build_root}", + f"--pte={pte_file}", + f"--target={target}", + "--build_type=Release", + f"--system_config={system_config}", + f"--output={elf_build_path}", + ] + ) + + elf_file = os.path.join(elf_build_path, "cmake-out", "arm_executor_runner") + return elf_file + + +def run_elf_with_fvp(script_path: str, elf_file: str, target: str): + run_external_cmd( + [ + "bash", + os.path.join(script_path, "run_fvp.sh"), + f"--elf={elf_file}", + f"--target={target}", + ] + ) + + +if __name__ == "__main__": + + args = get_args() + script_path = os.path.join("backends", "arm", "scripts") + + if args.build_libs: + build_libs(args.test_output, script_path) + + if args.model: + model_name = args.model.split(" ")[0].split(";")[0] + if not model_name: + print("ERROR: Bad --model specified") + if not args.target: + print("ERROR: --model need --target to also be set") + + output = os.path.join( + args.test_output, f"{model_name}_arm_delegate_{args.target}" + ) + + pte_file = build_pte( + args.test_output, + model_name, + args.target, + args.system_config, + args.memory_mode, + output, + args.no_intermediate, + ) + print(f"PTE file created: {pte_file} ") + + if "ethos-u" in args.target: + elf_build_path = os.path.join( + output, f"{model_name}_arm_delegate_{args.target}" + ) + + elf_file = build_ethosu_runtime( + args.test_output, + script_path, + pte_file, + args.target, + args.system_config, + elf_build_path, + ) + print(f"ELF file created: {elf_file} ") + + run_elf_with_fvp(script_path, elf_file, args.target) + print(f"Model: {model_name} on {args.target} -> PASS") diff --git a/docs/source/executorch-arm-delegate-tutorial.md b/docs/source/executorch-arm-delegate-tutorial.md index ff6d4abbbac..feb8f0335fa 100644 --- a/docs/source/executorch-arm-delegate-tutorial.md +++ b/docs/source/executorch-arm-delegate-tutorial.md @@ -200,7 +200,7 @@ Following script will serve as a helper utility to help us generate the `.pte` f ```bash python3 -m examples.arm.aot_arm_compiler --model_name="softmax" -# This should produce ./softmax.pte +# This should produce ./softmax_arm_ethos-u55-128.pte ``` ### Delegated Workflow @@ -221,12 +221,14 @@ Similar to the non-delegate flow, the same script will server as a helper utilit ```bash python3 -m examples.arm.aot_arm_compiler --model_name="add" --delegate -# should produce ./add_arm_delegate.pte +# should produce ./add_arm_delegate_ethos-u55-128.pte ``` ### Delegated Quantized Workflow Before generating the `.pte` file for delegated quantized networks like MobileNetV2, we need to build the `quantized_ops_aot_lib` +You can just run the `backends/arm/scripts/build_quantized_ops_aot_lib.sh` script to build this for you or build it yourself like this. + ```bash cd @@ -245,7 +247,7 @@ cmake --build cmake-out-aot-lib --parallel -- quantized_ops_aot_lib After the `quantized_ops_aot_lib` build, we can run the following script to generate the `.pte` file ```bash python3 -m examples.arm.aot_arm_compiler --model_name="mv2" --delegate --quantize --so_library="$(find cmake-out-aot-lib -name libquantized_ops_aot_lib.so)" -# should produce ./mv2_arm_delegate.pte.pte +# should produce ./mv2_arm_delegate_ethos-u55-128.pte ```
@@ -262,6 +264,14 @@ Now let's try to run these `.pte` files on a Corstone-300 and Corstone-320 platf In this section, we will go over steps that you need to go through to build the runtime application. This then run on the target device. In the executorch repository we have a functioning script which does the exact same steps. It is located at `executorch/examples/arm/run.sh`. We will use that to build necessary pieces and finally run the previously generated PTE file on an FVP. +By default the `run.sh` will use `arm_test/` as an build and output folder and you will find the build artifacts under it. This can be contolled/overrided with the `--et_build_root` and the `--output` flags if needed. + +e.g. running `examples/arm/run.sh --model_name=add --target=ethos-u85-128` will produce a pte and elf file like this: + +```bash +arm_test/add/add_arm_delegate_ethos-u85-128.pte +arm_test/add/cmake-out/arm_executor_runner +``` Also before we get started, make sure that you have completed ExecuTorch cmake build setup, and the instructions to setup the development environment described [earlier](#set-up-the-developer-environment). The block diagram below demonstrates, at the high level, how the various build artifacts are generated and are linked together to generate the final bare-metal executable. @@ -286,23 +296,19 @@ To run a `.pte` file with the Arm backend delegate call instructions, we will ne - `libexecutorch_delegate_ethos_u.a` -These libraries are generated in `build_executorch` and `build_quantization_aot_lib` function of the `run.sh` script. +These libraries are generated by the `backends/arm/scripts/build_executorch.sh`, `backends/arm/scripts/build_portable_kernels.sh` and `backends/arm/scripts/build_quantized_ops_aot_lib.sh` scripts called from the `run.sh` script. -In this function, `EXECUTORCH_SELECT_OPS_LIST` will decide the number of portable operators included in the build and are available at runtime. It must match with `.pte` file's requirements, otherwise you will get `Missing Operator` error at runtime. +The `--portable_kernels` flag can be used to set the build flag `EXECUTORCH_SELECT_OPS_LIST` when running `backends/arm/scripts/build_portable_kernels.sh` that will decide the number of portable operators included in the build and are available at runtime. It must match with `.pte` file's requirements, otherwise you will get `Missing Operator` error at runtime. For example, there in the command line above, to run SoftmaxModule, we only included the softmax CPU operator. Similarly, to run AddModule in a non-delegated manner you will need add op and so on. As you might have already realized, for the delegated operators, which will be executed by the Arm backend delegate, we do not need to include those operators in this list. This is only for *non-delegated* operators. -```{tip} -The `run.sh` script takes in `--portable_kernels` option, which provides a way to supply a comma seperated list of portable kernels to be included. -``` - ### Building the executor_runner Bare-Metal Application The SDK dir is the same one prepared [earlier](#setup-the-arm-ethos-u-software-development). And, we will be passing the `.pte` file (any one of them) generated above. Note, you have to generate a new `executor-runner` binary if you want to change the model or the `.pte` file. This constraint is from the constrained bare-metal runtime environment we have for Corstone-300/Corstone-320 platforms. -This is performed by the `build_executorch_runner` function in `run.sh`. +This is performed by the `backends/arm/scripts/build_executorch_runner.sh` script runned from `run.sh`. ```{tip} The `run.sh` script takes in `--target` option, which provides a way to provide a specific target, Corstone-300(ethos-u55-128) or Corstone-320(ethos-u85-128) @@ -310,7 +316,10 @@ The `run.sh` script takes in `--target` option, which provides a way to provide ## Running on Corstone FVP Platforms -Once the elf is prepared, regardless of the `.pte` file variant is used to generate the bare metal elf. The below command is used to run the [MV2Model](#mv2module) on Corstone-320 FVP +Once the elf is prepared, regardless of the `.pte` file variant is used to generate the bare metal elf. `run.sh` will run the FVP for you via the `backends/arm/scripts/run_fvp.sh` script but you can also run it directly. + + +The below command is used to run the [MV2Model](#mv2module) on Corstone-320 FVP ```bash ethos_u_build_dir=examples/arm/executor_runner/ diff --git a/examples/arm/aot_arm_compiler.py b/examples/arm/aot_arm_compiler.py index 33d8bc5ebf2..ccd736f7fce 100644 --- a/examples/arm/aot_arm_compiler.py +++ b/examples/arm/aot_arm_compiler.py @@ -484,15 +484,15 @@ def get_args(): # noqa C901 ): raise RuntimeError(f"Model {args.model_name} cannot be delegated.") - if args.system_config is None: + if "ethos-u" in args.target and args.system_config is None: if "u55" in args.target: args.system_config = "Ethos_U55_High_End_Embedded" elif "u85" in args.target: - args.system_confg = "Ethos_U85_SYS_DRAM_Mid" + args.system_config = "Ethos_U85_SYS_DRAM_Mid" else: raise RuntimeError(f"Invalid target name {args.target}") - if args.memory_mode is None: + if "ethos-u" in args.target and args.memory_mode is None: if "u55" in args.target: args.memory_mode = "Shared_Sram" elif "u85" in args.target: @@ -591,6 +591,7 @@ def get_args(): # noqa C901 output_name = os.path.join(args.output, output_name) save_pte_program(exec_prog, output_name) + print(f"PTE file saved as {output_name}.pte") if args.evaluate: evaluate_model( diff --git a/examples/arm/run.sh b/examples/arm/run.sh index 1a50f59d454..ce92312b652 100755 --- a/examples/arm/run.sh +++ b/examples/arm/run.sh @@ -9,15 +9,13 @@ set -eu - - ######## ### Hardcoded constants ######## script_dir=$(cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd) +et_root_dir=$(cd ${script_dir}/../.. && pwd) +et_root_dir=$(realpath ${et_root_dir}) -# Default Ethos-u tool folder override with --scratch-dir= -root_dir=${script_dir}/ethos-u-scratch model_name="" aot_arm_compiler_flags="--delegate --quantize" @@ -31,23 +29,26 @@ extra_build_flags="" build_only=false system_config="" memory_mode="" +et_build_root="${et_root_dir}/arm_test" +ethos_u_scratch_dir=${script_dir}/ethos-u-scratch -help() { +function help() { echo "Usage: $(basename $0) [options]" echo "Options:" echo " --model_name= Model to run, can be a builtin, examples/models or a filename Default to all builtin models" echo " --aot_arm_compiler_flags= Only used if --model_name is used Default: ${aot_arm_compiler_flags}" echo " --portable_kernels= Comma separated list of portable (non delagated) kernels to include Default: ${portable_kernels}" echo " --target= Target to build and run for Default: ${target}" - echo " --output= Output folder Default: ${output_folder}" + echo " --output= Target build output folder Default: ${output_folder}" echo " --etdump Adds Devtools etdump support to track timing, etdump area will be base64 encoded in the log" - echo " --debug_build Build with debug flag, default is Release" - echo " --extra_build_flags Extra flags to pass to cmake like -DET_ARM_BAREMETAL_METHOD_ALLOCATOR_POOL_SIZE=60000 Default: none " + echo " --build_type= Build with Release, Debug or RelWithDebInfo, default is ${build_type}" + echo " --extra_build_flags= Extra flags to pass to cmake like -DET_ARM_BAREMETAL_METHOD_ALLOCATOR_POOL_SIZE=60000 Default: none " echo " --build_only Only build, don't run FVP" - echo " --scratch-dir= Path to your Ethos-U scrach dir if you not using default" echo " --system_config= System configuration to select from the Vela configuration file (see vela.ini). Default: Ethos_U55_High_End_Embedded for EthosU55 targets, Ethos_U85_SYS_DRAM_Mid for EthosU85 targets." echo " NOTE: If given, this option must match the given target. This option also sets timing adapter values customized for specific hardware, see ./executor_runner/CMakeLists.txt." echo " --memory_mode= Memory mode to select from the Vela configuration file (see vela.ini), e.g. Shared_Sram/Sram_Only. Default: 'Shared_Sram' for Ethos-U55 targets, 'Sram_Only' for Ethos-U85 targets" + echo " --et_build_root= Executorch build output root folder to use, defaults to ${et_build_root}" + echo " --scratch-dir= Path to your Ethos-U scrach dir if you not using default ${ethos_u_scratch_dir}" exit 0 } @@ -60,43 +61,26 @@ for arg in "$@"; do --target=*) target="${arg#*=}";; --output=*) output_folder="${arg#*=}" ; output_folder_set=true ;; --etdump) build_with_etdump=true ;; - --debug_build) build_type="Debug" ;; + --build_type=*) build_type="${arg#*=}";; --extra_build_flags=*) extra_build_flags="${arg#*=}";; --build_only) build_only=true ;; - --scratch-dir=*) root_dir="${arg#*=}";; --system_config=*) system_config="${arg#*=}";; --memory_mode=*) memory_mode="${arg#*=}";; + --et_build_root=*) et_build_root="${arg#*=}";; + --scratch-dir=*) ethos_u_scratch_dir="${arg#*=}";; *) ;; esac done -root_dir=$(realpath ${root_dir}) -output_folder=$(realpath ${output_folder}) -mkdir -p ${output_folder} -if [ "$output_folder_set" = true ] ; then - executor_runner_path=${output_folder} -else - executor_runner_path=${script_dir}/executor_runner -fi -executor_runner_path=$(realpath ${executor_runner_path}) - -mkdir -p ${root_dir}/ethos-u -ethos_u_root_dir="$(cd ${root_dir}/ethos-u && pwd)" -setup_path_script=${root_dir}/setup_path.sh +# Default Ethos-u tool folder override with --scratch-dir= +ethos_u_scratch_dir=$(realpath ${ethos_u_scratch_dir}) +setup_path_script=${ethos_u_scratch_dir}/setup_path.sh +toolchain_cmake=${script_dir}/ethos-u-setup/arm-none-eabi-gcc.cmake +_setup_msg="please refer to ${script_dir}/setup.sh to properly install necessary tools." -# Executorch -et_root_dir=$(cd ${script_dir}/../.. && pwd) -et_build_dir=${et_root_dir}/cmake-out # Set target based variables -fvp_model=FVP_Corstone_SSE-300_Ethos-U55 -if [[ ${target} =~ "ethos-u85" ]] -then - echo "target is ethos-u85 variant so switching to CS320 FVP" - fvp_model=FVP_Corstone_SSE-320 -fi - if [[ ${system_config} == "" ]] then system_config="Ethos_U55_High_End_Embedded" @@ -115,227 +99,6 @@ then fi fi -toolchain_cmake=${script_dir}/ethos-u-setup/arm-none-eabi-gcc.cmake -_setup_msg="please refer to ${script_dir}/ethos-u-setup/setup.sh to properly install necessary tools." - -if ! [[ $portable_kernels =~ ^((^|,)aten::[a-zA-Z0-9_]+\.[a-zA-Z0-9_]*out)*$ ]]; then - echo " ERROR: specified argument --portable_kernels=${portable_kernels}" - echo " is in the wrong format please use \"aten::.out,aten::.out,...\"" - echo " e.g. \"aten::_softmax.out,aten::add.out\"" - exit 1 -fi - -# Generate a pte file -# output from this function is the pte filename e.g. echo should be avoided or directed to stderr e.g. >&2 -function generate_pte_file() { - [[ $# -ne 2 ]] && { echo "[${FUNCNAME[0]}]" "Expecting model and model_compiler_flags flag, got, $*"; exit 1; } - local model=${1} - local model_short_name=$(basename -- "${model}" ".py") - local model_compiler_flags=${2} - - local model_filename=${model_short_name}_arm_${target}.pte - if [[ "${model_compiler_flags}" == *"--delegate"* ]]; then - # Name aligned with default aot_arm_compiler output - model_filename=${model_short_name}_arm_delegate_${target}.pte - fi - cd $et_root_dir - - local pte_file - pte_file=$(realpath ${output_folder}/${model_filename}) - rm -f "${pte_file}" - - SO_EXT=$(python3 -c 'import platform; print({"Darwin": "dylib", "Linux": "so", "Windows": "dll"}.get(platform.system(), None))') - # We are using the aot_lib from build_quantization_aot_lib below - SO_LIB=$(find cmake-out-aot-lib -name libquantized_ops_aot_lib.${SO_EXT}) - - local ARM_AOT_CMD="python3 -m examples.arm.aot_arm_compiler --model_name=${model} --target=${target} ${model_compiler_flags} --output ${output_folder} --so_library=$SO_LIB --system_config=${system_config} --memory_mode=${memory_mode}" - echo "CALL ${ARM_AOT_CMD}" >&2 - ${ARM_AOT_CMD} 1>&2 - - [[ -f ${pte_file} ]] || { >&2 echo "Failed to generate a pte file - ${pte_file}"; exit 1; } - echo "${pte_file}" -} - -# build ExecuTorch Libraries -function build_executorch() { - set -x - - [[ -d "${et_build_dir}" ]] \ - && echo "[${FUNCNAME[0]}] Warn: using already existing build-dir for executorch: ${et_build_dir}!!" - mkdir -p "${et_build_dir}" - - cd "${et_root_dir}" - - build_with_etdump_flags="" - if [ "$build_with_etdump" = true ] ; then - ( set +x ; - echo "--------------------------------------------------------------------------------" ; - echo "Build ExecuTorch Libraries host flatcc bin ${build_type} into ${et_root_dir} - cmake-out-host-tools/bin/flatcc" ; - echo "--------------------------------------------------------------------------------" ) - - - # Build host flatcc bin - mkdir -p cmake-out-host-tools - cmake \ - -DCMAKE_INSTALL_PREFIX=${et_build_dir} \ - -DCMAKE_BUILD_TYPE=${build_type} \ - -DEXECUTORCH_BUILD_EXECUTOR_RUNNER=OFF \ - -DEXECUTORCH_ENABLE_LOGGING=ON \ - -DEXECUTORCH_BUILD_ARM_BAREMETAL=ON \ - -DEXECUTORCH_BUILD_KERNELS_QUANTIZED=ON \ - -DEXECUTORCH_BUILD_EXTENSION_RUNNER_UTIL=ON \ - -DEXECUTORCH_BUILD_DEVTOOLS=ON \ - -DEXECUTORCH_ENABLE_EVENT_TRACER=ON \ - -DEXECUTORCH_SEPARATE_FLATCC_HOST_PROJECT=ON \ - -DFLATCC_ALLOW_WERROR=OFF \ - -DFLATC_EXECUTABLE="$(which flatc)" \ - ${extra_build_flags} \ - -Bcmake-out-host-tools \ - "${et_root_dir}" - - mkdir -p cmake-out-host-tools/bin - cp third-party/flatcc/bin/flatcc cmake-out-host-tools/bin - - build_with_etdump_flags="-DEXECUTORCH_BUILD_DEVTOOLS=ON \ - -DEXECUTORCH_ENABLE_EVENT_TRACER=ON \ - -DEXECUTORCH_SEPARATE_FLATCC_HOST_PROJECT=OFF \ - -DEXECUTORCH_BUILD_EXTENSION_DATA_LOADER=OFF \ - -DFLATCC_ALLOW_WERROR=OFF \ - -DFLATCC_EXECUTABLE=${et_root_dir}/cmake-out-host-tools/bin/flatcc " - fi - - ( set +x ; - echo "--------------------------------------------------------------------------------" ; - echo "Build ExecuTorch Libraries target libs with --target install ${build_type} into '${et_root_dir}' - '${et_build_dir}'" ; - echo "--------------------------------------------------------------------------------" ) - - # Build - cmake \ - -DCMAKE_INSTALL_PREFIX=${et_build_dir} \ - -DCMAKE_BUILD_TYPE=${build_type} \ - -DCMAKE_TOOLCHAIN_FILE="${toolchain_cmake}" \ - -DEXECUTORCH_BUILD_EXECUTOR_RUNNER=OFF \ - -DEXECUTORCH_BUILD_ARM_BAREMETAL=ON \ - -DEXECUTORCH_BUILD_KERNELS_QUANTIZED=ON \ - -DEXECUTORCH_BUILD_EXTENSION_RUNNER_UTIL=ON \ - -DEXECUTORCH_ENABLE_LOGGING=ON \ - ${build_with_etdump_flags} \ - -DFLATC_EXECUTABLE="$(which flatc)" \ - ${extra_build_flags} \ - -B${et_build_dir} \ - "${et_root_dir}" - - echo "[${FUNCNAME[0]}] Configured CMAKE" - - cmake --build ${et_build_dir} --parallel --target install --config ${build_type} -- - - ( set +x ; - echo "--------------------------------------------------------------------------------" ; - echo "Build ExecuTorch Libraries ${build_type} into '${et_root_dir}/examples/arm' - '${et_build_dir}/examples/arm'" ; - echo "--------------------------------------------------------------------------------" ) - - cmake \ - -DCMAKE_INSTALL_PREFIX=${et_build_dir} \ - -DCMAKE_BUILD_TYPE=${build_type} \ - -DCMAKE_TOOLCHAIN_FILE="${toolchain_cmake}" \ - -DEXECUTORCH_SELECT_OPS_LIST=${portable_kernels} \ - -DEXECUTORCH_BUILD_ARM_BAREMETAL=ON \ - ${extra_build_flags} \ - -B"${et_build_dir}/examples/arm" \ - "${et_root_dir}/examples/arm" - - cmake --build "${et_build_dir}/examples/arm" --parallel --config ${build_type} -- - - set +x - - cd "${et_build_dir}" - echo "[${FUNCNAME[0]}] Generated static libraries for ExecuTorch:" - find . -name "*.a" -exec ls -al {} \; -} - -# build Arm Baremetal executor_runner -function build_executorch_runner() { - echo "[${FUNCNAME[0]}] Generating ExecuTorch libraries" - [[ $# -ne 1 ]] && { echo "[${FUNCNAME[0]}]" "Expecting a single pte file as argument got, $*"; exit 1; } - local pte=${1} - if [[ ${target} == *"ethos-u55"* ]]; then - local target_cpu=cortex-m55 - else - local target_cpu=cortex-m85 - fi - echo "--------------------------------------------------------------------------------" - echo "Build Arm Baremetal executor_runner for ${target} - '${executor_runner_path}/cmake-out'" - echo "--------------------------------------------------------------------------------" - - cd ${script_dir}/executor_runner - - build_with_etdump_flags="" - if [ "$build_with_etdump" = true ] ; then - build_with_etdump_flags=" -DEXECUTORCH_ENABLE_EVENT_TRACER=ON " - fi - - cmake \ - -DCMAKE_BUILD_TYPE=${build_type} \ - -DCMAKE_TOOLCHAIN_FILE=${toolchain_cmake} \ - -DTARGET_CPU=${target_cpu} \ - -DET_DIR_PATH:PATH=${et_root_dir} \ - -DET_BUILD_DIR_PATH:PATH=${et_build_dir} \ - -DET_PTE_FILE_PATH:PATH="${pte}" \ - -DETHOS_SDK_PATH:PATH=${ethos_u_root_dir} \ - -DETHOSU_TARGET_NPU_CONFIG=${target} \ - ${build_with_etdump_flags} \ - -DPYTHON_EXECUTABLE=$(which python3) \ - -DSYSTEM_CONFIG=${system_config} \ - ${extra_build_flags} \ - -B ${executor_runner_path}/cmake-out - - echo "[${FUNCNAME[0]}] Configured CMAKE" - - cmake --build ${executor_runner_path}/cmake-out --parallel -- arm_executor_runner - echo "[${FUNCNAME[0]}] Generated baremetal elf file:" - find ${executor_runner_path}/cmake-out -name "arm_executor_runner" - echo "executable_text: $(find ${executor_runner_path}/cmake-out -name arm_executor_runner -exec arm-none-eabi-size {} \; | grep -v filename | awk '{print $1}') bytes" - echo "executable_data: $(find ${executor_runner_path}/cmake-out -name arm_executor_runner -exec arm-none-eabi-size {} \; | grep -v filename | awk '{print $2}') bytes" - echo "executable_bss: $(find ${executor_runner_path}/cmake-out -name arm_executor_runner -exec arm-none-eabi-size {} \; | grep -v filename | awk '{print $3}') bytes" -} - -# Execute the executor_runner on FVP Simulator -function run_fvp() { - [[ $# -ne 1 ]] && { echo "[${FUNCNAME[0]}]" "Expexted elf binary name, got $*"; exit 1; } - local elf_name=${1} - elf=$(find ${executor_runner_path} -name "${elf_name}") - [[ ! -f $elf ]] && { echo "[${FUNCNAME[0]}]: Unable to find executor_runner elf: ${elf}"; exit 1; } - num_macs=$(echo ${target} | cut -d - -f 3) - - if [[ ${target} == *"ethos-u55"* ]]; then - echo "Running ${elf} for ${target} run with FVP:${fvp_model} num_macs:${num_macs}" - ${fvp_model} \ - -C ethosu.num_macs=${num_macs} \ - -C mps3_board.visualisation.disable-visualisation=1 \ - -C mps3_board.telnetterminal0.start_telnet=0 \ - -C mps3_board.uart0.out_file='-' \ - -C mps3_board.uart0.shutdown_on_eot=1 \ - -a "${elf}" \ - --timelimit 220 || true # seconds - echo "[${FUNCNAME[0]}] Simulation complete, $?" - elif [[ ${target} == *"ethos-u85"* ]]; then - echo "Running ${elf} for ${target} run with FVP:${fvp_model} num_macs:${num_macs}" - ${fvp_model} \ - -C mps4_board.subsystem.ethosu.num_macs=${num_macs} \ - -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.shutdown_on_eot=1 \ - -a "${elf}" \ - --timelimit 220 || true # seconds - echo "[${FUNCNAME[0]}] Simulation complete, $?" - else - echo "Running ${elf} for ${target} is not supported" - exit 1 - fi -} - ####### ### Main ####### @@ -343,12 +106,10 @@ function run_fvp() { # This should be prepared by the setup.sh [[ -f ${setup_path_script} ]] \ || { echo "Missing ${setup_path_script}. ${_setup_msg}"; exit 1; } -source ${root_dir}/setup_path.sh -# basic checks before we get started -hash ${fvp_model} \ - || { echo "Could not find ${fvp_model} on PATH, ${_setup_msg}"; exit 1; } +source ${setup_path_script} +# basic checks before we get started hash arm-none-eabi-gcc \ || { echo "Could not find arm baremetal toolchain on PATH, ${_setup_msg}"; exit 1; } @@ -358,9 +119,24 @@ hash arm-none-eabi-gcc \ [[ -f ${et_root_dir}/CMakeLists.txt ]] \ || { echo "Executorch repo doesn't contain CMakeLists.txt file at root level"; exit 1; } -# build executorch libraries -build_executorch -cd $et_root_dir && backends/arm/scripts/build_quantized_ops_aot_lib.sh $build_type +# Build executorch libraries +cd $et_root_dir +if [ "$build_with_etdump" = true ] ; then + et_dump_flag="--etdump" +else + et_dump_flag="" +fi + +backends/arm/scripts/build_executorch.sh --et_build_root="${et_build_root}" --build_type=$build_type $et_dump_flag +backends/arm/scripts/build_portable_kernels.sh --et_build_root="${et_build_root}" --build_type=$build_type --portable_kernels=$portable_kernels + +# Build a lib quantized_ops_aot_lib +backends/arm/scripts/build_quantized_ops_aot_lib.sh --et_build_root="${et_build_root}" --build_type=$build_type + +SO_EXT=$(python3 -c 'import platform; print({"Darwin": "dylib", "Linux": "so", "Windows": "dll"}.get(platform.system(), None))') +# We are using the aot_lib from build_quantization_aot_lib below +SO_LIB=$(find "${et_build_root}/cmake-out-aot-lib" -name libquantized_ops_aot_lib.${SO_EXT}) + if [[ -z "$model_name" ]]; then # the test models run, and whether to delegate @@ -373,19 +149,51 @@ fi # loop over running the AoT flow and executing the model on device for i in "${!test_model[@]}"; do + model="${test_model[i]}" + model_compiler_flags="${model_compiler_flags[i]}" + echo "--------------------------------------------------------------------------------" - printf "Running e2e flow for model '%s' with flags '%s'\n" "${test_model[i]}" "${model_compiler_flags[i]}" + printf "Running e2e flow for model '%s' with flags '%s'\n" "${model}" "${model_compiler_flags}" echo "--------------------------------------------------------------------------------" - pte=$(generate_pte_file "${test_model[i]}" "${model_compiler_flags[i]}") - stat --printf="Generated pte_data_size: %s bytes\npte_file:%n\n" ${pte} + + cd $et_root_dir + model_short_name=$(basename -- "${model}" ".py") + model_filename=${model_short_name}_arm_${target}.pte + + if [[ "${model_compiler_flags}" == *"--delegate"* ]]; then + # Name aligned with default aot_arm_compiler output + model_filename=${model_short_name}_arm_delegate_${target}.pte + fi + + if [ "$output_folder_set" = false ] ; then + output_folder=${et_build_root}/${model_short_name} + fi + + output_folder=$(realpath ${output_folder}) + mkdir -p ${output_folder} + pte_file=$(realpath -m ${output_folder}/${model_filename}) + + rm -f "${pte_file}" + + ARM_AOT_CMD="python3 -m examples.arm.aot_arm_compiler --model_name=${model} --target=${target} ${model_compiler_flags} --intermediate=${output_folder} --output=${output_folder} --so_library=$SO_LIB --system_config=${system_config} --memory_mode=${memory_mode}" + echo "CALL ${ARM_AOT_CMD}" >&2 + ${ARM_AOT_CMD} 1>&2 + + [[ -f ${pte_file} ]] || { >&2 echo "Failed to generate a pte file - ${pte_file}"; exit 1; } + echo "pte_data_size: $(wc -c ${pte_file})" + echo "pte_file: ${pte_file}" + if [[ ${target} == *"TOSA"* ]]; then - echo "Build for ${target} skip generating .elf and running" + echo "Build for ${target} skip generating a .elf and running it" else + set -x # Rebuild the application as the pte is imported as a header/c array - build_executorch_runner "${pte}" + backends/arm/scripts/build_executorch_runner.sh "--pte=${pte_file}" --build_type=$build_type --target=$target --system_config=$system_config $et_dump_flag --extra_build_flags="$extra_build_flags" --ethosu_tools_dir="$ethos_u_scratch_dir" --output="${output_folder}" if [ "$build_only" = false ] ; then - run_fvp arm_executor_runner + # Execute the executor_runner on FVP Simulator + backends/arm/scripts/run_fvp.sh --elf=${output_folder}/cmake-out/arm_executor_runner --target=$target fi + set +x fi done