Skip to content

Commit d939b9b

Browse files
authored
Cortex-M backend: Add cmsis_nn pybind dependency (#19279)
- Add cmsis_nn to new optional-dependency called [cortex_m] - Additionally, add an install step that allows optional depedencies to install required build tools. This is needed for cortex_m. - Add cmsis_nn to backends/cortex_m/requirements-cortex-m.txt and add possibility to install from setup.sh to not break backwards compatability, and to mirror ethos_u and vgf backend. - Add guard to cortex_m/passes/__init__.py to fail gracefully if cortex_m dependencies are not installed. Signed-off-by: Erik Lundell <erik.lundell@arm.com>
1 parent 3924a22 commit d939b9b

8 files changed

Lines changed: 110 additions & 5 deletions

File tree

backends/cortex_m/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ include(FetchContent)
2323

2424
# CMSIS-NN configuration with dynamic path detection
2525
set(CMSIS_NN_VERSION
26-
"098d54a61e3e04e2b60e9010e871eef036ac5ae3"
26+
"d933672e7ca97eec70ef43230baee7b20c2a28ae"
2727
CACHE STRING "CMSIS-NN version to download"
2828
)
2929
set(CMSIS_NN_LOCAL_PATH

backends/cortex_m/passes/__init__.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,36 @@
33
# This source code is licensed under the BSD-style license found in the
44
# LICENSE file in the root directory of this source tree.
55

6+
from importlib.util import find_spec
7+
8+
9+
def _missing_dependencies_error(missing: str) -> ModuleNotFoundError:
10+
return ModuleNotFoundError(
11+
"Cortex-M backend dependencies are not installed "
12+
f"(missing: {missing}). Install ExecuTorch with "
13+
"`pip install executorch[cortex_m]`, or if building from source run "
14+
"`examples/arm/setup.sh --i-agree-to-the-contained-eula`."
15+
)
16+
17+
18+
def _ensure_cortex_m_dependencies() -> None:
19+
required_modules = {
20+
"cmsis_nn": "cmsis_nn",
21+
}
22+
missing_packages = []
23+
for module_name, package_name in required_modules.items():
24+
try:
25+
if find_spec(module_name) is None:
26+
missing_packages.append(package_name)
27+
except (ImportError, ValueError):
28+
missing_packages.append(package_name)
29+
30+
if missing_packages:
31+
raise _missing_dependencies_error(", ".join(missing_packages))
32+
33+
34+
_ensure_cortex_m_dependencies()
35+
636
from .activation_fusion_pass import ActivationFusionPass # noqa
737
from .clamp_hardswish_pass import ClampHardswishPass # noqa
838
from .convert_to_cortex_m_pass import ConvertToCortexMPass # noqa
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Copyright 2026 Arm Limited and/or its affiliates.
2+
#
3+
# This source code is licensed under the BSD-style license found in the
4+
# LICENSE file in the root directory of this source tree.
5+
6+
# These dependencies need to match pyproject.toml
7+
8+
cmsis_nn @ git+https://github.com/ARM-software/CMSIS-NN.git@d933672e7ca97eec70ef43230baee7b20c2a28ae
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Copyright 2026 Arm Limited and/or its affiliates.
2+
# All rights reserved.
3+
#
4+
# This source code is licensed under the BSD-style license found in the
5+
# LICENSE file in the root directory of this source tree.
6+
7+
import importlib
8+
9+
import pytest
10+
11+
12+
def _import_cmsis_nn():
13+
try:
14+
return importlib.import_module("cmsis_nn")
15+
except Exception as exc:
16+
pytest.fail(f"Failed to resolve cmsis_nn: {exc}")
17+
18+
19+
def test_cmsis_nn_convolve_wrapper_buffer_size() -> None:
20+
cmsis_nn = _import_cmsis_nn()
21+
22+
buf_size = cmsis_nn.convolve_wrapper_buffer_size(
23+
cmsis_nn.Backend.MVE,
24+
cmsis_nn.DataType.A8W8,
25+
input_nhwc=[1, 8, 8, 16],
26+
filter_nhwc=[8, 3, 3, 16],
27+
output_nhwc=[1, 6, 6, 8],
28+
padding_hw=[0, 0],
29+
stride_hw=[1, 1],
30+
dilation_hw=[1, 1],
31+
)
32+
33+
assert buf_size == 576

examples/arm/setup.sh

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ enable_baremetal_toolchain=1
2222
target_toolchain=""
2323
enable_fvps=1
2424
enable_vela=1
25+
enable_cortex_m=1
2526
enable_model_converter=0 # model-converter tool for VGF output
2627
enable_vgf_lib=0 # vgf reader - runtime backend dependency
2728
enable_emulation_layer=0 # Vulkan layer driver - emulates Vulkan ML extensions
@@ -48,6 +49,7 @@ OPTION_LIST=(
4849
"--target-toolchain Select toolchain: gnu (default), zephyr, or linux-musl"
4950
"--enable-fvps Enable FVP setup"
5051
"--enable-vela Enable VELA setup"
52+
"--disable-cortex-m-deps Do not setup what is needed for Cortex-M"
5153
"--enable-model-converter Enable MLSDK model converter setup"
5254
"--enable-vgf-lib Enable MLSDK vgf library setup"
5355
"--enable-emulation-layer Enable MLSDK Vulkan emulation layer"
@@ -123,6 +125,10 @@ function check_options() {
123125
enable_vela=1
124126
shift
125127
;;
128+
--disable-cortex-m-deps)
129+
enable_cortex_m=0
130+
shift
131+
;;
126132
--enable-model-converter)
127133
enable_model_converter=1
128134
shift
@@ -211,6 +217,11 @@ function setup_ethos_u_tools() {
211217
CMAKE_POLICY_VERSION_MINIMUM=3.5 BUILD_PYBIND=1 pip install --no-dependencies -r $et_dir/backends/arm/requirements-arm-ethos-u.txt
212218
}
213219

220+
function setup_cortex_m_tools() {
221+
log_step "cortex-m-tools" "Installing Cortex-M Python tooling"
222+
pip install --no-dependencies -r $et_dir/backends/cortex_m/requirements-cortex-m.txt
223+
}
224+
214225
function setup_mlsdk_dependencies() {
215226
log_step "mlsdk" "Installing MLSDK dependencies"
216227
pip install -r $et_dir/backends/arm/requirements-arm-vgf.txt
@@ -299,6 +310,7 @@ if [[ $is_script_sourced -eq 0 ]]; then
299310
"root=${root_dir}, target-toolchain=${target_toolchain:-<default>}"
300311
log_step "options" \
301312
"ethos-u: fvps=${enable_fvps}, toolchain=${enable_baremetal_toolchain}, vela=${enable_vela} | " \
313+
"cortex-m: deps=${enable_cortex_m} | " \
302314
"mlsdk: model-converter=${enable_model_converter}, vgf-lib=${enable_vgf_lib}, " \
303315
"emu-layer=${enable_emulation_layer}, vulkan-sdk=${enable_vulkan_sdk}"
304316

@@ -349,6 +361,11 @@ if [[ $is_script_sourced -eq 0 ]]; then
349361
setup_ethos_u_tools
350362
fi
351363

364+
if [[ "${enable_cortex_m}" -eq 1 ]]; then
365+
log_step "deps" "Installing Cortex-M CMSIS-NN tooling"
366+
setup_cortex_m_tools
367+
fi
368+
352369
log_step "main" "Setup complete"
353370
exit 0
354371
fi

install_executorch.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ def _parse_args() -> argparse.Namespace:
191191
help="Only installs necessary dependencies for core executorch and skips "
192192
" packages necessary for running example scripts.",
193193
)
194-
allowed_optional_dependencies = ["ethos_u", "vgf", "openvino"]
194+
allowed_optional_dependencies = ["cortex_m", "ethos_u", "vgf", "openvino"]
195195
parser.add_argument(
196196
"--optional-dependency",
197197
action="append",
@@ -225,7 +225,20 @@ def main(args):
225225
# Step 1: Install core dependencies first
226226
install_requirements(use_pytorch_nightly)
227227

228-
# Step 2: Install core package
228+
# Step 2: Install build dependencies for optional dependencies
229+
# They need to be installed before optional dependencies due to --no-build-isolation
230+
optional_build_dependencies: list[str] = []
231+
for optional_dep in args.optional_dependency:
232+
match optional_dep:
233+
case "cortex_m":
234+
optional_build_dependencies.extend(
235+
["pybind11>=2.10", "scikit-build-core>=0.7"]
236+
)
237+
if len(optional_build_dependencies) > 0:
238+
cmd = [sys.executable, "-m", "pip", "install", *optional_build_dependencies]
239+
subprocess.run(cmd, check=True)
240+
241+
# Step 3: Install core package
229242
package_spec = "."
230243
if args.optional_dependency:
231244
extras = ",".join(dict.fromkeys(args.optional_dependency))
@@ -246,7 +259,7 @@ def main(args):
246259
)
247260
subprocess.run(cmd, check=True)
248261

249-
# Step 3: Extra (optional) packages that is only useful for running examples.
262+
# Step 4: Extra (optional) packages that is only useful for running examples.
250263
if not args.minimal:
251264
install_optional_example_requirements(use_pytorch_nightly)
252265

pyproject.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,10 @@ dependencies=[
7777
]
7878

7979
[project.optional-dependencies]
80+
cortex_m = [
81+
# Keep this in sync with AoT deps from backends/cortex_m/requirements-cortex-m.txt
82+
"cmsis_nn @ git+https://github.com/ARM-software/CMSIS-NN.git@d933672e7ca97eec70ef43230baee7b20c2a28ae",
83+
]
8084
vgf = [
8185
# AoT vgf dependencies
8286
# Keep this in sync with AoT deps from backends/arm/requirements-arm-vgf.txt and

requirements-dev.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,4 @@ lintrunner-adapters==0.13.0
1414
pytest<9.0
1515
pytest-xdist
1616
pytest-rerunfailures==15.1
17-
pytest-json-report
17+
pytest-json-report

0 commit comments

Comments
 (0)