☰
端侧AI芯片软硬件协同设计实战:从架构到Bring-up的踩坑总结
2026/10/27 7:42:22 网站建设 项目流程

从“算力焦虑”说起,我在这轮AI芯片项目里最大的感想是:硬件设计从来不是把晶体管堆出来就算完事,真正让项目拖期的往往是软件栈与硬件接口之间那些看不见的对齐工作。这篇文章是软硬件协同设计系列的第二篇,重点聊聊从架构定义、启动流程、板级周边电路到bring-up阶段我实际踩过的坑和整理出的方法,给正在做端侧AI芯片或者打算从开发板过渡到自研芯片的工程师作个参考。

先说背景。我们团队做的是面向边缘设备的一款AI推理芯片,目标场景覆盖视觉检测、语音唤醒和轻量大模型推理,主控侧有一颗通用CPU核,旁边挂了自研的NPU(神经网络处理单元),DDR用LPDDR4X,启动介质选了QSPI Flash加eMMC双方案。整个流程走下来,前后经历了架构评估、RTL开发、软件栈搭建、板级打样、整机联调几个阶段,中间踩了不少坑,也沉淀出一些可以复用的经验。

1. 从“算力焦虑”说起:AI芯片为什么不能只堆MAC阵列

1.1 定义峰值算力时,你真正在定义什么

很多刚接触AI芯片的人,第一反应是看TOPS(每秒万亿次操作),似乎数字越大就越强。真正做架构时你会发现,TOPS只是一个理论上限,决定实际性能的往往是三件事:MAC阵列的规模和利用率、片上存储的容量与带宽、数据搬运的速度。

MAC阵列是AI芯片的心脏,由若干个乘加单元组成。以4TOPS@INT8的算力为例,如果主频跑1GHz,那大致需要4000个MAC单元同时工作(每个MAC一次完成一个乘法和一个加法)。这句话说起来轻巧,但放到版图上就是一大片面积,再加上对应的SRAM和互联网络,功耗和面积立刻变得紧张。算力目标从“想做到多少”到“能做多少”,中间隔着工艺、封装、散热、成本好几道坎。

我的建议是,定义峰值算力之前,先定义好“有效算力”:在你的目标模型里,MAC阵列能跑到百分之多少的利用率。实际项目中,一个卷积如果访存没优化好,MAC阵列经常干等着数据,利用率连40%都不到,再高的TOPS也是白搭。

1.2 把“内存墙”搬上台面

内存墙不是我发明的词,但几乎每个AI芯片团队都要跟它死磕。模型参数要读出来、中间激活值要存下来、计算结果要写回去,这些数据流如果都依赖外部DDR,功耗和延迟立刻爆炸。

我们这个项目里,片上SRAM总共做了大约4MB,用来放激活值和权重分片。4MB听起来不小,但一个YOLOv8m模型光权重就要40MB左右,根本塞不下。于是整个软件栈都围绕以下原则设计:

  • 权重分块加载:把大tensor切成小块,算完一块再拉下一块,避免一次性占用太多片内空间。
  • 激活值原地复用:能在一个buffer里反复读写的算子,绝不新开内存。
  • 输出立即搬运:计算完的feature map通过DMA及时搬走,给后续算子腾地方。

硬件设计也要配合这些软件策略。片上SRAM的端口数量、DMA通道数、总线带宽都需要针对访存模式做调优,而不能只盯着MAC阵列看。

1.3 软件视角与硬件视角的“翻译”问题

同样一个卷积运算,软件工程师脑子里想的是for (n, c, h, w)四层循环,硬件工程师脑子里想的却是数据从DDR到SRAM再到MAC阵列的流水线。两边如果不早点坐在一起对齐,后面必然互相扯皮。

我们项目里最典型的一次争执发生在卷积的padding处理上。软件组希望硬件支持任意padding值,方便模型转换;硬件组觉得支持固定的0、1、2三个档位就够了,省面积。最后两边各让一步:硬件支持0到7的任意padding,软件保证所有模型在转换前统一padding,并在图优化阶段把padding吸收进卷积算子里。这个折中在RTL冻结前两周才定下来,我们为此开了三次会。

我的体会是:架构阶段就要把编译器负责人和RTL负责人放在同一张桌子上,共同定“硬件抽象层”的语义。谁先定接口,谁就掌握了主动权,但谁也不能完全说了算。

