1#
2# SPDX-FileCopyrightText: Copyright 2019-2024 Arm Limited and/or its affiliates <open-source-office@arm.com>
3#
4# SPDX-License-Identifier: Apache-2.0
5#
6# Licensed under the Apache License, Version 2.0 (the License); you may
7# not use this file except in compliance with the License.
8# You may obtain a copy of the License at
9#
10# www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an AS IS BASIS, WITHOUT
14# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15# See the License for the specific language governing permissions and
16# limitations under the License.
17#
18
19cmake_minimum_required(VERSION 3.15.6)
20
21project(cmsis_nn_unit_tests VERSION 0.0.1)
22
23set(CMSIS_PATH "</path/to/CMSIS>" CACHE PATH "Path to CMSIS.")
24
25add_compile_options(-fomit-frame-pointer
26                    -Werror
27                    -Wimplicit-function-declaration
28                    -Wunused-variable
29                    -Wunused-function
30                    -Wno-redundant-decls
31                    -Wvla)
32
33option(BUILD_CMSIS_NN_UNIT "If building the unit tests from another project, i.e. \
34platform dependencies need to be provided externally." OFF)
35
36if (${CMSIS_PATH} STREQUAL "</path/to/CMSIS>")
37  message(FATAL_ERROR "CMSIS_PATH not set. Did you provide -DCMSIS_PATH=<path/to/CMSIS>?")
38endif()
39
40if(NOT BUILD_CMSIS_NN_UNIT)
41    set(BUILD_CMSIS_NN_UNIT_TESTS_FOR_FVP_BASED_CORSTONE_300 ON)
42else()
43    set(BUILD_CMSIS_NN_UNIT_TESTS_FOR_FVP_BASED_CORSTONE_300 OFF)
44endif()
45
46if(BUILD_CMSIS_NN_UNIT_TESTS_FOR_FVP_BASED_CORSTONE_300)
47    set(FVP_CORSTONE_300_PATH "${CMAKE_CURRENT_SOURCE_DIR}/Corstone-300" CACHE PATH
48        "Dependencies for using FVP based on Arm Corstone-300 software.")
49    set(CMAKE_EXECUTABLE_SUFFIX ".elf")
50endif()
51
52# Build the functions to be tested.
53add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../.. cmsis-nn)
54add_compile_options(${CMSIS_OPTIMIZATION_LEVEL})
55
56# Target for all unit tests.
57add_custom_target(cmsis_nn_unit_tests)
58
59# This function should be used instead of add_executable.
60set_property(GLOBAL PROPERTY cmsis_nn_unit_test_executables)
61function(add_cmsis_nn_unit_test_executable)
62    get_property(tmp GLOBAL PROPERTY cmsis_nn_unit_test_executables)
63    foreach(target ${ARGV})
64        set(tmp "${tmp} ${target}")
65        add_executable(${target})
66        if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
67            target_link_options(${target} PRIVATE "--specs=nosys.specs")
68        endif()
69        add_dependencies(cmsis_nn_unit_tests ${target})
70    endforeach()
71    set_property(GLOBAL PROPERTY cmsis_nn_unit_test_executables "${tmp}")
72endfunction(add_cmsis_nn_unit_test_executable)
73
74add_subdirectory(TestCases/test_arm_avgpool_s16)
75add_subdirectory(TestCases/test_arm_avgpool_s8)
76add_subdirectory(TestCases/test_arm_convolve_1x1_s8_fast)
77add_subdirectory(TestCases/test_arm_convolve_1x1_s4_fast)
78add_subdirectory(TestCases/test_arm_convolve_s16)
79add_subdirectory(TestCases/test_arm_convolve_s8)
80add_subdirectory(TestCases/test_arm_convolve_s4)
81add_subdirectory(TestCases/test_arm_convolve_1_x_n_s8)
82add_subdirectory(TestCases/test_arm_depthwise_conv_3x3_s8)
83add_subdirectory(TestCases/test_arm_depthwise_conv_fast_s16)
84add_subdirectory(TestCases/test_arm_depthwise_conv_s16)
85add_subdirectory(TestCases/test_arm_depthwise_conv_s4)
86add_subdirectory(TestCases/test_arm_depthwise_conv_s4_opt)
87add_subdirectory(TestCases/test_arm_depthwise_conv_s8)
88add_subdirectory(TestCases/test_arm_depthwise_conv_s8_opt)
89add_subdirectory(TestCases/test_arm_ds_cnn_l_s8)
90add_subdirectory(TestCases/test_arm_ds_cnn_s_s8)
91add_subdirectory(TestCases/test_arm_elementwise_add_s16)
92add_subdirectory(TestCases/test_arm_elementwise_add_s8)
93add_subdirectory(TestCases/test_arm_elementwise_mul_s16)
94add_subdirectory(TestCases/test_arm_elementwise_mul_s8)
95add_subdirectory(TestCases/test_arm_fully_connected_s16)
96add_subdirectory(TestCases/test_arm_fully_connected_s8)
97add_subdirectory(TestCases/test_arm_fully_connected_s4)
98add_subdirectory(TestCases/test_arm_grouped_convolve_s8)
99add_subdirectory(TestCases/test_arm_lstm_unidirectional_s8)
100add_subdirectory(TestCases/test_arm_max_pool_s16)
101add_subdirectory(TestCases/test_arm_max_pool_s8)
102add_subdirectory(TestCases/test_arm_softmax_s16)
103add_subdirectory(TestCases/test_arm_softmax_s8)
104add_subdirectory(TestCases/test_arm_softmax_s8_s16)
105add_subdirectory(TestCases/test_arm_svdf_s8)
106add_subdirectory(TestCases/test_arm_svdf_state_s16_s8)
107add_subdirectory(TestCases/test_arm_transpose_conv_s8)
108add_subdirectory(TestCases/test_arm_lstm_unidirectional_s16)
109
110set(MAKE_CMD "python3")
111set(MAKE_CMD_SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/unittest_targets.py")
112set(MAKE_CMD_SCRIPT_OPTION "--download-and-generate-test-runners")
113MESSAGE(STATUS "Downloading Unity and generating test runners for CMSIS-NN unit tests if needed..")
114execute_process(COMMAND ${MAKE_CMD} ${MAKE_CMD_SCRIPT} ${MAKE_CMD_SCRIPT_OPTION}
115                        WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
116add_subdirectory(Unity)
117
118# Link common dependencies.
119get_property(executables GLOBAL PROPERTY cmsis_nn_unit_test_executables)
120string(REPLACE " " ";" cmsis_nn_unit_test_list_of_executables ${executables})
121foreach(target ${cmsis_nn_unit_test_list_of_executables})
122    target_link_libraries(${target} LINK_PUBLIC unity)
123    target_link_libraries(${target} LINK_PUBLIC cmsis-nn)
124endforeach()
125
126if(BUILD_CMSIS_NN_UNIT_TESTS_FOR_FVP_BASED_CORSTONE_300)
127    add_library(retarget STATIC
128        ${FVP_CORSTONE_300_PATH}/retarget.c
129        ${FVP_CORSTONE_300_PATH}/uart.c)
130
131    # Build CMSIS startup dependencies based on TARGET_CPU.
132    string(REGEX REPLACE "^cortex-m([0-9]+)$" "ARMCM\\1" ARM_CPU ${CMAKE_SYSTEM_PROCESSOR})
133    if(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "cortex-m33")
134        set(ARM_FEATURES "_DSP_FP")
135    elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "cortex-m4")
136        set(ARM_FEATURES "_FP")
137    elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "cortex-m7")
138        set(ARM_FEATURES "_DP")
139    else()
140        set(ARM_FEATURES "")
141    endif()
142    add_library(cmsis_startup STATIC)
143    target_sources(cmsis_startup PRIVATE
144        ${CMSIS_PATH}/Device/ARM/${ARM_CPU}/Source/startup_${ARM_CPU}.c
145        ${CMSIS_PATH}/Device/ARM/${ARM_CPU}/Source/system_${ARM_CPU}.c)
146    target_include_directories(cmsis_startup PUBLIC
147        ${CMSIS_PATH}/Device/ARM/${ARM_CPU}/Include
148        ${CMSIS_PATH}/CMSIS/Core/Include)
149    target_compile_options(cmsis_startup INTERFACE -include${ARM_CPU}${ARM_FEATURES}.h)
150    target_compile_definitions(cmsis_startup PRIVATE ${ARM_CPU}${ARM_FEATURES})
151
152    # Linker file settings.
153    set(LINK_FILE "${FVP_CORSTONE_300_PATH}/linker" CACHE PATH "Linker file.")
154    if (CMAKE_CXX_COMPILER_ID STREQUAL "ARMClang")
155        set(LINK_FILE "${FVP_CORSTONE_300_PATH}/linker.scatter")
156        set(LINK_FILE_OPTION "--scatter")
157        set(LINK_ENTRY_OPTION "--entry")
158        set(LINK_ENTRY "Reset_Handler")
159    elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
160        set(LINK_FILE "${FVP_CORSTONE_300_PATH}/linker.ld")
161        set(LINK_FILE_OPTION "-T")
162        set(LINK_ENTRY_OPTION "")
163        set(LINK_ENTRY "")
164    endif()
165
166    # Link in FVP dependencies to every unit test.
167    get_property(executables GLOBAL PROPERTY cmsis_nn_unit_test_executables)
168    string(REPLACE " " ";" cmsis_nn_unit_test_list_of_executables ${executables})
169    foreach(target ${cmsis_nn_unit_test_list_of_executables})
170        target_link_libraries(${target} PRIVATE retarget)
171        target_link_libraries(${target} PRIVATE $<TARGET_OBJECTS:cmsis_startup> cmsis_startup)
172
173        add_dependencies(${target} retarget cmsis_startup)
174
175        target_compile_definitions(${target} PUBLIC USING_FVP_CORSTONE_300)
176
177        target_link_options(${target} PRIVATE ${LINK_FILE_OPTION} ${LINK_FILE} ${LINK_ENTRY_OPTION} ${LINK_ENTRY})
178        set_target_properties(${target} PROPERTIES LINK_DEPENDS ${LINK_FILE})
179    endforeach()
180endif()
181