Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
77 changes: 10 additions & 67 deletions .ci/scripts/test_cortex_m_e2e.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,76 +6,19 @@
# This source code is licensed under the BSD-style license found in the
# LICENSE file in the root directory of this source tree.

# End-to-end test for Cortex-M backend: export a model via aot_arm_compiler
# with cortex-m55+int8 target, then run the .bpte on Corstone-300 FVP.
#
# Usage: bash .ci/scripts/test_cortex_m_e2e.sh <model_name>
# Example: bash .ci/scripts/test_cortex_m_e2e.sh mv2
# CI wrapper: export a model for the Cortex-M backend and run it on the
# Corstone-300 FVP via examples/arm/run.sh. The real work (export, runner
# build, FVP launch, Test_result: PASS/FAIL check) is done by run.sh and
# the run_fvp.sh it invokes.

set -eux
set -eu

MODEL=$1
mkdir -p "./cortex_m_e2e/${MODEL}"
WORK_DIR=$(realpath "./cortex_m_e2e/${MODEL}")
script_dir=$(realpath "$(dirname "${BASH_SOURCE[0]}")")
et_root_dir=$(realpath "${script_dir}/../..")

echo "=== Exporting ${MODEL} with cortex-m55+int8 ==="
python -m backends.arm.scripts.aot_arm_compiler \
-m "${MODEL}" \
bash "${et_root_dir}/examples/arm/run.sh" \
--model_name="${MODEL}" \
--target=cortex-m55+int8 \
--quantize \
--bundleio \
--intermediates="${WORK_DIR}/intermediates" \
--output="${WORK_DIR}/${MODEL}.bpte"

BPTE="${WORK_DIR}/${MODEL}.bpte"
test -f "${BPTE}" || { echo "FAIL: ${BPTE} not produced"; exit 1; }
echo "=== Exported ${BPTE} ($(stat --printf='%s' "${BPTE}") bytes) ==="

ELF="arm_test/arm_semihosting_executor_runner_corstone-300/arm_executor_runner"
test -f "${ELF}" || { echo "FAIL: executor runner not found at ${ELF}"; exit 1; }

LOG_FILE=$(mktemp)

# Create a tiny dummy input file — the runner requires -i but BundleIO
# ignores it and uses the embedded test inputs instead.
dd if=/dev/zero of="${WORK_DIR}/dummy.bin" bs=4 count=1 2>/dev/null

echo "=== Running ${MODEL} on Corstone-300 FVP ==="
FVP_Corstone_SSE-300_Ethos-U55 \
-C ethosu.num_macs=128 \
-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 \
-C cpu0.semihosting-enable=1 \
-C cpu0.semihosting-stack_base=0 \
-C cpu0.semihosting-heap_limit=0 \
-C "cpu0.semihosting-cwd=${WORK_DIR}" \
-C "ethosu.extra_args='--fast'" \
-C "cpu0.semihosting-cmd_line='executor_runner -m ${MODEL}.bpte -i dummy.bin -o out'" \
-a "${ELF}" \
--timelimit 300 2>&1 | tee "${LOG_FILE}" || true

echo "=== Checking FVP output ==="

if grep -q "Test_result: PASS" "${LOG_FILE}"; then
echo "=== SUCCESS: ${MODEL} e2e BundleIO test PASSED on FVP ==="
rm "${LOG_FILE}"
exit 0
fi

if grep -q "Test_result: FAIL" "${LOG_FILE}"; then
echo "FAIL: ${MODEL} BundleIO output mismatch"
rm "${LOG_FILE}"
exit 1
fi

if grep -qE "(^[EF][: ].*$)|(^.*Hard fault.*$)|(^.*Assertion.*$)" "${LOG_FILE}"; then
echo "FAIL: ${MODEL} FVP run hit a fatal error"
rm "${LOG_FILE}"
exit 1
fi

echo "FAIL: ${MODEL} no BundleIO test result found in FVP output"
rm "${LOG_FILE}"
exit 1
--bundleio
7 changes: 3 additions & 4 deletions .github/workflows/trunk.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1083,8 +1083,7 @@ jobs:
.ci/scripts/setup-arm-baremetal-tools.sh
source examples/arm/arm-scratch/setup_path.sh

# Build cortex-m test runner with bundled IO support
backends/cortex_m/test/build_test_runner.sh