2. 指令集、启动流程与第一块“骨架”

2.1 异构SoC的指令集划分

AI芯片通常不是单一处理器,而是异构SoC:CPU核负责控制逻辑和Linux/RTOS跑起来,NPU负责张量运算,可能还有DSP处理音频信号。指令集的选择要分开看:

  • CPU核:可以直接授权ARM Cortex核或RISC-V核。我们选了RISC-V,主要考虑是授权费可控、定制空间大。如果产品对生态依赖强,ARM会更省心,但成本和灵活性要权衡。
  • NPU:一般是自己定义一套专用指令集,不对外暴露。NPU的指令不是给程序员看的,是给编译器生成的,所以指令格式要固定、解析要简单、流水线要规整。我们设计的时候尽量把指令长度统一到64位,方便取指和译码。
  • DSP核:如果产品做音频处理,一颗带SIMD的低功耗DSP很值得,但我们这轮没加,因为语音唤醒模型直接被NPU吃掉了。

指令集一旦定了,后面编译器、汇编器、反汇编工具都得跟着走。改指令格式的成本极高,所以评审时要特别较真,宁可多花两周讨论,也不要在后面流片后才发现指令编码有歧义。

2.2 从BootROM到应用加载:AI芯片的“第一口气”

SoC上电后,第一段代码是固化在芯片内部的BootROM。BootROM体积很小,但职责很大:初始化时钟和DDR,从外部启动介质读取引导程序,并校验其合法性。对AI芯片来说,这个过程比普通MCU更复杂,因为NPU通常没有独立的启动路径,需要CPU先跑起来,再通过驱动加载NPU固件。

我们项目的启动流程大致是:

  1. BootROM初始化最基本的PLL和DDR控制器。
  2. BootROM读取QSPI Flash前64KB的Bootloader。
  3. Bootloader初始化完整时钟树、电源域、外设控制器。
  4. Bootloader从eMMC读取kernel或裸机应用。
  5. 应用运行后,再通过驱动把NPU的固件搬运到NPU私有的SRAM,启动NPU。

这个过程中最容易被忽视的是DDR training。同样一颗LPDDR4X颗粒,在不同PCB走线、不同温度下,训练参数都可能不同。我们第一版板卡在常温下能跑通,放到高温箱里就随机死机,最后排查发现是DDR training参数留的裕量不够,重新校准并把训练结果固化成默认配置才解决。

2.3 一个真实的启动翻车现场:换QSPI芯片之后

这里分享一个从社区里看到的案例,也类似我们自己遇到的问题:一台设备原本用的是8MB QSPI Flash,后来为了降成本换成16MB的兼容型号,结果系统直接启动不起来。原因并不复杂——新Flash的JEDEC ID不同,BootROM里预设的驱动表没有覆盖这个型号,或者命令序列兼容性差,导致读不到正确的Bootloader。

这类问题在自研平台上更容易遇到。解决方案通常有三类:

  • 优先选择BootROM已知型号清单里的Flash,不要贪便宜换没验证过的。
  • 如果必须换,就要有热风枪焊回去的心理准备,同时准备好JTAG接口,用来绕过BootROM直接烧写。
  • 在板卡设计阶段加一个“恢复按钮”,Bootloader里做分区冗余:主分区启动失败后自动切备份分区。

我们后来在新一轮板卡上做了双备份启动方案,主备份都用QSPI,另外在eMMC里也放了一份Bootloader。启动失败时BootROM优先尝试QSPI,失败后尝试eMMC,这极大降低了变砖的概率。

3. 软件栈的“三驾马车”:编译器、runtime与算子库

3.1 编译器:从计算图到汇编的翻译官

AI芯片的编译器,本质上承担着把深度学习框架导出的计算图翻译成硬件指令的工作。一个典型的离线编译流程是:

  1. 输入ONNX模型或者自定义IR(中间表示)。
  2. 做图优化:算子融合、常量折叠、维度推导。
  3. 把计算图切分成子图,匹配到硬件支持的算子模板。
  4. 对卷积、全连接这些算子做循环分块和指令调度。
  5. 生成NPU汇编,再交给汇编器生成二进制。

图优化是性价比最高的环节,例如把Conv+BN+ReLU融合成一个算子,运算量没变,但访存次数能少三分之一。硬件设计时就要为这种融合提供支持,比如在卷积单元里内置scale和bias通道,输出结果直接过ReLU,再决定是否写回内存。

