资讯动态

AI芯片软硬件协同设计:从算子映射到流片bring-up的工程实践

发布时间:2026/10/23 1:06:02 来源:尧图企业网站定制
1. AI芯片软硬件协同设计的整体思路拆解1.1 为什么软硬件必须一起设计做AI芯片这行的人都有一个共识硬件性能再强如果软件栈跟不上实际跑出来的吞吐量可能连理论峰值的30%都不到。我见过太多团队花了大半年流片结果驱动和编译器拖了后腿最终产品落地时被客户吐槽“跑分好看、实战拉胯”。所以这一节我想先把软硬件协同设计的核心逻辑讲清楚。AI芯片和传统CPU最大的区别在于它的计算模式高度专用化。CPU要处理各种分支跳转、通用逻辑而AI芯片主要干一件事——矩阵乘加运算。这个差异决定了AI芯片的硬件架构可以做得非常激进比如用大规模脉动阵列、用超宽的片上缓存、用定制化的数据流。但激进的前提是软件必须精确地知道硬件想要什么格式的数据、什么时候该预取、什么时候该同步。打个比方AI芯片的硬件像是一条高度自动化的流水线软件则是那条流水线的调度系统。如果调度系统不知道流水线每个工位的节拍就会出现有的工位空转、有的工位堵死的情况。软硬件协同设计的目标就是让调度系统和流水线在设计阶段就互相配合而不是先造流水线再硬写调度逻辑。1.2 从算子到指令的映射链路一个典型的AI芯片软件栈从上层往下走大概经过这几层框架层比如各种深度学习框架的模型→ 图优化层 → 算子编译层 → 指令生成层 → 驱动层 → 硬件。每一层都在做“翻译”和“优化”的工作。我拿一个卷积算子举例。框架层拿到的是一个conv2d的调用图优化层会把它和后面的BN、ReLU融合成一个复合算子算子编译层会把它拆成im2col加矩阵乘指令生成层再把它映射到硬件支持的矩阵乘指令上。这个链路里每一步都有取舍融合太多会导致寄存器压力大拆得太细又会有调度开销。实操心得在设计指令集的时候一定要让编译器团队和硬件团队坐在一起定指令格式。我踩过的坑是硬件团队先定了一套很“优雅”的指令编码结果编译器团队发现根本没法做指令级并行调度最后不得不加了一堆伪指令来打补丁。1.3 数据流架构的选择逻辑AI芯片的数据流架构大致分三种权重 stationary、输出 stationary、行 stationary。选哪种不是拍脑袋决定的要看你主要跑什么模型。权重 stationary 适合权重固定、输入不断变化的场景比如推理服务。输出 stationary 适合输出通道多、需要累加的场景。行 stationary 是折中方案在移动端芯片里比较常见。选择的核心依据是你的目标模型里哪一类数据复用率最高就把哪一类数据固定在片上。这里有个简单的估算方法假设你的片上缓存能放N个权重那么权重 stationary 架构下每加载一次权重可以处理M个输入样本M越大能效越高。但如果M太小权重加载的开销就盖过了计算收益。所以架构选型一定要结合目标batch size来算。2. 核心模块的硬件设计细节与实操要点2.1 矩阵乘加阵列的尺寸怎么定矩阵乘加阵列是AI芯片的计算核心它的尺寸直接决定了芯片的面积、功耗和峰值算力。尺寸定小了算力不够定大了面积和功耗爆炸。我一般用这个思路来估算先确定目标算力。假设你要做一颗面向边缘推理的芯片目标INT8算力是4 TOPS。再确定目标频率比如800MHz。那么每个时钟周期需要完成的乘加次数是 4e12 / 800e6 5000次。如果每个PE每个周期做一次乘加那就需要5000个PE。考虑到阵列通常是方形的可以取64x644096或者72x725184。但这只是理论值实际还要考虑数据供给能不能跟上。如果片上缓存带宽不够阵列再大也是空转。我通常会让缓存带宽至少是阵列峰值需求的1.5倍留出余量应对不规则访问。2.2 片上缓存的层次划分AI芯片的片上缓存一般分三级寄存器文件、近端缓存、全局缓存。寄存器文件在PE内部容量最小但带宽最高近端缓存服务一个PE簇全局缓存所有PE共享。划分的依据是数据复用距离。权重通常在全局缓存里放一份然后广播到各个PE簇输入特征图在近端缓存里做行缓存部分和在寄存器文件里累加。这个划分不是固定的要根据具体算子的数据复用模式来调整。注意全局缓存的bank数量要仔细设计。bank太少会有冲突bank太多面积和功耗上去了。我一般用32或64个bank每个bank位宽128字节这样能覆盖大多数算子的访问模式。2.3 数据精度与位宽的选择AI芯片支持的数据精度越来越多FP32、FP16、BF16、INT8、INT4甚至INT2。硬件上怎么支持这些精度有几种做法一种是每个PE支持可重构的位宽比如一个PE可以配置成1个INT8乘法器或者2个INT4乘法器。这种做法的好处是灵活性高坏处是面积开销大。另一种是分开设计INT8阵列和FP16阵列各做各的好处是各自优化到极致坏处是面积利用率低。我个人的经验是如果目标场景以INT8推理为主就专门优化INT8通路FP16只做少量支持。如果要做训练芯片那FP16/BF16必须是主力INT8作为辅助。不要试图做一个“什么精度都强”的芯片那通常意味着什么精度都不够强。3. 软件栈的关键实现与编译优化3.1 图优化阶段的算子融合策略图优化是软件栈里最能出性能的地方。算子融合的核心思想是减少中间结果的写回和读取。比如convBNReLU这三个算子如果不融合中间要写两次全局内存、读两次全局内存融合之后BN的参数直接折叠进卷积权重ReLU在输出时直接做截断中间结果根本不落内存。但融合不是越多越好。融合太多会导致单个融合算子的寄存器需求超过硬件限制编译器不得不插入spill代码反而更慢。我一般会设置一个融合上限比如最多融合5个算子超过就拆成两个融合组。3.2 指令调度与流水线编排指令调度的目标是把计算指令、访存指令、同步指令编排好让硬件流水线尽量不空转。这里的关键是软件流水在当前迭代计算的时候预取下一次迭代的数据。实现软件流水需要编译器知道硬件的流水线深度。比如矩阵乘指令的延迟是8个周期那么预取就要提前至少8个周期发起。如果预取太早数据会挤占缓存预取太晚计算会等数据。我通常会让编译器做一个自动的流水线平衡根据指令延迟和缓存大小算出最优的预取距离。# 伪代码示意软件流水的预取距离计算 def compute_prefetch_distance(compute_latency, cache_size, data_size_per_iter): # 预取距离至少覆盖计算延迟 min_distance compute_latency # 预取距离不能超过缓存能容纳的迭代数 max_distance cache_size // data_size_per_iter # 取两者之间的一个平衡值 return min(max_distance, max(min_distance, (min_distance max_distance) // 2))3.3 内存分配与生命周期管理AI芯片的片上内存通常不大几十MB到几百MB。怎么在有限的内存里放下所有中间张量是编译器要解决的核心问题之一。常用的方法是内存复用分析每个张量的生命周期把生命周期不重叠的张量分配到同一块内存。这个分析做得好不好直接决定了能不能跑大模型。我见过一个案例同样的模型内存复用优化前需要200MB优化后只要80MB直接让芯片能跑起来了。优化的关键是精确的生命周期分析要考虑控制流、要考虑原地操作、要考虑异步拷贝。实操心得在做内存复用的时候一定要留出至少10%的余量。因为实际运行时会有碎片、会有对齐要求、会有临时buffer。把内存算得刚刚好运行时大概率OOM。4. 常见问题排查与性能调优实录4.1 算力利用率上不去的排查思路算力利用率低是最常见的问题。排查的时候我一般按这个顺序来先看是不是访存瓶颈。用性能计数器看DDR带宽利用率和片上缓存带宽利用率。如果DDR带宽跑满了那就是数据供给不够要么优化数据复用要么压缩数据位宽。如果片上缓存带宽跑满了那就是缓存层次设计有问题要么加bank要么改数据布局。再看是不是指令调度问题。看流水线停顿周期数如果停顿很多那就是依赖链太长或者预取没做好。这时候要检查编译器的调度策略看看是不是有改进空间。最后看是不是算子本身的问题。有些算子在硬件上就是跑不快比如depthwise卷积、比如transpose。这种时候要么换算法要么在硬件上做特殊支持。4.2 精度掉点的定位方法AI芯片上跑量化模型精度掉点很常见。定位的时候我一般分三步第一步逐层对比。把量化模型和浮点模型在每一层的输出都dump出来算余弦相似度。哪一层掉得厉害问题就在哪一层。第二步分析数据分布。看那一层的输入和权重分布是不是有长尾、是不是有离群值。如果有那可能是量化参数选得不好需要调整截断阈值。第三步检查硬件行为。有些芯片的累加器位宽不够累加多了会溢出有些芯片的饱和处理有问题负数截断会出错。这些都要用测试用例覆盖到。4.3 多核协同的同步问题现在的AI芯片通常有多个计算核核间同步是个大问题。同步做不好要么死锁要么性能暴跌。我一般用生产者-消费者模型来做核间同步。每个核有自己的任务队列完成任务后把结果放到共享内存然后发一个信号量。下游核收到信号后取数据继续处理。关键是信号量的粒度要合适太粗了并行度不够太细了同步开销大。问题现象可能原因排查方法解决思路算力利用率低于50%访存瓶颈查DDR和缓存带宽计数器优化数据复用或压缩位宽流水线停顿多依赖链太长查流水线停顿周期数改进指令调度或预取策略量化精度掉点多量化参数不佳逐层对比余弦相似度调整截断阈值或混合精度多核死锁同步顺序错误查信号量等待链统一同步顺序或加超时内存不够生命周期分析不准查内存分配日志改进复用分析或留余量注意性能调优一定要有数据支撑不要凭感觉猜。我见过太多人一上来就改架构结果改了半天发现是编译器的一个bug。先测量、再分析、最后动手这个顺序不能乱。5. 从设计到流片的工程化经验5.1 仿真验证环境的搭建AI芯片流片前的验证工作量巨大仿真环境搭得好不好直接决定流片成功率。我一般会搭三层验证环境第一层是单元级验证针对每个PE、每个缓存bank做定向测试。第二层是子系统级验证把阵列、缓存、控制器连起来跑真实算子。第三层是系统级验证跑完整的模型对比和浮点参考的精度。仿真速度是个大问题。纯RTL仿真跑一个ResNet-50可能要几天。我通常会用FPGA原型做加速把关键模块综合到FPGA上仿真速度能快100倍以上。5.2 功耗与面积的权衡AI芯片的功耗和面积是硬约束。同样的算力功耗和面积差一倍产品竞争力就差很多。降低功耗的手段主要有降低电压频率、减少数据搬运、增加时钟门控。降低面积的手段主要有复用计算单元、压缩缓存、优化布局布线。但这些手段都有代价。降低电压频率会降算力减少数据搬运可能需要改架构增加时钟门控会增加设计复杂度。我一般会在项目早期就定好功耗和面积的目标然后每个模块都按这个目标去设计而不是最后再来擦屁股。5.3 流片后的bring-up流程流片回来之后的bring-up是最紧张的阶段。我一般按这个流程走先测电源和时钟确保芯片能正常上电、时钟能跑起来。再测JTAG和调试接口确保能读写寄存器。然后测内存确保DDR能正常读写。接着测计算单元跑一个最简单的矩阵乘对比结果。最后跑完整模型做性能和精度测试。每一步都要有明确的通过标准不通过就停下来排查不要带着问题往下走。我见过有人跳过内存测试直接跑模型结果模型结果不对排查了两周才发现是DDR的某个bit有问题。实操心得bring-up阶段一定要准备一个“最小测试集”包含十几个最基本的测试用例覆盖电源、时钟、复位、寄存器、内存、计算。每次改完代码或者换芯片先跑这个测试集通过了再跑大模型。这样能快速定位问题是出在基础功能还是复杂逻辑。6. 软硬件协同设计的未来演进方向6.1 可重构架构的探索固定架构的AI芯片只能跑特定类型的模型模型一变性能就崩。可重构架构的思路是让硬件能根据模型特点动态调整。比如根据算子的数据复用模式动态配置缓存的大小和带宽分配。可重构的难点在于重构开销。如果重构一次要几百个周期那频繁重构反而更慢。我看到的比较可行的方案是粗粒度重构比如按层重构一层算完再重构下一层。这样重构开销可以摊薄到整层的计算时间里。6.2 编译器和硬件的联合优化未来的趋势是编译器和硬件不再分家而是联合设计。编译器在编译时能知道硬件的详细参数硬件在设计时也能参考编译器的需求。比如编译器告诉硬件“我需要一个能快速做transpose的单元”硬件就加一个转置网络。这种联合设计需要工具链的支持。我了解到一些团队在做DSL领域特定语言用DSL描述硬件和软件然后自动生成RTL和编译器。这个方向很有前景但目前还不太成熟主要是DSL的表达能力有限复杂算子描述起来很费劲。6.3 面向大模型的芯片设计挑战大模型的参数量动辄几百亿对芯片的内存容量和带宽提出了极高要求。传统的AI芯片设计思路是“算力优先”但在大模型场景下内存带宽往往比算力更关键。我个人的判断是未来面向大模型的AI芯片会在内存层次上做更多创新比如用HBM、用近存计算、用存内计算。同时软件栈也要配合做更激进的内存复用、更高效的分布式推理。软硬件协同设计在这个场景下会更加重要因为任何一方的短板都会被大模型放大。最后分享一个小技巧在做软硬件协同设计的时候一定要尽早建立一个端到端的性能模型。这个模型不需要很精确但要有足够的覆盖度能快速评估不同设计决策的影响。我通常用Excel或者Python脚本搭这个模型输入是架构参数和模型参数输出是预估的性能和功耗。这个模型在项目早期能帮你避免很多方向性错误比等到RTL写完再发现问题要划算得多。

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

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

免费获取报价 →
↑