3. 开发环境搭建:MDK/AC6安装、CMSIS-Pack配置、CMSIS-NN源码获取、第一个示例工程编译

做嵌入式AI开发,环境搭建是第一道坎。很多人觉得装个IDE有什么难的?但我在带团队时发现,80%的编译问题都出在环境配置上——要么Pack版本不对,要么编译器路径没指对。这一章,咱们就把这些坑一个个填平。

3.1 开发工具链概览

先看看我们这一章要搭什么。说白了,就是三样东西:

  • IDE:MDK(Keil uVision)——ARM Cortex-M的事实标准
  • 编译器:AC6(Arm Compiler 6)——MDK内置,基于LLVM,优化能力强
  • 软件包:CMSIS-Pack + CMSIS-NN源码——ARM官方的神经网络加速库

下面这张图,把整个环境的结构和流程画清楚了:

CMSIS-NN 开发环境结构图 MDK Keil uVision (IDE) Arm Compiler 6 (AC6) CMSIS-Pack (ARM::CMSIS) CMSIS-Core (内核抽象层) CMSIS-NN (神经网络算子库) 说明:CMSIS-NN 依赖 CMSIS-Core 提供的数据类型和内存操作接口

嗯,这张图你看懂了,整个环境就清晰了。MDK负责管理工程,AC6负责编译,CMSIS-Pack提供底层支持,CMSIS-NN才是我们真正要调优的对象。

3.2 MDK与AC6安装

3.2.1 获取安装包

去Keil官网下载MDK-ARM最新版。我个人习惯用5.38或更高版本,因为内置的AC6版本比较新,对CMSIS-NN的优化支持更好。

小提示: 如果你公司有正版License,直接用。个人学习可以用评估版——代码限制32KB,但跑个小模型验证算子完全够用。

3.2.2 安装步骤

  1. 双击安装包,一路Next。注意安装路径不要有中文和空格。
  2. 选择组件时,确保勾选「Arm Compiler 6」。我记得有次帮同事排查问题,发现他编译报错,就是因为没装AC6,用的还是老旧的AC5。
  3. License管理:安装完成后打开uVision,菜单栏 File → License Management,添加你的License。
  4. 验证安装:新建一个空工程,在Project → Manage → Project Components中,确认编译器显示为「Arm Compiler 6.x」。
注意: AC6默认使用C99标准,但CMSIS-NN有些头文件用了C11的特性。你需要在工程选项的「C/C++」标签页中,把语言标准改成「gnu11」或「c11」。我曾经因为没改这个,编译报了一堆语法错误,折腾了半小时。

3.3 CMSIS-Pack配置

CMSIS-Pack是ARM官方提供的软件包管理机制。说白了,就是帮你把CMSIS-Core、CMSIS-DSP、CMSIS-NN这些组件一键装好,不用自己手动去GitHub扒拉源码。

3.3.1 安装Pack

有两种方式:

方式 操作 适用场景
在线安装 uVision → Pack Installer → 搜索「ARM::CMSIS」→ Install 网络通畅,推荐
离线安装 从Keil官网下载 .pack 文件 → 双击安装 内网开发,或需要固定版本

我建议用离线安装。为什么?因为在线安装经常因为网络问题中断,而且版本会跟着官网更新——你今天装的5.9.0,下周可能就变成6.0.0了,团队里版本不一致,代码一同步就炸。

3.3.2 确认Pack版本

安装完成后,在uVision的Project → Manage → Pack Installer中,你应该能看到类似这样的信息:

ARM::CMSIS 5.9.0
  ├── CMSIS-Core 5.9.0
  ├── CMSIS-DSP 1.14.0
  └── CMSIS-NN 4.1.0
关键点: CMSIS-NN 4.1.0 是目前最稳定的版本。如果你用更新的版本,注意检查API是否有breaking change。我踩过这个坑——从4.0升级到4.1时,arm_convolve_s8的参数结构变了,编译不报错,但运行结果全错。

3.4 CMSIS-NN源码获取