我们团队踩过的坑是算子切分粒度过细。早期编译器把一个大型卷积拆成几百个小任务,每个任务单独分配缓冲区,结果调度开销反而比计算开销还大。后来改成“任务尽量大、数量尽量少”的原则,配合双缓冲机制,整体性能一下子提升了一倍多。

3.2 runtime:管线调度与内存管理的隐形管家

runtime是跑在CPU上的常驻软件,负责给NPU派活。它要做的包括:

  • 内存池管理:预先从DDR里申请大块连续内存,按需切给不同算子,避免频繁malloc。
  • 任务队列维护:把算子的输入输出地址封装成描述符,按顺序提交给NPU。
  • 同步机制:CPU要等NPU算完再接着干下一件事,就需要信号量或中断。
  • 电源管理:NPU空闲时关时钟、降电压,任务到来时快速唤醒。

这里有个容易忽略的点:描述符本身也存在DDR里,NPU要从DDR拉取描述符再执行,这个过程如果描述符格式设计得不好,容易产生额外的解析开销。我们后来把描述符做成固定大小、固定对齐的结构体,支持批量提交,CPU一次写好多条,NPU逐条执行,极大减少了交互次数。

3.3 算子库:卷积、归一化、残差连接这些家常菜怎么炒

编译器负责“翻译”,算子库负责“做法”。同一个卷积,用不同的实现方式,性能差好几倍。我们在项目里主要做了以下优化:

  • 卷积实现:支持direct卷积和winograd算法两种模式。3x3大stride的场景用winograd能减少乘法次数,但winograd对数值精度更敏感,INT8量化时偶尔要回退到direct模式保精度。
  • 访存重排:把feature map按通道重排成NCHWc格式(c为每次向量化处理的通道数),让DMA搬运更连续。
  • 算子融合:把残差连接的加法直接放进上一次卷积的写回阶段,省掉一次整张feature map的读写。

开发算子库是很枯燥的工作,一个卷积要反复调参:分块大小、DMA触发时机、双缓冲深度,每一个参数都可能让性能波动20%以上。我的建议是一开始就搭好性能自动回归平台,每次改动都记录耗时,避免改着改着性能又回到原点。

4. 板级设计的“隐形工作量”:电源、时钟、看门狗和周边芯片

4.1 一颗AI芯片上电的那一刻:时序与轨压

AI芯片的供电比普通MCU复杂得多。核心电压、DDR电压、IO电压、PLL电压,不同电压轨有严格的上下电顺序。如果Core电压先于IO电压升起,或者电压跌落超过容限,芯片可能进入未知状态,甚至损坏。

我们为这个项目专门做了一张时序表,部分关键电压轨如下:

电压轨标称值上电顺序说明
VDD_IO1.8V1先给IO供电,保证引脚状态确定
VDD_PLL1.8V2PLL供电,需要干净的滤波
VDD_DDR1.1V3DDR PHY供电
VDD_CORE0.8V4核心逻辑供电,最后上电

上电芯片采用了专门的电源管理芯片(PMIC)配合控制信号实现。若用分立DC-DC搭建,可以用一个使能信号链控制先后次序。这里要特别提醒的是,不要完全依赖芯片手册默认的电压值,一定要实测核心电压的纹波,纹波过大时NPU高负载会出随机计算错误,并且极难定位。

4.2 被低估的外围芯片:从LED驱动到升压电源

很多人以为AI板卡设计只要搞定主芯片就行,实际上外围芯片往往能拖慢整个项目进度。我举两个例子。

第一个是LED背光驱动芯片,严格来说它不是为AI芯片服务的,但人机交互的指示灯、屏幕背光都离不开它。这类芯片的FB(反馈)脚和CS(电流采样)脚调整很有讲究:FB脚通过电阻分压设定恒定输出电压,CS脚通过采样电阻设定输出电流上限。我们在一个原型板上发现背光亮度抖得很厉害,查了半天发现是采样电阻的PCB走线太长,引入了噪声干扰。把采样电阻靠近CS脚、走线加粗之后问题就消失了。这种模拟电路的直觉,往往是数字工程师最欠缺的。

