资讯详情

AI芯片核心架构详解:脉动阵列原理、硬件实现与FPGA实操

📅 2026/10/26 16:33:17 | 华诺云谱 👁 阅读
AI芯片核心架构详解:脉动阵列原理、硬件实现与FPGA实操
做AI芯片这行绕不开的一个名词就是脉动阵列Systolic Array。Google TPU第一代就靠它打天下后面不少NPU、AI加速器的核心计算单元也都用了类似的架构。我第一次在架构文档里看到这个词的时候第一反应是这名字也太玄了。后来把数据流图亲手画出来才明白它本质上把取数—计算—访存的旧思路改成了数据在算力网格里自己流的新思路。这篇文章就把脉动阵列的原理、硬件结构、参数测算、软件调度以及我在FPGA上实际跑阵列踩过的坑一次讲清楚。不管你是做芯片架构、写编译器还是单纯想搞懂AI芯片内部到底怎么算的都能从里面拿走点东西。1. 脉动阵列是什么从仓库排队到传送带流水线1.1 核心思想把取数计算变成流水线送料要理解脉动阵列我先讲一个朴素的生产线比喻。传统CPU算一个矩阵乘法大致是取指令 - 取数据 - 算一次 - 写回每一步都要和内存打交道。这就相当于车间里每个工人都要跑到仓库去拿材料干完活再把半成品送回仓库。工人数量一多仓库门口就堵死了大部分时间不是在干活而是在排队。脉动阵列换了思路每个工位处理单元PE只负责一个固定动作材料数据通过传送带片上的数据总线自动送到每个工位。工位之间通过邻居直接交接数据算完直接传给下一个工位根本不用回仓库。于是整个车间可以塞进成百上千个工人还省掉了在仓库门口排队的时间。脉动这个词指的就是数据像心跳一样在时钟节拍的驱动下有节奏地在阵列里流动。每个PE在每个时钟周期做一次乘累加MAC然后把结果传给下一个PE。数据流不停计算单元就不闲着。从架构角度看脉动阵列解决的是AI计算里最痛的内存墙问题——计算单元太多内存带宽喂不饱。它用数据复用来换取带宽同一份数据在阵列里被反复使用访存次数大幅减少。1.2 一维脉动阵列怎么算点积先看最简单的一维情况算一个点积 y w·x三个元素。把三个权重 w0、w1、w2 提前塞进三个PE。输入数据 x0、x1、x2 从最左端依次进入每个PE干两件事把输入数据和自己的权重相乘加上左边传来的部分和然后传给右边的PE。第0拍x0进PE0PE0里出来 w0*x0 的部分和传给PE1第1拍x1进PE0同时 x0 的部分和进PE1PE1用 w1 乘 x0 再累加第2拍x2进PE0x1进了PE1x0进了PE2……几个周期后最后的累加结果刚好从最右端流出等于完整点积。整个过程没有一次去内存取中间结果的操作数据像水流一样从头到尾冲了一遍计算也顺便完成了。这就是脉动阵列脉动二字的来源数据像心跳一样在时钟节拍驱动下成波次流动每个PE只做最本分的工作——乘、加、转交。所有数据通路都是邻居到邻居的短连线不用走全局总线所以频率能拉得很高芯片布局也规整。1.3 二维阵列如何从点积扩展到矩阵乘法一维只能算向量真正的AI计算是矩阵乘法所以要摆成二维阵列。把权重矩阵预先铺在PE网格上每格一个权重输入数据从一侧流入部分和在另一侧流向最后在阵列边缘把结果收集起来。以Google TPU v1为例256x256个PE预先把一个256x256的权重块加载进阵列接下来从左侧送入输入数据每来一个数据一整列PE同时做MAC操作权重在整个计算过程中待在PE里不动所以被身后的输入数据反复复用了几十上百次。这直接砍掉了大量访存请求——在AI推理这种权重多、复用高的场景下节省的带宽相当可观。二维阵列比起一维最大的变化是多了并行度这个维度。一维里数据是一条线地流二维里数据是一片地流同一时刻256列PE同时在算不同的乘累加吞吐量直接乘以256。这也就是为什么TPU敢在功耗墙限制下打出92 TOPS的INT8算力——它靠的不是把单个PE做得多复杂而是把大量简单PE堆成网格让数据自己流动。2. 硬件结构拆解一个PE和一个阵列2.1 单个PE的内部结构和数据通路一个PE体积很小里面就是乘法器 加法器 两个寄存器一个放权重一个放累加值外加输入输出端口。INT8精度下乘法器做 8bit x 8bit 乘法得到16bit结果加法器累加到32bit上。相乘的两路输入分别是邻居传来的数据和本地寄存器里的权重每一次这样的操作就是一个MAC。为什么累加器要用32bit你想256个PE沿同一行累加每个乘积16bit累加256次需要额外8bit再留点余量32bit是最稳妥的选择。如果偷懒用16bit数据一多直接溢出算出来的结果错得离谱而且这种错误毫无规律排查起来特别费劲。做硬件设计时位宽这种地方最容易埋雷前期多花点面积后期能少掉一堆头发。PE之间的连线也很讲究。邻居之间默认有寄存器所以数据在PE间传递要多一拍——这拍延迟就是脉动。整个阵列的时序就是靠这一个个寄存器stage对齐的少了它们组合逻辑路径太长频率根本提不上去。我见过有人为了省寄存器把邻居连线改成纯组合逻辑结果主频直接从700MHz掉到300MHz得不偿失。2.2 三种数据流模式权重静止、输入静止、输出静止脉动阵列的数据流模式主要有三种对应不同的谁不动、谁流动策略模式谁不动谁在流动适用场景权重静止Weight Stationary权重留在PE里输入数据在阵列中流动部分和输出卷积、全连接权重复用比高输入静止Input Stationary输入数据留在PE里权重循环流过每个PE权重频繁更新、输入被多轮复用的场景输出静止Output Stationary部分和留在PE里累加输入和权重都流动输出数据量小、需要反复累积的场景TPU选的是权重静止理由很简单神经网络推理时权重的总量远远大于单个输入的数据量而且权重一旦加载可以在多个输入batch里反复使用。把权重固定在PE里省掉的DRAM带宽最多。你可以把权重想象成车间里固定的模具输入数据像原料一样一批批流过每批原料经过同一套模具加工自然省去了频繁换模具的时间。三种模式不是谁更高级而是对应不同访存特征。做架构选型时第一件事就是统计你的算法到底是哪个维度复用多——是权重复用多神经网络通常如此还是输入复用多比如一些信号处理、搜索场景再决定数据流。选错了阵列利用率会很难看。2.3 阵列周围的缓存和数据分发网络PE阵列只是计算核心真正喂饱它需要三层东西片上缓存SRAM累加器缓冲Accumulator Buffer以及数据分发网络。TPU v1的流程大致是这样Host通过DMA把权重搬到片上SRAM控制器把权重分发到PE阵列输入数据从片外或片上缓冲区流入阵列结果从阵列底部收集进累加缓冲。片上SRAM容量有限所以整个软件栈都在围绕如何把数据分块、调度使每次搬运的数据在阵列里被用够转。累加器缓冲经常被忽视但它很重要。PE里的累加寄存器只能存一个输出点的部分和如果输出张量很大必须定期把部分和排空Drain到外部累加缓冲再继续算。这个Drain操作会打断计算流水所以调度时要尽量让Drain时机和权重加载/数据搬运错开别让阵列停下来等。数据分发网络也容易成为瓶颈。把权重从SRAM广播到所有PE时信号fan-out很大如果布线不合理时序直接崩。工程上常用寄存器复制Register Replication让每个扇区都有独立驱动源本质上是用面积换时序收敛。3. 关键参数怎么定规模、带宽与算力测算3.1 阵列规模怎么选算力峰值和利用率的拉扯256x256不是拍脑袋定的。算一下65536个PE每个PE每周期一次MAC700MHz主频下每秒做 65536 x 700M ≈ 4.6e13 次MAC每次MAC是2次运算乘加所以峰值算力约 9.2e13 次运算/秒也就是92 TOPS。这是TPU v1公布的数字。但峰值不是一切。阵列越大小矩阵利用率越低一个128x128的卷积层输出放到256x256阵列里只能用到1/4的PE剩下3/4的PE只能空转。实际工程里要用tiling把一个逻辑小矩阵拆成和阵列匹配的块让每个PE都不闲着。阵列规模还受面积和功耗约束65536个PE本身就是一大片逻辑加上连接网络、片上SRAM、累加缓冲占die面积很大。所以做产品时要在利用率、面积、良率之间找平衡——堆PE堆到极致不代表整体算力最高要看的是有效算力实际跑模型时算出的吞吐而不是峰值。这也是为什么总有人拿峰值算力吹牛一测真实模型就露馅。3.2 带宽需求计算为什么数据流会饿死计算单元算完峰值再算带宽。假设阵列每周期要消费256个输入数据256列PE同时喂数据INT8就是256字节/周期。700MHz下输入数据带宽需求 256 B x 700M ≈ 179 GB/s。如果权重每次都从外部读256x25664KB/周期那就是44 TB/s——没有任何DRAM扛得住HBM也就几百GB/s到1TB/s。所以权重必须驻留在片上SRAM里反复复用只在切权重块时才从DRAM补货。这就是数据复用比的意义。一个权重从DRAM进片上缓存一次如果被阵列里的256个PE各用一次复用比就是256把复用比做高有效带宽需求就降下来了。TPU能跑起来靠的就是权重静止模式下极高的复用比。如果你设计的阵列复用比只有个位数那还不如用GPU至少人家生态成熟。实际工程里带宽计算要分两条路径权重路径和输入路径。权重路径靠复用压低需求输入路径靠片上缓存和批量调度争取时间。两条路径只要一条超过DRAM能力阵列就会饿死算力利用率直线下降。我的经验是做系统设计时先算带宽再定阵列规模别反过来否则流片回来发现性能差一大截只能干瞪眼。3.3 稀疏模型和脉动阵列的冲突不是所有模型都适合现在很多模型做了剪枝、量化权重和激活里有大量0。理论上跳过0能省算力但脉动阵列对稀疏很不友好PE之间严格按节奏流动一旦要跳0要么加复杂的门控逻辑要么让数据流变形。TPU v1根本不支持稀疏老实算0。后续有研究尝试做稀疏感知的脉动阵列但控制复杂度、负载不平衡问题都很难绕过去。如果某个PE遇到0权重它依然要花一个周期跑完MAC只是结果等于没加。你没法判断哪个PE闲下来了、把任务重新分给谁因为数据流是固定的。稀疏技术在脉动阵列上从省算力变成了省电但不算省时间收益很有限。所以如果你的模型是很稀疏的比如剪枝到10%密度别指望脉动阵列自动受益反而可能因为利用率低而更亏。这时候GPU或者稀疏专用加速器可能更合适。做架构选型时要清醒一点脉动阵列最适合的是密集矩阵乘法这恰恰是绝大多数神经网络的常态所以它有存在价值但它不是万能的。4. 从算法到硬件的最后一公里映射、调度与部署4.1 把矩阵乘法映射到阵列空间维度和时间维度一个矩阵乘 C A x B朴素写法是三层循环 i, j, k。在脉动阵列上你要决定哪些循环展开到空间用不同PE并行算哪些循环留在时间上串行。通常 i 和 j 展开到阵列的二维空间k 循环做流水级串行。举例输出矩阵是1024x1024阵列是256x256那么 i 分成4块、j分成4块共16个输出块。每个块对应一个256x256的小矩阵乘法送入阵列执行16块按顺序调度。这就是最基础的tiling。tile选多大直接影响复用比tile太小每次加载的权重还没被充分复用就换了带宽浪费tile太大片上SRAM放不下要频繁Drain。常用做法是扫一遍不同tile配置跑仿真看实际吞吐选最优。这里有个反直觉的点矩阵乘法里k维度归约维度的循环通常放在最内层但在脉动阵列上k维度恰恰是数据流动的方向最好把它流水化而不是展开成空间并行。因为流水化以后上一拍的乘积通过累加器自然累积不需要额外写回内存。理解了这一点你才算真正理解了脉动阵列的编程模型。4.2 编译器调度tiling、double buffering和流水重叠编译器/调度器的职责是决定tile大小、循环顺序、数据搬移时机、流水重叠。TVM里做脉动阵列调度时要手写循环变换最大化数据复用——把权重加载放在最外层的循环让它在多个tile之间重用把输入数据的搬移和计算重叠让DRAM搬运和PE计算并行流水。流水重叠很关键如果每个tile都先搬完数据再算搬运时间和计算时间串行整体效率就打折。好的调度做double buffering把存储分成两个buffer一个在搬下一块数据另一个在供当前计算用。这样搬运和计算同时进行实际吞吐能提升30%-50%。我踩过的一个坑是只优化了计算循环忽略了数据搬移循环。仿真看着PE利用率挺高但总线因为连续突发传输被占满计算单元被迫等待。后来把搬运改成burst模式又加了流水延迟匹配整体吞吐才上去。记住脉动阵列系统是数据供给系统不是计算系统瓶颈往往在数据搬运。4.3 部署流程训练到INT8量化再到阵列执行实际部署流程通常分五步训练得到FP32模型用校准数据集统计激活分布按KL散度或百分位选量化阈值把权重和激活量化到INT8做算子融合比如ConvBNReLU合并、图优化把卷积/全连接层转换成矩阵乘算子按tiling调度到脉动阵列生成指令DMA搬运数据阵列执行结果回写量化的坑很多。有些层对量化误差极其敏感比如带残差的shortcut相加误差会累积校准数据集不够有代表性时实测精度掉得厉害。我的经验是先跑全INT8 sanity测试再用少量校准样本拟合阈值最后在验证集上对比FP32和INT8的精度差。如果精度不达标优先检查是不是激活分布有长尾——把尾部clip掉往往比调权重量化更有效。算子融合也不可忽视。ConvBNReLU融合以后中间结果不用写回内存直接留在片上省掉一整轮访存。在脉动阵列这种以数据流为核心的架构上任何一次片上数据保留都价值巨大。指令生成时还要注意阵列指令让PE同时干活控制指令要尽量减少气泡把不同层的计算尽量拼成连续的大块矩阵乘减少阵列启停次数。5. FPGA上跑一个16x16脉动阵列实操与踩坑5.1 最小系统搭建从一维到二维在FPGA上验证脉动阵列从16x16开始最合适。每个PE写成parameterized模块权重通过AXI-Lite配置写入输入数据从BRAM读出结果写回BRAM。顶层控制逻辑用FSMIDLE - LOAD_WEIGHT - COMPUTE - DRAIN - IDLE。这里有个很实用的小技巧先把一维8个PE的阵列跑通验证数据流时序再扩展成16x16二维。很多人一上来就铺二维结果波形一片混乱排查起来头大。一维阵列的波形直观数据流的方向、延迟、累加关系一眼就能看明白二维出问题时你根本分不清是行方向错还是列方向错。时序问题也常见。PE之间的组合逻辑路径主要是乘法器和加法器8bit乘8bit的乘法器16bit结果、32bit加法100MHz没问题。但如果权重广播总线fan-out太高16行16列就是256个负载直接时序违例。处理方法是给权重总线加寄存器复制把信号分成四组驱动。5.2 调试中遇到的四个经典问题问题现象可能原因解决方案输出结果错位输入数据没有按列延迟对齐第j列数据需要晚j拍进入在数据分发路径上插入每列对应的延迟寄存器累加结果溢出累加器位宽不够累加器用32bit中间结果周期性地Drain到外部累加缓冲时序违例PE组合逻辑过长 / fan-out太高拆流水级、复制寄存器、优化乘法器结构性能只有峰值50%tile太小或数据搬运与计算没重叠增大tile做double buffering把DMA搬运和计算流水重叠数据错位这个坑我印象最深。脉动阵列的精髓在于到点、到位但做的时候容易忽略列间的延迟差第0列的数据先到第1列的数据晚1拍第2列晚2拍……如果不手动对齐算出来的结果全乱。写RTL时直接把每列输入路径上加好对应延迟后面调试省一半时间。累加溢出也坑过我。起初用16bit累加器以为就16x16256个累加16bit够了结果因为部分和要在阵列里累加再传递实际累加次数远超预期数据直接溢出而且溢出后的结果完全没规律。后来老老实实改成32bit累加器问题立刻消失。后面做芯片设计时一看到累加器位宽就条件反射地多算几轮余量。5.3 两个让调试事半功倍的测试技巧第一个技巧先做矩阵维度和阵列匹配的sanity check用单位矩阵、常数矩阵、小随机矩阵三组测试结果正确性一键验证。单位矩阵能查数据通路有没有接反常数矩阵能查累加有没有丢项小随机矩阵能查溢出和精度问题。三组全过基本可以确信阵列逻辑是对的。第二组测试用连续大矩阵观察实际吞吐对比理论峰值。实测下来调度没优化的系统跑不到50%优化后能到85%以上——差距基本都在数据搬运。这时候抓波形看DMA和PE之间的握手信号能清楚看到计算单元在多少周期里是空闲等待的。第二个技巧仿真时强制给每个PE的输入输出波形打日志仿真通过后再上板。FPGA上不好抓内部信号仿真时把数据流打出来对照矩阵乘法结果逐拍核对哪个PE、哪个周期算出错一目了然。线上调试找不到问题一查日志就定位了。写到这里我自己在调脉动阵列那段时期的体会又浮上来了。做一个脉动阵列最难的不是把PE搭出来而是让每一拍的数据都正好出现在该出现的地方——这本质上是硬件和软件一起设计的问题。硬件定了数据流软件负责调度喂数任何一个环节掉链子阵列就只能空转。如果你也想深入这块我的建议很简单先拿一个小阵列在FPGA上跑一遍把数据流图亲手画出来把时序图一拍拍对。等你亲眼看着数据像脉搏一样流过阵列的时候就会明白为什么这么多AI芯片把宝押在这种呆板但高效的结构上了。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