Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
RA8-M85-vision-board
移植CMSIS-NN v6.0.0版本到VisionBoard
发布于 2024-06-16 23:59:37 浏览:310
订阅该版
[tocm] ## CMSIS-NN是什么? 官方的解释是: > CMSIS NN software library is a collection of efficient neural network kernels developed to maximize the performance and minimize the memory footprint of neural networks on Arm Cortex-M processors. > 翻译一下就是: > CMSIS NN 软件库是一组高效的神经网络核(函数),旨在最大限度地提高 Arm Cortex-M 处理器上神经网络的性能并最大限度地减少内存占用。 > CMSIS-NN是一个计算库,它向上提供了神经网络(NN)计算接口,实现了神经网络计算的硬件加速。它内部实现了纯CPU计算、DSP计算、MVE计算,屏蔽了底层硬件的具体细节,降低了编程难度。 ## 为什么移植CMSIS-NN v6.0.0版本? ### CMSIS-NN核心特性 总结一下官方的介绍,可以知道CMSIS-NN库的核心特性: - 专为Cortex-M处理器开发; - 神经网络计算函数; - 最大化性能; - 最小化内存占用; ### CMSIS-NN的硬件和软件支持 除此之外,CMSIS-NN库还有几点也是值得关注的: - 支持DSP扩展的处理器,使用SIMD优化,例如Cortex-M4核; - 支持ARM的Heilum技术的处理器,使用M核向量扩展(MVE,M-profile Vector Extension)进行优化,例如 Cortex-M55 或 Cortex-M85; - MVE扩展恰好是ARM Cortex-M85内置的; - VisionBoard主控芯片瑞萨RA8D1的CPU核正是ARM Cortex-M85; - CMSIS-NN可以作为TensorFlow Lite for Microcontroller的后端实现; ### CMSIS-NN v6.0.0版本新特性 CMSIS-NN v6.0.0版本的发布说明中,介绍了新特性: - 全连接(FC)、卷积(CONV)和深度卷积(DWCONV)添加了MVE指令的int4类型支持; - 重新实现 LSTM 以与 TFLM 参考内核保持一致; - LSTM 对 int16 输入的支持 - DSP/MVEI 支持转置卷积 - 支持分组卷积 - 支持 FC 的非零滤波器偏移 - 对 MVEI 的 Int16 输入卷积支持 - 对 int16x8 卷积的 Int32 偏置支持 更能多内容可以查看本文末尾的CMSIS-NN v6.0.0 Release Note; ## 如何移植CMSIS-NN v6.0.0到VisionBoard? ### 创建RT-Thread项目 RT-Thread Studio创建基于VisionBoard开发板的模板项目,过程比较简单,不再赘述。 ### 添加CMSIS-NN源码 RT-Thread Studio创建基于VisionBoard开发板的模板项目后, 在packages目录手动下载CMSIS-NN v6.0.0版本: ```bash cd packages git clone -b v6.0.0 https://github.com/ARM-software/CMSIS-NN.git ``` ### 修改RT-Thread代码 修改项目顶层的Kconfig文件,添加如下代码行: ```bash source "packages/CMSIS-NN/Kconfig" ``` 注意:Kconfig修改需要完需要保证最后有一行空行,否则menuconfig命令会报奇怪的错误。 检查packages目录内是否有SConsript文件,并且内容如下: ```bash 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') ``` 如果没有,可以手动创建。 ### 修改CMSIS-NN代码 CMSIS-NN目录顶层创建SConscript文件,内容如下: ```bash 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('Source/' + m[1] + '/*.c') LOCAL_CCFLAGS = ["-Ofast"] objs = DefineGroup(name='CMSIS-NN', src=SOURCES, depend=[''], CPPPATH=CPPPATH, LOCAL_CCFLAGS=LOCAL_CCFLAGS) # CMSIS-NN papckage Kconfig PKG_USING_CMSIS_NN_TESTS option value USE_CMSIS_NN_TESTS = GetDepend(['PKG_USING_CMSIS_NN_TESTS']) # print('USE_CMSIS_NN_TESTS = ', USE_CMSIS_NN_TESTS) if USE_CMSIS_NN_TESTS: tests = cwd + '/Tests' dents = os.listdir(tests) for item in dents: sconsfile = os.path.join(tests, item, 'SConscript') if os.path.isfile(sconsfile): objs += SConscript(sconsfile) Return('objs') ``` 创建Kconfig文件,内容如下: ```bash menu "CMSIS-NN submodule config" menuconfig PKG_USING_CMSIS_NN bool "Enable CMSIS-NN pacakge" default y 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 menuconfig PKG_USING_CMSIS_NN_TESTS bool "Enable CMSIS-NN Unit Tests" default n endmenu ``` ### 编译RT-Thread项目 完成以上修改之后,已经可以编译CMSIS-NN库的代码了。 在命令行中执行如下命令,编译整个项目: ```bash scons -j 16 ``` 编译输出最后部分如下图所示: ![Untitled](https://pics-1303990412.cos.ap-shanghai.myqcloud.com/202406162357287.png) ## 如何测试CMSIS-NN v6.0.0? ### 生成RT-Thread平台的测试代码 CMSIS-NN库内部带有了单元测试,具体位于 Tests/UnitTest 子目录,其中 unittest_targets.py 脚本可以用于生成测试脚手架代码,使用方法如下: ![Untitled](https://pics-1303990412.cos.ap-shanghai.myqcloud.com/202406162357289.png) 当前生成的代码是单独生成elf文件,并在ARM虚拟硬件(AVH)平台上运行的。 默认情况下,执行python unittest_targets.py --download-and-generate-test-runners命令,会为每个测试用例生成一个main函数,每个测试用例单独编译为一个elf文件,之后使用ARM虚拟硬件(AVH)执行elf进行测试。 为了能够生成在RT-Thread上运行的测试代码,需要修改部分测试脚本代码,实现将每个测试用例注册为一个独立的finsh命令,在串口命令行中交互测试。 具体修改的代码差异如下: ```diff diff --git a/Tests/UnitTest/unittest_targets.py b/Tests/UnitTest/unittest_targets.py index 1f88249..166518f 100755 --- a/Tests/UnitTest/unittest_targets.py +++ b/Tests/UnitTest/unittest_targets.py @@ -412,6 +412,50 @@ def parse_generated_test_runner(test_runner): 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 ``` 这段修改实现了: - 调用unity的ruby脚本,传递main_name命令行参数,用于修改单元测试入口函数名称(不指定默认是main); - 生成一个独立的RT-Thread finsh命令注册代码.c文件; - 生成一个用于编译的SConscript代码文件; 另外,再结合上层目录的SConcsript包含所有子目录的SConscript,就可以实现将所有单元测试编译为finsh命令了。 修改完该脚本文件后,执行如下命令,生成RT-Thread平台测试代码: ```bash cd packages/CMSIS-NN/Tests/UnitTest python unittest_targets.py --download-and-generate-test-runners ``` 命令执行输出如下: ![Untitled](https://pics-1303990412.cos.ap-shanghai.myqcloud.com/202406162357290.png) ### 测试代码的构建规则SConscript 然后需要在CMSIS-NN的Tests子目录内,添加SConscript文件: ```bash import os from building import * objs = [] cwd = GetCurrentDir() USE_CMSIS_NN_TESTS = GetDepend(['PKG_USING_CMSIS_NN_TESTS']) if USE_CMSIS_NN_TESTS: ents = os.listdir(cwd) for item in ents: if os.path.isfile(os.path.join(cwd, item, 'SConscript')): objs += SConscript(os.path.join(item, 'SConscript')) Return('objs') ``` 这段SConscript的作用是,将子目录的SConscript脚本包含到整个项目的构建流程中去。 接着需要在CMSIS-NN的Tests/UnitTest子目录内,添加SConscript文件: ```bash import os from building import * objs = [] cwd = GetCurrentDir() # unity 库核心代码 SOURCES = [ 'Unity/src/unity.c' ] CPPPATH = [cwd + '/Unity/src'] objs += DefineGroup('Unity', SOURCES, depend = [''], CPPPATH = CPPPATH) # 包含测试用例子目录的 SConscript cases_dir = cwd + '/TestCases' dir_ents = os.listdir(cases_dir) for item in dir_ents: sconsfile = os.path.join(cases_dir, item, 'SConscript') if os.path.isfile(sconsfile): objs += SConscript(sconsfile) Return('objs') ``` 完成以上修改后,通过menuconfig打开 PKG_USING_CMSIS_NN_TESTS 配置项目,再次 scons 编译,就可以编译单元测试代码文件了。 ### 解决链接失败问题 但是还会有一些编译错误,原因主要有: 1. 原来的测试脚本为每个测试用例独立生成main函数,每个目录单独编译; 1. 链接到一起时,会有大量重复的setUp/tearDown/resetTest/verifyTest函数定义; 2. 原来的测试数据数组没有加static修饰,被重复include到多个.c文件; 1. 链接到一起时,会有数组重复的数组定义; 为了解决上述两类问题,分别创建两个脚本。 修复重复函数定义的 fix_testCode.sh: ```bash #!/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' ``` 解决方法,所有 setUp/tearDown/resetTest/verifyTest 函数添加 weak 属性修饰; 修复重复数组定义的 fix_testData.sh: ```bash #!/bin/bash grep --color -lr '^const ' TestCases/TestData/ | xargs sed -i 's|^const |static const |g' ``` 解决方法,所有测试数据的数组添加static修饰。 分别执行上面两个脚本之后,再次编译,就没有编译错误了。 ### 运行单元测试 顺利编译之后,下载固件: ```bash pyocd flash --target=R7FA8D1BH --erase=auto --frequency=8000000 rtthread.hex ``` 运行后,在串口输入help命令可以看到: ![Untitled](https://pics-1303990412.cos.ap-shanghai.myqcloud.com/202406162357291.png) 输入命令,运行avgpool算子的测试: ![Untitled](https://pics-1303990412.cos.ap-shanghai.myqcloud.com/202406162357292.png) 其他几个算子的s8类型测试: ![Untitled](https://pics-1303990412.cos.ap-shanghai.myqcloud.com/202406162357293.png) ![Untitled](https://pics-1303990412.cos.ap-shanghai.myqcloud.com/202406162357294.png) ![Untitled](https://pics-1303990412.cos.ap-shanghai.myqcloud.com/202406162357295.png) ![Untitled](https://pics-1303990412.cos.ap-shanghai.myqcloud.com/202406162357296.png) ![Untitled](https://pics-1303990412.cos.ap-shanghai.myqcloud.com/202406162357297.png) ![Untitled](https://pics-1303990412.cos.ap-shanghai.myqcloud.com/202406162357299.png) ![Untitled](https://pics-1303990412.cos.ap-shanghai.myqcloud.com/202406162357300.png) ![Untitled](https://pics-1303990412.cos.ap-shanghai.myqcloud.com/202406162357301.png) ![Untitled](https://pics-1303990412.cos.ap-shanghai.myqcloud.com/202406162357302.png) ![Untitled](https://pics-1303990412.cos.ap-shanghai.myqcloud.com/202406162357303.png) ![Untitled](https://pics-1303990412.cos.ap-shanghai.myqcloud.com/202406162357304.png) 修改后的CMSIS-NN代码仓: https://github.com/xusiwei/CMSIS-NN ## 参考链接 1. CMSIS-NN源码仓: [https://github.com/ARM-software/CMSIS-NN](https://github.com/ARM-software/CMSIS-NN) 2. TFLM项目介绍页: [https://tensorflow.google.cn/lite/microcontrollers/overview?hl=zh-cn](https://tensorflow.google.cn/lite/microcontrollers/overview?hl=zh-cn) 3. CMSIS-NN v6.0.0 Release Note: [https://github.com/ARM-software/CMSIS-NN/releases/tag/v6.0.0](https://github.com/ARM-software/CMSIS-NN/releases/tag/v6.0.0) 4. Ruby下载页面: [https://www.ruby-lang.org/zh_cn/downloads/](https://www.ruby-lang.org/zh_cn/downloads/)
3
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
xusiwei1236
https://blog.csdn.net/xusiwei1236
文章
11
回答
41
被采纳
6
关注TA
发私信
相关文章
1
Vision Board 兩個專案編譯問題
2
Vision-Board例程编译后不通过
3
Vision Board 示例7编译失败问题
4
Vision board OpenMV的摄像头画面有干扰条纹噪声
5
micropython能否增加一个tiny版本的yolov2目标检测功能?
6
Vision Board问题反馈:采集帧率低
7
RT-Thread Studio 编译OpenMV 工程出错 (RA8 Vision Board)
8
Vision Board如何接入外部摄像头
9
怎样把SCI当作SPI使用
10
Vision Board使用openmv无法下载脚本
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
I2C_IIC
ESP8266
UART
WIZnet_W5500
ota在线升级
cubemx
PWM
flash
freemodbus
BSP
packages_软件包
潘多拉开发板_Pandora
定时器
ADC
flashDB
GD32
socket
编译报错
中断
Debug
rt_mq_消息队列_msg_queue
SFUD
msh
keil_MDK
ulog
C++_cpp
MicroPython
本月问答贡献
出出啊
1518
个答案
343
次被采纳
小小李sunny
1444
个答案
290
次被采纳
张世争
813
个答案
177
次被采纳
crystal266
547
个答案
161
次被采纳
whj467467222
1222
个答案
149
次被采纳
本月文章贡献
出出啊
1
篇文章
1
次点赞
小小李sunny
1
篇文章
1
次点赞
张世争
1
篇文章
3
次点赞
crystal266
2
篇文章
2
次点赞
whj467467222
2
篇文章
2
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部