
1. AI芯片软硬件协同设计的核心逻辑1.1 为什么软硬件必须一起设计做AI芯片这行的人都有一个共识硬件堆算力不难难的是让软件能把硬件的算力真正吃满。我见过太多团队流片回来的芯片理论算力标称几百TOPS实际跑主流模型连三成都不到问题几乎都出在软硬件脱节上。传统芯片设计流程是硬件先定义好指令集和微架构软件团队再基于手册去写编译器和算子库。这条路在通用CPU时代走得通因为CPU的指令集足够灵活编译器优化空间大。但AI芯片不一样它的架构高度专用化矩阵乘法单元、向量处理单元、片上存储层次怎么排布直接决定了哪些算子能高效执行、哪些会撞墙。如果硬件设计阶段不考虑软件怎么映射等流片回来再让软件去适配基本等于给自己挖坑。所以现在主流做法是软硬件协同设计英文叫HW/SW Co-Design。核心思路是在架构定义阶段就让编译器、算子库、甚至模型部署团队介入用真实的模型负载去驱动硬件参数的选择。比如片上SRAM要多大不是拍脑袋定的而是拿目标模型的计算图和中间张量大小去反推。再比如数据通路的位宽也要看量化方案能做到什么程度。1.2 从模型到芯片的映射链路理解这条链路是做好协同设计的前提。一个典型的AI模型从训练框架导出到芯片上跑起来中间要经过好几层转换。第一层是图优化层。训练框架导出的计算图往往包含大量冗余节点比如连续的reshape、transpose、常量折叠等。这一层要做算子融合把convbnrelu合成一个复合算子减少访存次数。第二层是算子映射层。每个融合后的算子要映射到硬件上具体的计算单元。这里的关键是tiling策略——大矩阵乘法要切成多大的块才能既填满计算阵列又不溢出片上缓存。这个切分参数直接跟硬件的PE阵列尺寸、寄存器文件大小、SRAM带宽挂钩。第三层是指令调度层。确定好算子映射后要生成具体的指令序列安排数据搬运和计算的流水线。这里最怕的是流水线气泡计算单元等数据或者数据到了计算单元闲着。第四层是运行时层。负责内存分配、任务调度、多核同步等。这一层跟硬件的DMA引擎、中断控制器、多核互联拓扑强相关。这四层里任何一层跟硬件设计不匹配性能就会打折扣。我个人的经验是第一层和第二层的优化空间最大但也最依赖对硬件细节的了解。编译器团队如果不知道SRAM的bank划分方式就做不出好的tiling策略。1.3 协同设计中的关键权衡软硬件协同设计本质上是一系列权衡。我列几个最典型的。灵活性与效率的权衡。硬件越专用能效越高但能支持的算子种类越少。如果芯片只支持3x3卷积那遇到5x5卷积就得拆成多个3x3效率下降。但如果做成完全可编程的又失去了专用加速的意义。常见的折中是支持一组可参数化的算子模板比如卷积的kernel size、stride、dilation都可配但计算阵列的结构固定。片上存储容量与面积的权衡。SRAM越大能缓存的中间张量越多访存越少但面积和功耗也越大。这里有个经验公式片上SRAM容量应该至少能放下目标模型最大层的输入和输出张量之和。如果放不下就要考虑用HBM或DDR做二级缓存但带宽和延迟会差一个数量级。数据位宽与精度的权衡。INT8量化已经是标配INT4也在逐步成熟。位宽减半理论算力翻倍但精度损失需要算法侧用量化感知训练来补偿。硬件设计时要考虑是否支持混合精度比如权重INT4、激活INT8这需要计算单元有灵活的位宽配置能力。多核互联拓扑的权衡。核多了互联网络复杂面积和功耗上去了。但核少了并行度不够。常见的拓扑有mesh、ring、crossbar。mesh扩展性好但跳数多延迟大crossbar延迟低但面积随核数平方增长。一般8核以下用crossbar16核以上用mesh或ring。2. 硬件设计的关键模块拆解2.1 计算阵列的设计要点计算阵列是AI芯片的心脏。目前主流有两种架构脉动阵列和SIMD向量阵列。脉动阵列的代表是TPU风格的设计。数据从阵列边缘流入在PE之间逐拍传递每个PE做一次乘加。这种结构的优势是数据复用率高权重可以预加载到PE里激活值在阵列中流动。但缺点是灵活性差遇到非矩阵乘的算子就效率很低。设计时要考虑阵列尺寸比如128x128还是256x256。阵列越大单周期算力越高但tiling的粒度也越大小算子填不满。SIMD向量阵列更灵活每个周期可以执行向量乘加、向量超越函数等。适合处理element-wise算子和归一化层。但能效比脉动阵列低因为数据复用需要靠寄存器文件和缓存来支撑。我个人的经验是纯脉动阵列适合云端推理这种大batch场景而端侧芯片往往采用脉动向量混合架构用脉动阵列处理卷积和全连接用向量单元处理激活、池化、归一化等。设计计算阵列时有个容易忽略的点PE之间的互联延迟。如果阵列太大数据从一端传到另一端需要多个周期这会影响流水线效率。解决办法是分段流水或者用H树形广播。2.2 片上存储层次的设计AI芯片的存储层次通常分三级寄存器文件、片上SRAM、片外DRAM。寄存器文件在PE内部容量最小但带宽最高。设计时要考虑每个PE需要多少个寄存器来缓存权重和激活值。一般权重寄存器需要能放下一个tile的权重激活寄存器需要能放下对应的输入。片上SRAM是中间层容量从几百KB到几十MB不等。关键参数是bank数量和位宽。bank数量决定了并行访问能力位宽决定了单次能取多少数据。比如64个bank每个bank 32位那单周期可以取256字节。这个带宽要跟计算阵列的需求匹配。如果计算阵列每周期需要512字节那SRAM带宽就不够会成为瓶颈。片外DRAM是最后一级容量大但延迟高。设计时要考虑DDR控制器或HBM控制器的带宽和调度策略。AI负载的访存模式往往是大块连续读所以DRAM的burst length要设大一些减少命令开销。这里有个实操心得片上SRAM的bank划分要跟计算阵列的tiling策略对齐。如果tiling是按行切分那SRAM的bank也应该按行组织这样一次可以取出一整行数据避免bank冲突。2.3 数据通路的位宽与精度设计数据通路的位宽设计直接决定了芯片的算力和能效。目前主流是INT8部分场景用INT4或FP16。INT8的设计相对成熟。乘法器可以用8x8的阵列累加器用32位防止溢出。关键是要支持per-channel量化因为不同通道的权重分布差异很大per-tensor量化会损失精度。硬件上需要为每个通道存储一个scale和zero-point这增加了存储开销但精度提升明显。INT4是趋势。4位乘法器面积只有8位的一半左右算力翻倍。但INT4的精度损失需要算法侧配合。常见做法是权重INT4、激活INT8这样既减少了权重存储和带宽又保证了激活的精度。硬件上需要支持混合精度的乘加即4位权重乘以8位激活结果累加到32位。FP16主要用于训练和科学计算场景。FP16乘法器面积比INT8大不少但动态范围好。有些芯片同时支持INT8和FP16通过配置寄存器切换。设计位宽时要注意累加器的位宽。INT8乘INT8的结果是16位但累加多个乘积后可能溢出。一般用32位累加器支持至少65536次累加不溢出。如果是INT4乘INT8结果12位累加器可以小一些但为了统一还是用32位。2.4 多核互联与任务调度当单核算力不够时就要多核。多核互联的设计决定了芯片的扩展性。常见的互联拓扑有几种。Crossbar是全连接任意两个核之间一跳可达延迟最低但面积随核数平方增长适合4到8核。Mesh是二维网格每个核只跟上下左右邻居相连扩展性好但跳数多适合16核以上。Ring是环形面积小但延迟随核数线性增长。任务调度方面静态调度适合规则负载比如卷积层可以预先切分好分配给各核。动态调度适合不规则负载比如检测模型里的ROI Align每个核处理的数量不一样。硬件上需要支持核间中断和同步机制。我踩过的一个坑是多核共享SRAM的bank冲突。如果两个核同时访问同一个bank就会 stall。解决办法是地址交错把连续地址映射到不同bank这样不同核访问不同地址段时不会冲突。3. 软件栈的设计与优化3.1 编译器后端的关键优化编译器是连接模型和硬件的桥梁。AI芯片的编译器后端跟通用CPU编译器差别很大核心任务是算子映射和内存分配。算子映射的核心是tiling。以卷积为例输入特征图是HxWxC卷积核是KxKxCxN。要把它切分成适合硬件计算阵列的小块。切分参数包括tile的高度、宽度、输入通道数、输出通道数。这些参数要满足几个约束tile的输入和输出能放进片上SRAMtile的计算量能填满计算阵列tile之间的数据复用率尽量高。内存分配的核心是生命周期分析。计算图里每个张量都有定义和使用生命周期有重叠的不能共享内存。编译器要算出每个张量的生命周期然后做内存复用。好的内存分配能把峰值内存降低一半以上。指令调度方面要尽量让数据搬运和计算重叠。硬件上通常有DMA引擎负责搬运计算阵列负责计算。编译器要生成指令让DMA提前把下一块数据搬进来同时计算阵列处理当前块。这就是双缓冲。3.2 算子库的手写优化技巧虽然编译器能自动生成代码但关键算子还是需要手写汇编或intrinsic来榨取最后一点性能。手写算子的第一步是确定数据流。以矩阵乘法为例有输出固定、权重固定、输入固定等不同数据流。输出固定是每个PE负责一个输出元素累加所有输入和权重的乘积。权重固定是把权重预加载到PE里输入在阵列中流动。输入固定类似。选择哪种数据流取决于矩阵的形状和硬件结构。第二步是寄存器分配。PE内部的寄存器数量有限要精打细算。权重寄存器、输入寄存器、累加器寄存器要合理分配。如果寄存器不够就要把部分数据放到SRAM但会增加访存。第三步是流水线编排。计算和访存要交错进行避免流水线气泡。比如在计算当前tile的同时用DMA搬运下一个tile的数据。我个人的经验是手写算子性能通常比编译器自动生成的高20%到50%但开发工作量大。所以策略是核心算子手写长尾算子靠编译器。3.3 运行时与驱动层的设计运行时负责模型加载、内存管理、任务调度、多核同步等。这一层跟硬件寄存器打交道最多。模型加载时要把权重从DRAM搬到片上SRAM或寄存器。这里要考虑权重的排布格式。如果硬件计算阵列需要权重按特定顺序排列运行时就要做格式转换。这个转换可以在线做也可以离线做。离线做省时间但占存储在线做省存储但费时间。内存管理方面运行时通常维护一个内存池避免频繁的malloc和free。AI负载的内存分配模式比较规律可以预分配大块内存然后自己管理。任务调度方面运行时要把计算图的任务分配给各核。对于多核芯片要考虑负载均衡。如果某个核的任务特别重其他核就要等它。解决办法是把大任务切分成小任务动态分配给空闲的核。多核同步方面硬件通常提供barrier或semaphore机制。运行时要在任务边界插入同步点确保数据依赖正确。3.4 量化与精度调优的软件配合量化是AI芯片软件栈的重要一环。硬件支持INT8甚至INT4但量化方案需要软件来实现。量化流程通常包括统计权重和激活的分布确定scale和zero-point插入量化节点微调模型恢复精度。统计分布时常用的方法是移动平均最小最大值或KL散度。移动平均最小最大值简单但可能被离群值影响。KL散度更鲁棒但计算量大。确定scale和zero-point时要区分对称量化和非对称量化。对称量化zero-point为0适合权重。非对称量化zero-point非零适合激活因为激活经过ReLU后都是非负的。插入量化节点后模型精度通常会下降。需要用少量数据做微调让模型适应量化误差。微调时学习率要小否则会破坏预训练权重。硬件设计时要跟量化方案配合。比如如果硬件支持per-channel量化软件就要按通道统计scale。如果硬件只支持per-tensor软件就要做更精细的校准。4. 实操流程与性能调优4.1 从模型到芯片的完整部署流程假设你有一个训练好的模型要部署到自研AI芯片上。完整流程大概是这样。第一步模型导出。从训练框架导出为ONNX或类似格式。导出时要确认算子版本跟芯片支持的一致。不支持的算子要替换或自定义。第二步图优化。做常量折叠、算子融合、死代码消除。这一步通常由编译器前端完成。第三步量化。如果芯片用INT8就要做训练后量化或量化感知训练。量化后要验证精度如果掉点太多就要调整量化策略。第四步算子映射。编译器把优化后的图映射到硬件算子。这一步会生成tiling参数和内存分配方案。第五步指令生成。根据算子映射生成具体的指令序列包括计算指令和DMA指令。第六步运行时加载。把指令和权重加载到芯片上启动执行。第七步性能分析。用profiler看各层的耗时和硬件利用率找出瓶颈。第八步迭代优化。针对瓶颈层调整tiling参数或手写算子。这个流程里第三步和第四步是最容易出问题的。量化精度不够会导致模型不可用算子映射不合理会导致性能差。4.2 性能瓶颈的定位方法性能瓶颈定位是调优的第一步。常用的方法有几种。硬件计数器是最直接的。大多数AI芯片都有性能计数器记录计算阵列利用率、SRAM带宽利用率、DMA吞吐等。如果计算阵列利用率低说明tiling不合理或数据供给不足。如果SRAM带宽利用率高说明访存是瓶颈。逐层profiling也很有效。把模型每层的耗时打出来看哪层最慢。通常卷积层和全连接层是大头但如果某个element-wise层特别慢可能是算子实现有问题。Roofline模型可以帮你判断是计算瓶颈还是访存瓶颈。算一下每层的计算强度和硬件的计算强度上限。如果低于上限就是访存瓶颈要优化数据复用。如果接近上限就是计算瓶颈要提升算力或减少计算量。我个人的经验是大部分性能问题出在访存上。计算阵列利用率低往往是因为数据供给跟不上。解决办法是增大tiling的复用率或者用双缓冲隐藏搬运延迟。4.3 常见性能问题的调优手段针对不同的性能问题有不同的调优手段。如果是计算阵列利用率低先看tiling参数。tile太小填不满阵列tile太大溢出SRAM。要找到平衡点。另外看数据流权重固定还是输出固定不同数据流适合不同形状的矩阵。如果是SRAM带宽瓶颈看bank冲突。如果多个PE同时访问同一个bank就会stall。调整数据排布让访问分散到不同bank。另外看数据复用率如果每个数据只被用一次那SRAM带宽需求就大。增大tiling的复用率可以降低带宽需求。如果是DMA瓶颈看搬运效率。DMA的burst length要设大减少命令开销。另外看搬运和计算是否重叠如果没有重叠就要用双缓冲。如果是多核负载不均看任务切分。把大任务切小动态分配。另外看核间同步开销如果同步太频繁就要合并任务减少同步点。4.4 精度与性能的联合调优精度和性能往往矛盾。量化到INT4性能好但精度差用FP16精度好但性能差。联合调优的目标是在满足精度约束下最大化性能。一个实用的方法是混合精度。敏感层用高精度不敏感层用低精度。哪些层敏感通常第一层和最后一层比较敏感因为第一层处理原始输入最后一层影响输出分布。中间的卷积层相对不敏感。另一个方法是量化感知训练。在训练时就模拟量化误差让模型适应。这样量化后精度损失小可以用更低的位宽。硬件设计时也可以支持混合精度。比如权重INT4、激活INT8计算单元支持4位乘8位。这样既减少了权重带宽又保证了激活精度。我个人的经验是INT8量化通常掉点不到1%可以接受。INT4量化掉点可能到2%到3%需要量化感知训练来补偿。如果掉点超过5%就要考虑混合精度或调整量化方案。5. 常见问题与排查技巧实录5.1 硬件设计阶段的典型问题问题一计算阵列利用率上不去。流片前仿真发现阵列利用率只有50%。排查发现是tiling参数跟阵列尺寸不匹配。阵列是128x128但tiling的输出通道只切了64导致一半PE闲置。调整tiling参数后利用率到85%。问题二SRAM带宽不够。仿真发现计算阵列经常等数据。算了一下阵列每周期需要256字节但SRAM只提供128字节。解决办法是增加SRAM bank数量或者调整数据排布让一次能取更多数据。问题三多核互联拥塞。16核mesh拓扑仿真发现某些核之间的通信延迟特别大。排查发现是路由算法不合理数据包绕了远路。改用XY路由后延迟降低。问题四功耗超标。动态功耗比预算高30%。排查发现是时钟树功耗大因为阵列太大时钟要驱动很多寄存器。解决办法是分段时钟或者降低频率提高并行度。5.2 软件栈开发中的典型问题问题一编译器生成的代码性能差。自动生成的卷积代码比手写的慢3倍。排查发现是tiling策略太保守tile切得太小。调整编译器参数后差距缩小到1.5倍。问题二量化后精度掉太多。INT8量化后精度掉了5%。排查发现是激活的离群值太大per-tensor量化scale被拉大导致大部分值量化误差大。改用per-channel量化后精度恢复到1%以内。问题三内存不够。模型加载时OOM。排查发现是内存分配没有复用每个张量都分配了新内存。改进生命周期分析后峰值内存降低40%。问题四多核同步死锁。运行时偶尔卡死。排查发现是barrier使用不当某个核提前到达barrier但其他核还没到导致死等。改用带超时的同步机制后问题消失。5.3 部署上线的典型问题问题一模型精度跟仿真不一致。仿真精度正常上板后精度掉点。排查发现是硬件有bug某个累加器溢出没有正确处理。修复硬件后精度恢复。问题二性能跟仿真差距大。仿真性能达标上板后只有一半。排查发现是DRAM带宽不够仿真时假设的带宽是理论值实际达不到。优化DRAM调度后性能提升。问题三散热问题。芯片跑满时温度过高降频。排查发现是散热设计不够风道不合理。改进散热后温度降低20度。问题四驱动兼容性问题。某些主机上驱动加载失败。排查发现是PCIe配置空间访问方式不兼容。修改驱动后问题解决。5.4 独家避坑技巧汇总技巧一仿真时要用真实模型。不要用随机数据仿真要用真实模型的真实输入。随机数据的访存模式跟真实数据差别很大仿真结果不可靠。技巧二留足片上SRAM余量。设计时SRAM容量要留20%余量因为实际模型的张量大小可能比预期大。如果SRAM不够性能会断崖式下降。技巧三编译器要支持手动覆盖。自动tiling不一定最优要允许开发者手动指定tiling参数。这样遇到自动优化效果不好的算子可以手动调优。技巧四量化校准集要有代表性。校准集要从真实数据里采样覆盖各种场景。如果校准集偏了量化scale就不准精度会掉。技巧五多核任务切分要均匀。切分任务时要考虑各核的实际算力不要简单平均分。如果某个核频率低或者缓存小就要少分点任务。技巧六性能计数器要够用。硬件设计时要预留足够的性能计数器否则调优时两眼一抹黑。至少要有计算阵列利用率、SRAM带宽、DMA吞吐、各核忙闲统计。技巧七驱动要支持热更新。固件和驱动要支持在线更新否则每次改bug都要重新流片或拆机成本太高。技巧八跟算法团队保持同步。算法在迭代模型结构在变。硬件和软件设计要跟算法团队保持沟通了解未来半年的模型趋势避免刚流片就过时。这些经验都是我在实际项目中踩坑踩出来的。有些坑踩一次就够了有些坑反复出现。关键是要建立一套完整的仿真、验证、调优流程让问题尽早暴露。流片前发现的问题都是小问题流片后发现的都是大问题。