news 2026/9/5 1:40:16

嵌入式AI部署实战:用MATLAB/Simulink自动生成C代码

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
嵌入式AI部署实战:用MATLAB/Simulink自动生成C代码

1. 嵌入式AI落地的最后一公里:从模型到硬件的代码生成

做嵌入式AI的朋友应该都有同感:模型在PC上跑得好好的,一旦要往单片机或者ARM核上迁移,事情就变得麻烦起来。手写C代码去复现卷积算子?光是矩阵乘法就够折腾,更别提还要自己处理内存对齐、数据量化、算子优化这些事。这一篇系列文章,我重点讲一讲怎么用MATLAB/Simulink这套工具链,把训练好的AI模型自动生成C/C++代码,直接部署到嵌入式硬件上。内容围绕代码生成与部署展开,核心关键词是MATLAB、Simulink、嵌入式硬件、AI,适合正在做边缘计算部署、或者准备把算法从PC端往嵌入式端迁移的工程师参考。

先说结论:用MATLAB/Simulink做嵌入式AI部署,最大的价值不是“能生成代码”本身,而是它把“模型训练—算法验证—代码生成—硬件集成”这条链路打通了。你可以在同一个环境里完成从浮点模型到定点代码的转换,而且生成出来的代码可读性不错,还可以继续集成到现有的嵌入式工程里,不像有些自动生成的代码那样只能看不能用。这套方案踩过的坑不少,但也有相当多值得借鉴的地方,下面展开聊。

2. 整体思路与工具链:为什么选MATLAB/Simulink这条路

2.1 嵌入式AI部署的三种主流路线

在讲MATLAB方案之前,先明确一下嵌入式AI部署目前的主流路线,方便对照理解。

  • 路线一:专用AI推理框架。比如TensorFlow Lite Micro、CMSIS-NN、ONNX Runtime等,这类方案依赖运行时库,在MCU上通过解释执行或半编译的方式运行模型。优点是生态成熟、算子覆盖广;缺点是框架本身占用Flash/RAM资源,对低端MCU不太友好,而且算法工程师经常需要手动调整算子实现来适配硬件。
  • 路线二:手写算子+手工优化。模型导出权重后,完全用C手动实现推理逻辑。优点是代码可控、容量最小;缺点是开发周期长、容易出错,而且每换一个模型结构就要重新写一遍。
  • 路线三:基于模型自动生成代码。也就是MATLAB/Simulink Embedded Coder + Deep Learning Toolbox这条路线,模型在Simulink中搭建或者导入,经过配置后直接生成嵌入式C代码。

路线三是我个人在实际项目中最常用的一条路径。它的核心思路不是去维护一个庞大的运行时框架,而是把模型“翻译”成直接在目标硬件上运行的静态代码,生成出来的代码没有额外的解释层,执行效率更高,也更适合对实时性有要求的控制类或信号处理类场景。

2.2 MATLAB/Simulink工具链在AI部署中的角色分工

MATLAB/Simulink在嵌入式AI部署中涉及到几个关键工具箱,先梳理清楚分工,后面操作才不会乱。

  • Deep Learning Toolbox:负责AI模型的导入、训练、可视化,以及把模型转换成Simulink可用的模块。支持导入TensorFlow-Keras、ONNX、PyTorch等格式的模型,这解决了“模型从别的框架训练好”的情况。
  • Simulink:搭建整个嵌入式算法的数据流模型。你可以把AI推理模块、前处理模块、后处理模块、外部通信接口全部放到一张模型图里,保证整体逻辑可控。
  • Embedded Coder:负责把Simulink模型生成高效、紧凑的C/C++代码,支持针对特定芯片的优化配置。
  • MATLAB Coder:负责把MATLAB代码直接转成C/C++,适合没有使用Simulink图形化建模的情况。
  • GPU Coder:如果目标硬件是NVIDIA GPU平台(比如Jetson系列),可以用这个生成CUDA代码,不过这一篇以MCU和ARM Cortex-M为主,不展开讲。

把这几块组合起来,典型的工作流程是:先用Deep Learning Toolbox导入或训练模型,然后转成Simulink模型,再通过Embedded Coder生成面向嵌入式硬件的C代码,最后把生成的代码放到你的嵌入式工程里编译烧录调试。整体思路清晰,每个环节都有对应的工具支撑,这比自己在工程里折腾一堆脚本和手动转换要高效得多。