# Export model and run on FVP
# Export model and run on FVP (run.sh builds the cortex-m test
# runner, exports the model, and launches the FVP with bundled
# Test_result PASS/FAIL checking).
bash .ci/scripts/test_cortex_m_e2e.sh ${{ matrix.model }}
53 changes: 50 additions & 3 deletions backends/arm/scripts/run_fvp.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ _setup_msg="please refer to ${et_root_dir}/examples/arm/setup.sh to properly ins

elf_file=""
data_file=""
bundle_file=""
target="ethos-u55-128"
timeout="600"
etrecord_file=""
Expand All @@ -29,6 +30,7 @@ help() {
echo "Options:"
echo " --elf=<ELF_FILE> elf file to run"
echo " --data=<FILE>@<ADDRESS> Place a file in memory at this address, useful to emulate a PTE flashed into memory instead as part of the code."
echo " --bundle=<BPTE_FILE> Bundled program (.bpte) to load via semihosting. Required for cortex-m targets; the FVP launches a semihosting executor_runner that reads the bundle from the host filesystem and checks the embedded reference outputs."
echo " --target=<TARGET> Target to build and run for Default: ${target}"
echo " --timeout=<TIME_IN_SEC> Maximum target runtime, used to detect hanging, might need to be higer on large models Default: ${timeout}"
echo " --etrecord=<FILE> If ETDump is used you can supply a ETRecord file matching the PTE"
Expand All @@ -41,6 +43,7 @@ for arg in "$@"; do
-h|--help) help ;;
--elf=*) elf_file="${arg#*=}";;
--data=*) data_file="--data ${arg#*=}";;
--bundle=*) bundle_file="${arg#*=}";;
--target=*) target="${arg#*=}";;
--timeout=*) timeout="${arg#*=}";;
--etrecord=*) etrecord_file="${arg#*=}";;
Expand All @@ -52,7 +55,7 @@ done

elf_file=$(realpath ${elf_file})

if [[ ${target} == *"ethos-u55"* ]]; then
if [[ ${target} == *"ethos-u55"* || ${target} == cortex-m55* ]]; then
fvp_model=FVP_Corstone_SSE-300_Ethos-U55
else
fvp_model=FVP_Corstone_SSE-320
Expand All @@ -71,7 +74,12 @@ hash ${fvp_model} \