第二个是升压电源芯片。AI板卡里偶尔需要把3.7V锂电升到5V或12V给外设供电,升压芯片的选型和电感选型很关键。电感饱和电流不够,大电流一上就饱和,电源效率立刻崩掉,甚至产生啸叫。我们也遇到过升压输出带载后电压跌落,原因是反馈电阻离电感太近,被开关噪声耦合。调整布局后就稳定了。

注意:任何电源芯片的反馈引脚,都要尽量远离电感与开关节点。这句话是板级设计里最便宜也最值钱的经验。

4.3 看门狗芯片与“系统跑飞后谁来兜底”

AI系统的复杂性决定了它一定会死机,区别只在于死机后能不能自己恢复。硬件看门狗芯片就是兜底方案。项目里我们选了带窗口喂狗功能的看门狗芯片,喂狗窗口内有信号才复位计数,喂太早或太晚都会触发复位。

NPU一旦跑飞,CPU可能卡在某个循环里,此时喂狗逻辑也会跟着卡住,硬件看门狗就能在超时后拉低复位脚,让整个系统重新启动。但要注意,复位之后如果bootloader没有做个状态标记,你就不知道上一次是因为什么重启的。我们在RTC域里保持一个重启原因寄存器,bootloader每次启动时读出来并记录日志,这对现场问题分析特别重要。

4.4 原理图多页分割:以后少挨板级工程师的骂

自研芯片的原理图往往又大又复杂,几百个引脚不可能一页画完。我们坚持“按功能域分页”:主芯片电源、DDR、外设接口、NPU调试接口、时钟电路各占一页。这个习惯是从用设计工具画第一版原理图时养成的,虽然一开始多花了几小时,但后面评审和查错的效率提升是成倍的。

分页画图要注意网络标签的统一命名。电源域要带后缀,比如VDD_CORE_0V8、VDD_DDR_1V1,不加后缀的话ERC(电气规则检查)容易漏报。我们也用自动生成引脚网络列表的脚本,从RTL顶层导出IO列表,再导入原理图工具,既避免手写错误,又保证网表和RTL一致。

5. 从开发板到流片:参考平台、验证与后端落地

5.1 为什么团队里总要有几块开发板

自研芯片没回来之前,软件团队不能干等着。我们准备了三种参考平台:

  • 主控平台:RK3588开发板。它自带6TOPS NPU和丰富的IO,先在上面跑通Linux、跑通摄像头数据流,相当于提前验证应用层逻辑。
  • MCU平台:STM32系列。用来验证上电时序控制、看门狗逻辑、I2C外设访问等板级控制代码,这些代码后面可以平移到新平台。
  • Wi-Fi/物联平台:ESP32。验证网络连接和OTA升级流程,确保后续设备能远程更新固件。

开发板的价值不只是让软件先行开发,更重要的是给硬件验证提供“对照物”。当自研芯片的某个行为跟参考平台不一致时,你至少要判断:是我们硬件的问题,还是参考平台本身就特殊。我们遇到过把DDR带宽不足误判成NPU故障的情况,靠的就是在RK3588上跑同一套代码做对照,发现开发板也有相似瓶颈,才把注意力转回软件侧。

5.2 验证策略:不只是跑通功能

芯片验证工作经常被视为“后端活”,但我在项目里越来越觉得,验证策略要从前端架构阶段就开始设计。我们坚持的验证三层结构是:

  • 单元级验证:每个NPU算子模块独立验证,用UVM环境构造随机激励,对比参考模型。
  • 子系统集成:CPU、DMA、SRAM、NPU四者协同跑通一条典型流水线。
  • 整芯片原型:在FPGA原型平台跑完整的编译产物,相当于芯片流片前的一次总彩排。

FPGA原型验证几乎是必做的。虽然FPGA跑不到目标频率,但它能提前发现不少软硬件交互的问题。我们曾在FPGA上跑通了一个检测模型,虽然帧率只有真实芯片的十分之一,但整个软件栈的调用链都被验证了一遍,流片回来后bring-up轻松了很多。

5.3 芯片测试与后端:流片前最后一次“挑毛病”

流片后的测试环节包括晶圆测试和封装后测试,测试时需要设计者提前准备好诊断用例。DFT(可测试性设计)不是随便插几个扫描链就行,要考虑测试覆盖率、测试时间、引脚复用。NPU这种大规模并行阵列,内部状态多,扫描链设计特别费心思。我们花了不少时间做可观测性设计:在每个算子模块的输入输出处加调试寄存器,方便定位是哪个环节出错。

