最近几年做AI芯片的项目越来越多但很多团队做出来的片子标杆测试很好看一上真实模型就掉链子。问题往往不在某个硬件模块本身而是整条链路上软硬件到底怎么切、怎么协同。我参与过一个边缘侧推理芯片的设计规模不大目标8TOPS算力跑主流检测模型端到端延迟压在30毫秒以内。今天就用这个例子把AI芯片软硬件设计的关键环节拆开聊聊从算力指标怎么定到算子怎么映射再到存储墙和互联设计最后是软件栈怎么配合。内容不涉及具体产品但流程和思路是可以复用的。先说一个基本判断AI芯片设计的核心不是“堆算力”而是“把用户程序跑出高利用率”。8TOPS的峰值算力只是一个包装值真正决定产品好坏的是有效算力——也就是跑实际网络时能稳定达到的利用率。而这个利用率几乎全部是由软硬件切分策略决定的。1. 算力指标不是越高越好关键是能不能用满做AI芯片第一个被问的就是“峰值算力多少”。8TOPS听起来很明确但这个数字背后有一整套假设乘加位宽是INT8还是FP16算不算稀疏加速是单核跑满还是多核并行这些口径不同数字可以差出好几倍。我在项目里定指标的方式是反着来的先明确目标模型和延迟再倒推有效算力。比如要在30毫秒内跑完一个单帧1080p检测主算子大约6.2G次乘加运算。如果不考虑任何开销光算MAC的等效算力需求是30ms处理6.2GMAC折合每秒约206.7 GMAC/s也就是约413 GOPS乘加算2次浮点或定点运算。考虑到实际利用率不可能到100%按设计目标70%利用率反推峰值算力至少要590 GOPS。而如果按INT8实现就是大约8TOPS——我们后来定的“8TOPS”这个宣传数字其实是这么算出来的。为什么强调口径因为很多人拿到一个标称“8TOPS”的芯片以为真能跑出8T的吞吐结果实测下来只有2到3T然后骂硬件不给力。这不全是硬件的锅——通常是你跑的算子形状和硬件排布不匹配或者存储带宽跟不上或者DMA调度和计算没重叠。硬件只是提供了一个潜力上限能不能兑现靠的是整个软硬件系统。还有一个容易忽略的点INT8和FP16的算力标称完全是两回事。如果芯片声称8TOPS一定要问清是INT8还是FP16。FP16的8TOPS做成INT8通常会翻倍取决于架构是否支持而实际部署中很多模型还需要混合精度不能只看一个数字。2. 从卷积到MAC阵列算子映射是需求翻译机AI芯片硬件设计的逻辑起点不是“我要做一个8核处理器”而是“我的算子要在什么形状下跑”。以卷积为例一个标准的3×3卷积滑动窗口在空间和通道维度不断复用数据。如果你拿CPU或GPU的思路去设计很容易陷入“每个线程算一个输出点”的惯性。但AI芯片不是这样——它更适合做的是“把一批输出通道的计算打包给一个MAC阵列”。我们当时选择的映射策略是输出通道做并行切分输入通道做串联累加。一个16×16的MAC阵列硬件上就是一列算16个不同输出通道一行做16个输入通道的乘加累加。这样适配起来非常直接。然后问题变成输入图像切成多少个tile、每次塞多少个输入通道可以既保持阵列满负荷又不会让SRAM爆炸。这里有个实操经验与其写一堆复杂的调度算法不如先做一个简单的“纯卷积峰值测试”——把所有输入输出固定在片上不经过DMA看MAC阵列到底能跑多满。如果这个测试也只能跑到60%那硬件设计大概率有问题如果能跑满说明问题出在数据搬运和同步上。这个测法几乎不花时间却能帮你快速分清瓶颈是计算还是存储。我们还研究过一个细节卷积核的形状是否固定。很多AI芯片为了省事只能跑3×3其他形状全部拆成1×1组合。这在小网络里还勉强能用但遇到大模型、大感受野时拆解带来的数据重排开销会吃掉很多性能。所以我们的阵列设计支持通用矩阵乘的抽象不管是卷积还是全连接还是Transformer里的线性投影都统一映射到矩阵乘。这套方案在工程实现上稍微复杂但收益明显——之后适配新模型时不用改硬件。3. 算力越强存储墙越要命数据搬运才是真正的瓶颈我见过不少团队把MAC阵列做大结果性能反而没涨多少。深挖之后发现问题几乎都出在存储墙上计算一次只要几十个周期但取一次数据要几百个周期。在IC设计里这叫“memory-bound”几乎每个AI芯片都逃不开。片上存储的设计思路我总结为三层递进第一层是寄存器/累加器这是离MAC最近的数据归属速度最快但容量极小。第二层是SRAM也就是常说的片上缓存用来放输入tile、权重tile和输出tile容量在几百KB到几MB之间。第三层是片外DRAM或者HBM这个容量大但延迟和带宽都不便宜。关键设计动作是“数据复用”。我用一个简单的估算来说明假设SRAM带宽是256字节/周期主频1GHz那每秒能从SRAM读出256GB数据。如果8TOPS的MAC阵列跑满每周期要消费64字节的输入和64字节的权重才能喂饱。这组数字虽然粗糙但反应了一个关系MAC的算力增长是线性的存储带宽的工程代价却是超线性的。你多塞一组MAC就要多铺一堆SRAM读写端口最后片上布线都成了问题。实际项目里我们采用了“tiling double buffering”的组合策略。tiling就是把大特征图切成小块尽量让一个小图在SRAM里完成所有计算再搬走double buffering就是让DMA搬运下一块数据的同时MAC阵列计算当前这块。这两个手段叠加大约能把搬运时间隐藏掉60%以上。剩下的不能隐藏的就是你MAC利用率的天花板。还一个容易被忽视的地方输出数据的写回。大多数人只关心输入怎么搬进来忽略输出怎么搬出去。如果网络很宽比如输出通道很多写回带宽不够照样阻塞流水线。我们吃过一次亏某个卷积层的输出tile特别大结果DMA读和写同时抢带宽整体性能掉了一半。后来在调度里加了读写通道的优先级控制才算解决。3.1 为什么说“算得不够快”往往是“搬得不够快”我用一个生活化的比方来解释这个道理。假设你是一个厨师炉子MAC阵列一分钟能炒10道菜但传菜员DMA一分钟只能端过来5道菜的原料那你再怎么提高厨艺实际出菜速度就是5道。AI芯片的很多性能问题不是“计算不够快”而是“原料供应跟不上”。这个“原料供应”包含两部分一是从片外DRAM到SRAM的搬入二是SRAM的访问带宽。前者是大动脉后者是毛细血管。两者任何一个塞住MAC都得空转。设计阶段一定要把“数据流图”画出来标注每个算子的输入大小、输出大小、复用次数然后逐级核对带宽预算。宁可早一点发现瓶颈并调整拍频也别等流片回来再抓瞎。4. 片上互联不只是布线它决定了多核能不能一起干活做到这一步很多项目会卡在“多核协同”上。芯片内部通常有多个计算核每个核都有自己的MAC阵列和SRAM核与核之间要交换数据——这就是片上网络NoC的职责。我看到很多小团队在这一步犯难要么懒得设计直接用一根共享总线便宜省事但多核同时访问时带宽互相打架要么设计得太重用了复杂的mesh网络但实际负载根本用不上那么多路径。AI芯片的互联设计不应该追求通用系统的灵活性而要贴合自己的并发模型。我们的设计目标很朴素让每个核的大部分数据在本地SRAM里自给自足只有必须跨核的数据才走NoC。所以NoC的压力被大幅降低带宽需求实际上很小。为了让这个目标能落地算子切分时就要考虑“每个核的输出能否直接用在后处理里”尽量减小跨核通信的频次。还有一个细节是同步开销。多核并行最怕“等你等它”一个核算完要等另一个核的数据才能继续。如果同步周期太长并行度再高也白搭。我们在硬件上加了barrier同步单元让核间的等待控制在几十个周期内这样实际并行效率能到85%以上。开发时也请软件同事提前介入让他们清楚知道核间同步的粒度写算子时能主动避免频繁的全局同步。再提一句布线布局。AI芯片面积有限SRAM占了一大块走线的长度和拥塞程度直接决定最高主频。如果为了好看把NoC设计得极其规整但导致关键路径过长主频上不去那还不如降复杂度保频率。硬件设计始终是平衡的艺术没有放之四海而皆准的方案。5. 软件栈不是插线板编译器、算子库和运行时必须和硬件一起长如果只把软件当成“事后的解释器”这个项目十有八九会延期。AI芯片的软硬件设计“软”字占一半而且必须是同步设计——硬件还在写RTL的时候软件团队就应该开始搭编译器框架和算子库了。算子库的角色是“让常见算子直接跑得不错”。我们为每个常用算子手写了优化版本比如3×3卷积、1×1卷积、深度卷积、矩阵乘、归一化、池化等。手写算子的好处是你能精确控制数据排布和循环变换。但手写也有代价——新算子层出不穷不可能全都人工优化。所以算子库的定位应该是一个“含有一组高质量模板”的集合而不是一个封闭的静态的库。编译器的任务则是“把新算子压进模板的审美体系里”。它做两件事图优化和指令映射。图优化负责算子的融合、改写和内存复用指令映射负责把算子的循环结构映射到MAC阵列的指令序列。这套流程有点像“翻译官”硬件是个只懂几种固定句式的老外编译器要把用户的任意算法转换成老外能听懂的短句。这里有一个很关键的经验编译器不要只依赖自动调优因为自动搜索空间的边界往往就是硬件架构的边界搜索不到就是搜索不到。我的做法是手动调优做主路径自动搜索只负责在边界附近小幅扰动。举个例子固定矩阵乘的映射方式为“输出通道并行、输入通道累加”然后让编译器在tile大小、缓冲位置、读写顺序上自动搜索。这样既能保证线上稳定又能榨出最后几个百分点的性能。运行时runtime也不能忽略。它调度DMA、管理多核分配、控制功耗和温度。如果你把硬件设计成“必须由运行时精确配置才能工作”的状态那运行时复杂度和硬件耦合度会急剧上升反而容易出错。我倾向于硬件的默认状态就能工作运行时只做优化性决策这样软件出了bug也不会导致硬件完全不可用。5.1 一个“算子先做还是硬件先定”的争议怎么破项目里经常会有这种争论硬件团队说“你先把算子需求发我”软件团队说“你先把寄存器模型发我”两边互相等。拆解这个问题关键是分清楚哪些需求是长期不变的“锚点”哪些是可以妥协的“变量”。锚点是那些在绝大多数模型中都有、且变化不大的计算模式卷积、矩阵乘、逐元素操作、池化、归一化。这些必须在硬件设计的第一周就定下来连MAC阵列的尺寸、累加器的位宽都要围绕它们来设计。变量则是具体网络里的特殊层比如动态形状的分支、非常规的激活函数。这些可以走编译器标量处理或者CPU核去兜底不需要立刻定死。我们的做法是用一版“虚拟指令集时钟级行为模型”做软硬件接口。硬件团队按这个接口做RTL软件团队按这个接口写模拟器上的算子原型。两边不互相等但接口定得很细致连每次DMA搬运的配置字段都提前约束好。这样到仿真阶段几乎不会出现“底层怎么不按约定实现”的争吵。6. 实测阶段最重要的三件事先测边界再测融合最后测长尾设计和验证做完终于到了FPGA原型或流片回来。这个时候最容易犯的错误是一上来就整模型跑benchmark。我建议反过来先做三个层层递进的实测。第一测边界每个算子单独跑用峰值数据把MAC阵列打满测真实的最大算力。如果这个数字达不到设计值先别怪软件回头查流水线、查memory端口冲突、查阵列的复位逻辑。边界测稳定了才有资格谈后续。第二测融合把两个相邻算子连接起来比如卷积归一化测一下数据是否能先做个融合减少一次tile搬运。这里最容易暴露DMA调度和计算流的叠合能力。我在项目里曾把融合后的算子性能提升了30%靠的就是把归一化的缩放系数直接融进卷积的偏置项省掉了一次整层的读写。第三测长尾跑真实的完整模型尤其是那种分支多、shape不规则的模型。测试时一定要记录每一层实际消耗的周期和利用MAC周期的比例而不是只看整个模型跑完的毫秒数。很多性能黑洞出现在看似不起眼的小层上比如reshape、split、concat这些“操作型算子”它们在mac阵列上可能根本不做计算但数据搬移的时间却非常可观。这组实测做完基本能定位出整个系统的真实瓶颈再决定是优化编译器、调整runtime还是修改硬件。6.1 容易忽略的“memory layout”坑最后分享一个我们踩了很久的坑希望后来者绕开数据排布方式memory layout。同一个特征图是按“通道-高-宽”存还是按“高-宽-通道”存对某些算子的亲和度完全不同。卷积喜欢连续通道逐元素操作喜欢连续元素。如果你的算子库内部数据排布不统一那每次转换都是一次隐形的数据搬运。我们前期因为图省事让不同算子各自决定layout结果引入了大量转换层。后来统一成模型推理时全部用“通道优先块化”的方式存储权重在编译期重排成硬件友好的格式而不是运行期转换。这个决定让整体性能提升了接近两成也大大简化了runtime的逻辑。AI芯片的软硬件设计不是一个“先硬件后软件”的串行流程更像一套骨架和肌肉同步发育的过程。算力指标要反推算子映射要精算存储和互联要围绕数据流来塑形软件栈要提前进场而实测时还得有耐心一轮轮去抠细节。这个领域没有捷径但每一个环节想清楚了做出来的东西会明显比对手更“耐跑”。我自己的最大体会是把“效率”当作设计的第一性指标而不是“规格”。规格是给市场听的效率才是你和客户真正长期相处的东西。