[[ ! -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)
if [[ ${target} == cortex-m* ]]; then
# Cortex-M CPU-only; the NPU is unused but the FVP still needs a value.
num_macs=128
else
num_macs=$(echo ${target} | cut -d - -f 3)
fi

echo "--------------------------------------------------------------------------------"
echo "Running ${elf_file} for ${target} run with FVP:${fvp_model} num_macs:${num_macs} timeout:${timeout}"
Expand All @@ -97,7 +105,46 @@ if [[ -n "${trace_file}" ]]; then
extra_args_u85+=(-C "mps4_board.subsystem.ethosu.extra_args=--pmu-trace ${trace_file}")
fi

if [[ ${target} == *"ethos-u55"* ]]; then
if [[ ${target} == cortex-m* ]]; then
[[ -z "${bundle_file}" ]] \
&& { echo "[${BASH_SOURCE[0]}] --bundle=<BPTE_FILE> is required for cortex-m targets"; exit 1; }
bundle_file=$(realpath "${bundle_file}")
bundle_dir=$(dirname "${bundle_file}")
bundle_name=$(basename "${bundle_file}")
# The bundled-IO runner requires its -i argument to refer to a real file
# even though inputs come from the embedded bundle; the caller is expected
# to have created fvp_dummy_input.bin next to the bundle.
[[ ! -f "${bundle_dir}/fvp_dummy_input.bin" ]] \
&& { echo "[${BASH_SOURCE[0]}] ${bundle_dir}/fvp_dummy_input.bin is missing; the caller must create a placeholder input file next to the bundle"; exit 1; }
${nobuf} ${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 \
-C cpu0.semihosting-enable=1 \
-C cpu0.semihosting-stack_base=0 \
-C cpu0.semihosting-heap_limit=0 \
-C "cpu0.semihosting-cwd=${bundle_dir}" \
-C "ethosu.extra_args=--fast" \
-C "cpu0.semihosting-cmd_line=executor_runner -m ${bundle_name} -i fvp_dummy_input.bin -o out" \
-a "${elf_file}" \
--timelimit ${timeout} 2>&1 | sed 's/\r$//' | tee ${log_file} || true
echo "[${BASH_SOURCE[0]}] Simulation complete, $?"
if grep -q "Test_result: PASS" "${log_file}"; then
echo "[${BASH_SOURCE[0]}] Bundled I/O check PASSED for ${bundle_name}"
rm "${log_file}"
exit 0
elif grep -q "Test_result: FAIL" "${log_file}"; then
echo "[${BASH_SOURCE[0]}] Bundled I/O check FAILED for ${bundle_name}"
rm "${log_file}"
exit 1
else
echo "[${BASH_SOURCE[0]}] No Test_result line found in FVP output for ${bundle_name}"
rm "${log_file}"
exit 1
fi
elif [[ ${target} == *"ethos-u55"* ]]; then
${nobuf} ${fvp_model} \
-C ethosu.num_macs=${num_macs} \
-C mps3_board.visualisation.disable-visualisation=1 \
Expand Down
6 changes: 6 additions & 0 deletions backends/cortex_m/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ backends/cortex_m/test/build_test_runner.sh # Buil
pytest --config-file=backends/arm/test/pytest.ini backends/cortex_m/test # Run tests with correct configuration file
```

For an end-to-end bundled-IO FVP run of a single model (export → build → FVP → `Test_result: PASS`), use `examples/arm/run.sh`:
```
examples/arm/run.sh --model_name=<model> --target=cortex-m55+int8 --bundleio
```
This drives `aot_arm_compiler --bundleio`, invokes `build_test_runner.sh`, and launches the Corstone-300 FVP via `backends/arm/scripts/run_fvp.sh`.

## Supported operators
Refer to `backends/cortex_m/test/ops` for currently supported accelerated ops/dtypes. Additionally, the quantizer targets pure "data-movement ops" such as data copies, slicing and concatenations to use quantized dtypes using the portable-kernels operator library.
In general however, operators not supported by Cortex-M are kept in `fp32` using non-accelerated portable-kernels. It is recommended to analyze the graph after lowering to understand how much of the graph has been accelerated.
Expand Down
25 changes: 25 additions & 0 deletions examples/arm/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,12 @@ if [ "$perf_overlay" = true ] && [ "$model_explorer" != true ]; then
exit 1
fi

# Cortex-M backend is an operator-library, not a delegate; force-disable
# --delegate when targeting cortex-m so users don't need --no_delegate.
if [[ ${target} == cortex-m* ]]; then
aot_arm_compiler_flag_delegate=""
fi

if ! [[ ${pte_placement} == "elf" ]]; then
if ! [[ "$pte_placement" =~ ^0x[0-9a-fA-F]{1,16}$ ]]; then
echo "ERROR: Placing the PTE in memory failed, address is larger then 64bit $pte_placement"
Expand Down Expand Up @@ -347,6 +353,25 @@ for i in "${!test_model[@]}"; do

if [[ ${target} == *"TOSA"* ]]; then
echo "Build for ${target} skip generating a .elf and running it"
elif [[ ${target} == cortex-m* ]]; then
# Cortex-M backend uses a shared semihosting executor_runner (built
# by build_test_runner.sh) that loads the .bpte at runtime, rather
# than per-model runners with the PTE baked in.
if [ "$bundleio" != true ]; then
echo "Error: --target=${target} requires --bundleio (the cortex-m runner loads bundled inputs via semihosting)"
exit 1
fi
set -x
backends/cortex_m/test/build_test_runner.sh
cortex_m_elf="${et_root_dir}/arm_test/arm_semihosting_executor_runner_corstone-300/arm_executor_runner"
# The bundled-IO runner requires its -i argument to point at a real file
# even though inputs come from the bundle; write a placeholder next to
# the .bpte for run_fvp.sh to pass along.
dd if=/dev/zero of="$(dirname "${pte_file}")/fvp_dummy_input.bin" bs=4 count=1 2>/dev/null
if [ "$build_only" = false ] ; then
backends/arm/scripts/run_fvp.sh --elf="${cortex_m_elf}" --target="${target}" --bundle="${pte_file}"
fi
set +x
elif [[ ${target} == *"vgf"* ]]; then
echo "Build and run for VKML, (target: ${target})"
set -x
Expand Down
Loading