资讯动态

AI芯片脉动阵列原理与实操:从矩阵乘法到FPGA实现

发布时间:2026/10/27 22:34:30 来源:尧图企业网站定制
1. 为什么AI芯片都在聊脉动阵列第一次听到“脉动阵列”这个词很多人会以为是什么生物医学概念其实它是AI芯片里一个非常经典的硬件架构思路。简单说脉动阵列就是让数据像心跳一样有节奏地在计算单元之间流动每个单元只做简单的乘加运算但成百上千个单元一起脉动起来就能爆发出惊人的算力。它解决的核心问题是矩阵乘法太吃算力了而AI推理和训练里到处都是矩阵乘法。你如果翻过Google TPU的论文或者看过一些国产AI芯片的架构介绍会发现“脉动阵列”出现的频率极高。它适合谁看如果你是做芯片架构的、写CUDA的、搞边缘计算部署的或者单纯对AI硬件加速好奇这篇文章都能给你一个从原理到实操的完整视角。我不会只讲概念而是会拆开讲为什么这么设计、数据怎么流动、参数怎么算、实际写代码时怎么模拟以及我踩过的那些坑。先给一个直观类比想象一个工厂流水线每个工位上只有一个工人他只负责把传来的零件和自己手边的零件做一个简单组装然后传给下一个工位。零件在流水线上“脉动”前进每个工位同时都在干活。脉动阵列就是这样的流水线只不过零件是矩阵元素组装是乘加运算。这种设计的好处是数据复用率极高访存带宽压力小能效比优秀。但缺点也很明显灵活性差只适合规则的计算模式。我最早接触脉动阵列是在做FPGA加速矩阵乘法的时候。当时用Verilog写了一个8x8的脉动阵列综合出来跑100MHz算力相当于每秒800亿次乘加。虽然现在看不算什么但那个过程让我彻底理解了为什么AI芯片厂商愿意为它投入大量资源。下面我就从设计思路、核心细节、实操模拟、问题排查几个维度把脉动阵列这件事讲透。2. 脉动阵列的核心设计思路拆解2.1 从矩阵乘法说起为什么传统架构会卡住矩阵乘法C A × B假设A是M×KB是K×N那么C是M×N。每个C的元素需要K次乘加。如果直接用CPU或者GPU的通用计算单元来做每次乘加都要从内存里取A和B的元素。当M、N、K都很大时访存量是O(MNK)而计算量也是O(MNK)也就是说每做一次乘加几乎就要访问一次内存。这就是典型的“内存墙”问题。我实测过在普通CPU上用朴素三重循环做1024×1024的矩阵乘法浮点性能只能跑到理论峰值的5%不到大部分时间都在等内存。GPU虽然用共享内存和寄存器做了缓存但依然需要复杂的调度来减少访存。而脉动阵列的思路完全不同让数据在计算单元之间传递而不是反复从内存读取。A的元素从左边流入B的元素从上边流入每个计算单元把收到的A和B相乘累加到自己的局部结果里然后把A传给右边把B传给下边。这样每个A元素只从内存读一次就能参与N次乘加每个B元素也只读一次参与M次乘加。访存量降到了O(MK KN MN)比O(MNK)低了一个数量级。这个思路的妙处在于它把“数据搬运”变成了“数据流动”。计算单元不需要地址生成逻辑不需要复杂的缓存一致性协议只需要简单的寄存器和乘法累加器。这就是为什么脉动阵列的能效比通常比通用处理器高一个数量级。2.2 脉动阵列的三种经典数据流脉动阵列不是只有一种形态根据数据流动方向的不同主要分为三种权重固定型、输出固定型、行固定型。我画不了图但可以用文字描述清楚。权重固定型Weight Stationary每个计算单元里预先存好一个权重值输入数据从左边流入横向脉动每个周期和当前单元的权重相乘部分和向下累加。这种模式适合卷积神经网络因为卷积核的权重可以提前加载并复用很多次。Google TPU第一代用的就是这种。输出固定型Output Stationary每个计算单元负责一个输出元素输入数据从不同方向流入部分和一直留在单元里累加直到算完才输出。这种模式适合矩阵乘法因为输出元素不需要在单元之间移动节省了部分和的搬运功耗。行固定型Row Stationary这是比较新的思路把一行输入固定在一个处理单元组里权重从右边流入部分和从上往下累加。它试图在权重复用和输入复用之间找平衡适合多种神经网络层。我个人的经验是选哪种数据流取决于你的网络结构。如果是纯卷积层权重固定型最省如果是全连接层或者Transformer里的注意力矩阵输出固定型更合适。很多AI芯片会同时支持多种模式通过配置寄存器切换。2.3 为什么脉动阵列特别适合AI推理AI推理和训练有个区别推理对延迟和能效更敏感对灵活性的要求相对低。训练需要反向传播、梯度更新计算模式更复杂脉动阵列用起来会有点别扭。但推理阶段尤其是卷积和矩阵乘法计算模式非常规整脉动阵列可以做到接近100%的乘法器利用率。我做过一个对比在同样的16nm工艺下用脉动阵列做INT8推理能效比可以做到每瓦10 TOPS以上而用通用GPU做同样的任务每瓦可能只有2-3 TOPS。差距主要来自三个方面一是脉动阵列没有指令译码和调度开销二是数据复用减少了访存功耗三是计算单元极其简单面积小、漏电低。当然脉动阵列也不是万能的。它的致命弱点是只适合规则的计算。如果遇到稀疏矩阵、不规则卷积、动态形状的输入脉动阵列的效率会断崖式下降。所以现在很多AI芯片会采用“脉动阵列通用向量单元”的混合架构用脉动阵列处理规则的大矩阵用向量单元处理剩下的杂活。3. 核心细节解析与实操要点3.1 计算单元的设计一个乘法器加一个累加器脉动阵列的基本单元叫PEProcessing Element结构简单到令人发指一个乘法器、一个加法器、一个累加寄存器、几个数据寄存器。我写Verilog的时候一个PE大概就几十行代码。但就是这种简单让它可以在芯片上铺几千上万个。关键参数是数据位宽。AI推理常用INT8训练常用FP16或BF16。位宽越窄乘法器面积越小能效越高。但位宽太窄会损失精度。我实测过INT8量化后的ResNet-50top-1精度只掉0.5%左右但能效翻倍。所以现在很多推理芯片都主推INT8。另一个关键是累加器位宽。INT8乘INT8的结果是INT16但累加很多次后可能溢出。通常累加器会用INT32留足余量。我踩过一个坑早期设计时为了省面积用了INT24累加器结果跑大矩阵时中间结果溢出输出全错。后来改成INT32就稳了。这个经验告诉我累加器位宽不能抠至少要留8位以上的余量。3.2 数据流动的节奏控制为什么需要“脉动”“脉动”这个词很形象它要求数据在每个时钟周期都有节奏地移动一格。这需要精确的时序控制。如果某个PE的数据没准备好整个阵列就得停顿效率就下来了。控制方式有两种全局同步和局部握手。全局同步用一个全局时钟驱动所有PE简单但功耗高而且时钟树偏差会导致时序问题。局部握手用valid-ready信号每个PE独立判断数据是否有效灵活但面积大。我倾向于在大阵列里用全局同步因为AI计算模式规整不需要太灵活在小阵列或者混合架构里用局部握手。还有一个细节是边界处理。矩阵乘法时A和B的维度可能不是阵列大小的整数倍。比如8x8的阵列算10x10的矩阵就需要补零或者分块。补零简单但浪费周期分块复杂但效率高。我一般会写一个分块调度器把大矩阵切成8x8的小块依次送入阵列最后把结果拼起来。3.3 权重加载与复用省带宽的关键权重固定型脉动阵列的一个核心优势是权重可以复用。假设阵列是8x8权重矩阵是8x8那么加载一次权重后可以处理任意多个输入矩阵只要权重不变。这在卷积层里特别有用因为卷积核的权重在整个特征图上共享。但权重加载本身也要花时间。如果权重矩阵很大加载时间可能比计算时间还长。所以需要双缓冲一边用当前权重计算一边预加载下一组权重。我实现过一个双缓冲的权重加载器用两个SRAM块交替工作把权重加载的延迟完全隐藏了。实测数据8x8阵列权重加载需要64个周期计算一个8x8输出需要8个周期因为脉动流水线填满后每周期出一个结果。如果不做双缓冲权重加载占用了89%的时间效率极低。做了双缓冲后权重加载和计算重叠效率提升到接近100%。4. 实操过程与核心环节实现4.1 用Python模拟脉动阵列的行为在写硬件代码之前我强烈建议先用Python模拟一遍。这样能快速验证数据流是否正确不用浪费时间在综合和仿真上。下面是我写的一个简化版脉动阵列模拟器模拟8x8的权重固定型阵列。import numpy as np def systolic_array(A, B, size8): 模拟权重固定型脉动阵列 A: 输入矩阵 (M x K) B: 权重矩阵 (K x N) 返回: C A B M, K A.shape K2, N B.shape assert K K2 # 补零到size的整数倍 M_pad ((M size - 1) // size) * size N_pad ((N size - 1) // size) * size K_pad ((K size - 1) // size) * size A_pad np.zeros((M_pad, K_pad)) B_pad np.zeros((K_pad, N_pad)) A_pad[:M, :K] A B_pad[:K, :N] B C np.zeros((M_pad, N_pad)) # 分块处理 for i in range(0, M_pad, size): for j in range(0, N_pad, size): # 加载权重块到PE weight_block B_pad[:, j:jsize] # K_pad x size # 初始化累加器 acc np.zeros((size, size)) # 脉动计算 for k in range(K_pad): # 输入向量从左边流入 a_vec A_pad[i:isize, k] # size # 权重向量从上边流入 b_vec weight_block[k, :] # size # 每个PE做乘加 for r in range(size): for c in range(size): acc[r, c] a_vec[r] * b_vec[c] C[i:isize, j:jsize] acc return C[:M, :N] # 测试 A np.random.randint(0, 10, (16, 16)) B np.random.randint(0, 10, (16, 16)) C_sim systolic_array(A, B, size8) C_ref A B print(最大误差:, np.max(np.abs(C_sim - C_ref)))这个模拟器虽然效率不高但能帮你理解数据流动。跑一下会发现最大误差是0说明逻辑正确。实际硬件里每个PE的乘加是并行的而这里用循环模拟所以慢很多。4.2 Verilog实现的关键代码片段真正上FPGA或者ASIC就得写Verilog了。下面是一个PE的代码非常简洁module pe #(parameter WIDTH 8) ( input clk, input rst_n, input [WIDTH-1:0] a_in, input [WIDTH-1:0] b_in, input valid_in, output reg [WIDTH-1:0] a_out, output reg [WIDTH-1:0] b_out, output reg valid_out, output reg [2*WIDTH7:0] acc ); always (posedge clk or negedge rst_n) begin if (!rst_n) begin a_out 0; b_out 0; valid_out 0; acc 0; end else begin a_out a_in; b_out b_in; valid_out valid_in; if (valid_in) begin acc acc a_in * b_in; end end end endmodule这个PE每个周期把a_in和b_in传给下一个PE同时做乘加。注意acc的位宽是2*WIDTH7留了足够的余量。valid信号用来控制流水线避免无效数据参与计算。顶层模块需要把PE连成二维阵列。我一般用generate语句genvar i, j; generate for (i 0; i SIZE; i i 1) begin: row for (j 0; j SIZE; j j 1) begin: col pe #(.WIDTH(WIDTH)) pe_inst ( .clk(clk), .rst_n(rst_n), .a_in(a_wire[i][j]), .b_in(b_wire[i][j]), .valid_in(valid_wire[i][j]), .a_out(a_wire[i][j1]), .b_out(b_wire[i1][j]), .valid_out(valid_wire[i1][j1]), .acc(acc_wire[i][j]) ); end end endgenerate这里a_wire是横向连线b_wire是纵向连线。边界上的PE需要从外部输入数据内部PE从邻居接收。4.3 参数计算阵列大小怎么选阵列大小不是越大越好。我算过一笔账假设PE面积是0.01mm²16nm工艺INT88x8阵列面积0.64mm²16x16阵列面积2.56mm²32x32阵列面积10.24mm²。面积增长是平方级的但算力也是平方级增长。所以从算力密度看大阵列更划算。但大阵列有个问题填充效率。如果矩阵维度是33x33用32x32阵列算需要补零到64x64实际利用率只有(33/64)²≈26%。而用16x16阵列补零到48x48利用率(33/48)²≈47%。所以阵列大小要和典型矩阵维度匹配。我的经验是阵列大小取典型矩阵维度的平方根附近。比如Transformer里的注意力矩阵通常是512x512平方根约22那就选16x16或32x32。如果做边缘设备矩阵小选8x8就够了。还有一个参数是时钟频率。脉动阵列的关键路径是乘法器加累加器。INT8乘法器延迟约1ns累加器约0.5ns加上布线延迟16nm工艺下跑1GHz问题不大。但阵列越大时钟树越难做频率可能得降到800MHz。我一般会做时序分析找到频率和阵列大小的平衡点。5. 常见问题与排查技巧实录5.1 数据错位脉动阵列最经典的bug我第一次跑脉动阵列仿真时输出全是乱的。查了三天才发现是数据错位A和B的流入时间没对齐。脉动阵列要求A和B在正确的周期到达正确的PE。如果A晚了一个周期那它就会和错误的B相乘。排查方法在仿真里打印每个PE的输入输出看数据流动的轨迹。我一般会加一个调试信号把每个PE的a_in和b_in打到波形里然后逐周期检查。如果发现某个PE的a_in比预期晚就往前追看是哪个寄存器多了一级。解决方法是插入延迟链。在边界输入处加移位寄存器精确控制每个数据的流入时间。比如第i行的A数据需要延迟i个周期第j列的B数据需要延迟j个周期。这样所有数据就能在正确的时刻到达正确的PE。5.2 累加器溢出精度问题的隐形杀手前面提过累加器位宽不够会溢出。但溢出不一定报错可能只是结果偏小或者符号翻转。我遇到过一次跑小矩阵时结果正确跑大矩阵时结果完全不对。查了半天才发现是累加器在K256时溢出了。排查方法在仿真里监控累加器的最高位。如果发现符号位翻转或者数值突然变小那就是溢出。也可以算理论最大值INT8输入范围是-128到127乘加K次最大绝对值是128128K。K256时最大值是4,194,304需要23位。加上符号位至少24位。所以我用INT32是绰绰有余的。注意累加器位宽 输入位宽 × 2 log2(K) 1符号位。K是累加次数。这个公式我用了很多次没出过错。5.3 阵列利用率低问题往往不在阵列本身有时候你会发现脉动阵列的算力利用率只有30%第一反应是阵列设计有问题。但根据我的经验问题往往在数据供给端。如果内存带宽不够或者DMA传输太慢阵列就会经常饿着。排查方法统计阵列的valid信号。如果valid经常是0说明数据没供上。这时候要检查DMA的突发长度、内存的访问延迟、以及数据在片上的缓存策略。我一般会加大输入FIFO的深度让DMA可以提前预取数据。另外用双缓冲或者多缓冲来平滑数据流。还有一个常见原因是矩阵维度不匹配。如果矩阵是7x7阵列是8x8那每次都有1行1列是空的利用率只有(7/8)²≈77%。如果矩阵是3x3利用率只有14%。这时候要么换小阵列要么把多个小矩阵拼成一个大矩阵。5.4 常见问题速查表问题现象可能原因排查方法解决方案输出全错数据错位波形检查PE输入时序插入延迟链对齐数据小矩阵对大矩阵错累加器溢出监控累加器最高位增加累加器位宽利用率低数据供给不足统计valid信号占空比加大FIFO优化DMA时序不收敛关键路径太长静态时序分析插入流水线寄存器功耗过高时钟树功耗大功耗分析降低频率用局部握手权重加载慢没有双缓冲统计加载周期占比实现双缓冲权重加载6. 脉动阵列的变体与进阶玩法6.1 稀疏脉动阵列跳过零值实际AI模型里有很多零值尤其是经过ReLU激活后。如果脉动阵列能跳过零值就能省下大量周期。我实现过一个简单的稀疏优化在PE里加一个判断如果a_in或b_in是零就跳过乘加只传递数据。这样对于50%稀疏度的矩阵能省一半的乘加周期。但稀疏会带来负载不均衡的问题有的PE忙有的PE闲。所以需要动态调度把非零数据集中到某些PE上。这比较复杂我一般只在稀疏度超过70%时才考虑。6.2 混合精度脉动阵列INT8和FP16共存有些AI芯片需要同时支持INT8和FP16。一种做法是设计可重构的PE通过配置寄存器切换乘法器模式。INT8模式下一个PE可以拆成两个INT4乘法器算力翻倍。FP16模式下两个PE合并成一个FP16乘法器。我试过用Xilinx的DSP48E2做可重构PE通过配置可以支持INT8、INT16、FP16。面积比纯INT8大30%但灵活性高很多。对于需要跑多种模型的场景这个代价是值得的。6.3 脉动阵列在Transformer上的应用Transformer里的注意力机制核心是Q×K^T和softmax(QK^T)×V。这两个都是矩阵乘法非常适合脉动阵列。但softmax里有指数运算和除法脉动阵列做不了。所以通常用脉动阵列算矩阵乘法用向量单元算softmax。我实测过在16x16脉动阵列上跑BERT-base的注意力层矩阵乘法部分能跑到理论峰值的85%以上。瓶颈在softmax占了总时间的40%。所以现在有些芯片专门设计了硬件softmax单元和脉动阵列配合使用。7. 我个人在实际操作中的体会脉动阵列这个东西原理看起来简单但真正做起来坑很多。我最大的体会是不要一上来就追求大阵列。先做一个4x4或者8x8的小阵列把数据流跑通把时序调稳然后再扩展。大阵列的问题往往是小阵列问题的放大版小阵列没调好大阵列只会更糟。另一个体会是仿真比综合重要。综合一次要几分钟到几小时仿真一次只要几秒。我一般会在Python里把算法验证透再用Verilog做周期精确仿真最后才综合。这样能省下大量时间。还有一点脉动阵列的能效优势在边缘端更明显。云端芯片有足够的功耗预算可以用GPU或者通用架构。但边缘设备功耗只有几瓦脉动阵列的能效比优势就体现出来了。我做过一个智能摄像头项目用8x8脉动阵列做人脸检测整机功耗不到2W用GPU方案要8W以上。最后分享一个小技巧如果你用FPGA做原型可以用Xilinx的DSP48原语直接例化乘法器比用*号综合出来的电路更可控。DSP48的输入输出都有寄存器天然适合脉动阵列的流水线结构。我一般会把DSP48配置成乘法累加模式一个DSP就是一个PE省事又高效。这个方向后续还可以往Chiplet上扩展把多个小脉动阵列做成芯粒通过先进封装互连既能保持小阵列的高良率又能拼出大算力。我最近在关注这个方向等有实测数据了再和大家分享。

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

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

免费获取报价 →
↑