2.3 方案优势与适用边界

任何工具都有它的适用范围,这个方案也不例外。

优势部分:

  • 全流程可视化,模型结构、数据流、状态机都一目了然,审查和交接都方便;
  • 自动处理数据类型的转换(浮点到定点),这是手写代码时最容易出错的地方;
  • 生成的代码针对目标硬件做了优化,比如可以配置生成CMSIS-DSP优化的代码,在Cortex-M上跑起来效率不低;
  • 支持硬件在环(Processor-in-the-Loop,PIL)测试,不烧片就能先验证代码在目标处理器上的行为。

适用边界:

  • 如果目标是部署超大模型(上百MB的Transformer等),这套工具链的空间和时间开销确实不如专用推理框架优化得极致;
  • 如果模型结构非常冷门且涉及大量自定义算子,工具链的算子支持可能会受限;
  • 如果想要极致的Flash/RAM占用(比如1KB Flash做推理),手工优化的C代码仍不可替代。

所以我的经验是:这个方案最适合中低复杂度模型,比如分类网络、目标检测的小型网络、信号分类模型、控制策略模型等,部署在Cortex-M、Cortex-A或者部分DSP平台上,工程效益相当明显。

3. 核心细节与实操要点:模型准备与转换

3.1 模型格式与导入:从PyTorch/TensorFlow到MATLAB

现在实际项目中大部分算法工程师用PyTorch或者TensorFlow训练模型,所以“怎么把模型弄到MATLAB里”是第一道关卡。

一个通用做法是:导出ONNX格式,再用MATLAB导入ONNX模型。具体步骤很简单:

net = importNetworkFromONNX('model.onnx');

这一步会把ONNX模型解析成MATLAB的DAGNetwork或者dlnetwork对象。导入成功后,可以用analyzeNetwork(net)查看网络结构,确认每一层的连接关系和参数数量。

在导出ONNX时,有几个细节容易踩坑:

  • 输入输出名称要固定。ONNX模型里的输入节点名称和输出节点名称一旦变化,MATLAB导入后对应的层名也会变,后面配置的时候容易对不上。建议在PyTorch导出时就固定好输入输出名字。
  • 动态维度问题。如果模型用动态Batch Size或动态分辨率导出,MATLAB导入时可能会报警告。实际部署到嵌入式端时,通常输入尺寸是固定的,建议在导出ONNX时就固定输入维度,后面生成代码会省心很多。
  • 算子兼容性。ONNX中如果包含一些比较新的算子(比如某些Transformer里的Attention变体),MATLAB可能不支持直接导入,需要手动转换成支持的层组合,或者换用importNetworkFromPyTorch(新版本MATLAB支持直接导入PyTorch模型)。

如果你不想经过ONNX,也完全可以在MATLAB里直接用Deep Learning Toolbox的层API搭建网络,然后用自定义数据训练,训练完成后再转Simulink模型。这个方法的好处是全程不出MATLAB环境,但前提是你的模型结构相对标准,不需要用到框架里的特殊层。

3.2 Simulink模型搭建:AI模块与周边逻辑的配合

模型导入完成后,不能直接就在Simulink里生成代码,你得把AI推理模块放到合适的模型框架里。Simulink对AI推理的封装,主要有两种方式。

方式一:用Predict模块直接调用网络。

在Simulink库浏览器中找到Deep Learning Toolbox下的Predict模块,把网络对象关联到该模块上。Predict模块接受输入数据,输出网络的预测结果。这种方式适合快速验证、不需要在推理前后做太多复杂逻辑的场景。

方式二:用MATLAB Function模块封装推理调用。

在Simulink里拖一个MATLAB Function模块,在里面写类似下面的代码:

function out = predictWrapper(in) persistent net; if isempty(net) net = coder.loadDeepLearningNetwork('mynet.mat', 'net'); end out = predict(net, in); end

这种方式的优势在于灵活,可以在函数内部加前处理、后处理逻辑,方便后续扩展。而且coder.loadDeepLearningNetwork这个函数的用法本身就嵌入到代码生成流程里,适合嵌入式目标。

