1#!/usr/bin/env bash 2# Copyright (c) Meta Platforms, Inc. and affiliates. 3# All rights reserved. 4# 5# Copyright 2023-2024 Arm Limited and/or its affiliates. 6# 7# This source code is licensed under the BSD-style license found in the 8# LICENSE file in the root directory of this source tree. 9 10set -eu 11 12 13 14######## 15### Hardcoded constants 16######## 17script_dir=$(cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd) 18 19# Default Ethos-u tool folder override with --scratch-dir=<FOLDER> 20root_dir=${script_dir}/ethos-u-scratch 21 22model_name="" 23aot_arm_compiler_flags="--delegate --quantize" 24target="ethos-u55-128" 25output_folder_set=false 26output_folder="." 27build_only=false 28portable_kernels="aten::_softmax.out" 29 30help() { 31 echo "Usage: $(basename $0) [options]" 32 echo "Options:" 33 echo " --model_name=<MODEL> Model to run, can be a builtin, examples/models or a filename Default to all builtin models" 34 echo " --aot_arm_compiler_flags=<FLAGS> Only used if --model_name is used Default: ${aot_arm_compiler_flags}" 35 echo " --portable_kernels=<OPS> Comma separated list of portable (non delagated) kernels to include Default: ${portable_kernels}" 36 echo " --target=<TARGET> Target to build and run for Default: ${target}" 37 echo " --output=<FOLDER> Output folder Default: ${output_folder}" 38 echo " --build_only Only build, don't run FVP" 39 echo " --scratch-dir=<FOLDER> Path to your Ethos-U scrach dir if you not using default" 40 exit 0 41} 42 43for arg in "$@"; do 44 case $arg in 45 -h|--help) help ;; 46 --model_name=*) model_name="${arg#*=}";; 47 --aot_arm_compiler_flags=*) aot_arm_compiler_flags="${arg#*=}";; 48 --portable_kernels=*) portable_kernels="${arg#*=}";; 49 --target=*) target="${arg#*=}";; 50 --output=*) output_folder="${arg#*=}" ; output_folder_set=true ;; 51 --build_only) build_only=true ;; 52 --scratch-dir=*) root_dir="${arg#*=}";; 53 *) 54 ;; 55 esac 56done 57 58root_dir=$(realpath ${root_dir}) 59output_folder=$(realpath ${output_folder}) 60mkdir -p ${output_folder} 61if [ "$output_folder_set" = true ] ; then 62 executor_runner_path=${output_folder} 63else 64 executor_runner_path=${script_dir}/executor_runner 65fi 66executor_runner_path=$(realpath ${executor_runner_path}) 67 68ethos_u_root_dir="$(cd ${root_dir}/ethos-u && pwd)" 69ethos_u_build_dir=${ethos_u_root_dir}/core_platform/build 70setup_path_script=${root_dir}/setup_path.sh 71 72# Executorch 73et_root_dir=$(cd ${script_dir}/../.. && pwd) 74et_build_dir=${et_root_dir}/cmake-out 75 76fvp_model=FVP_Corstone_SSE-300_Ethos-U55 77if [[ ${target} =~ "ethos-u85" ]] 78then 79 echo "target is ethos-u85 variant so switching to CS320 FVP" 80 fvp_model=FVP_Corstone_SSE-320 81fi 82 83toolchain_cmake=${script_dir}/ethos-u-setup/arm-none-eabi-gcc.cmake 84_setup_msg="please refer to ${script_dir}/ethos-u-setup/setup.sh to properly install necessary tools." 85 86if ! [[ $portable_kernels =~ ^((^|,)aten::[a-zA-Z0-9_]+\.[a-zA-Z0-9_]*out)*$ ]]; then 87 echo " ERROR: specified argument --portable_kernels=${portable_kernels}" 88 echo " is in the wrong format please use \"aten::<OP1>.out,aten::<OP2>.out,...\"" 89 echo " e.g. \"aten::_softmax.out,aten::add.out\"" 90 exit 1 91fi 92 93# Generate a pte file 94function generate_pte_file() { 95 [[ $# -ne 2 ]] && { echo "[${FUNCNAME[0]}]" "Expecting model and model_compiler_flags flag, got, $*"; exit 1; } 96 local model=${1} 97 local model_short_name=$(basename -- "${model}" ".py") 98 local model_compiler_flags=${2} 99 100 local model_filename=${model_short_name}_arm_${target}.pte 101 if [[ "${model_compiler_flags}" == *"--delegate"* ]]; then 102 # Name aligned with default aot_arm_compiler output 103 model_filename=${model_short_name}_arm_delegate_${target}.pte 104 fi 105 cd $et_root_dir 106 107 local pte_file 108 pte_file=$(realpath ${output_folder}/${model_filename}) 109 rm -f "${pte_file}" 110 111 SO_EXT=$(python3 -c 'import platform; print({"Darwin": "dylib", "Linux": "so", "Windows": "dll"}.get(platform.system(), None))') 112 # We are using the aot_lib from build_quantization_aot_lib below 113 SO_LIB=$(find cmake-out-aot-lib -name libquantized_ops_aot_lib.${SO_EXT}) 114 115 python3 -m examples.arm.aot_arm_compiler --model_name="${model}" --target=${target} ${model_compiler_flags} --output ${output_folder} --so_library="$SO_LIB" 1>&2 116 [[ -f ${pte_file} ]] || { >&2 echo "Failed to generate a pte file - ${pte_file}"; exit 1; } 117 echo "${pte_file}" 118} 119 120# Build .so library to register quant ops with AoT flow 121function build_quantization_aot_lib() 122{ 123 SITE_PACKAGES="$(python3 -c 'from distutils.sysconfig import get_python_lib; print(get_python_lib())')" 124 CMAKE_PREFIX_PATH="${SITE_PACKAGES}/torch" 125 126 cd $et_root_dir 127 mkdir -p cmake-out-aot-lib 128 cmake \ 129 -DCMAKE_BUILD_TYPE=Release \ 130 -DEXECUTORCH_BUILD_XNNPACK=OFF \ 131 -DEXECUTORCH_BUILD_KERNELS_QUANTIZED=ON \ 132 -DEXECUTORCH_BUILD_KERNELS_QUANTIZED_AOT=ON \ 133 -DCMAKE_PREFIX_PATH="$CMAKE_PREFIX_PATH" \ 134 -DPYTHON_EXECUTABLE=python3 \ 135 -Bcmake-out-aot-lib \ 136 "${et_root_dir}" 137 138 cmake --build cmake-out-aot-lib --parallel -- quantized_ops_aot_lib 139} 140 141 142# build ExecuTorch Libraries 143function build_executorch() { 144 set -x 145 146 [[ -d "${et_build_dir}" ]] \ 147 && echo "[${FUNCNAME[0]}] Warn: using already existing build-dir for executorch: ${et_build_dir}!!" 148 mkdir -p "${et_build_dir}" 149 150 cd "${et_root_dir}" 151 cmake \ 152 -DCMAKE_INSTALL_PREFIX=${et_build_dir} \ 153 -DEXECUTORCH_BUILD_EXECUTOR_RUNNER=OFF \ 154 -DCMAKE_BUILD_TYPE=Release \ 155 -DEXECUTORCH_ENABLE_LOGGING=ON \ 156 -DEXECUTORCH_BUILD_ARM_BAREMETAL=ON \ 157 -DEXECUTORCH_BUILD_KERNELS_QUANTIZED=ON \ 158 -DEXECUTORCH_BUILD_EXTENSION_RUNNER_UTIL=ON \ 159 -DFLATC_EXECUTABLE="$(which flatc)" \ 160 -DCMAKE_TOOLCHAIN_FILE="${toolchain_cmake}" \ 161 -B${et_build_dir} \ 162 "${et_root_dir}" 163 164 echo "[${FUNCNAME[0]}] Configured CMAKE" 165 166 cmake --build ${et_build_dir} --parallel --target install --config Release 167 168 cmake \ 169 -DCMAKE_INSTALL_PREFIX=${et_build_dir} \ 170 -DCMAKE_BUILD_TYPE=Release \ 171 -DEXECUTORCH_SELECT_OPS_LIST=${portable_kernels} \ 172 -DEXECUTORCH_BUILD_ARM_BAREMETAL=ON \ 173 -DCMAKE_TOOLCHAIN_FILE="${toolchain_cmake}" \ 174 -B"${et_build_dir}"/examples/arm \ 175 "${et_root_dir}"/examples/arm 176 cmake --build ${et_build_dir}/examples/arm --parallel -- 177 178 set +x 179 180 cd "${et_build_dir}" 181 echo "[${FUNCNAME[0]}] Generated static libraries for ExecuTorch:" 182 find . -name "*.a" -exec ls -al {} \; 183} 184 185# build Arm Baremetal executor_runner 186function build_executorch_runner() { 187 echo "[${FUNCNAME[0]}] Generating ExecuTorch libraries" 188 [[ $# -ne 1 ]] && { echo "[${FUNCNAME[0]}]" "Expecting a single pte file as argument got, $*"; exit 1; } 189 local pte=${1} 190 if [[ ${target} == *"ethos-u55"* ]]; then 191 local target_cpu=cortex-m55 192 local target_board=corstone-300 193 else 194 local target_cpu=cortex-m85 195 local target_board=corstone-320 196 fi 197 cd ${script_dir}/executor_runner 198 cmake -DCMAKE_TOOLCHAIN_FILE=${toolchain_cmake} \ 199 -DTARGET_CPU=${target_cpu} \ 200 -DTARGET_BOARD=${target_board} \ 201 -DETHOSU_TARGET_NPU_CONFIG=${target} \ 202 -B ${executor_runner_path}/cmake-out \ 203 -DETHOS_SDK_PATH:PATH=${ethos_u_root_dir} \ 204 -DET_DIR_PATH:PATH=${et_root_dir} \ 205 -DET_BUILD_DIR_PATH:PATH=${et_build_dir} \ 206 -DET_PTE_FILE_PATH:PATH="${pte}" \ 207 -DPYTHON_EXECUTABLE=$(which python3) 208 echo "[${FUNCNAME[0]}] Configured CMAKE" 209 210 cmake --build ${executor_runner_path}/cmake-out --parallel -- arm_executor_runner 211 echo "[${FUNCNAME[0]}] Generated baremetal elf file:" 212 find ${executor_runner_path}/cmake-out -name "arm_executor_runner" 213 echo "executable_text: $(find ${executor_runner_path}/cmake-out -name arm_executor_runner -exec size {} \; | grep -v filename | awk '{print $1}') bytes" 214 echo "executable_data: $(find ${executor_runner_path}/cmake-out -name arm_executor_runner -exec size {} \; | grep -v filename | awk '{print $2}') bytes" 215 echo "executable_bss: $(find ${executor_runner_path}/cmake-out -name arm_executor_runner -exec size {} \; | grep -v filename | awk '{print $3}') bytes" 216} 217 218# Execute the executor_runner on FVP Simulator 219function run_fvp() { 220 [[ $# -ne 1 ]] && { echo "[${FUNCNAME[0]}]" "Expexted elf binary name, got $*"; exit 1; } 221 local elf_name=${1} 222 elf=$(find ${executor_runner_path} -name "${elf_name}") 223 [[ ! -f $elf ]] && { echo "[${FUNCNAME[0]}]: Unable to find executor_runner elf: ${elf}"; exit 1; } 224 num_macs=$(echo ${target} | cut -d - -f 3) 225 226 if [[ ${target} == *"ethos-u55"* ]]; then 227 echo "Running ${elf} for ${target} run with FVP:${fvp_model} num_macs:${num_macs}" 228 ${fvp_model} \ 229 -C cpu0.CFGITCMSZ=11 \ 230 -C ethosu.num_macs=${num_macs} \ 231 -C mps3_board.visualisation.disable-visualisation=1 \ 232 -C mps3_board.telnetterminal0.start_telnet=0 \ 233 -C mps3_board.uart0.out_file='-' \ 234 -C mps3_board.uart0.shutdown_on_eot=1 \ 235 -a "${elf}" \ 236 --timelimit 120 || true # seconds 237 echo "[${FUNCNAME[0]}] Simulation complete, $?" 238 elif [[ ${target} == *"ethos-u85"* ]]; then 239 echo "Running ${elf} for ${target} run with FVP:${fvp_model} num_macs:${num_macs}" 240 ${fvp_model} \ 241 -C mps4_board.subsystem.cpu0.CFGITCMSZ=11 \ 242 -C mps4_board.subsystem.ethosu.num_macs=${num_macs} \ 243 -C mps4_board.visualisation.disable-visualisation=1 \ 244 -C vis_hdlcd.disable_visualisation=1 \ 245 -C mps4_board.telnetterminal0.start_telnet=0 \ 246 -C mps4_board.uart0.out_file='-' \ 247 -C mps4_board.uart0.shutdown_on_eot=1 \ 248 -a "${elf}" \ 249 --timelimit 120 || true # seconds 250 echo "[${FUNCNAME[0]}] Simulation complete, $?" 251 else 252 echo "Running ${elf} for ${target} is not supported" 253 exit 1 254 fi 255} 256 257####### 258### Main 259####### 260# Source the tools 261# This should be prepared by the setup.sh 262[[ -f ${setup_path_script} ]] \ 263 || { echo "Missing ${setup_path_script}. ${_setup_msg}"; exit 1; } 264source ${root_dir}/setup_path.sh 265 266# basic checks before we get started 267hash ${fvp_model} \ 268 || { echo "Could not find ${fvp_model} on PATH, ${_setup_msg}"; exit 1; } 269 270hash arm-none-eabi-gcc \ 271 || { echo "Could not find arm baremetal toolchain on PATH, ${_setup_msg}"; exit 1; } 272 273[[ -f ${toolchain_cmake} ]] \ 274 || { echo "Could not find ${toolchain_cmake} file, ${_setup_msg}"; exit 1; } 275 276[[ -f ${et_root_dir}/CMakeLists.txt ]] \ 277 || { echo "Executorch repo doesn't contain CMakeLists.txt file at root level"; exit 1; } 278 279# build executorch libraries 280build_executorch 281build_quantization_aot_lib 282 283if [[ -z "$model_name" ]]; then 284 # the test models run, and whether to delegate 285 test_model=( "softmax" "add" "add3" "mv2" ) 286 model_compiler_flags=( "" "--delegate" "--delegate" "--delegate --quantize" ) 287else 288 test_model=( "$model_name" ) 289 model_compiler_flags=( "$aot_arm_compiler_flags" ) 290fi 291 292# loop over running the AoT flow and executing the model on device 293for i in "${!test_model[@]}"; do 294 echo "--------------------------------------------------------------------------------" 295 printf "Running e2e flow for model '%s' with flags '%s'\n" "${test_model[i]}" "${model_compiler_flags[i]}" 296 echo "--------------------------------------------------------------------------------" 297 pte=$(generate_pte_file "${test_model[i]}" "${model_compiler_flags[i]}") 298 stat --printf="Generated pte_data_size: %s bytes\npte_file:%n\n" ${pte} 299 if [[ ${target} == *"TOSA"* ]]; then 300 echo "Build for ${target} skip generating .elf and running" 301 else 302 # Rebuild the application as the pte is imported as a header/c array 303 build_executorch_runner "${pte}" 304 if [ "$build_only" = false ] ; then 305 run_fvp arm_executor_runner 306 fi 307 fi 308done 309 310exit 0 311