11月5号更新
1、更新SDK到0.14
使用
sdk_ses_compile_options(-mabi=ilp32f)
sdk_ses_compile_options(-march=rv32imafc)
添加 了浮点指令,在工程编译的设置参数中设置如图,-O3的效果。

编译后,下载运行得到如下效果,计时精度为10us。

目前SDK0.14版本解决了浮点指令下打印浮点数的问题。
2、此外SDK 0.14更新了新的图形化工程创建工具:

3、更新SDK0.14以及工程下载地址
链接:https://pan.baidu.com/s/18MGnLS3hQNNIXb70e4Czlg
提取码:atv5
10月13日
一、什么是TInyML?
TinyML (Tiny machine learning)是机器学习和嵌入式 IoT 设备的交叉领域。TinyML 源自物联网 IoT 的概念。物联网的传统做法,是将数据从本地设备发送到云端处理。这种方式会存在隐私、延迟、存储和能源效率等方面存在问题。
从能耗的角度来看,数据传输非常耗能,不论是有线传输还是无线传输,都要比本地计算高出至少一个数量级。如果数据在本地就能够完成合格的AI运算,就是最节能的做法。
隐私性。数据传输的过程有一定的隐私风险,数据有可能会被恶意拦截。如果数据存储在云端,而备份不足时,其安全性也不可保证。所以,将核心数据保存在本地,很大程度上减少了通信数量,可以提高安全性和隐私性。
存储。对于许多物联网设备而言,存储大量的数据是没用的,随用随丢就好。比如说,一个监控摄像头每天24小时保持开机状态,对着一个人迹罕至的仓库入口。在一天的绝大部分时间里,这个摄像头没有任何用处,因为这里什么也没有发生。所以,升级一套更智能的可以随时激活拍摄的系统更为重要。这不需要很昂贵的存储容量,也不需要大量的数据传送到云端。
延迟。对于标准的IoT设备而言,设备收集数据传到云端进行处理,然后根据算法获得一个结果,然后返回本地执行。整个过程就是响应周期。如果你用过Amazon的智能音箱Echo你就有可能遇到过延迟、卡顿的情况,这取决于你的网络环境。对于集成有语音识别功能的微型设备而言,这极大的减少了网络延迟带来的问题。
以上这些问题推动了边缘计算的发展,也就是说,在边缘设备(边缘,是相对于“云”的概念)上执行AI算法是一种高效便捷的方法。因为边缘设备在计算能力、存储能力和功能性都受到很大的限制,所以针对不同的应用场景开发出专门的算法、数据结构尤为重要。
参考:
https://www.tinyml.org/
https://zhuanlan.zhihu.com/p/265865500
二、Tinymaix库
TinyMaix是面向单片机的超轻量级的神经网络推理库,即TinyML推理库,可以让你在任意单片机上运行轻量级深度学习模型。设计原则:易用性 > 移植性 > 速度 > 空间。
关键特性:
- 核心代码少于400行(tm_layers.c+tm_model.c+arch_cpu.h), 代码段(.text)少于3KB
- 低内存消耗,甚至Arduino ATmega328 (32KB Flash, 2KB Ram) 都能基于TinyMaix跑mnist(手写数字识别)
- 支持INT8/FP32/FP16模型,实验性地支持FP8模型,支持keras h5或tflite模型转换
- 支持多种芯片架构的专用指令优化: ARM SIMD/NEON/MVEI,RV32P, RV64V
- 友好的用户接口,只需要load/run模型~
- 支持全静态的内存配置(无需malloc)
- MaixHub 在线模型训练支持
代码地址:https://github.com/sipeed/TinyMaix
三、先楫HPM6750EVK开发板
HPM6750EVK是基于上海先楫半导体科技有限公司的高性能实时 RISC-V 微控制器HPM6750 的MCU为核心设计的开发板,为工业自动化及边缘计算应用提供了极大的算力、高效的控制能力及丰富的多媒体功能。
性能:
• RISC-V 内核支持双精度浮点运算及强大的 DSP 扩展,主频高达 816 MHz,创下了高达 9220CoreMarkT M 和高达 4651 DMIPS 的 MCU 性能新记录。
• 32KB 高速缓存 (I/D Cache) 和高达 512KB 的零等待指令和数据本地存储器 (ILM / DLM),加上 1MB 内置
SRAM,极大避免了低速外部存储器引发的性能损失。

四、在先楫HPM6750EVK上移植tinymaix库
HPM6750的单核性能非常优秀,如果在它上面单核跑Tinymaix会有什么样的效果,下面移植看看单核效果。
HPM6750EVK上移植mnist识别数字
下载先楫的开发包可以查看贴下载:https://verimake.com/d/239-mcuhpm6000,帖子中的百度网盘失效了,尴尬,使用下面的网盘下载,在下载文件中有SDK开发包。
链接:https://pan.baidu.com/s/1qvyXhhbDmn4ug3oJeHJrbg?pwd=4peg
提取码:4peg
在SDK文件夹中有sdk_env_v0.13.1.zip,解压缩后得到如图文件内容。

双击start_cmd.cmd可以自动配置好相关的路径和开发的环境,只有在该cmd窗口下不需要再配置相关环境。

进入hmp_sdk/samples目录下就可以看到开发板的相关案例代码。在该目录下新建文件夹tinymaix用来存放即将移植的代码。在tinymaix文件夹中使用git工具c git clone https://github.com/sipeed/TinyMaix.git
克隆出tinymaix的源码。需要系统安装过git。

下载好后得到以下文件:

拷贝include、src、tools三个文件夹到上一级目录,即刚刚在hmp_sdk/samples目录下创建的tinymaix目录下。将examples里面的需要运行的样例也拷贝到hmp_sdk/samples目录下(以mnist为例)创建的tinymaix目录下。则在hmp_sdk/samples/tinymaix目录下就有了需要使用的目录