从我的实操经验来看,方式二更适合复杂的项目。因为实际部署时,AI推理模块很少是孤零零一个predict就完事的,前面通常有滤波、归一化、特征提取,后面有阈值判断、状态切换、输出打包等,MATLAB Function模块可以把这些逻辑组合在一起,整个Simulink模型看起来也更干净。

3.3 数据预处理与后处理:藏在模型之外的工程细节

这里特别提醒一下,很多人在做AI部署时只关心网络本身的推理,而忽略了前后处理,结果模型部署到嵌入式端后效果和PC端对不上。问题往往不在推理,而在前后处理的精度和顺序。

  • 输入数据格式:训练时用的归一化参数(均值、方差或缩放系数),在嵌入式端必须严格一致。通常建议在MATLAB Function里用固定的常量参数计算,不要依赖外部变量初始化。
  • 图像类输入:如果模型输入是图像,要确认通道顺序(RGB还是BGR)。大部分嵌入式摄像头输出的是RGB,但有些模型训练时用的是BGR,这个不对齐,预测结果完全乱掉。
  • 输出数据解读:分类网络输出的是得分向量,要经过Softmax转到概率;目标检测网络输出的是框坐标和置信度,需要做NMS(非极大值抑制)后处理。这些逻辑可以放在Simulink模型里,用Stateflow或者MATLAB Function模块实现。

我见过一个实际案例:算法工程师把训练好的模型直接导出部署,在PC端测试效果很好,上板之后预测准确率明显下降。排查了一晚上,最后发现问题是摄像头采集的图像格式是YUV422,转成RGB时没有恢复成正确的通道顺序,模型输入乱了。这个教训让我养成了一个习惯:凡是做嵌入式AI部署,第一步先打印一小段输入数据的数值和图像,和PC端比对,确认一致再继续后面的工作。

4. 实操过程与核心环节实现:代码生成全流程

4.1 配置Embedded Coder:目标硬件与优化选项

代码生成是整个流程中最关键的一步,很多细节都在这一步决定成败。这里拿一个经典场景举例:目标硬件是STM32F767(Cortex-M7内核),需要把训练好的一个信号分类模型部署上去。

从Simulink模型生成代码的操作路径是:

  1. 打开模型配置参数(Ctrl+E);
  2. Solver选项卡中,把求解器类型设置为Fixed-step,步长根据实际采样周期设置,比如信号采样率是1kHz,步长可以设为0.001s;
  3. Code Generation选项卡中,选择系统目标文件ert.tlc,这是Embedded Coder的实时目标文件,生成代码的风格和效率都是为嵌入式环境优化的;
  4. Hardware Implementation选项卡中,选择目标硬件设备,这里选择ARM Compatible -> ARM Cortex-M,如果用的是STM32系列,可以选择具体的芯片型号,没有对应的也可以选通用的Cortex-M;
  5. Code Generation > Interface中,配置代码接口格式,比如函数名、参数传递方式等。

需要说明的是,代码生成功能依赖于Embedded Coder工具箱,如果没有这个工具箱,Simulink默认只能生成用于仿真的代码,不能用于嵌入式部署。确认许可证没问题的方法很简单,命令行输入license('test', 'Embedded_Coder'),返回1就说明可用。

4.2 浮点转定点的必要性:为什么嵌入式端常用单精度甚至定点推理

很多初接触嵌入式AI的工程师会有疑问:PC端默认都是双精度浮点,为什么我生成的代码里面变量类型是single(单精度)?嵌入式端为什么不直接用double

原因很好理解。大部分MCU的浮点单元只支持单精度运算,用双精度会导致软件模拟,速度慢好几倍。而最极端的场景,比如Cortex-M0这类没有FPU的内核,连单精度浮点运算都是库函数模拟的,性能更低。所以实际部署时,常见的思路是先转成单精度浮点,再根据情况考虑定点量化。

在MATLAB/Simulink中,数据类型的转换可以半自动完成。你可以借助Fixed-Point Designer工具,通过以下步骤实现:

  1. 给模型中的Constant、Signal等模块显式设置输出数据类型为single,或者用Data Type Conversion模块强制转换;
  2. 使用Fixed-Point Tool对模型进行定点化分析,该工具会扫描模型中所有运算节点,推荐合适的小数位宽;
  3. 如果模型包含网络层本身,可以通过Deep Learning Quantization相关功能(新版本支持)对网络做量化,比如把权重从单精度缩放到int8表示,同时维护量化参数以在推理时反量化。

