diff --git a/SConscript b/SConscript new file mode 100644 index 00000000..ca95be14 --- /dev/null +++ b/SConscript @@ -0,0 +1,12 @@ +import os +from building import * + +objs = [] +cwd = GetCurrentDir() +list = os.listdir(cwd) + +for item in list: + if os.path.isfile(os.path.join(cwd, item, 'SConscript')): + objs = objs + SConscript(os.path.join(item, 'SConscript')) + +Return('objs') diff --git a/Source/Kconfig b/Source/Kconfig new file mode 100644 index 00000000..e5319438 --- /dev/null +++ b/Source/Kconfig @@ -0,0 +1,49 @@ +menu "CMSIS-NN submodule config" + +menuconfig PKG_USING_CMSIS_NN + bool "Enable CMSIS-NN pacakge" + default n + +if PKG_USING_CMSIS_NN + config CMSIS_NN_ACTIVATION + bool "Activation functions" + default y + + config CMSIS_NN_BASICMATHSNN + bool "Basic math functions" + default y + + config CMSIS_NN_CONCATENATION + bool "Concatenation functions" + default y + + config CMSIS_NN_CONVOLUTION + bool "Convolution functions" + default y + + config CMSIS_NN_FULLYCONNECTED + bool "Fully connection functions" + default y + + config CMSIS_NN_LSTM + bool "LSTM functions" + default y + + config CMSIS_NN_POOLING + bool "Pooling functions" + default y + + config CMSIS_NN_RESHAPE + bool "Reshape functions" + default y + + config CMSIS_NN_SOFTMAX + bool "Softmax functions" + default y + + config CMSIS_NN_SVDF + bool "SVDF functions" + default y +endif + +endmenu diff --git a/Source/SConscript b/Source/SConscript new file mode 100644 index 00000000..53142073 --- /dev/null +++ b/Source/SConscript @@ -0,0 +1,69 @@ +import os +from building import * + +# enable all module by default +ACTIVATION = True +BASICMATHSNN = True +CONCATENATION = True +CONVOLUTION = True +FULLYCONNECTED = True +LSTM = True +POOLING = True +RESHAPE = True +SOFTMAX = True +SVDF = True + +# CMSIS-NN papckage Kconfig option value +USE_CMSIS_NN_CONFIG = GetDepend(['PKG_USING_CMSIS_NN']) + +# Kconfig sourced, and CMSIS-NN package enabled +if USE_CMSIS_NN_CONFIG: + ACTIVATION = GetDepend(['CMSIS_NN_ACTIVATION']) + BASICMATHSNN = GetDepend(['CMSIS_NN_BASICMATHSNN']) + CONCATENATION = GetDepend(['CMSIS_NN_CONCATENATION']) + CONVOLUTION = GetDepend(['CMSIS_NN_CONVOLUTION']) + FULLYCONNECTED = GetDepend(['CMSIS_NN_FULLYCONNECTED']) + LSTM = GetDepend(['CMSIS_NN_LSTM']) + POOLING = GetDepend(['CMSIS_NN_POOLING']) + RESHAPE = GetDepend(['CMSIS_NN_RESHAPE']) + SOFTMAX = GetDepend(['CMSIS_NN_SOFTMAX']) + SVDF = GetDepend(['CMSIS_NN_SVDF']) + +# Always needed if any other module above is on. +NNSUPPORT = ACTIVATION or BASICMATHSNN or CONCATENATION or CONVOLUTION or \ + FULLYCONNECTED or LSTM or POOLING or RESHAPE or SOFTMAX or SVDF + +submodules = ( + (ACTIVATION, 'ActivationFunctions'), + (BASICMATHSNN, 'BasicMathFunctions'), + (CONCATENATION, 'ConcatenationFunctions'), + (CONVOLUTION, 'ConvolutionFunctions'), + (FULLYCONNECTED, 'FullyConnectedFunctions'), + (LSTM, 'LSTMFunctions'), + (NNSUPPORT, 'NNSupportFunctions'), + (POOLING, 'PoolingFunctions'), + (RESHAPE, 'ReshapeFunctions'), + (SOFTMAX, 'SoftmaxFunctions'), + (SVDF, 'SVDFunctions') +) + +# print('USE_CMSIS_NN_CONFIG=', USE_CMSIS_NN_CONFIG) +# print('submodules=', submodules) + +cwd = GetCurrentDir() + +CPPPATH = [cwd + '/../Include'] +SOURCES = [] + +for m in submodules: + if m[0]: + SOURCES += Glob(m[1] + '/*.c') + +LOCAL_CCFLAGS = ["-Ofast"] + +objs = DefineGroup(name='CMSIS-NN', src=SOURCES, depend=[''], + CPPPATH=CPPPATH, + LOCAL_CCFLAGS=LOCAL_CCFLAGS, + ) + +Return('objs') diff --git a/Tests/SConscript b/Tests/SConscript new file mode 100644 index 00000000..ca95be14 --- /dev/null +++ b/Tests/SConscript @@ -0,0 +1,12 @@ +import os +from building import * + +objs = [] +cwd = GetCurrentDir() +list = os.listdir(cwd) + +for item in list: + if os.path.isfile(os.path.join(cwd, item, 'SConscript')): + objs = objs + SConscript(os.path.join(item, 'SConscript')) + +Return('objs') diff --git a/Tests/UnitTest/SConscript b/Tests/UnitTest/SConscript new file mode 100644 index 00000000..ca95be14 --- /dev/null +++ b/Tests/UnitTest/SConscript @@ -0,0 +1,12 @@ +import os +from building import * + +objs = [] +cwd = GetCurrentDir() +list = os.listdir(cwd) + +for item in list: + if os.path.isfile(os.path.join(cwd, item, 'SConscript')): + objs = objs + SConscript(os.path.join(item, 'SConscript')) + +Return('objs') diff --git a/Tests/UnitTest/TestCases/SConscript b/Tests/UnitTest/TestCases/SConscript new file mode 100644 index 00000000..ca95be14 --- /dev/null +++ b/Tests/UnitTest/TestCases/SConscript @@ -0,0 +1,12 @@ +import os +from building import * + +objs = [] +cwd = GetCurrentDir() +list = os.listdir(cwd) + +for item in list: + if os.path.isfile(os.path.join(cwd, item, 'SConscript')): + objs = objs + SConscript(os.path.join(item, 'SConscript')) + +Return('objs') diff --git a/Tests/UnitTest/fix_testCode.sh b/Tests/UnitTest/fix_testCode.sh new file mode 100644 index 00000000..dd995c85 --- /dev/null +++ b/Tests/UnitTest/fix_testCode.sh @@ -0,0 +1,5 @@ +#!/bin/bash +grep --color -lr 'setUp' TestCases/ | xargs sed -i 's|void setUp(void)|__attribute__((weak)) void setUp(void)|g' +grep --color -lr 'tearDown' TestCases/ | xargs sed -i 's|void tearDown(void)|__attribute__((weak)) void tearDown(void)|g' +grep --color -lr 'resetTest' TestCases/ | xargs sed -i 's|void resetTest(void)|__attribute__((weak)) void resetTest(void)|g' +grep --color -lr 'verifyTest' TestCases/ | xargs sed -i 's|void verifyTest(void)|__attribute__((weak)) void verifyTest(void)|g' diff --git a/Tests/UnitTest/fix_testData.sh b/Tests/UnitTest/fix_testData.sh new file mode 100644 index 00000000..1f38ba71 --- /dev/null +++ b/Tests/UnitTest/fix_testData.sh @@ -0,0 +1,2 @@ +#!/bin/bash +grep --color -lr '^const ' TestCases/TestData/ | xargs sed -i 's|^const |static const |g' diff --git a/Tests/UnitTest/unittest_targets.py b/Tests/UnitTest/unittest_targets.py index 1f882495..166518f3 100755 --- a/Tests/UnitTest/unittest_targets.py +++ b/Tests/UnitTest/unittest_targets.py @@ -412,6 +412,50 @@ def is_func_to_parse(func): f.write(line) +COMMAND_CODE_TEMPLATE='''/* AUTOGENERATED FILE. DO NOT EDIT. */ +#include + +extern int {runner_name}(void); + +MSH_CMD_EXPORT_ALIAS({runner_name}, {cmd_name}, {runner_name}); +''' + +COMMAND_SCONS_TEMPLATE = '''#AUTOGENERATED FILE. DO NOT EDIT. +import os +from building import * + +objs = [] +cwd = GetCurrentDir() + +SOURCES = [ + {src_list} +] + +LOCAL_CCFLAGS = [ + '-Dmalloc=rt_malloc', + '-Dfree=rt_free', + '-include rt-thread.h' +] + +objs = DefineGroup(name='Unity', src=SOURCES, depend=[''], CPPPATH=[]) + +Return('objs') +''' + + +def generate_command_code(runner_file, runner_name): + cmd_name = runner_name.replace('unity_test_arm', '').replace('_runner', '') + cmd_code = COMMAND_CODE_TEMPLATE.format(runner_name=runner_name, cmd_name=cmd_name) + with open(runner_file, 'tw+', encoding='utf-8') as f: + f.write(cmd_code) + + +def generate_command_scons(scons_file, source_list): + scons_code = COMMAND_SCONS_TEMPLATE.format(src_list=',\n '.join([f"'{src}'" for src in source_list])) + with open(scons_file, 'wt+', encoding='utf-8') as f: + f.write(scons_code) + + def parse_tests(targets, main_tests, specific_test=None): """ Generate test runners, extract and return path to unit test(s). @@ -449,7 +493,16 @@ def parse_tests(targets, main_tests, specific_test=None): os.remove(old_files) # Generate test runners - run_command('ruby ' + UNITY_PATH + 'auto/generate_test_runner.rb ' + test_code + ' ' + test_runner) + runner_name = os.path.basename(test_runner).replace('.c', '') + cmd_file = test_runner.replace('unity_test', 'finsh_cmd') + scons_file = os.path.dirname(test_code) + '/../SConscript' + src_list = [test_code, test_runner, cmd_file] + src_list = [src.replace(f'{directory}/{dir}/', '') for src in src_list ] + print(f'EXEC: ruby {UNITY_PATH}auto/generate_test_runner.rb --main_name={runner_name} {test_code} {test_runner}') + run_command(f'ruby {UNITY_PATH}auto/generate_test_runner.rb --main_name={runner_name} {test_code} {test_runner}') + generate_command_code(cmd_file, runner_name) + generate_command_scons(scons_file, src_list) + test_found = parse_test(test_runner, targets) if not test_found: return False