Skip to content

Commit b9da8c9

Browse files
author
Github Executorch
committed
Summary: Add Stateful FC Cortex-m linearOps
Integrate with CMSIS-NN with per-channel quantization support Test Plan: Run e2e test on FVP simulator ./examples/arm/run_mcu_models_fvp.sh --target=cortex-m55 --models=qlinear Reviewers: Subscribers: Tasks: Tags:
1 parent 02b39bf commit b9da8c9

File tree

9 files changed

+1412
-51
lines changed

9 files changed

+1412
-51
lines changed

backends/cortex_m/CMakeLists.txt

Lines changed: 52 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ if(NOT CMAKE_CXX_STANDARD)
1212
set(CMAKE_CXX_STANDARD 17)
1313
endif()
1414

15-
# Source root directory for executorch.
15+
# Source root directory for executorch
1616
if(NOT EXECUTORCH_ROOT)
1717
set(EXECUTORCH_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/../..)
1818
endif()
@@ -21,70 +21,90 @@ include(${EXECUTORCH_ROOT}/tools/cmake/Utils.cmake)
2121
include(${EXECUTORCH_ROOT}/tools/cmake/Codegen.cmake)
2222
include(FetchContent)
2323

24-
# CMSIS-NN version to download
24+
# CMSIS-NN configuration with dynamic path detection
2525
set(CMSIS_NN_VERSION
26-
"v4.1.0"
26+
"v7.0.0"
2727
CACHE STRING "CMSIS-NN version to download"
2828
)
29-
30-
# Declare CMSIS-NN as a FetchContent project
31-
FetchContent_Declare(
32-
cmsis_nn
33-
GIT_REPOSITORY https://github.com/ARM-software/CMSIS-NN.git
34-
GIT_TAG ${CMSIS_NN_VERSION}
29+
set(CMSIS_NN_LOCAL_PATH
30+
""
31+
CACHE PATH "Path to existing local CMSIS-NN installation"
3532
)
3633

37-
# Download and make CMSIS-NN available
38-
FetchContent_MakeAvailable(cmsis_nn)
34+
# Try to find existing / local CMSIS-NN installation. This is useful for
35+
# debugging and testing with local changes. This is not common, as the CMSIS-NN
36+
# library is downloaded via FetchContent in the default/regular case.
37+
if(CMSIS_NN_LOCAL_PATH AND EXISTS "${CMSIS_NN_LOCAL_PATH}")
38+
message(STATUS "Using CMSIS-NN from specified path: ${CMSIS_NN_LOCAL_PATH}")
39+
add_subdirectory(${CMSIS_NN_LOCAL_PATH} cmsis_nn_build)
40+
else()
41+
# Use FetchContent with automatic fallback
42+
message(STATUS "Using CMSIS-NN via FetchContent")
43+
44+
FetchContent_Declare(
45+
cmsis_nn
46+
GIT_REPOSITORY https://github.com/ARM-software/CMSIS-NN.git
47+
GIT_TAG ${CMSIS_NN_VERSION}
48+
GIT_SHALLOW TRUE
49+
)
50+
51+
FetchContent_GetProperties(cmsis_nn)
52+
if(NOT cmsis_nn_POPULATED)
53+
FetchContent_Populate(cmsis_nn)
54+
add_subdirectory(${cmsis_nn_SOURCE_DIR} ${cmsis_nn_BINARY_DIR})
55+
endif()
56+
endif()
3957

40-
# Print paths for debugging
41-
message(STATUS "CMSIS-NN source dir: ${cmsis_nn_SOURCE_DIR}")
42-
message(STATUS "CMSIS-NN binary dir: ${cmsis_nn_BINARY_DIR}")
58+
# Add MVEI define to cmsis-nn target
59+
if(TARGET cmsis-nn)
60+
target_compile_definitions(cmsis-nn PUBLIC ARM_MATH_MVEI=1)
61+
get_target_property(CMSIS_NN_INCLUDES cmsis-nn INTERFACE_INCLUDE_DIRECTORIES)
62+
message(STATUS "CMSIS-NN include dirs: ${CMSIS_NN_INCLUDES}")
63+
else()
64+
message(
65+
FATAL_ERROR
66+
"CMSIS-NN target not found. Check your CMSIS_NN_LOCAL_PATH or network connection."
67+
)
68+
endif()
4369

4470
# Cortex-M ops kernel sources
4571
set(_cortex_m_kernels__srcs
4672
${CMAKE_CURRENT_SOURCE_DIR}/ops/op_quantize_per_tensor.cpp
4773
${CMAKE_CURRENT_SOURCE_DIR}/ops/op_dequantize_per_tensor.cpp
4874
${CMAKE_CURRENT_SOURCE_DIR}/ops/op_quantized_add.cpp
75+
${CMAKE_CURRENT_SOURCE_DIR}/ops/op_quantized_linear.cpp
4976
)
5077

51-
# Generate C++ bindings to register kernels into Executorch (for runtime)
78+
# Generate C++ bindings to register kernels into Executorch
5279
set(_yaml_file ${CMAKE_CURRENT_LIST_DIR}/ops/operators.yaml)
5380
gen_selected_ops(LIB_NAME "cortex_m_ops_lib" OPS_SCHEMA_YAML "${_yaml_file}")
54-
5581
generate_bindings_for_kernels(
5682
LIB_NAME "cortex_m_ops_lib" CUSTOM_OPS_YAML "${_yaml_file}"
5783
)
58-
message("Generated files ${gen_command_sources}")
5984

60-
# Build a library for cortex_m_kernels
85+
# Build library for cortex_m_kernels
6186
add_library(cortex_m_kernels ${_cortex_m_kernels__srcs})
62-
target_compile_options(cortex_m_kernels PUBLIC ${_common_compile_options})
6387

64-
# Include directories for cortex_m_kernels
65-
target_include_directories(
88+
# Use PRIVATE for implementation dependencies to avoid INTERFACE pollution
89+
target_link_libraries(
6690
cortex_m_kernels
67-
PRIVATE ${EXECUTORCH_ROOT}/..
68-
${EXECUTORCH_ROOT}/runtime/core/portable_type/c10
69-
${cmsis_nn_SOURCE_DIR}/Include
91+
PRIVATE cmsis-nn
92+
PRIVATE executorch
7093
)
7194

72-
# Link directly to the CMSIS-NN static library file
73-
target_link_libraries(
74-
cortex_m_kernels PUBLIC ${cmsis_nn_BINARY_DIR}/libcmsis-nn.a executorch
95+
# Include directories for cortex_m_kernels
96+
target_include_directories(
97+
cortex_m_kernels PRIVATE ${EXECUTORCH_ROOT}/..
98+
${EXECUTORCH_ROOT}/runtime/core/portable_type/c10
7599
)
76100

77-
# Add dependency to ensure CMSIS-NN builds before we try to link. Use the actual
78-
# CMSIS-NN target name (usually 'cmsis-nn')
79-
add_dependencies(cortex_m_kernels cmsis-nn)
80-
81101
# cortex_m_ops_lib: Register Cortex-M ops kernels into Executorch runtime
82102
gen_operators_lib(
83103
LIB_NAME "cortex_m_ops_lib" KERNEL_LIBS cortex_m_kernels DEPS executorch
84104
)
85105

86106
install(
87-
TARGETS cortex_m_kernels cortex_m_ops_lib
107+
TARGETS cortex_m_kernels cortex_m_ops_lib cmsis-nn
88108
EXPORT ExecuTorchTargets
89109
DESTINATION lib
90110
PUBLIC_HEADER DESTINATION include/executorch/backends/cortex_m/ops/

backends/cortex_m/ops/cortex_m_ops_common.h

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ using ScalarType = executorch::aten::ScalarType;
2222
using Scalar = torch::executor::Scalar;
2323
using Error = executorch::runtime::Error;
2424

25+
// From arm_nn_math_types.h
26+
#define ARM_NN_Q31_MAX ((int32_t)(0x7FFFFFFFL))
27+
#define ARM_NN_Q31_MIN ((int32_t)(0x80000000L))
28+
2529
// Basic tensor type / layout validation and dimension order checking
2630
inline void validate_cmsis_nn_tensor_requirements(
2731
const Tensor& input1,
@@ -32,16 +36,19 @@ inline void validate_cmsis_nn_tensor_requirements(
3236
// Basic dtype validation
3337
ET_CHECK_MSG(
3438
input1.scalar_type() == expected_dtype,
35-
"Input1 dtype must be %hhd",
36-
expected_dtype);
39+
"Input1 dtype must be %hhd, got %hhd",
40+
expected_dtype,
41+
input1.scalar_type());
3742
ET_CHECK_MSG(
3843
input2.scalar_type() == expected_dtype,
39-
"Input2 dtype must be %hhd",
40-
expected_dtype);
44+
"Input2 dtype must be %hhd, got %hhd",
45+
expected_dtype,
46+
input2.scalar_type());
4147
ET_CHECK_MSG(
4248
output.scalar_type() == expected_dtype,
43-
"Output dtype must be %hhd",
44-
expected_dtype);
49+
"Output dtype must be %hhd, got %hhd",
50+
expected_dtype,
51+
output.scalar_type());
4552

4653
// Dim order consistency
4754
ET_CHECK_MSG(
@@ -114,6 +121,33 @@ inline void validate_quantization_params(
114121
"Single quant Output");
115122
}
116123

124+
// Refer to CMSIS-NN 'arm_nn_requantize' implementation for details:
125+
// https://fburl.com/afvegf0m
126+
// multiplier: Range {ARM_NN_Q31_MIN + 1, Q32_MAX}
127+
// shift : Range {-31, 30}
128+
inline bool validate_per_channel_quant_params(
129+
const int32_t* multipliers,
130+
const int32_t* shifts,
131+
int num_channels) {
132+
for (int i = 0; i < num_channels; ++i) {
133+
// Multiplier: {ARM_NN_Q31_MIN + 1, ARM_NN_Q31_MAX}
134+
if (multipliers[i] <= ARM_NN_Q31_MIN || multipliers[i] > ARM_NN_Q31_MAX) {
135+
ET_LOG(
136+
Error,
137+
"weight_multiplier[%d] out of CMSIS-NN range: %d",
138+
i,
139+
multipliers[i]);
140+
return false;
141+
}
142+
// Shift: {-31, 30} for arm_nn_requantize
143+
if (shifts[i] < -31 || shifts[i] > 30) {
144+
ET_LOG(Error, "weight_shift[%d] out of range: %d", i, shifts[i]);
145+
return false;
146+
}
147+
}
148+
return true;
149+
}
150+
117151
inline Error resize_to_broadcast_target_size(
118152
const Tensor& input1,
119153
const Tensor& input2,

0 commit comments

Comments
 (0)