我个人的建议是:如果目标MCU带FPU(比如Cortex-M4F、Cortex-M7),可以先用单精度跑通整个流程,效率通常已经可以接受;只有确实遇到Flash/RAM或计算性能瓶颈,再考虑定点量化。一来定点化的调试周期更长,二来量化带来的精度损失需要重新验证,性价比未必高。

4.3 代码生成执行:从Simulink模型到C源文件

配置完成后,点击Build按钮(或使用快捷键Ctrl+B),Embedded Coder会开始执行代码生成。生成过程中控制台会打印一系列日志,包括模型编译、代码生成、编译检查等步骤。

生成完成后,在模型所在目录下会生成一个xxx_ert_rtw文件夹,里面包含生成的C源文件、头文件和构建信息。关键文件有:

  • model.cmodel.h:模型主逻辑实现;
  • model_data.c:参数和全局变量定义;
  • rtwtypes.h:数据类型别名定义;
  • model_private.h:内部函数声明;
  • ert_main.c:独立的main函数示例,方便你理解如何调用模型接口。

如果打开生成的model.h,你会看到类似下面的接口函数声明:

extern void model_initialize(void); extern void model_step(void); extern void model_terminate(void);

这就是Embedded Coder典型的代码风格:initialize负责初始化模型状态和参数;step是模型的核心步进函数,每一个采样周期调用一次;terminate负责清理资源。

把这个step函数放到你的嵌入式主循环或定时器中断里,就可以在真实硬件上运行了。代码生成这一步看似黑盒,实际上如果按照上面的配置来走,整个过程稳定且可复现,这也是我觉得这套工具链最靠谱的地方。

4.4 外部模式与处理器在环:不烧片先验证的逻辑

代码生成完了,千万不要直接烧片就完事。这里给你推荐一个我每次必做的步骤:PIL测试

PIL(Processor-in-the-Loop)模式的意思是:模型在Simulink中生成代码后,把代码下载到目标处理器上运行,Simulink作为上位机向目标发送输入数据、接收输出数据,这样就能在真实硬件上验证模型行为。相比直接在开发板上跑,PIL的优势是可以用Simulink里的各种信号源和Scope模块来查看结果,调试体验好很多。

具体操作方法是:

  1. 在Simulink模型中右键Predict模块或整个子系统,选择C/C++ Code -> Build This Subsystem
  2. 在弹出窗口中选择PIL模式,配置好目标硬件通信接口(通常是串口或者以太网);
  3. 点击Build后,Simulink会自动把生成的代码部署到目标板上,并在模型环境中调用目标板上的函数;
  4. 运行Simulink仿真,观察输出结果。

PIL模式下还有一个重要选项叫External Mode(外部模式),它允许你在Simulink中实时修改参数,而不需要重新编译烧录。这一点在调试控制类算法时特别有用,可以大幅节省调参时间。

4.5 一个完整的最小示例:手写数字识别网络部署

为了让你对整个流程有一个完整感知,这里分享一个我自己做过的小demo:用Simulink生成代码,把MNIST手写数字识别模型部署到STM32F407上。

模型结构非常简单:输入是28x28的灰度图像,拉平后经过一个全连接层(784->128),再接ReLU,再经过一个全连接层(128->10),最后Softmax输出。

在MATLAB中的操作步骤如下:

% 加载预训练模型(假设在MATLAB中已经训练好,或者从ONNX导入) net = importNetworkFromONNX('mnist_cnn.onnx'); % 分析网络结构,确认输入层名称 analyzeNetwork(net); % 保存网络以供Simulink使用 save('mnist_net.mat', 'net');

然后在Simulink中搭建模型:

  • 输入部分:用一个Constant模块或者Signal From Workspace模块提供28x28的输入数据;
  • AI推理部分:用Predict模块并关联到mnist_net.mat中的网络;
  • 后处理部分:用MATLAB Function模块编写Softmax和argmax逻辑;
  • 输出部分:用Scope或者To Workspace模块查看结果。

