M85-GCC13.3-CMSIS-接入笔记

Cortex-M85 + GCC 13.3 + CMSIS-DSP/NN 接入笔记

适用场景:Cortex-M85 内核(Alif Ensemble E1/E3/E5、瑞萨 RA8D1 等),

工具链固定使用 arm-none-eabi-gcc 13.3.Rel1,使用 CMSIS-DSP / CMSIS-NN 的

Helium (MVE) 内核库,不使用 GCC 自动向量化。

一句话:13.3 + 手写 intrinsics / 调库 是 OK 组合,坑不在工具链版本。


0. TL;DR

工具链 arm-none-eabi-gcc 13.3.Rel1(GCC 13.3.1 + binutils 2.41)
编译器参数 -mcpu=cortex-m85 -mfloat-abi=hard -mthumb(FPU 由 -mcpu 隐含,不要加 -mfpu=fpv5-d16
DSP 库 libarm_cortexM85lfd_mve_math.aMVE-F + FP-Armv8 DP;_mve_ 后缀;默认统一用 lfd)
NN 库 厂方提供或 CMSIS-NN 8.0+ 的 M85 Helium 变体(命名不统一)
必开宏 ARM_MATH_CM85 + ARM_MATH_HELIUM
必关宏 ARM_MATH_AUTOVECTORIZE(别开!)
决策性宏 ARM_NN_TRUNCATE_INPUT默认不定义,按模型训练配置决定;不是性能开关)
栈对齐 __initial_sp 必须是 8 的倍数(末位十六进制为 08);调用 Helium 函数前 SP 同样需 8-byte 对齐
CMSIS-DSP 版本 ≥ 6.0.0 (CMake 源码构建,Helium 变体带 _mve_ 后缀);5.9.0 是 prebuilt pack 老格式
CMSIS-NN 版本 ≥ 8.0.0(首版含 M85 Helium)

1. 工具链安装(Windows)

1.1 下载

1.2 安装后验证

bash 复制代码
arm-none-eabi-gcc --version
# 期望: arm-none-eabi-gcc (Arm GNU Toolchain 13.3.Rel1 ...) 13.3.1 ...

arm-none-eabi-gcc -mcpu=cortex-m85 -mfloat-abi=hard -mthumb \
  -E -dM -x c /dev/null | grep -E "__ARM_ARCH|__ARM_FP|__ARM_FEATURE"
# 应包含:
#   __ARM_ARCH_8_1M_MAIN__
#   __ARM_FEATURE_MVE
#   __ARM_FEATURE_FP_DP

不出现 __ARM_FEATURE_MVE = 编译选项不对。

1.3 路径建议

复制代码
D:\tools\arm-gnu-toolchain\13.3\
D:\tools\cmake\
D:\tools\mingw64\           # 拿 make
D:\tools\openocd\           # 烧录 / 调试

四个 bin 目录都加进 PATH路径里不要有中文或空格


2. 编译选项(Makefile 模板)

makefile 复制代码
TARGET  = firmware
CPU     = -mcpu=cortex-m85
# M85 自带 FPU(Armv8.1-M / FP-ARMv8),由 -mcpu 隐含声明
# ❌ 不要写 -mfpu=fpv5-d16 ------ 那是 Cortex-M7 的命名,跟 M85 冲突
FABI    = -mfloat-abi=hard
THUMB   = -mthumb

# 关键宏:开 Helium,关自动向量化
DEFS   += -DARM_MATH_CM85
DEFS   += -DARM_MATH_HELIUM
# ❌ 绝对不要 -DARM_MATH_AUTOVECTORIZE
#   这个宏会触发 GCC 对 CMSIS 标量代码自动向量化
#   13.3 auto-vec 偶发错码 / 漏向量化,要避开

# ❌ 绝对不要 -ffast-math(见下方 2.1 解释)
#   包括它隐式打开的 -ffp-contract=fast / -ffinite-math-only / -funsafe-math-optimizations
#   对 CMSIS-DSP / CMSIS-NN 这种依赖 IEEE 754 严格语义的库,会引发难复现的数值漂移

CFLAGS  = $(CPU) $(FABI) $(THUMB) $(DEFS) -O2 -g3 -ffunction-sections -fdata-sections
LDFLAGS = $(CPU) $(FABI) $(THUMB) -Wl,--gc-sections -Wl,--print-memory-usage