后端物理设计同样是关键一环。前端RTL写的是逻辑行为,后端要考虑时钟树、功耗、时序收敛。一个常见的矛盾是:前端想加更多调试寄存器提升可达性,后端却头疼这些寄存器让时序更难收敛。双方的取舍必须在流片前定好,否则版图阶段再改,成本极高。时序约束文件(SDC)在前端阶段就要认真写,越早让后端拿到完整约束,越能减少后期往复。

5.4 AI工具如何渗透进芯片设计流程

芯片设计流程本身很传统,但AI工具正在逐渐渗透进来。我在这个项目里的实际体验有三点:

  • AI编程助手写脚本很顺手。现在常用的AI编程插件,在数据分析、脚本脚本开发方面都很高效。比如让AI助手生成一个解析日志并自动统计回归结果的Python脚本,一分钟就能拿到能跑的版本,以前手写至少半小时。
  • AI辅助资料检索。查IP手册、查信号时序时,用AI工具快速定位关键段落,比人肉翻PDF快很多。但我会提醒自己,所有关键参数都要回到原始手册核实,绝对不能只信AI的总结。
  • AI辅助评审。让AI模型检查代码风格、找出未使用的信号、对比两份网表差异,虽然不能代替人工,但能节省不少机械性重复劳动。

另外,AI工具在专利交底书整理上也帮了不少忙,它能帮我把技术方案描述得更有层次,检索对比文件时出候选列表。不过最终写法和法律细节还是得靠专业人员把关,工具只是辅助。

6. 写在二轮迭代后:一些不写进Spec的体会

6.1 软硬件联调的“时间账”

统计下来,我们软硬件联调的时间占整个项目周期将近一半。很多人以为芯片回来之后就是写代码、调bug,实际上是三天两头发现接口文档和实际行为对不上。最典型的例子是寄存器位的默认值:RTL里复位值是0,但软件组默认它是1,等芯片回来一跑就翻车。

我的方案是做一个自动化的寄存器数据库。RTL里每个寄存器字段的偏移、位宽、默认值、读写属性全部从一份ARM文件(或CSV)自动生成头文件和文档,RTL和软件共用同一份来源。这样硬件改代码,软件立刻就能同步,不会再出现“文档落后于代码”的尴尬。

6.2 文档和命名:低成本投入,高回报率

芯片项目的协作人数一多,命名规范和文档质量就成了效率分水岭。我们吃过亏之后,强制推行了几条规则:

  • 信号命名统一用{来源}_{功能}_{方向}格式,例如npu2cpu_intr表示NPU发给CPU的中断。
  • 寄存器文档必须包含“为什么”这一栏,不只写“是什么”。
  • 每次架构变更都要同步更新一个“变更记录”MD文件,写明变更原因、影响模块、验证状态。

这些事看起来琐碎,但在人员流动时价值巨大。新同事接手,只要看变更记录和寄存器数据库,一天就能进入状态,而不是到处找人问“这块当时是怎么定的”。

6.3 小团队适合的起步路径

如果团队人不多,预算有限,我建议不要一开始就奔向全自研大芯片,从以下路径走更稳妥:

  1. 先选一颗成熟SoC跑通你的完整软件栈。
  2. 把真正想做差异化的算法负载单独放到FPGA原型上验证。
  3. 再考虑流片,并尽量选择已有成熟IP授权的方案,把自研范围集中在NPU这个核心模块上。

我们如果重来一遍,大概会在RTL开发前再多花一个月做系统建模,尽早把编译器和性能模型跑起来。性能模型做得越细,越能在RTL冻结前发现性能不达标的风险,修改成本比流片后低一个数量级。

最后我想说的是:AI芯片的软硬件设计没有太多浪漫的地方,它更像是一门平衡的艺术——在成本、功耗、面积、性能、可编程性之间不断找那个“够用且不难受”的点。我个人的习惯是,每做一个新项目,第一件事不是打开EDA工具,而是先写一份一页纸的软硬件职责边界备忘录,列出哪部分由硬件保证、哪部分由软件兜底、哪部分两者共同负责。这份一页纸,往往在半年后比那些几百页的架构文档还有用。希望这篇分享能帮正走在类似路上的你少踩几个坑。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询