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
42 changes: 38 additions & 4 deletions backends/arm/scripts/build_executor_runner.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ _setup_msg="please refer to ${et_root_dir}/examples/arm/setup.sh to properly ins
source "${script_dir}/utils.sh"

pte_file=""
target="ethos-u55-128"
target=""
build_type="Release"
bundleio=false
system_config=""
Expand All @@ -37,14 +37,15 @@ help() {
echo " --pte=<PTE_FILE>|<ADDR>|semihosting Set to a pte file (generated by the aot_arm_compier) to include the model in the elf."
echo " Or a hex address in the format of 0x00000000 if placed in memory you need to place it on this ADDR on your target, with your flash tool or other means."
echo " Or specify the word 'semihosting' to supply pte at runtime."
echo " --target=<TARGET> Target to build and run for Default: ${target}"
echo " --target=<TARGET> Target to build and run for. Defaults to the PTE Ethos-U compile spec target when available, otherwise ethos-u55-128"
echo " --build_type=<TYPE> Build with Release, Debug or RelWithDebInfo, default is ${build_type}"
echo " --bundleio Support both pte and Bundle IO bpte using Devtools BundelIO with Input/RefOutput included"
echo " --system_config=<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 " Defaults to ON when --pte points to a .bpte file."
echo " --system_config=<CONFIG> System configuration to select from the Vela configuration file (see vela.ini). Defaults to the Ethos-U delegate compile spec in the PTE when available, otherwise target-specific defaults."
echo " NOTE: If given, this option must match the given target. This option along with the memory_mode sets timing adapter values customized for specific hardware, see ./executor_runner/CMakeLists.txt."
echo " --memory_mode=<CONFIG> Vela memory mode, used for setting the Timing Adapter parameters of the Corstone platforms."
echo " Valid values are Shared_Sram(for Ethos-U55, Ethos-U65, Ethos-85), Sram_Only(for Ethos-U55, Ethos-U65, Ethos-U85) or Dedicated_Sram(for Ethos-U65, Ethos-U85)."
echo " Default: Shared_Sram for the Ethos-U55 and Sram_Only for the Ethos-U85"
echo " Defaults to the Ethos-U delegate compile spec in the PTE when available, otherwise target-specific defaults."
echo " --etdump Adds Devtools etdump support to track timing and output, etdump area will be base64 encoded in the log"
echo " --extra_build_flags=<FLAGS> Extra flags to pass to cmake like -DET_ARM_BAREMETAL_METHOD_ALLOCATOR_POOL_SIZE=60000 Default: none "
echo " --output=<FOLDER> Output folder Default: <MODEL>/<MODEL>_<TARGET INFO>.pte"
Expand Down Expand Up @@ -114,6 +115,9 @@ else
else
echo "PTE included in elf from file ${pte_file}"
pte_file=$(realpath ${pte_file})
if [[ ${pte_file} == *.bpte ]]; then
bundleio=true
fi
pte_data="-DET_PTE_FILE_PATH:PATH=${pte_file}"
if [ "$output_folder_set" = false ] ; then
# remove file ending
Expand All @@ -130,6 +134,36 @@ et_build_dir=${et_build_root}/cmake-out
mkdir -p ${et_build_dir}
et_build_dir=$(realpath ${et_build_dir})

# If a PTE file is supplied, derive any unset Ethos-U runtime config from it.
if [[ -f "${pte_file}" ]] && [[ -z "${target}" || -z "${system_config}" || -z "${memory_mode}" ]]; then
set +e
extracted_config=$(python3 "${script_dir}/ethosu_pte_info.py" --format=tsv "${pte_file}")
extracted_status=$?
set -e

if [[ ${extracted_status} -eq 0 ]]; then
IFS=$'\t' read -r extracted_target extracted_system_config extracted_memory_mode <<< "${extracted_config}"

if [[ ${target} == "" ]]; then
target="${extracted_target}"
fi
if [[ ${system_config} == "" ]]; then
system_config="${extracted_system_config}"
fi
if [[ ${memory_mode} == "" ]]; then
memory_mode="${extracted_memory_mode}"
fi
else
echo "Warning: Failed to derive Ethos-U compile spec defaults from ${pte_file}"
fi
fi

# Default values if getting defaults from compile spec failed.
if [[ ${target} == "" ]]
then
target="ethos-u55-128"
fi

if [[ ${system_config} == "" ]]
then
system_config="Ethos_U55_High_End_Embedded"
Expand Down
143 changes: 143 additions & 0 deletions backends/arm/scripts/ethosu_pte_info.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
#!/usr/bin/env python3

# 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.

from __future__ import annotations

import argparse
import json
from dataclasses import asdict, dataclass
from pathlib import Path

from executorch.backends.arm.ethosu import EthosUBackend, EthosUCompileSpec
from executorch.devtools.pte_tool.pte_info import (
DelegateInfo,
get_delegate_infos_from_pte,
)


@dataclass(frozen=True)
class EthosUDelegateConfig:
target: str
system_config: str
memory_mode: str


def _extract_flag_value(flags: list[str], prefix: str) -> str | None:
for flag in flags:
if flag.startswith(prefix):
return flag.removeprefix(prefix)
return None


def _config_from_delegate(delegate_info: DelegateInfo) -> EthosUDelegateConfig:
compile_spec = EthosUCompileSpec._from_list(delegate_info.compile_specs)
if compile_spec.target is None:
raise ValueError("Missing Ethos-U target in delegate compile spec.")

system_config = _extract_flag_value(compile_spec.compiler_flags, "--system-config=")
if system_config is None:
raise ValueError(
f"Missing --system-config flag in Ethos-U compile spec for {compile_spec.target}."
)

memory_mode = _extract_flag_value(compile_spec.compiler_flags, "--memory-mode=")
if memory_mode is None:
raise ValueError(
f"Missing --memory-mode flag in Ethos-U compile spec for {compile_spec.target}."
)

return EthosUDelegateConfig(
target=compile_spec.target,
system_config=system_config,
memory_mode=memory_mode,
)


def get_ethosu_delegate_configs_from_pte(
pte_path: str | Path,
) -> list[EthosUDelegateConfig]:
configs: list[EthosUDelegateConfig] = []
for delegate_info in get_delegate_infos_from_pte(pte_path):
if delegate_info.delegate_id != EthosUBackend.__name__:
continue
configs.append(_config_from_delegate(delegate_info))
return configs


def get_ethosu_delegate_config_from_pte(
pte_path: str | Path,
) -> EthosUDelegateConfig | None:
configs = get_ethosu_delegate_configs_from_pte(pte_path)
if not configs:
return None

unique_configs = sorted(
{
(config.target, config.system_config, config.memory_mode)
for config in configs
}
)
if len(unique_configs) != 1:
joined = ", ".join(
f"target={target} system_config={system_config} memory_mode={memory_mode}"
for target, system_config, memory_mode in unique_configs
)
raise ValueError(
"Found multiple Ethos-U delegate compile spec configurations in "
f"{pte_path}: {joined}"
)

target, system_config, memory_mode = unique_configs[0]
return EthosUDelegateConfig(
target=target,
system_config=system_config,
memory_mode=memory_mode,
)


def _get_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(
description=(
"Read the Ethos-U delegate compile spec from a .pte/.bpte file and "
"print the resolved target, system_config and memory_mode."
)
)
parser.add_argument("pte_path", help="Path to a .pte or .bpte file.")
parser.add_argument(
"--format",
choices=("pretty", "json", "tsv"),
default="pretty",
help="Output format. Default: %(default)s.",
)
return parser.parse_args()


def _print_config(config: EthosUDelegateConfig, output_format: str) -> None:
if output_format == "json":
print(json.dumps(asdict(config), sort_keys=True))
return
if output_format == "tsv":
print(f"{config.target}\t{config.system_config}\t{config.memory_mode}")
return

print(f"target={config.target}")
print(f"system_config={config.system_config}")
print(f"memory_mode={config.memory_mode}")


def main() -> int:
args = _get_args()
config = get_ethosu_delegate_config_from_pte(args.pte_path)
if config is None:
return 2

_print_config(config, args.format)
return 0


if __name__ == "__main__":
raise SystemExit(main())
131 changes: 131 additions & 0 deletions backends/arm/test/misc/test_ethosu_pte_info.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
# 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.

from executorch.backends.arm.ethosu import EthosUCompileSpec
from executorch.backends.arm.scripts.ethosu_pte_info import (
get_ethosu_delegate_config_from_pte,
)
from executorch.exir._serialize import _PTEFile, _serialize_pte_binary
from executorch.exir.backend.compile_spec_schema import CompileSpec
from executorch.exir.schema import (
BackendDelegate,
BackendDelegateDataReference,
BackendDelegateInlineData,
Buffer,
Chain,
ContainerMetadata,
DataLocation,
ExecutionPlan,
Program,
SubsegmentOffsets,
)
from pytest import raises


def _make_pte_bytes(*, delegates: list[BackendDelegate]) -> bytes:
program = Program(
version=0,
execution_plan=[
ExecutionPlan(
name="forward",
container_meta_type=ContainerMetadata(
encoded_inp_str="[]", encoded_out_str="[]"
),
values=[],
inputs=[],
outputs=[],
chains=[Chain(inputs=[], outputs=[], instructions=[], stacktrace=None)],
operators=[],
delegates=delegates,
non_const_buffer_sizes=[0],
)
],
constant_buffer=[Buffer(storage=b"")],
backend_delegate_data=[BackendDelegateInlineData(data=b"delegate-data")],
segments=[],
constant_segment=SubsegmentOffsets(segment_index=0, offsets=[]),
mutable_data_segments=[],
named_data=[],
)
return bytes(_serialize_pte_binary(_PTEFile(program)))


def _write_pte(tmp_path, *delegates: BackendDelegate):
pte_path = tmp_path / "model.pte"
pte_path.write_bytes(_make_pte_bytes(delegates=list(delegates)))
return pte_path


def _ethosu_delegate(
target: str,
system_config: str,
memory_mode: str,
index: int = 0,
) -> BackendDelegate:
return BackendDelegate(
id="EthosUBackend",
processed=BackendDelegateDataReference(
location=DataLocation.INLINE, index=index
),
compile_specs=EthosUCompileSpec(
target=target,
system_config=system_config,
memory_mode=memory_mode,
)._to_list(),
)


def test_ethosu_pte_info_no_target(tmp_path) -> None:
pte_path = _write_pte(
tmp_path,
_ethosu_delegate(
target="ethos-u85-256",
system_config="Ethos_U85_SYS_DRAM_Mid",
memory_mode="Dedicated_Sram_384KB",
),
)

config = get_ethosu_delegate_config_from_pte(pte_path)

assert config is not None
assert config.target == "ethos-u85-256"
assert config.system_config == "Ethos_U85_SYS_DRAM_Mid"
assert config.memory_mode == "Dedicated_Sram_384KB"


def test_ethosu_pte_info_returns_none_without_ethosu_delegate_no_target(
tmp_path,
) -> None:
pte_path = _write_pte(
tmp_path,
BackendDelegate(
id="OtherBackend",
processed=BackendDelegateDataReference(
location=DataLocation.INLINE, index=0
),
compile_specs=[CompileSpec(key="k", value=b"v")],
),
)

assert get_ethosu_delegate_config_from_pte(pte_path) is None


def test_ethosu_pte_info_rejects_mixed_configs_no_target(tmp_path) -> None:
pte_path = _write_pte(
tmp_path,
_ethosu_delegate(
target="ethos-u55-128",
system_config="Ethos_U55_High_End_Embedded",
memory_mode="Shared_Sram",
),
_ethosu_delegate(
target="ethos-u85-256",
system_config="Ethos_U85_SYS_DRAM_Mid",
memory_mode="Sram_Only",
),
)

with raises(ValueError, match="multiple Ethos-U delegate compile spec"):
get_ethosu_delegate_config_from_pte(pte_path)
25 changes: 25 additions & 0 deletions devtools/pte_tool/BUCK
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,18 @@ fbcode_target(_kind = runtime.python_library,
],
)

fbcode_target(_kind = runtime.python_library,
name = "pte_info_lib",
srcs = [
"pte_info.py",
],
deps = [
"//executorch/devtools/bundled_program/serialize:lib",
"//executorch/exir/_serialize:lib",
"//executorch/exir/backend:compile_spec_schema",
],
)

fbcode_target(_kind = runtime.python_binary,
name = "diff_pte",
srcs = [
Expand All @@ -27,3 +39,16 @@ fbcode_target(_kind = runtime.python_binary,
"//executorch/exir/_serialize:lib",
],
)

fbcode_target(_kind = runtime.python_binary,
name = "pte_info",
srcs = [
"pte_info.py",
],
main_function = "executorch.devtools.pte_tool.pte_info.main",
deps = [
"//executorch/devtools/bundled_program/serialize:lib",
"//executorch/exir/_serialize:lib",
"//executorch/exir/backend:compile_spec_schema",
],
)
Loading
Loading