# 链接库(按 Helium + 双精度 FP 选)
# 关键: Helium 变体带 _mve_ 后缀,没有这个后缀的是标量版
LIBS   += -larm_cortexM85lfd_mve_math  # CMSIS-DSP (Helium + DP)
LIBS   += -larm_nn_M85                 # CMSIS-NN,按原厂 pack 名称(命名不统一)
LIBS   += -lm -lc -lnosys

cortex-m85 在 GCC 13.3 里隐含 armv8.1m.main+mve.fp+fp.dp

不需要手写 -march。如果手写,请保持一致。

2.1 -ffast-math 为什么在 M85 + Helium 项目里是雷

-ffast-math 不是一个 flag,它是一组 flag 的合集,会改写 GCC 的 FP 语义假设:

隐式开启 行为 对 CMSIS 的影响
-ffp-contract=fast 自动把 a*b + c 折叠成 FMA CMSIS-DSP 部分函数依赖 mul / add 的中间舍入,折叠后行为变
-ffinite-math-only 假设输入无 NaN/Inf,删除相关检查 滤波 / ML 边界态穿过去,结果突变
-funsafe-math-optimizations 改 FP 运算顺序、提常量 CMSIS-NN 多层累加误差逐层放大
-fno-math-errno 假设 math.h 函数不写 errno CMSIS 用到 sqrt / pow 时行为可能不一致
-fno-signaling-nans 关闭 sNaN 触发 FP exception 调试期用 NaN 做"未初始化"哨兵的代码失效

经验法则 :CMSIS-DSP / CMSIS-NN 永远 -O2 不开 fast-math。如果你确认 自己的某段代码(不调 CMSIS)想榨 FMA / 改运算顺序,做成函数级 attribute

c 复制代码
/* 例:某个独立的非 DSP 工具函数,知道自己在干什么 */
__attribute__((optimize("ffast-math", "tree-loop-vectorize")))
static inline float my_fast_rms(const float *x, int n) {
    float s = 0.f;
    for (int i = 0; i < n; i++) s += x[i] * x[i];
    return sqrtf(s / n);
}

或者 #pragma GCC optimize("ffast-math") 限定一个小编译单元,不要全局开


3. CMSIS-DSP 接入

3.1 库选型(极容易选错)

Reminder :CMSIS-DSP 6.0+(CMake 源码构建)后,Helium 变体的库文件名带 _mve_ 中缀

老版本(5.9 prebuilt pack)里 Helium 和标量混在同一个 .a 里,靠宏选。

你要构建的是源码版本,所以下面的命名规则以 6.0+ CMake 输出为准。

CMSIS-DSP 6.0+ CMake 默认产出(双 FPU 字符 = 字节序+FPU 类型,_mve_ 中缀 = Helium):

文件名 标量/Helium FPU M85 项目使用
libarm_cortexM85l_math.a 标量 无 FPU
libarm_cortexM85lf_math.a 标量 单精度
libarm_cortexM85lfd_math.a 标量 双精度 ❌(是标量版,不是 Helium 版!)
libarm_cortexM85l_mve_math.a MVE-I(整数 Helium) 无 FPU ⚠️(仅整数 DSP 路径可用,浮点 DSP 不可用)
libarm_cortexM85lf_mve_math.a MVE-F 单精度 ⚠️(不推荐;见下方 lf vs lfd 建议)
libarm_cortexM85lfd_mve_math.a MVE-F 双精度 默认用这个
libarm_cortexM85b*_math.a 大端(一般不用) ---

链错最常见的现象:函数体里全是标量指令、vldrwq 一条都没有,profile 速度跟纯 C 一样。

FPU 命名澄清 :M85 是 Armv8.1-M 架构,FPU 是 Armv8.1-M Floating Point Extension(FP-Armv8) ------属于 Armv8.1-M 自己的 FP 扩展,不是 Armv7E-M 时代的 FPv5-D16(M4/M7 那个)。

GCC 在 M85 上不会定义 __ARM_FPV5__,而是 __ARM_FEATURE_FP_DP