Pack安装后,CMSIS-NN的源码其实已经在你电脑里了。默认路径是:

C:\Users\你的用户名\AppData\Local\Arm\Packs\ARM\CMSIS\5.9.0\CMSIS\NN

但这个路径藏得深,而且每次更新Pack版本,路径都会变。我个人习惯把源码复制一份到工程目录下,这样版本可控,也方便修改调试。

3.4.1 源码结构

我们看看CMSIS-NN源码里都有什么:

目录/文件 说明
Include/ 头文件,定义API接口和数据结构
Source/ 算子实现,按功能分文件夹(Convolution、Pooling、Activation等)
Source/Softmax/ Softmax算子的各种实现(纯C、带DSP指令、带MVE指令)
Source/NNSupportFunctions/ 辅助函数,比如数据重排、量化转换

你想想看,这么多文件,我们真正要优化的其实就Source/下面那几个核心算子。后面的章节,我们会一个一个拆开来看。

3.5 第一个示例工程编译

理论说完了,咱们动手。我准备了一个最小工程,就做一件事:调用CMSIS-NN的卷积算子,在模拟器上跑通。

3.5.1 创建工程

  1. uVision → Project → New uVision Project,选择你的目标芯片(比如STM32F407)。
  2. 在Manage Run-Time Environment中,勾选:
    • CMSIS → CORE
    • CMSIS → NN
  3. 添加一个main.c文件到工程。

3.5.2 编写测试代码

下面这段代码,就是一个最简单的CMSIS-NN卷积调用:

#include "arm_nnfunctions.h"
#include "arm_nnsupportfunctions.h"

#define INPUT_CH  3
#define OUTPUT_CH 8
#define KERNEL_DIM 3
#define INPUT_DIM 32

int8_t input_data[INPUT_DIM * INPUT_DIM * INPUT_CH];
int8_t output_data[INPUT_DIM * INPUT_DIM * OUTPUT_CH];
int8_t kernel_data[KERNEL_DIM * KERNEL_DIM * INPUT_CH * OUTPUT_CH];
int32_t bias_data[OUTPUT_CH];

int main(void)
{
    arm_convolve_s8(input_data,
                    INPUT_DIM, INPUT_DIM, INPUT_CH,
                    kernel_data,
                    OUTPUT_CH, KERNEL_DIM, KERNEL_DIM,
                    1, 1,  // stride
                    0, 0,  // padding
                    NULL,  // 量化参数(简化示例传NULL)
                    NULL,
                    output_data,
                    NULL);

    while(1);
    return 0;
}
注意: 上面的代码为了演示做了大量简化。实际使用时,量化参数(conv_params、quant_params)必须正确初始化,否则结果完全不可用。我们会在第5章「量化参数详解」中专门讲这个。

3.5.3 编译与调试

按F7编译。如果一切顺利,你应该看到:

Build Output:
  compiling main.c...
  assembling startup_stm32f407xx.s...
  linking...
  Program Size: Code=1232 RO-data=320 RW-data=8 ZI-data=1024
  ".\Objects\test.axf" - 0 Error(s), 0 Warning(s).

看到「0 Error(s)」了吗?恭喜,你的第一个CMSIS-NN工程编译成功了。

如果报错,99%是以下原因:

  • 找不到头文件:检查工程选项中C/C++的Include Paths,确保包含了CMSIS-NN的Include目录。
  • 链接错误:检查是否添加了CMSIS-NN的库文件或源文件路径。
  • AC6版本问题:确认编译器选的是AC6,不是AC5。
调试技巧: 用软件模拟器(Simulator)跑,不需要硬件开发板。在工程选项的Debug标签页,选择「Use Simulator」,然后按Ctrl+F5进入调试模式。你可以单步执行,观察寄存器和内存的变化——这对理解算子内部逻辑非常有帮助。

3.6 本章小结

这一章,我们把开发环境从头搭了一遍:MDK安装、AC6配置、CMSIS-Pack部署、源码获取、第一个工程编译。看起来步骤多,但做熟了其实就十分钟的事。