打开hmp_sdk/samples/tinymaix/mnist目录下的CMakeLists.txt,修改添加相关代码如下:
cmake_minimum_required(VERSION 3.13)
find_package(hpm-sdk REQUIRED HINTS $ENV{HPM_SDK_BASE})
project(mnist)
sdk_app_inc(../src ../include)
sdk_app_src(../src/tm_layers.c
../src/tm_layers_fp8.c
../src/tm_layers_O1.c
../src/tm_model.c
../src/tm_stat.c
)
sdk_compile_definitions(-DBOARD_SHOW_CLOCK=0)
sdk_app_src(main.c)
generate_ses_project()
打开sdk_env_v0.13.1目录下的start_cmd.cmd,双击打开。输入cd hpm_sdk/samples/tinymaix/mnist进入mnist目录准备构建工程。

进入后dir查看下是否有CMakeLists.txt.

输入generate_project -b hpm6750evk 构建项目。

完成后在改目录下生成了一个hpm6750evk_build目录

箭头所指是可以使用segger_embedded_studio工具导入的工程。segger安装请自行百度,这里不再赘述了。
打开segger软件,选择导入工程。

选择刚刚文件夹中的工程文件。
打开main.c,修改几处内容。添加以下的头文件
#include "board.h"
#include "hpm_sysctl_drv.h"
#include "hpm_gptmr_drv.h"
#include "hpm_debug_console.h"
修改头文件如图

去掉前面的../../tools/tmdl/,如果都去掉需要将给文件夹下的该文件拷贝到tinymaix/include目录下。如果不拷贝,那就需要修改为#include “../tools/tmdl/mnist_valid_q.h”保证工程能找到该文件即可。
添加变量定义c volatile uint32_t g_us_tick;
用来存放微秒计数值,用来统计运算时间。
在main函数中添加board_init();用来初始化开发板。
接下啦就需要使用到定时器,在SDK中有定时器代码可以直接使用,在board.c中有个c void board_timer_create(uint32_t ms, board_timer_cb cb)
但它是进行ms级定时的,所以我们可以修改,也可以自己参照它重写一个us级定时。在board.c中添加
void board_timer_create_us(uint32_t us, board_timer_cb cb)
{
uint32_t gptmr_freq;
gptmr_channel_config_t config;
timer_cb = cb;
gptmr_channel_get_default_config(BOARD_CALLBACK_TIMER, &config);
clock_add_to_group(BOARD_CALLBACK_TIMER_CLK_NAME, 0);
gptmr_freq = clock_get_frequency(BOARD_CALLBACK_TIMER_CLK_NAME);
config.reload = gptmr_freq / 1000000 * us;
gptmr_channel_config(BOARD_CALLBACK_TIMER, BOARD_CALLBACK_TIMER_CH, &config, false);
gptmr_enable_irq(BOARD_CALLBACK_TIMER, GPTMR_CH_RLD_IRQ_MASK(BOARD_CALLBACK_TIMER_CH));
intc_m_enable_irq_with_priority(BOARD_CALLBACK_TIMER_IRQ, 1);
gptmr_start_counter(BOARD_CALLBACK_TIMER, BOARD_CALLBACK_TIMER_CH);
}
和原来程序相比就是原来除以1000进行毫秒定时变成了除以1000000变成微秒定时。
记得在board.h中添加这个函数的声明,便于在main中调用。
main函数中添加代码:
board_timer_create_us(10,timer_callback);
其中参数10表示定时10us,timer_callback是回调函数,需要编写如下:
void timer_callback(void)
{
g_us_tick++;
}
就是微秒计数。
最后这个10微秒计数添加到Tinymaix中。打开include目录下tm_port.h文件,在第54行,修改如下

这样就是将微秒计数值传递给计算时间的宏定义。
编译代码,调试,打开串口助手可以得到下面的结果。4.14ms 🤣

接下啦可以优化下编译参数。
点击工程右击选择option

然后按图添加编译参数-O3

重新编译调试运行得到下面的结果。

1.216ms
这个结果查看对比会发现结果和主频800多M不相称啊。
目前发现的问题是编译参数选择的是imac指令集,没有使用浮点指令集,猜测是可能原因之一。
FPU如何使用在文档中资料很少。下一篇将继续研究HPM6750的FPU如何使用。
###问题
1、在CMakelists.txt中添加
sdk_compile_options("-O3")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3 ")
其中任何一个都没法使得生成的工程里添加-O3的参数。
2、使用generate_project -b hpm6750evk -t flash_xip可以使得生成的工程在completed生成下载到flash中的程序,如果这样做,运行程序后奇怪的是程序只能打印logo之后就卡死了,程序最初的 TM_PRINTF("mnist demo\n");打印开始信息都无法执行。这里的TM_PRINTF就是printf。
3、segger猜测采用的是增量式编译,如果之前代码没有变动是不会改变原来编译的结果,这样会照成其他代码修改后在链接时候出错,所以在编译之前可以在build菜单下先选择clean下再编译代码就可以通过了。
其余的坑再补充》》》》》
关于FPU相关问题
1、目前发现添加浮点指令集支持后,不能使用-O的编译参数,添加后会照成打印浮点数卡死。
2、Cmakelists.txt中修改工具链,添加
set(SES_TOOLCHAIN_VARIANT "Andes")
sdk_ses_compile_options(-mabi=ilp32f)
sdk_ses_compile_options(-march=rv32imafc)
后,使用单精度浮点指令,编译后运行发现代码跑不完成,

通过检查发现问题是malloc(x),无法分配内存空间。猜测可能是工具链Andes的库有问题。