文档里之前写的"FPv5-D16"是历史命名残留,按 M85 实际硬件是错的。
lf vs lfd 选型建议 :默认统一用 lfd

  • CMSIS-DSP 绝大多数函数是 float32,用 lfd 跟用 lf 性能一样

  • lfd ABI 完整,跟 -mfloat-abi=hard + -mcpu=cortex-m85 默认 ABI 一致

  • 未来代码里若不小心调了 *_f64,有定义可用,不会出链接错 / fault

  • 仅在明确知道没 DP 硬件、且需要最小库尺寸时,才用 lf
    lf 调 f64 的实际后果(不是一定 hardfault):

  • 链接时:undefined reference to arm_xxx_f64(最常见)

  • 运行时:函数实现可能 trap、可能软模拟、可能直接走 UsageFault / HardFault,取决于具体 CMSIS 版本和实现

  • 编译器:若 ABI 不一致(hard vs softfp),可能编译期就被拒

3.2 头文件包含

c 复制代码
#include "arm_math.h"           // 主头
// 或更细的拆分
#include "arm_math_types.h"
#include "arm_math_memory.h"
#include "arm_math_statistics.h"

3.3 第一次调用前的 sanity check

c 复制代码
#include "arm_math.h"

void cmsis_dsp_selftest(void) {
    // 1. 验证编译期 ABI 选对
    assert(__ARM_FEATURE_MVE != 0);          // Helium 编译进来了
    assert(__ARM_FEATURE_FP_DP != 0);       // DP FPU 编译进来了
    assert(__ARM_ARCH_8_1M_MAIN__ != 0);     // 架构对了

    // 2. 跑 CMSIS 自带的 selftest(覆盖大部分基础函数)
    //    注意: selftest 里有些用 *_f64,必须 lfd 库才能跑通
    arm_status s = arm_mat_mult_f32_selftest();
    if (s != ARM_MATH_SUCCESS) {
        // 库没链对 / 宏没设对
    }
}

3.4 从源码构建(推荐)

比 prebuilt pack 好的原因:版本可控、可选 flag、跟厂方 SDK 集成一致、CI 友好。

3.4.1 拉代码
bash 复制代码
git clone --branch main https://github.com/ARM-software/CMSIS-DSP.git
cd CMSIS-DSP
# 6.0+ 用 CMake;5.x 还是 Makefile
3.4.2 CMake 构建命令
bash 复制代码
mkdir build && cd build

cmake .. \
  -DCMAKE_TOOLCHAIN_FILE=../cmsis-cm85-mve-dp.cmake \
  -DCMAKE_BUILD_TYPE=Release \
  -DBUILD_TESTING=OFF \
  -DDSP_CONFIG=Generic

# 工具链文件(自己写一个,或用 CMSIS 提供的)

工具链文件 cmsis-cm85-mve-dp.cmake

cmake 复制代码
set(CMAKE_SYSTEM_NAME Generic)
set(CMAKE_SYSTEM_PROCESSOR arm)

set(CMAKE_C_COMPILER   arm-none-eabi-gcc)
set(CMAKE_CXX_COMPILER arm-none-eabi-g++)
set(CMAKE_ASM_COMPILER arm-none-eabi-gcc)

# 关键:M85 (Armv8.1-M) + FP-Armv8 DP + Helium
# 注意: -mcpu=cortex-m85 已隐含 MVE-I/MVE-F + FP-Armv8,不需要额外 -mfpu
set(CMAKE_C_FLAGS_INIT    "-mcpu=cortex-m85 -mfloat-abi=hard -mthumb -O2")
set(CMAKE_ASM_FLAGS_INIT  "-mcpu=cortex-m85 -mfloat-abi=hard -mthumb")

set(CMAKE_C_ARCHIVE_CREATE   "<CMAKE_AR> qcs <TARGET> <LINK_FLAGS> <OBJECTS>")
set(CMAKE_C_ARCHIVE_FINISH   "true")
3.4.3 编译 + 验证
bash 复制代码
cmake --build . -j

# 产物路径(不同版本略有差异,6.0+ 一般在 build/lib/ 下)
ls build/lib/
# 期望: libarm_cortexM85lfd_mve_math.a  ← 找这个

# 没找到?先看哪些 target 生成了
cmake --build . --target help | grep -i m85
3.4.4 链接到自己的工程

.a 文件路径加到工程 Makefile 的 LIBRARY_PATH,或直接绝对路径:

makefile 复制代码
CMSIS_DSP_LIB = $(abspath third_party/CMSIS-DSP/build/lib/libarm_cortexM85lfd_mve_math.a)
LDFLAGS += -L$(dir $(CMSIS_DSP_LIB))
LIBS    += -larm_cortexM85lfd_mve_math

4. CMSIS-NN 接入

