资讯动态

AI芯片软硬件协同设计:从数据搬运到编译器栈的实战经验

发布时间:2026/10/13 22:10:21 来源:尧图企业网站定制
做AI芯片这行圈内常讲一句话“硬件决定天花板软件决定地板。”过去几年我见过不少团队硬件流片了才发现编译器跟不上驱动写不出来模型跑起来性能只有理论峰值的零头。反过来也有团队把软件栈堆得无比豪华结果芯片架构在数据搬运上吃了大亏再好的算子库也救不回来。AI芯片的软硬件设计本质上是在算力、带宽、存储、编译、运行时之间寻找一个能落地的平衡点它不是硬件工程师的独角戏也不是软件工程师的闭门造车。这几年国产AI芯片、边缘NPU、端侧推理加速器层出不穷随便翻一个项目的技术文档里面都会写“支持主流深度学习框架”“提供高效编译器”。但真做起来你就会发现最难的往往不是某个算子怎么实现而是从算法需求到芯片微架构再到工具链整个链条能不能对得上。这篇文章不打算讲教科书式的理论我想用我做加速器项目的实际经验把这摊事拆开揉碎讲一遍芯片怎么设计、软件栈怎么搭、软硬件怎么协同迭代以及那些文档里不会写的坑。适合正在做AI芯片相关项目的人也适合想转行做异构计算、编译器、体系结构的同学参考。1. 先搞清楚AI芯片到底在解决什么问题1.1 传统芯片为什么跑不动神经网络很多人第一次接触AI芯片时会有一个困惑CPU和GPU不是都能跑神经网络吗为什么还要专门做一颗芯片答案藏在两个词里数据搬运和并行度。神经网络的核心操作是卷积、矩阵乘法、归一化、激活其中计算量最集中的是矩阵乘法和卷积。这类运算有一个共同特点大量数据被反复使用。一个卷积层输入特征图的一个像素会被多个输出通道的卷积核读取权重也会被所有空间位置的输入复用。传统CPU为了灵活性把数据和指令都存在片外内存里每算一次都要从内存取指令、取数据算完再写回。这个过程里真正用于计算的晶体管可能只有一小部分大部分时间都耗在了“搬数据”上。GPU比CPU聪明它用大量线程掩盖延迟带宽也比CPU高得多但GPU毕竟是通用并行计算设备它在处理不规则逻辑、动态分支时很强遇到固定模式的矩阵乘也算得很快。问题是它的功耗和面积代价太高对于推理场景来说能效比远远不够。AI芯片的思路很直接把神经网络里最频繁出现的计算模式固化成专用硬件单元让数据在片上流动尽量减少对片外内存的访问。方向对了但这也带来了新的麻烦——硬件一旦固化软件就要想尽办法去适配它。这就是AI芯片软硬件设计一体性的根源。1.2 软硬件协同到底在协同什么我见过最离谱的一个项目硬件团队花了一年半时间做了一个性能峰值很高的加速器流片回来后才发现编译器团队连指令集都还没定稿。后来逼不得已软件团队在驱动层里绕了好多弯牺牲了一半性能才算跑通。这事给我留下的教训是AI芯片从第一天起软硬件就是一起设计的不是两个项目。协同的核心可以拆成三个层面。第一是计算模式的协同你要明确芯片主要跑哪些模型、哪些算子卷积和矩阵乘的比例是多少激活函数用哪种这些直接决定硬件单元怎么排布。第二是数据流的协同权重放哪、激活放哪、中间结果怎么缓存这个决策既要看硬件上SRAM够不够也要看编译器能不能把数据分块调度好。第三是编程接口的协同软件开发者用一个什么样的抽象去看待芯片是一堆寄存器还是一种类似kernel的编程模型会直接决定编译器的实现难度和驱动层的复杂度。换句话说硬件设计不能只考虑峰值算力软件设计也不能只考虑框架兼容。真正决定一颗AI芯片好不好用的是一条完整的链路模型在框架里走完图优化算子被编译器映射到硬件指令指令在运行时被调度到计算单元数据在DMA和片上缓存之间流转。这条链路上任何一环出现断层最终都会表现为“项目延期”和“性能达不到预期”。2. 硬件设计从计算单元到数据流2.1 MAC阵列为什么成为绝对主流几乎所有AI芯片的计算核心都离不开MAC阵列也就是乘累加单元阵列。一个MAC单元执行一次a*bc的操作对于定点数来说这几乎就是AI芯片里最基础的肌肉记忆。以最简单的卷积为例一个输出通道的某一个位置的结果是把输入特征图对应区域的所有通道、所有空间点与卷积核权重做乘加后累加得到的。如果硬件里有一排MAC单元它们可以同时计算不同的输出位置或者同时处理不同的输入通道这就实现了并行。把MAC单元排成二维阵列就形成了我们常说的脉动阵列或运算阵列。设计阵列大小时很多团队会直接参考目标模型的算子形状。比如一个边缘场景的神经网络输入是224x224的图片第一层卷积输出通道可能是64或128那阵列宽度设计成64、128就比较自然太小浪费带宽太大面积又受不了。MAC阵列的工作方式分很多流派。有的设计让权重固定在片上输入数据依次流过阵列这叫权重固定型数据流有的设计让输入特征图固定不动权重在整个计算过程中循环这叫输入固定型数据流。名字听起来玄乎本质上就是决定“到底谁在片上来回搬运谁在片外慢慢取”。我在实际项目中通常会让编译器直接感知数据流类型因为编译器在排调度顺序时必须知道哪些数据是常驻的、哪些是流动的才能决定分块大小。2.2 存储层次和带宽才是真正的性能瓶颈如果说MAC阵列是AI芯片的心脏那存储层次和互连就是血管。芯片不吃饭不行数据喂不进去计算单元再猛也是空转。一个很简单的估算公式峰值算力是P单位TOPS每次计算需要的操作数如果是2字节比如INT8的一个乘加需要1字节激活和1字节权重那么理论上每秒需要从某个地方读到2P字节的数据才能让计算单元满负荷。假设一颗芯片算力是32TOPS那在完全没有数据复用的情况下需要64TB/s的带宽——这个数字比现在最先进的高带宽内存还夸张得多。所以芯片设计者必须用另一招把数据缓存到离计算单元最近的SRAM里让数据在片上被反复使用片外只承担“分块”级别的搬运。片上存储怎么分是个大学问。很多NPU会把SRAM分成权重缓冲、输入缓冲和输出缓冲三块分别对应计算过程中三种不同的数据流。为了让软件效率最大化存储大小和编译器能做的分块策略直接相关。比如权重缓冲只有256KB那编译器一次就只能装载一定大小的权重块算子维度就要按这个尺寸切分。如果硬件团队和编译器团队没有商量好SRAM容量和端口数量后面几乎一定会出现“编译器生成的代码在硬件上跑出大量空泡”的悲剧。另外用DMA直接内存访问做异步搬运也是标准操作。计算单元在算上一块数据的时候DMA可以把下一块数据从DDR搬到片上SRAM。这种双缓冲机制能大幅隐藏访存延迟。但这里有一个软件必须配合的点编译器要把搬运指令和计算指令交错安排好否则硬件设计了这个流水能力软件不调度照样白搭。2.3 硬件设计流程和工具选型硬件设计本身有一套成熟流程规格定义、微架构设计、RTL编码、仿真验证、FPGA原型、逻辑综合、布局布线、流片。RTL编码阶段国内多数团队现在还在用SystemVerilog但我个人非常推荐小规模项目尝试Chisel或SpinalHDL这类基于高级语言描述硬件的方案。它们最大的优势是可以参数化地生成运算阵列改一个并行度参数整个RTL配套的流水逻辑和接口也跟着变。我们之前做过一个模拟项目X用Chisel描述了一个可配置的脉动阵列从32x32改成64x64的算力配置只需要改几个参数省掉了大量复制粘贴RTL的时间。但要注意高级语言生成RTL并不能替代验证。仿真验证仍然要花掉硬件团队60%以上的时间。AI芯片验证最常用的方法是用一个参考模型通常是用Python或C写的算法模型和RTL仿真做对齐。喂同样的数据跑同样的算子比对每次输出的结果。这个参考模型不只是硬件验证要用软件模拟器也要用后面编译器生成的指令跑出来的结果也要跟它比对。所以这个参考模型一定要维护好它是整个软硬件协同的中枢。3. 软件栈设计让硬件真正跑起来3.1 编程接口与运行时软件如何“看到”芯片硬件做出来了软件团队要开始面对一个更现实的问题写程序的开发者用什么样的接口来操作这块芯片。上一代很多加速器的做法是直接给寄存器软件工程师写一堆底层驱动去设置寄存器开启搬运、触发计算。这种做法对固件开发是没问题的但对上层应用开发者来说太痛苦。现在主流做法是给一层抽象类似“命令队列”或者“Kernel任务”的接口。我印象很深的一次经历是我们设计了一个简单的软件抽象应用层把一个算子的输入输出和参数打包成一个任务描述扔进一个异步执行队列硬件跑完任务后通过中断通知完成。就这么一个看似简单的设计把驱动的工作量立刻分成两块。同步版本比较简单提交任务后原地等待但计算单元空转严重异步版本复杂一些需要维护多个任务在队列里并行执行DMA搬运和计算单元的重叠度也更高。我们最终在接口上直接限制了“驱动层必须支持至少四个任务并发”因为测试下来小于这个并发数DMA的搬运延迟根本藏不住。运行时还有一个绕不开的点动态Shape。很多端侧模型输入分辨率不定或者用到了动态batch。硬件设计如果只支持固定Shape那你软件上就得偷偷用padding补到某个对齐的规格浪费计算资源。我们一开始把硬件设计成了支持范围检查、不支持动态地址跳跃的模式后来发现编译器要额外生成很多边界判断代码性能损失很大。如果你做边缘推理芯片我建议从第一天起就想清楚是否要支持动态Shape如果要地址计算单元和相关寄存器得提前留好。3.2 编译器与算子映射从框架落到指令软件栈里最难啃的是编译器。AI编译器做的事情通俗讲三步把深度学习框架比如PyTorch的模型解析成计算图在图上做优化算子融合、常量折叠、布局转换然后把优化后的算子映射到芯片的指令集上。算子融合是性价比最高的一步。卷积后面通常跟着偏置加、激活函数甚至可以跟归一化层合并。卷积如果单独计算要把输出写回DDR再读出来做归一化再写回去做激活。在芯片上这些“中间结果”完全可以留在SRAM里传递。硬件设计时通常会把卷积单元、激活运算单元设计成可以直接拼接的流水。编译器要做的就是识别出这种融合模式生成一条能一气呵成的指令序列。我们之前测试过一个典型的MobileNet类模型做了算子融合之后DDR访问量能减少差不多一半这不是小数目。再往下是算子映射。编译器要把一个卷积算子切分成多个子任务依次搬运输入块、加载权重、执行计算、写回输出。这个切分的逻辑叫“Tiling”。Tiling策略依赖于硬件参数SRAM多大、MAC阵列多大、权重缓冲多大、DMA搬运粒度多大。所以编译器要有一个硬件描述文件把芯片的存储容量、对齐要求、指令编码都写清楚。这里特别要强调的是硬件团队给编译器的参数必须和实际RTL一致。我们有一次改了SRAM容量上限但没有同步更新编译器配置结果生成的代码在仿真环境里频频出现存储越界排查了整整三天最后才发现是参数没同步。3.3 低精度量化性能翻倍的隐形利器芯片能不能用好还有很大程度取决于是否支持低精度计算。INT8已经把推理性能翻过一倍的INT4和INT8混用更是近年来的热门方向。硬件支持低精度的代价不是换个计算单元那么简单而是整个数据通路——包括缓存位宽、DMA搬运宽度、累加器精度——都要跟着调整。最常见的做法是累加器用INT32防止多次乘加累加时溢出然后写回时再根据量化参数缩放到INT8。软件这边量化算法的选择也很讲究。最简单的方案是预先算好每层权重和激活的缩放因子推理时直接按固定点计算。但这个方案对激活分布的鲁棒性差需要在校准集上统计真实分布一般是跑几百张有代表性的图片看每层激活的最大值、最小值或分布的百分位点。更激进的做法是训练感知量化在训练阶段就模拟低精度的舍入误差让模型权重去适应量化噪声。很多团队只把量化当成“训练完以后加一个转换脚本”这是不够的。深度优化的模型通常要用量化感知训练才能保证精度不掉。4. 软硬件协同设计的关键流程4.1 规格定义先从目标模型反推需求做AI芯片最忌讳的就是直接从“我要做一个128TOPS的芯片”开始。正确的做法是从目标模型和应用场景反推。比如说你的芯片要跑一个车载场景的检测网络你先把这套网络的FLOPs算出来再把片外存储带宽算出来再看在目标帧率下每个算子允许消耗多少时间。这里我给一个简化的换算方法。假设一个模型总共需要50GMACs目标帧率是30FPS那每秒要完成1.5TMACs。如果芯片的MAC阵列利用率为60%那理论峰值至少要做到1.5/0.62.5TMACs换算成INT8大概5TOPS。这个数字决定了MAC阵列大小、工作频率和对应的SRAM容量范围。片外带宽则根据每个MAC操作平均要搬多少字节来估。有了初版算力、带宽、存储三个参数才能进入微架构设计。否则直接拍脑袋定规格很容易出现算力足够但带宽不够或者存储小到编译器完全没发挥空间的情况。我们内部习惯把这套推理过程固化成一张“性能预算表”每一层算子列出计算量、访存量、允许时间、片上缓存需求然后对着这张表去调整硬件参数。这张表也是软件团队做性能建模的基础两个团队必须对同一组数字负责。4.2 性能建模与模拟器先模拟再流片软件团队最怕的是硬件还没回来没有地方可以联调。解决办法是做一个周期级或近似周期级的软件模拟器。模拟器的本质是把硬件的计算单元、DMA搬运、缓冲区、命令队列抽象出来按照同样的时序规则计算每个操作要多少个cycle。芯片架构师用这个模拟器验证峰值性能编译器开发者用这个模拟器看指令调度的效果驱动团队甚至可以把它当“虚拟硬件”来跑完整的软件栈。我在模拟器上踩过一个坑模拟器性能模型做得太乐观没有考虑不同DMA通道间的仲裁开销结果实际流片后性能比模拟器预测值低了20%。这个教训后来变成了一个规定模拟器里必须包含资源的竞争模型至少要把总线带宽上限、SRAM端口冲突、中断开销这些因素加进去。宁可模拟器悲观一点也比流片后被现实打脸强。做性能建模时还有一个很实用的指标Roof-line模型。它把芯片性能画成两条边的屋顶斜边代表计算密集区域的“算术强度”和带宽决定性能平台部分代表算力峰值。把目标模型的算子放到这张图上一眼就能看出每个算子是“计算受限”还是“访存受限”。对应地优化手段完全不同计算受限的算子要优化MAC阵列利用率和指令调度访存受限的算子要优化数据复用、调整分块大小、减少DDR访问。这个模型听上去简单但几乎是我做每颗AI芯片最常用的决策工具。4.3 硬件和软件如何在流片前共同推进既然硬件还没回来软件团队能不能提前干活当然能。关键是搭建一个“带着走”的软硬件联调环境。我在参与某跨平台系统设计时就把环境分成了三层。最底层是RTL仿真速度慢但准确适合验证关键指令的时序。中间层是模拟器速度较快适合跑完整算子库的测试。最顶层是FPGA原型速度最快适合把整个编译器和运行时框架跑起来甚至可以跑真实模型。FPGA原型并不是把ASIC设计原封不动烧进去就行。由于FPGA频率低、片上存储容量小很多ASIC设计跑不起来或者跑得太慢。我们通常会把FPGA原型里的大阵列缩小、存储深度降级但保持指令接口和数据流向不变。这样编译器和驱动看到的还是一个“缩小的芯片”能把整套软件流程打通。等ASIC回来了只需要调整性能配置文件大多数代码可以直接复用。这个方法让我避免了“硬件回来才开始写软件”的死亡节奏。4.4 典型四步迭代流程我总结了一个比较通用的软硬件协同迭代流程适用于从零设计一颗AI芯片或者给已有芯片做定义阶段优化。第一步是识别热点算子。从目标应用的代表模型出发统计每个算子占总运行时间的比例通常卷积和矩阵乘会占大头激活函数、归一化等占小头但也不能忽略因为访存可能让它们意外地贵。第二步是为热点算子建立参考实现。用Python或者C写一个朴素版本保证数值正确这是后续所有验证的基准。第三步是把这些算子映射到硬件方案上计算存储和带宽需求然后把结果反馈给微架构设计。第四步是拿性能模型跑一轮仿真看看瓶颈在哪里回头调整硬件参数或软件调度策略。这个循环通常要走好几轮而且越早发现问题代价越低。注意这四步里最容易忽视的是第三步和第四步之间的反馈。硬件架构师倾向于认为瓶颈可以通过编译器优化解决编译器工程师倾向于认为换一个微架构参数就能解决。最后往往是架构师和编译器工程师坐在一起对着性能模型一条一条抠数据流才能真正解决问题。5. 常见问题与排错技巧实录5.1 算力很高跑模型就是慢这个现象背后十有八九是数据搬运问题。我遇到过好几次一个算子的理论计算时间只有50微秒但实际跑了500微秒原因就是权重和输入数据存在DDR里DMA搬运太碎或者没有用双缓冲。排查时先在硬件计数器里看DDR空闲和计算单元空闲的比例。如果计算单元大部分时间在等待DMA优先检查编译器生成的搬运指令和计算指令之间的间隔。另一个常见原因是分块太小每搬运一小块就触发一次长延迟。解决办法是增大分块尺寸尽量让数据在片上多次复用。有些时候还得检查命令队列是否串行执行连续几个任务在等前一个完全结束这样DMA和计算的重叠就完全没了。5.2 编译器生成的代码结果正确但性能很差这种情况通常是编译器没有按硬件微架构的flow来组织指令。比如有些MAC阵列对矩阵的维度有对齐要求编译器没有把小的矩阵问题padding到硬件对齐尺寸导致阵列很多行空转。或者循环嵌套的顺序不匹配数据布局导致每次访问SRAM都发生bank冲突白白增加延迟。这种问题的排查方式是把一条编译后的指令序列和模拟器的周期计数对齐看哪一段指令流水停滞停滞的原因是什么。我们在项目中曾给编译器加了一个“微架构感知的调度后端”专门根据指令依赖关系和SRAM端口占用情况进行指令重排性能提升非常明显。5.3 模拟器性能很好FPGA上跑不快FPGA原型和ASIC之间有几个天然差异。第一是频率低如果你的ASIC目标频率是1GHzFPGA可能只能跑200MHz计算能力直接差5倍。第二是片上SRAM的分布和数量不同FPGA的BRAM配置未必能完整映射你的存储层次。第三是外部DDR接口的行为不同FPGA开发板的DDR带宽不一定和最终芯片一致。所以用FPGA原型验证性能之前先要把性能期望值按频率缩放过而不是直接拿FPGA的帧率除以模型的计算量。如果缩放后仍然明显低于预期再查数据搬运是否阻塞。5.4 快速排查速查表现象可能原因排查方法计算单元利用率低DMA搬运不及时同步执行查看流水线和命令队列中的等待事件跑小模型正常大模型崩溃分块策略导致SRAM溢出检查编译器内存规划和SRAM容量一致性数值错误累加器精度不足或量化参数错误比对参考实现打印每层输出统计性能模型和实测漂移大总线仲裁和访问冲突未建模给模拟器加入资源竞争模型指令集改动后驱动失效寄存器描述手工维护不一致用寄存器描述文件自动生成头文件和驱动代码6. 关于AI芯片软硬件设计我个人的几点体会回头再看我觉得AI芯片的软硬件设计核心不是选多先进的工艺也不是把MAC阵列堆得有多大而是把“模型—算子—指令—数据流”这条链路的每一环都打通。硬件团队和软件团队在项目第一周就要坐在一起共同定指令集、存储层次、编程接口然后一起维护一套性能模型和参考实现这样才不会在流片前后互相抱怨。最后分享一个小技巧不管项目多紧张一定保留一份从训练好的模型到芯片上推理结果的端到端测试哪怕模型很小比如只用两层卷积的小网络。只要这条路能每轮迭代都不堵车整个软硬件系统就永远有一个可以依赖的“生命线”。在芯片还在设计阶段时这条生命线就是软件模拟器加参考模型芯片回来后它就是你最值得信任的验收标尺。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