配置好Embedded Coder后生成代码,在STM32的定时器中断中周期调用model_step(),用串口把输出结果传回上位机验证。

这个demo的完整操作,我用了大概半天时间从零搭完,其中大部分时间花在配置硬件接口和调试串口通信上,模型转换和代码生成反而很顺利。这也说明,工具链本身已经相当成熟,真正考验人的是如何把周边系统集成好。

5. 常见问题与排查技巧实录

5.1 代码生成失败:算子不支持或配置错误

现象:点击Build后,错误日志提示某个层或模块不支持代码生成。

排查思路

  • 首先看完整的错误信息。如果错误指向某个层(比如某些自定义层),可能是该层不在Deep Learning Toolbox的代码生成支持列表中。解法有两种:一是把这一层替换成等价的层的组合(比如某些自定义激活函数可以拆成基本数学运算);二是用MATLAB Function模块手动实现该层的前向计算,然后配置该函数为支持代码生成。
  • 如果错误指向配置问题,比如Solver设置不对或者采样时间不一致,检查Simulink模型中的Configuration Parameters,确保所有模块的采样时间一致,且Solver选择Fixed-step
  • 如果提示缺少工具箱许可证,命令行输入ver查看已安装工具箱列表,确认Embedded Coder和Deep Learning Toolbox都已安装。

5.2 生成代码编译不过:内存分配或头文件冲突

现象:代码生成成功,但是在Keil/IAR中编译报错,常见的有未定义变量、头文件找不到、内存溢出等。

最常见的原因是动态内存分配。默认设置下,Embedded Coder生成的代码可能使用malloc/free来分配一些内部数据结构的存储空间。在PC上没问题,但在嵌入式工程中,如果你没有提供对应的堆实现,或者堆太小,就会出现编译或运行时错误。

解决办法是配置为静态内存分配

  • 打开Configuration Parameters -> Code Generation -> Interface
  • Code interface中选择Classic call interface,并勾选Pass root-level I/O as为合适的参数传递方式;
  • 另外,可以勾选Support floating-point numbers以及Support dynamic memory allocation in MATLAB functions选项的状态,根据实际需求调整。建议把动态内存分配选项关闭,让代码尽量使用静态内存。

另一个常见问题是头文件路径冲突,尤其当你把生成代码和自己的工程代码放在一起时,可能存在同名头文件。建议把生成代码统一放到独立目录中,并在编译器的Include路径设置中,保证这个目录在搜索顺序的最前面。

5.3 运行结果与PC端不一致:数据类型和字节序问题

现象:代码生成编译烧录后,预测结果和PC端Simulink仿真不一致,甚至完全错误。

这里的排查步骤,我建议按照以下顺序来:

  1. 检查输入数据是否一致。在模型中使用Signal From Workspace或者直接给Predict模块喂固定数据,和嵌入式端打印出来的输入数据逐字节比对。这是最基础、也最容易出问题的一环。
  2. 检查数据类型是否一致。确认模型的输入层数据类型是single还是double。如果训练时模型使用double,而嵌入式端输入给的是float,精度会有细微差别,但一般不会差到完全不可用。如果差别很大,说明不是精度问题,而是某个节点数据类型不对,需要检查模型中所有Data Type Conversion模块的配置。
  3. 检查字节序。Cortex-M默认是小端模式,如果你的上位机机器是大端,或者通信协议里字节序被颠倒,输出结果自然不对。调试的时候打印几个中间数值,和Simulink仿真时的对应值比对,很快就能定位。
  4. 检查浮点运算环境。某些MCU在低功耗模式下FPU不可用,或者编译器没有启用FPU优化,导致浮点运算实际上走了软件模拟,结果和正常仿真会有精度差异。确认编译器选项中开启了-mfpu=fpv5-d16等对应硬件的FPU选项。

5.4 执行时间超标:模型推理耗时过长

现象:硬件运行后,一个推理周期的时间明显超过实时要求。

