
1. 从一次流片失败聊起AI芯片软硬件协同到底难在哪前两年跟一个做推理加速卡的朋友吃饭他跟我吐槽说他们第一版芯片回来之后实测性能只有仿真数据的六成功耗还超了预算的三成。问题出在哪不是RTL写错了也不是后端布局布线崩了而是算法团队在模型侧做的一次算子融合优化硬件端根本不知道——软件以为硬件会按某个顺序执行硬件按自己的流水线节奏跑两边对不上DMA搬运次数比预期多了一倍带宽直接成为瓶颈。这件事特别典型。AI芯片跟传统SoC最大的区别就在于它的价值几乎完全取决于软硬件配合的紧密度。你做一个通用CPU指令集定好了编译器生态跟上就行硬件微架构对上层基本透明。但AI芯片不一样它的指令集、数据流、存储层次、甚至片上网络的拓扑结构都是为特定类型的计算模式卷积、矩阵乘、注意力机制量身定制的。一旦软件栈没有把硬件的特性吃透或者硬件设计时没有充分考虑软件调度的灵活性性能就会断崖式下跌。所以这篇内容我想聊的是AI芯片的软硬件设计到底应该怎么协同考虑。不是泛泛地讲“软硬件协同设计很重要”这种废话而是从架构选型、数据流设计、编译器配合、算子映射、存储层次规划这几个具体维度把我在实际项目和跟同行交流中积累的经验和踩过的坑尽量讲透。适合正在做AI加速器架构设计的工程师、做推理框架优化的算法工程师以及想理解AI芯片全栈设计逻辑的技术管理者。不管你是刚入行还是已经做过几颗芯片应该都能从中找到一些可以直接参考的思路。2. 架构选型为什么你的第一版AI芯片不该追求通用性2.1 从应用场景倒推架构而不是反过来我见过太多团队在立项时的第一反应是“我们要做一个能支持所有主流模型的AI加速器。”这个想法听起来很合理但实际上是灾难的开始。为什么因为“支持所有模型”意味着你的硬件必须足够灵活而灵活性在芯片设计里是用面积、功耗和频率换来的。你为了支持各种奇怪的算子加了一堆可配置的通路和缓冲区结果就是核心计算单元的利用率上不去PPAPower, Performance, Area全面崩盘。正确的做法是先锁定一到两个核心应用场景把这类场景的计算模式吃透然后针对性地设计数据流和计算阵列。比如你做的是边缘侧的视觉推理那主要负载就是卷积和少量的全连接卷积核尺寸集中在3x3和1x1输入分辨率不会太大。这种情况下你可以放心地做一个固定尺寸的脉动阵列把权重缓存和特征图缓存的比例调到一个经过验证的黄金值不需要考虑太多动态重构的问题。反过来说如果你做的是云端训练芯片那矩阵乘的尺寸变化范围极大从几十到几千都有可能这时候你就必须考虑分块策略的动态可配置性以及片间互联的带宽扩展能力。这两种场景对硬件的要求几乎是正交的想用一颗芯片通吃结果往往是谁都吃不好。2.2 数据流的选择决定了整个芯片的骨架AI芯片设计里有一个核心决策数据流Dataflow怎么选。常见的有权重固定Weight Stationary、输出固定Output Stationary、行固定Row Stationary这几大类。这个选择不是学术问题它直接决定了你的片上存储怎么划分、PE阵列怎么连接、甚至编译器要怎么写。我拿一个实际案例来说明。假设你要做一个16x16的脉动阵列做3x3卷积。如果选权重固定那每个PE里都要存一份权重权重从片外加载一次之后就不动了特征图在阵列里流动。这种方案的好处是权重复用率极高适合batch size比较大的推理场景。但缺点是如果模型换了权重全变了你得重新加载整个阵列的权重这个加载时间在切换模型时是不可忽略的。如果选输出固定那每个PE负责计算一个输出像素的部分和特征图和权重都在阵列里流动。这种方案对卷积核尺寸变化比较友好但部分和需要在PE之间传递对互联网络的压力更大。实操心得如果你不确定选哪种数据流可以先在Excel里做一个简单的建模把你要支持的主要算子的数据复用次数算出来然后对比不同数据流下的片外访存量。这个建模不需要多精确但能帮你快速排除明显不合理的方案。2.3 指令集设计别让编译器替你擦屁股很多AI芯片团队在定义指令集时喜欢把指令做得特别“语义化”比如一条指令直接完成一个卷积层。这种CISC风格的指令看起来很美编译器简单但实际落地时会发现两个致命问题一是硬件解码逻辑极其复杂时序很难收敛二是灵活性极差遇到一个没预料到的算子组合就得改硬件。我的建议是指令集尽量RISC化把复杂的控制逻辑交给编译器。硬件只提供最基础的原子操作加载、存储、矩阵乘、激活、池化。然后通过编译器的调度把这些原子操作组合成完整的算子。这样做的好处是硬件设计简单、频率容易做高而且新算子可以通过软件快速支持不需要重新流片。当然这要求你的编译器团队足够强。但话说回来做AI芯片本来就是一个软硬件深度耦合的事情编译器弱的话硬件再强也发挥不出来。3. 存储层次设计AI芯片真正的性能瓶颈所在3.1 为什么计算单元不是瓶颈访存才是我经常跟人说AI芯片的设计里计算单元的设计是最简单的部分。你做一个256x256的MAC阵列只要面积和功耗允许堆上去就行了。真正难的是怎么让数据及时地送到这些计算单元面前。有一个经验公式在典型的AI推理负载中每做一次MAC运算需要从片外读取大约0.1到1字节的数据。这意味着如果你的芯片算力是100 TOPS那访存带宽需求就是10到100 TB/s。这个数字是什么概念目前最先进的HBM3e单堆栈带宽大约是1.2 TB/s你需要几十个堆栈才能满足。这显然不现实。所以AI芯片设计的核心矛盾就是算力增长的速度远远快于访存带宽增长的速度。解决这个矛盾的唯一办法就是提高数据复用率让数据在片内多停留、多复用减少对片外带宽的依赖。3.2 片上存储的分层策略一个典型的AI芯片存储层次是这样的层级典型容量访问延迟带宽主要用途寄存器文件几十KB1 cycle极高PE内部操作数片上SRAM几MB到几十MB几cycle很高特征图/权重缓存片外DRAMGB级别几百cycle有限模型参数/中间结果设计的关键在于怎么划分SRAM的用途。是全部给权重还是全部给特征图还是按比例分这个比例不是拍脑袋定的要根据你的目标算子的算术强度Arithmetic Intensity来算。算术强度 计算量 / 访存量。以3x3卷积为例假设输入通道64输出通道64特征图尺寸56x56。计算量是56x56x64x64x3x3 ≈ 1.16亿次MAC。访存量呢输入特征图是56x56x64 ≈ 20万字节权重是64x64x3x3 ≈ 3.7万字节。算术强度大约是1.16亿 / 24万 ≈ 483 MAC/Byte。这个强度很高意味着数据复用空间很大你可以把权重和部分特征图放在SRAM里大大减少片外访问。但如果是1x1卷积算术强度会低很多因为输入通道和输出通道之间的复用减少了。这时候你就需要更大的SRAM来缓存特征图或者接受更高的片外带宽需求。3.3 双缓冲和预取隐藏访存延迟的必备技巧即使你有了足够的SRAM如果访存和计算是串行的那计算单元还是会有大量空闲时间。解决办法就是双缓冲Double Buffering把SRAM分成两块一块用于当前计算另一块用于预取下一批数据。当计算单元在处理当前数据时DMA引擎在后台把下一批数据搬进来。这个技术听起来简单但实操中有几个坑缓冲区大小要匹配计算时间。如果预取的数据量太大DMA搬不完计算单元就得等如果太小DMA频繁启动开销又上去了。一般建议预取缓冲区的大小等于计算单元处理一个完整tile所需的数据量。同步机制要设计好。双缓冲需要硬件信号量或者中断来协调DMA和计算单元的节奏。如果同步没做好很容易出现数据覆盖或者死锁。边界情况要处理。当特征图尺寸不能被tile大小整除时最后一个tile的数据量会少一些预取逻辑要能正确处理这种不规则的边界。注意事项双缓冲的SRAM面积开销是单缓冲的两倍。如果你的SRAM本来就紧张可以考虑用更细粒度的流水线来替代比如把一次大搬运拆成多次小搬运用更小的缓冲区实现类似的效果。4. 编译器与硬件的握手算子映射和调度策略4.1 算子映射不是简单的“一对一”编译器团队和硬件团队最容易出现分歧的地方就是算子映射。硬件团队觉得“我提供了这么灵活的PE阵列你编译器应该能自动映射所有算子”编译器团队觉得“你的硬件限制太多很多算子根本映射不上去”。这个矛盾的根源在于硬件设计时没有充分考虑算子的多样性。比如你设计了一个专门优化3x3卷积的脉动阵列但模型里突然出现了一个5x5的深度可分离卷积你的阵列尺寸对不上怎么办要么硬件支持动态重构要么编译器得把5x5拆成多个3x3来算但这样会引入额外的数据搬运和部分和累加开销。我的经验是在硬件设计早期就让编译器团队介入把目标模型集合里的所有算子列出来逐个分析映射方案。如果某个算子映射效率特别低要么改硬件要么在模型侧做等价变换比如把5x5卷积用两个3x3卷积近似精度损失在可接受范围内。4.2 调度策略先分块再流水编译器的调度策略直接决定了硬件的利用率。一个典型的调度流程是这样的算子融合把连续的卷积、BN、激活融合成一个计算图节点减少中间结果的写回。分块Tiling把大的特征图和权重切分成适合SRAM容量的小块。流水线编排确定每个块的加载、计算、写回顺序尽量让DMA和计算单元并行工作。指令生成把编排好的操作序列翻译成硬件指令。这里面最考验功力的是分块策略。块太大SRAM放不下块太小数据复用率低片外访存多。我一般会建议用一个简单的搜索算法以SRAM容量为约束以片外访存量最小为目标遍历可能的块尺寸组合找到一个近似最优解。这个搜索不需要很精确因为实际运行时还有双缓冲和预取来兜底。4.3 量化与编译器的配合现在做AI芯片不支持量化基本没法玩。但量化不只是硬件加个INT8乘法器那么简单它需要编译器和硬件的紧密配合。举个例子模型量化后激活值的范围是动态变化的每一层都不一样。如果硬件只支持固定的缩放因子那编译器就得在每层计算前插入一个缩放操作这会增加额外的指令开销。更好的做法是硬件支持动态缩放编译器把每层的缩放因子作为参数传给硬件硬件在累加完成后自动做缩放。这样既减少了指令数又提高了精度。实操心得量化的精度损失往往不是均匀的某些层对量化特别敏感。我一般会建议在编译器里加一个敏感度分析的pass自动识别出哪些层需要保留更高精度比如INT16哪些层可以用INT8甚至INT4。这个分析可以基于权重的分布和激活值的动态范围来做不需要跑完整的训练。5. 实操案例一个边缘推理芯片的软硬件协同设计过程5.1 需求定义和约束分析假设我们要设计一颗用于智能摄像头边缘推理的AI芯片目标场景是实时目标检测模型是YOLO系列的一个轻量级变体。约束条件如下功耗不超过2W帧率30fps 1080p模型大小不超过10MB精度mAP下降不超过2%从这些约束出发我们可以倒推出算力需求。YOLO轻量级变体大概需要2-3 GOPS每帧30fps就是60-90 GOPS。考虑到实际利用率通常50%-70%峰值算力需要做到150 GOPS左右。这个算力水平用INT8精度实现功耗控制在2W以内是可行的。5.2 硬件架构的确定基于上面的分析我们确定以下架构参数计算阵列128个INT8 MAC单元分成8x16的脉动阵列片上SRAM总共2MB其中1.5MB用于特征图缓存0.5MB用于权重缓存数据流权重固定因为YOLO的权重在推理过程中不变可以一次性加载指令集RISC风格基础指令包括LOAD、STORE、MAC、ACT、POOL量化支持INT8和INT16混合精度敏感层用INT16这个配置下理论峰值算力是128 MAC x 2乘加各算一次x 1GHz 256 GOPS。实际利用率按60%算大约150 GOPS满足需求。5.3 编译器栈的实现编译器这边我们基于TVM做了一些定制化的工作前端支持ONNX和TFLite模型导入图优化算子融合、常量折叠、死代码消除量化训练后量化PTQ支持逐层敏感度分析调度基于SRAM容量的自动分块和流水线编排后端生成我们自定义的指令序列这里面的关键点是调度器。我们实现了一个基于动态规划的调度算法以片外访存量最小为目标自动搜索最优的分块和流水线方案。实测下来相比手工调优自动调度在大多数算子上能达到90%以上的性能只有少数特别复杂的算子需要人工干预。5.4 实测结果和调优过程第一版芯片回来之后实测性能是120 GOPS功耗1.8W基本达标。但帧率只有25fps离30fps还差一点。排查后发现瓶颈在DMA搬运上权重加载的时间比预期长了20%因为权重缓存的bank冲突比较严重。解决办法是调整权重缓存的bank映射策略把经常同时访问的权重块映射到不同的bank。这个改动在RTL层面只改了几行代码但效果立竿见影权重加载时间降到了预期水平帧率也上到了32fps。这个案例说明了一个道理AI芯片的性能调优往往不是计算单元的问题而是存储和搬运的问题。你在架构设计阶段就要把存储层次和DMA通路考虑清楚否则后期调优会非常痛苦。6. 常见问题与排查技巧实录6.1 仿真性能很好实测性能腰斩这是最常见的问题原因通常有以下几个问题现象可能原因排查方法计算单元利用率低DMA带宽不足或调度不合理用性能计数器看DMA和计算单元的活跃周期比功耗超标时钟树功耗过高或SRAM漏电分别测量动态功耗和静态功耗精度不达标量化误差累积或累加器位宽不够逐层对比浮点和定点输出帧率不稳定双缓冲同步有问题或DMA冲突抓取DMA和计算单元的时序波形6.2 编译器生成的代码效率低编译器生成的代码效率低通常是因为调度器没有考虑到硬件的微架构细节。比如硬件支持双缓冲但编译器没有把加载和计算编排成流水线导致两者串行执行。解决办法是在编译器里加入硬件感知的调度pass把硬件的双缓冲、预取、bank冲突等特性作为约束条件纳入调度算法。这个工作比较繁琐但一旦做好性能提升非常明显。6.3 模型换了之后性能波动大这个问题在支持多模型的芯片上特别常见。原因是硬件的数据流和存储层次是针对某个特定模型优化的换了一个计算模式不同的模型硬件利用率就会下降。缓解办法有两个一是硬件设计时留一定的灵活性比如支持多种数据流模式二是编译器侧做更激进的算子变换把新模型的计算模式尽量往硬件擅长的方向靠。但根本上这个问题没有完美的解决方案只能通过明确目标场景来规避。避坑技巧在芯片立项时一定要明确“不做什么”。与其做一个什么都能跑但什么都跑不快的芯片不如做一个在特定场景下性能极致的芯片。AI芯片的竞争已经过了拼通用性的阶段现在拼的是场景适配度。7. 一些关于软硬件协同的个人体会做了几年AI芯片之后我最大的体会是硬件团队和软件团队必须坐在一起办公。这不是开玩笑物理距离真的会影响协作效率。硬件工程师在定义寄存器接口时编译器工程师在旁边就能立刻反馈“这个接口对调度不友好”编译器工程师在写调度算法时硬件工程师能马上指出“这个操作硬件不支持”。另一个体会是不要过早优化。我见过一些团队在架构设计阶段就纠结于某个算子的映射效率花了几个月时间反复修改结果芯片回来发现真正的瓶颈在别的地方。正确的做法是先做一个端到端的原型把整个软硬件流程跑通找到真正的瓶颈然后再针对性地优化。最后仿真环境一定要尽可能接近真实硬件。很多团队用理想化的仿真环境做性能评估忽略了DMA延迟、bank冲突、时钟树偏差这些实际因素结果流片回来发现性能对不上。我的建议是在仿真环境里加入这些非理想因素的模型哪怕精度不高也能帮你提前发现大问题。这个领域变化很快新的模型架构、新的存储技术、新的封装方式都在不断涌现。但软硬件协同设计的基本原则是不变的让数据流动得尽可能少让计算单元尽可能忙。抓住这个核心剩下的就是工程上的不断迭代和打磨了。