Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
174 changes: 165 additions & 9 deletions .ci/scripts/zephyr-utils.sh
Original file line number Diff line number Diff line change
@@ -1,19 +1,175 @@
#!/bin/bash
# Copyright (c) Meta Platforms, Inc. and affiliates.
# All rights reserved.
# Copyright 2026 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.

download_arm_zephyr_sdk () {
wget https://github.com/zephyrproject-rtos/sdk-ng/releases/download/v0.17.2/zephyr-sdk-0.17.2_linux-x86_64.tar.xz
tar -xf zephyr-sdk-0.17.2_linux-x86_64.tar.xz
rm -f zephyr-sdk-0.17.2_linux-x86_64.tar.xz
# Run instruction from zephyr/README.md
#
# This contains some CI helper runtime functions to dig out and run commands
# from zephyr/README.md
# It also try to verify that zephyr/executorch.yaml is in sync and the snippets
# in the README are sane with various regexps.
#
# Main functions is run_command_block_from_zephyr_readme it will dig out the code between
# the two ''' after a blockheader text and run it
#
# .e.g. from this README.md snippet
#
# Install requirements
# ```
# pip install something
# pip install something_else
# ```
#
# The blockheader is 'Install requirements' and the code block is 'pip install something ... \npip install something_else'
# so if we run
# run_command_block_from_zephyr_readme 'Install requirements' '^(pip|pip3)[[:space:]]+install([[:space:]].*)?$'
# it will make sure each lite start with pip or pip3 and then run them one by one



# Resolve and cache this script's directory; if sourced, and remember it globally
# So functions in this script can be used even after sourced and still find
# the correct paths
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
if [[ "${BASH_SOURCE[0]}" != "$0" ]]; then
export ZEPHYR_UTILS_DIR="${script_dir}"
fi

# Internal utility functions
_zephyr_utils_root_dir () {
local script_dir resolved

if [[ -n "${ZEPHYR_UTILS_DIR:-}" ]]; then
script_dir="${ZEPHYR_UTILS_DIR}"
else
resolved="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
if [[ "${BASH_SOURCE[0]}" != "$0" ]]; then
ZEPHYR_UTILS_DIR="${resolved}"
fi
script_dir="${resolved}"
fi

(cd "${script_dir}/../.." && pwd)
}

_zephyr_utils_readme_path () {
local root_dir
root_dir="$(_zephyr_utils_root_dir)"
printf '%s/zephyr/README.md\n' "${root_dir}"
}

_zephyr_utils_ensure_file () {
local path="$1"
local description="$2"
if [[ ! -f "${path}" ]]; then
echo "ERROR: ${description} not found at ${path}" >&2
return 1
fi
}

setup_zephyr_et_module () {
git clone --branch executorch-module-integration https://github.com/BujSet/zephyr.git
west init -l zephyr
west config manifest.project-filter -- +executorch
west -v update
_zephyr_utils_extract_block () {
local readme_path="$1"
local marker="$2"
awk -v marker="${marker}" '
$0 ~ marker { section=1; next }
section && /^```/ {
if (in_block) { exit }
in_block=1
next
}
in_block && /^```/ { exit }
in_block { sub(/\r$/, ""); print }
' "${readme_path}"
}

_zephyr_utils_run_simple_commands () {
local block="$1"
local allowedpattern="$2"
local description="$3"
temp_dir="$(mktemp -d)"
if [[ ! -d "${temp_dir}" ]]; then
echo "ERROR: Failed to create temporary directory for west init" >&2
return 1
fi
trap "rm -rf '${temp_dir}'; trap - RETURN" RETURN

local ran=0 cmd
while IFS= read -r cmd; do
cmd="${cmd#"${cmd%%[![:space:]]*}"}"
cmd="${cmd%"${cmd##*[![:space:]]}"}"
[[ -z "${cmd}" ]] && continue

if [[ ! "${cmd}" =~ ${allowedpattern} ]]; then
echo "ERROR: Unexpected command in '${description}' readme block: ${cmd} must match pattern: ${allowedpattern}" >&2
return 1
fi

if [[ "${cmd}" == *";"* || "${cmd}" == *"&&"* || "${cmd}" == *"||"* || "${cmd}" == *"|"* ]]; then
echo "ERROR: Command chaining is not allowed in '${description}' readme block: ${cmd}" >&2
return 1
fi

echo "Running: ${cmd}"
if ! eval "${cmd}"; then
return 1
fi
ran=1
done <<< "${block}"

if [[ ${ran} -eq 0 ]]; then
echo "ERROR: No commands found in block after ${description}" >&2
return 1
fi

return 0
}

run_command_block_from_zephyr_readme () {
local blockheader="$1"
local allowedpattern="$2"

echo "Run block '${blockheader}' from zephyr/README.md"

readme_path="$(_zephyr_utils_readme_path)"
_zephyr_utils_ensure_file "${readme_path}" "README.md" || return 1

block="$(_zephyr_utils_extract_block "${readme_path}" "${blockheader}")"

if [[ -z "${block}" ]]; then
echo "ERROR: Failed to locate ${blockheader} block in ${readme_path}" >&2
return 1
fi
_zephyr_utils_run_simple_commands "${block}" "${allowedpattern}" "${blockheader}"
}

# Check that zephyr/executorch.yaml match zephyr/README.md
verify_zephyr_readme () {
local readme_path manifest_path snippet

readme_path="$(_zephyr_utils_readme_path)"
manifest_path="$(_zephyr_utils_root_dir)/zephyr/executorch.yaml"

_zephyr_utils_ensure_file "${readme_path}" "README" || return 1
_zephyr_utils_ensure_file "${manifest_path}" "Manifest" || return 1

snippet="$(
_zephyr_utils_extract_block "${readme_path}" '<zephyr_build_root>/zephyr/submanifests/executorch\.yaml'
)"

if [[ -z "${snippet}" ]]; then
echo "ERROR: Failed to extract executorch.yaml snippet from ${readme_path}" >&2
return 1
fi

if diff -u <(printf '%s\n' "${snippet}") "${manifest_path}"; then
echo "zephyr/README.md executorch.yaml snippet is in sync with zephyr/executorch.yaml" >&2
return 0
fi

echo "ERROR: ${readme_path} executorch.yaml snippet is out of sync with ${manifest_path}" >&2
return 1
}
205 changes: 109 additions & 96 deletions .github/workflows/trunk.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,102 +55,115 @@ jobs:
# Build and test executorch
PYTHON_EXECUTABLE=python ${CONDA_RUN} bash .ci/scripts/test_model.sh "${MODEL_NAME}" "${BUILD_TOOL}" "${BACKEND}"

# test-models-arm-zephyr:
# name: test-models-arm-zephyr
# uses: pytorch/test-infra/.github/workflows/linux_job.yml@main
# strategy:
# matrix:
# model: [add, softmax, mv2]
# fail-fast: false
# with:
# runner: linux.2xlarge
# docker-image: ci-image:executorch-ubuntu-22.04-zephyr-sdk
# submodules: 'recursive'
# ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
# timeout: 120
# script: |
# MODEL_NAME=${{ matrix.model }}
# CONDA_ENV=$(conda env list --json | jq -r ".envs | .[-1]")
# conda activate "${CONDA_ENV}"
# if [[ ${{ matrix.model}} == "add" ]]; then
# SIM_LIMIT_SEC=60
# elif [[ ${{ matrix.model}} == "softmax" ]]; then
# SIM_LIMIT_SEC=60
# elif [[ ${{ matrix.model}} == "mv2" ]]; then
# SIM_LIMIT_SEC=5000
# else
# echo "Failed unsupported model selection ${{ matrix.model }}"
# exit 1
# fi
#
# source .ci/scripts/utils.sh
# source .ci/scripts/zephyr-utils.sh
# mkdir -p zephyr_scratch/
# cd zephyr_scratch
# export ZEPHYR_PROJ_ROOT=$(realpath $(pwd))
# export ARM_FVP_TUTORIALS_ROOT=$ZEPHYR_PROJ_ROOT/zephyr/samples/modules/executorch/arm-fvp-tutorials
#
# # TODO @Bujji: Should see if this can be moved into the docker image itself
# download_arm_zephyr_sdk
# ./zephyr-sdk-0.17.2/setup.sh -c -t arm-zephyr-eabi
# cd $ZEPHYR_PROJ_ROOT
# setup_zephyr_et_module
#
# # Run setup scripts for Arm FVP and Arm AOT Compilation
# cd $ZEPHYR_PROJ_ROOT/modules/lib/executorch
# install_executorch
# .ci/scripts/setup-arm-baremetal-tools.sh --target-toolchain zephyr
# source examples/arm/arm-scratch/setup_path.sh
# source $ZEPHYR_PROJ_ROOT/zephyr/zephyr-env.sh
#
# # Get the model as PTE
# python -m examples.arm.aot_arm_compiler \
# --model_name="${MODEL_NAME}" \
# --output="${MODEL_NAME}.pte"
#
# # Generate the C-style header
# cd $ARM_FVP_TUTORIALS_ROOT
# python build_model.py \
# --executorch-root $ZEPHYR_PROJ_ROOT/modules/lib/executorch \
# --pte-file $ZEPHYR_PROJ_ROOT/modules/lib/executorch/${MODEL_NAME}.pte \
# --output-path $ARM_FVP_TUTORIALS_ROOT/models/${MODEL_NAME}/src/
#
# cd $ARM_FVP_TUTORIALS_ROOT/models/${MODEL_NAME}/
#
# # Build the zephyr elf
# west build -p always -b mps3/corstone300/fvp -- \
# -DET_PTE_FILE_PATH_FOR_SELECTIVE_BUILD=$ZEPHYR_PROJ_ROOT/modules/lib/executorch/${MODEL_NAME}.pte
#
# # Run the simulation
# FVP_Corstone_SSE-300_Ethos-U55 -a build/zephyr/zephyr.elf \
# -C mps3_board.visualisation.disable-visualisation=1 \
# -C mps3_board.telnetterminal0.start_telnet=0 \
# -C mps3_board.uart0.out_file='sim.out' \
# -C cpu0.CFGITCMSZ=15 \
# -C cpu0.CFGDTCMSZ=15 \
# --simlimit ${SIM_LIMIT_SEC}
#
# # Disable exit on error
# set +e
# # Report failure if any of the ouptut verification checks fail
# grep -qF "ERROR" sim.out
# exit_status=$? #store 0 if found (failure), 1 if not (success)
# if [[ "$exit_status" -eq "0" ]]; then
# cat sim.out
# set -e
# exit 1
# fi
#
# # Report fail if simulation does not complete successfully
# grep -qF "SUCCESS: Program complete, exiting." sim.out
# exit_status=$? #store 0 if found (success), 1 if not (failure)
# if [[ "$exit_status" -eq "1" ]]; then
# cat sim.out
# set -e
# exit 1
# fi
# # Re-enable exit on error
# set -e
test-arm-backend-zephyr:
name: test-models-arm-zephyr
uses: pytorch/test-infra/.github/workflows/linux_job.yml@main
strategy:
fail-fast: false
with:
runner: linux.2xlarge
docker-image: ci-image:executorch-ubuntu-22.04-zephyr-sdk
submodules: 'recursive'
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
timeout: 120
script: |
#!/bin/bash

CONDA_ENV=$(conda env list --json | jq -r ".envs | .[-1]")
conda activate "${CONDA_ENV}"

# Test zephyr backend
set -e

export EXECUTORCH_PROJ_ROOT=$(realpath $(pwd))

# Source utility scripts
. .ci/scripts/utils.sh
. .ci/scripts/zephyr-utils.sh

# check that zephyr/README.md and zephyr/executorch.yaml are in sync
verify_zephyr_readme

# Based on instruction in zephyr/README.md

# Allowed only "pip install ..." commands
allowedpattern='^(pip|pip3)[[:space:]]+install([[:space:]].*)?$'
run_command_block_from_zephyr_readme 'Install requirements' "${allowedpattern}"

mkdir -p zephyr_scratch/

cd zephyr_scratch
export ZEPHYR_PROJ_ROOT=$(realpath $(pwd))

# TODO @Bujji: Should see if this can be moved into the docker image itself
wget https://github.com/zephyrproject-rtos/sdk-ng/releases/download/v0.17.4/zephyr-sdk-0.17.4_linux-x86_64.tar.xz
tar -xf zephyr-sdk-0.17.4_linux-x86_64.tar.xz
rm -f zephyr-sdk-0.17.4_linux-x86_64.tar.xz*

./zephyr-sdk-0.17.4/setup.sh -c -t arm-zephyr-eabi
export ZEPHYR_SDK_INSTALL_DIR=$(realpath ./zephyr-sdk-0.17.4)

cd $ZEPHYR_PROJ_ROOT

# Allowed only "west init ..." commands
run_command_block_from_zephyr_readme 'Setup zephyr repo' '^west[[:space:]]+init([[:space:]].*)?$'

cp ${EXECUTORCH_PROJ_ROOT}/zephyr/executorch.yaml zephyr/submanifests/

# Allowed only "west ..." commands
run_command_block_from_zephyr_readme 'Add ExecuTorch to Zephyr' '^west([[:space:]].*)?$'

# Switch to executorch in this PR e.g. replace modules/lib/executorch with the root folder of this repo
# instead of doing a re-checkout and figure out the correct commit hash etc
rm -Rf modules/lib/executorch
ln -s ${EXECUTORCH_PROJ_ROOT} modules/lib/executorch

# Allowed only "cd/git/install_executorch.sh" commands
run_command_block_from_zephyr_readme 'Setup ExecuTorch' '^(cd [^;&|]+|git submodule([[:space:]].*)?|\./install_executorch\.sh([[:space:]].*)?)$'

# As "cd" was an allowed command in the last run_command_block_from_zephyr_readme
# lets ensure we are back to the expected folder
cd $ZEPHYR_PROJ_ROOT

# Allowed only "examples/arm/setup.sh and source of the generated script" command
run_command_block_from_zephyr_readme 'Install TOSA, vela and FVPs' '^(modules/lib/executorch/examples/arm/setup\.sh([[:space:]].*)?|(source|\.) modules/lib/executorch/examples/arm/arm-scratch/setup_path\.sh)$'

echo "===== Corstone300 FVP setup ===="

# Allowed only "export" command
run_command_block_from_zephyr_readme 'Config Zephyr Corstone300 FVP' '^export([[:space:]].*)$'

echo "---- Ethos-U55 ----"
rm -Rf build

# Allowed only "aot_arm_compiler" command
run_command_block_from_zephyr_readme 'Prepare the Ethos-U55 PTE model' '^python -m modules.lib.executorch.examples.arm.aot_arm_compiler([[:space:]].*)?$'

# Allowed only "west build" command
run_command_block_from_zephyr_readme 'Run the Ethos-U55 PTE model' '^west build([[:space:]].*)?$'

echo "---- Cortex-M55 ----"
rm -Rf build

# Allowed only "aot_arm_compiler" command
run_command_block_from_zephyr_readme 'Prepare the Cortex-M55 PTE model' '^python -m modules.lib.executorch.examples.arm.aot_arm_compiler([[:space:]].*)?$'

# Allowed only "west build" command
run_command_block_from_zephyr_readme 'Run the Cortex-M55 PTE model' '^west build([[:space:]].*)?$'

echo "===== Corstone320 FVP setup ===="

# Allowed only "export" command
run_command_block_from_zephyr_readme 'Config Zephyr Corstone320 FVP' '^export([[:space:]].*)$'

echo "---- Ethos-U85 ----"
rm -Rf build

# Allowed only "aot_arm_compiler" command
run_command_block_from_zephyr_readme 'Prepare the Ethos-U85 PTE model' '^python -m modules.lib.executorch.examples.arm.aot_arm_compiler([[:space:]].*)?$'

# Allowed only "west build" command
run_command_block_from_zephyr_readme 'Run the Ethos-U85 PTE model' '^west build([[:space:]].*)?$'

test-models-linux-aarch64:
name: test-models-linux-aarch64
Expand Down
Loading
Loading