优化方向,按性价比排序:

  1. 确认硬件利用率:先在PIL模式下测量模型step函数的执行时间,用示波器或者硬件定时器打时间戳,确认瓶颈到底在网络推理、前处理还是后处理。不要一上来就盲目优化网络。
  2. 启用CMSIS-DSP优化:在Embedded Coder配置中,为Cortex-M目标启用CMSIS-DSP库,这样生成的矩阵乘法、卷积运算会自动调用CMSIS的优化算子,速度提升往往立竿见影。
  3. 模型结构简化:如果网络太大,考虑用更小的模型结构(减少通道数、使用深度可分离卷积等),或者在精度允许的情况下做量化。
  4. 内存搬移优化:嵌入式端的数据搬运开销往往被忽略。尽量减少大块数据的复制,输入输出使用指针传递而非值传递,能省不少时间。

老实说,我在实际项目里执行时间超标,大部分原因是前处理或者数据拷贝耗时太多,网络推理本身反而占比不高。所以优化之前一定要先测量,用数据说话。

5.5 问题排查速查表

问题可能原因建议解法
代码生成报错:层不支持算子不在支持列表替换层或MATLAB Function手动实现
编译失败:动态内存分配堆太小或未实现malloc关闭动态内存分配,使用静态内存
输出结果与仿真不一致输入数据格式/字节序/数据类型对比打印中间值,逐节点排查
推理时间过长未启用硬件优化/前处理耗时启用CMSIS-DSP优化,分析耗时占比
硬件运行崩溃内存越界/栈空间不足检查数组边界,调整编译器的堆栈配置

6. 个人经验与收尾建议

最后再分享几个我在实际项目中积累的小技巧,都是文档上不常写的。

第一,在生成代码之前,强烈建议先在Simulink里做一次Rapid AccelerationNormal模式的仿真跑通功能,确认模型结果正确,再进入代码生成。这样可以避免“模型本来就有bug,代码生成后问题被放大”的尴尬。

第二,给模型配置生成代码时,函数名最好手动固定下来,不要用默认的自动命名。比如在Configuration Parameters -> Code Generation -> Interface中把函数名设置为AI_Model_InitAI_Model_Step这样的清晰命名,后期集成到嵌入式工程时,可读性和可维护性都会好很多。

第三,开发过程中尽量保持数据对比的习惯。PC端仿真的结果记录下来,PIL测试的结果记录下来,硬件实测的结果也记录下来,三份数据放在一起对比。很多莫名其妙的问题,其实是因为在某一环节的数据传递中出了问题,有对比才能快速定位。

第四,如果目标平台支持,可以用Embedded Coder的Code Replacement Library定制底层实现。比如矩阵乘法可以替换成自己优化的汇编实现,或者使用厂商提供的DSP库函数,这能在不改变模型结构的前提下获得性能提升。

第五,在烧录到真实硬件之前,务必用PIL模式在目标芯片上验证一次。这一步看起来多花时间,但能避免大量“上板后才发现问题”的低效调试。

MATLAB/Simulink这套工具链,在嵌入式AI部署这个领域里确实帮项目省了不少事。它不见得在每一个技术指标上都是最优解,但胜在链路完整、可追溯、可复现,特别适合需要同时兼顾开发效率和部署可靠性的实际工程项目。做嵌入式部署,最重要的是把每一个环节的细节弄明白,工具只是辅助,真正决定项目成败的还是你对模型、对硬件的理解深度。如果使用这套流程的过程中遇到问题,欢迎在评论区交流,我也尽力把你踩过的坑变成之后大家的路标。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/5 1:32:21

Qwen3-0.6B模型全流程微调实战:从LoRA到部署的完整指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/5 1:31:59

国内GEO生成式引擎优化服务商推荐 2026

2026年,生成式引擎优化(GEO)行业完成了从边缘创新到企业营销核心基建的关键跨越。据艾瑞咨询《2026年GEO生成式引擎优化行业研究报告》显示,中国GEO市场规模将从2025年的6亿元攀升至2030年的518亿元,五年间实现86倍的高…

作者头像 李华
网站建设 2026/9/5 1:31:01

RISC-V切入AI芯片的三大路线:CPU+NPU、RVV与自定义指令

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/5 1:28:09

SPI通信FPGA实现全解析:从协议到Verilog代码与调试

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/5 1:25:27

Linux内核调度器PELT负载跟踪机制:原理、挂载摘除与实战排查

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/5 1:25:01

飞腾E2000Q处理器IO接口设计:BANK0~BANK5电路实战与调试指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华