4.1 库版本

  • CMSIS-NN 8.0.0 起加入 M85 Helium 内核
  • 低于 8.0.0 完全没有 M85 路径,编译器会报找不到 arm_convolve_Helium_* 之类符号

4.2 头文件 + 宏

c 复制代码
#include "arm_nnfunctions.h"
#include "arm_nn_types.h"
#include "arm_nn_math_types.h"

// 启用 Helium 路径(与 CMSIS-DSP 不同,CMSIS-NN 走更细的 NNLIB 宏)

// ARM_NN_TRUNCATE_INPUT:量化舍入策略开关(不是性能开关)
//
//   不定义(默认):round-to-nearest,TFLite Micro 默认行为
//                  → 与绝大多数 TFLite 导出模型匹配
//
//   定义时        :truncate toward zero(截断小数)
//                  → 仅当模型用 tf.quantization.fake_quant_with_min_max_vars
//                    且 fake_quant 内部用 truncate 模式训练时才需要
//
// 错配后果:输出误差 1~3 LSB,分类模型置信度可能翻转
// 决策依据:看模型训练脚本 / TFLite converter 的 quantizer 配置
//
// 经验:不确定时**不定义**(与 TFLM 行为一致),不要默认开

// #define ARM_NN_TRUNCATE_INPUT    // 默认注释,按模型训练配置决定

4.3 典型调用

c 复制代码
// conv 2D,input/output/channed 布局跟普通 CMSIS-NN 一样
arm_cmsis_nn_status rv = arm_convolve_s8(
    input_data, input_size,
    filter_data, bias_data,
    output_data, output_size);

4.4 选库注意

CMSIS-NN 库命名没完全统一,常见几种:

来源 库名 备注
官方 CMSIS-NN 源码(CMake build) libarm_nn_M85_Helium.alibarm_nn_mvei_*.a 命名随版本变化
官方老 pack libarm_nn_M85.a 不一定带 Helium
厂方 SDK(Alif / Renesas) libalif_nn_M85.a / libr_nn.a 自定义

建议自己从源码构建 (跟 DSP 一样),避免命名混乱。仓库:github.com/ARM-software/CMSIS-NN,8.0+ 起支持 M85 Helium。

自己从 CMSIS-NN 源码 build,参考 [Build Instructions Reference](#Build Instructions Reference)。


5. 启动文件 / 链接脚本

5.1 栈对齐(🔴 必看)

M85 + Helium 严格要求 调用任何 Helium 函数前 SP 8-byte 对齐

__initial_sp 必须是 8 的倍数:即十六进制地址最后一位为 08(二进制末 3 位 = 000)。

ld 复制代码
// .ld 里 __StackTop 必须 8-byte 对齐
_estack = 0x20020000;   // 例:RAM 末地址,末位十六进制 = 0(8 字节对齐 ✓)
5.1.1 在汇编启动文件里强对齐(✅ 推荐做法)

栈对齐必须且只能 在汇编启动文件(startup_xxx.s)里完成,在跳转到 mainSystemInit 之前

asm 复制代码
/* startup_xxx.s */
/* 在调用 SystemInit / __libc_init_array 之前先把 SP 强对齐到 8 字节 */
Reset_Handler:
    LDR     R0, =__StackTop
    MOV     SP, R0
    BIC     SP, SP, #7         ; SP 强对齐到 8 字节

    LDR     R0, =SystemInit
    BLX     R0
    LDR     R0, =__libc_init_array
    BLX     R0
    LDR     R0, =main
    BX      R0
5.1.2 ❌ 不要在 C 函数里强对齐 SP
c 复制代码
/* ❌ 这种写法会 HardFault,绝对不要用 */
__attribute__((constructor(0)))
static void align_sp(void) {
    __asm volatile (
        "mov r0, sp     \n"
        "bic r0, r0, #7 \n"
        "mov sp, r0     \n"
        ::: "r0", "memory"
    );
}

为什么是雷

  • 编译器在进入这个 C 函数时已经生成了 prologue(push {r7, lr}),LR 已经被 push 到旧 SP
  • 函数返回时 pop {r7, pc} SP 读,LR 位置错位
  • 即使你把 __attribute__((naked)) 加上自己控制 prologue/epilogue 也不行------因为 __libc_init_array(caller)自己的栈帧是按旧 SP 算的,你一调 SP,caller 的局部变量 / 帧指针全错位
  • 真实现象:构造函数能跑完,但后续 __libc_init_array 内部的循环 / caller 返回时会随机 HardFault

唯一 C 里的合法做法__attribute__((naked)) + 整个函数体就是手写汇编,并且在 reset 之后第一个被调用的位置用------但本质上这就是在写启动文件,没有 C 的意义,直接用汇编。

5.2 链接脚本里的 .bss / .data 对齐

ld 复制代码
MEMORY {
    FLASH (rx)  : ORIGIN = 0x08000000, LENGTH = 1M
    RAM   (xrw) : ORIGIN = 0x20000000, LENGTH = 256K
}

SECTIONS {
    .bss :
    {
        . = ALIGN(8);                  /* 🔴 8 字节对齐 */
        __bss_start__ = .;
        *(.bss*)
        *(COMMON)
        . = ALIGN(8);
        __bss_end__ = .;
    } >RAM

    .data :
    {
        . = ALIGN(8);                  /* 🔴 8 字节对齐 */
        __data_start__ = .;
        *(.data*)
        . = ALIGN(8);
        __data_end__ = .;
    } >RAM AT> FLASH
}

5.3 FPU 初始化(开 CPACR)

上电后 FPU 默认关,第一次用 Helium 前要开(用 CMSIS core 的标准写法):

c 复制代码
#include "core_cm85.h"   // 或厂家 SCB 头文件,里面定义了 SCB->CPACR

/* SystemInit() 里 */
SCB->CPACR |= ((3UL << (10U * 2U)) | (3UL << (11U * 2U)));  /* set CP10 and CP11 Full Access */
__DSB();
__ISB();

要点:

  • 用 CMSIS core 提供的 SCB->CPACR 结构体访问,不要自己 #define 寄存器地址
  • 3UL(unsigned long)而不是 3U,位运算更安全
  • 乘法用 (10U * 2U) 显式括号,避免任何对优先级的疑虑
  • 后面必须跟 __DSB(); __ISB();,等 CPACR 写入生效再继续执行

不打开 = 第一次用 FPU 就 hardfault(不是 Helium 自己的问题,是 FPU 的)。


6. 验证清单(build 完必跑)

6.1 编译期

bash 复制代码
# 1. Helium 特性进来了
echo | arm-none-eabi-gcc -mcpu=cortex-m85 -mfloat-abi=hard -mthumb -E -dM -x c - | \
  grep -E "__ARM_FEATURE_MVE|__ARM_FEATURE_FP_DP|__ARM_ARCH_8_1M_MAIN__"

# 2. 没开 auto-vec 宏
arm-none-eabi-gcc -dM -E main.c | grep -E "ARM_MATH_AUTOVECTORIZE"
# 期望: 没输出

# 3. ARM_MATH_CM85 / HELIUM 开了
arm-none-eabi-gcc -dM -E main.c | grep -E "ARM_MATH_CM85|ARM_MATH_HELIUM"
# 期望: 各一行 = 1

6.2 链接期

bash 复制代码
# 1. 真的链上了 Helium DSP 库(不是回落标量)
arm-none-eabi-nm --print-armap build/firmware.elf | grep -E "arm_cortexM85lfd_mve"
# 期望: 一堆带 .o 的记录

# 2. NN 库同理
arm-none-eabi-nm --print-armap build/firmware.elf | grep -E "nn_M85|arm_convolve"

6.3 反汇编验证

bash 复制代码
arm-none-eabi-objdump -d build/firmware.elf > firmware.disasm

# 看 DSP 函数体里有没有 MVE 指令
grep -E "vldrwq|vst1q|vmulq|vaddq" firmware.disasm | head -20
# 期望: 有大量匹配

# 看 NN conv 函数体
grep -E "vldrwq|vmlsdavax" firmware.disasm | head -20
# 期望: 有匹配(vmlsdavax 是 Helium 的 SDOT 指令)

6.4 运行时

bash 复制代码
# 用 OpenOCD + arm-none-eabi-gdb 看 PC 是否落在 Helium 指令
(gdb) target remote :3333
(gdb) b arm_mat_mult_f32
(gdb) c
(gdb) si
(gdb) disassemble
# 期望: 看到 vldrwq / vst1q 等

7. 常见问题速查

现象 根因 排查
undefined reference to arm_mat_mult_f32 库没链或链错 nm 看库内容;改用 lfd 变体
跳进 DSP 函数就 hardfault 栈不对齐 / FPU 没开 / 库选错 看 5.1 / 5.3 / 3.1
DSP 函数运行极慢,profile 看到全是标量指令 库没走 Helium 变体 重新选 lfd
ARM_MATH_AUTOVECTORIZE 被某头文件自动打开 CMSIS 5.9 之前有这种情况 在 Makefile 里 -UARM_MATH_AUTOVECTORIZE 显式 undef
编译报 '__ARM_FEATURE_MVE' undeclared -mcpu=cortex-m85 没生效 检查编译参数顺序,避免被 -march= 覆盖
NN 函数 arm_convolve_Helium_s8 not found CMSIS-NN < 8.0.0 升级 pack
link 时报 R_xxx out of range Helium 的 LD/ST 偏移量超 ±1024 函数里没用 register asm("qN") 约束,临时变量被打到栈外;加 -fno-strict-aliasing 或重构
__StackTop 不 8-byte 对齐 .ld 里的 RAM 末地址没对齐 LENGTH 改到 8 的倍数;或者启动时 bic sp, #7 强对齐
进 C 运行时随机 HardFault,SP 错位 用了 __attribute__((constructor)) + 内联汇编强改 SP 删掉那段 C 代码;在 startup_xxx.sReset_Handler 入口处强对齐
DSP / NN 输出偶发数值漂移 全局开了 -ffast-math,FMA 折叠 / 运算重排改了中间舍入 去掉 -ffast-math;真要 FMA 用函数级 __attribute__((optimize("ffp-contract=fast")))

8. 不要做的事

  • ❌ 不要加 -DARM_MATH_AUTOVECTORIZE
  • 不要全局加 -ffast-math (CMSIS-DSP / NN 依赖 IEEE 754 严格语义;需要 FMA / 重排运算的,做成函数级 __attribute__((optimize))
  • ❌ 不要同时开 -march=armv8.1m.main 又用 -mcpu=cortex-m85(冲突,GCC 会取后者)
  • ❌ 不要给 M85 加 -mfpu=fpv5-d16(那是 M7 的 FPU 命名,会跟 M85 冲突)
  • ❌ 不要混用 -mfloat-abi=softfphard(CMSIS 库是 hard ABI 编译的)
  • ❌ 不要把 .bss / .data 对齐降到 4
  • ❌ 不要为了"省一点空间"用 arm_cortexM85lfd_math.a(这是标量 版,不带 Helium;你想要的 Helium 版是 arm_cortexM85lfd_mve_math.a
  • ❌ 不要在 Helium 函数里调用 printf / newlib syscall(会破坏 Q 寄存器现场)
    • 改用 ITM / SWO / 单独的 log 任务
  • ❌ 不要在 C 函数里(包括 constructor)用内联汇编强改 SP
    • 编译器的 prologue/epilogue 已经按旧 SP 布局好了,乱动 SP 会让 caller / callee 栈帧全错位
    • 栈对齐必须且只能 在汇编启动文件 Reset_Handler 入口处做

9. 参考


维护 :这份笔记是 13.3 固定场景下的配置 baseline。

如果未来升 14.2 或更新 CMSIS 版本,要重新过一遍第 3、4、6 节。

相关推荐
caimouse1 小时前
Phase1Initialization 函数分析
单片机·嵌入式硬件·reactos
ysa0510301 小时前
【板子】德州扑克模拟
c++·笔记·板子
·醉挽清风·2 小时前
学习笔记—八股—操作系统
笔记·学习
2401_8275012813 小时前
51单片机(二):GPIO、中断、定时器与PWM
单片机·嵌入式硬件·51单片机
尼喃15 小时前
HDMI/DP高速差分接口ESD保护:0.3pF超低电容PWESD-5VSDF方案详解
嵌入式硬件
ACP广源盛1392462567315 小时前
GSV6155@ACP# 搭配 AI 服务器、AI PC 完整适配方案
大数据·服务器·人工智能·分布式·单片机·嵌入式硬件
fanchenxinok15 小时前
学习笔记:LabVIEW调用DLL解析S19文件教程
笔记·学习·labview·dll解析
疯狂打码的少年16 小时前
【软件工程】结构化设计:模块独立性与耦合内聚
java·开发语言·笔记·软件工程
Saniffer_SH16 小时前
NAND技术(二):从 Channel、Die/LUN、P/E Cycle 到 LDPC,一次讲透 NAND 里那些最容易误解的概念
人工智能·驱动开发·嵌入式硬件·测试工具·fpga开发·计算机外设·压力测试