第二章 NPU基础架构:硬件组成、计算单元、存储层次、数据通路与指令集的关系

NPU(Neural Processing Unit,神经网络处理单元)并非通用处理器的简单加速版,而是针对神经网络计算中“海量并行乘加”、“数据复用”、“有限控制流”三大特征进行极致优化的专用处理器。理解NPU的硬件架构,是设计高效指令集的前提。本章将拆解NPU的硬件组成、计算单元、存储层次与数据通路,并阐明它们与指令集设计之间的耦合关系。只有理解了硬件如何“物理地”执行指令,才能设计出既高效又可控的指令编码。

NPU硬件微架构与指令集耦合关系图 计算阵列 (MAC Array) 16x16 乘加单元 / 可配置脉动阵列 支持 INT8 / FP16 / BF16 向量单元 (Vector Unit) 激活 / 归一化 / 池化 标量单元 (Scalar Unit) 地址计算 / 循环控制 / 分支 片上SRAM (On-chip Buffer) 输入/权重/输出 三块独立Buffer 容量: 2MB - 8MB 外部存储接口 DDR4/LPDDR5/HBM 带宽: 64GB/s - 1TB/s 数据 中间结果 Load/Store Load/Store 指令控制单元 (ICU) 取指 / 译码 / 发射 / 流水线控制 控制信号 指令集架构 (ISA) CONV / LOAD / STORE / VECTOR / SYNC 数据流 控制流 存储访问

上图展示了NPU内部的核心模块及其连接关系。可以看到,NPU并非一个单核处理器,而是一个由标量单元(负责控制)、计算阵列(负责矩阵运算)、向量单元(负责逐元素运算)以及多级存储构成的异构计算系统。指令集设计必须覆盖这些模块的协同工作,才能发挥硬件峰值性能。

2.1 NPU硬件组成:三大子系统的分工与协作

核心原理:从“控制”与“计算”分离说起

与CPU强调分支预测和乱序执行不同,NPU的硬件设计遵循“弱控制、强数据流”原则。其硬件组成通常包含三大子系统:

  • 控制子系统(标量单元 + 指令控制单元):负责指令的取指、译码、循环计数、地址生成和同步。它通常是一个简单的RISC-V或ARM核,或者是一个专用的微码引擎。
  • 计算子系统(矩阵单元 + 向量单元):矩阵单元(MAC Array)专门处理卷积和全连接层中的乘加运算;向量单元处理激活函数、BatchNorm、池化等逐元素或逐通道操作。
  • 存储子系统(片上Buffer + 外部DDR):片上SRAM用于暂存输入特征图、权重和输出特征图,以降低对外部带宽的依赖。
关键认知:NPU的指令集不是给“程序员”看的,而是给“编译器”和“硬件调度器”看的。指令编码的每一个bit,都直接对应着硬件中某个多路选择器的开关状态或某个Buffer的读写地址。

判断方法:如何快速识别一个NPU的架构风格?

在阅读NPU数据手册或汇编代码时,可通过以下特征判断其硬件组织方式:

  1. 看指令宽度:若指令长度固定为64bit或128bit,通常为VLIW(超长指令字)风格,一条指令同时控制MAC阵列和向量单元。
  2. 看寄存器文件:若存在大量标量寄存器(如R0-R31),说明控制逻辑较复杂;若只有少量配置寄存器,则可能采用“流式”指令集。
  3. 看Buffer分区:若输入、权重、输出Buffer物理分离,则指令中必须显式指定“从哪个Buffer读,写到哪个Buffer”。

案例拆解:以某款端侧NPU为例

假设某NPU的硬件规格如下:

模块规格指令集影响
MAC阵列8x8 脉动阵列,支持INT8指令需包含8x8的矩阵尺寸描述,且要求数据按行主序排列
向量单元32路并行,支持ReLU/Add向量指令需指定向量长度(最大32)
片上Buffer输入Buffer 64KB,权重Buffer 64KB,输出Buffer 32KBLOAD指令需指定目标Buffer编号(0/1/2)
外部接口DDR4,带宽12.8GB/sDMA指令需支持突发传输长度配置

在该硬件上执行一个3x3卷积,编译器生成的指令序列为:LOAD buf0, addr_in, size → LOAD buf1, addr_w, size → CONV buf0, buf1, buf2, stride=1, pad=1 → STORE addr_out, buf2, size。可以看到,指令集直接暴露了硬件Buffer的物理结构。

常见误区:初学者常误以为NPU指令集越简单越好。实际上,如果指令集不暴露硬件Buffer的容量限制,编译器将无法进行有效的“数据分块(Tiling)”,导致频繁的DMA搬运,性能可能下降10倍以上。

2.2 计算单元:MAC阵列与向量单元的指令级协同

核心原理:乘加运算的并行化

NPU的计算核心是乘加单元(MAC)。一个MAC执行 D = A * B + C 操作。为了提升吞吐,硬件将多个MAC排列成脉动阵列(Systolic Array)或SIMD阵列。指令集设计的关键在于如何描述“数据如何流入阵列”。

  • 脉动阵列:数据从左侧和上方流入,每个MAC只与相邻MAC通信。指令只需指定“启动”和“尺寸”,数据流由硬件自动控制。
  • SIMD阵列:所有MAC共享同一个控制信号,执行相同的操作。指令需指定“向量长度”和“操作码”。

操作流程:一条CONV指令的硬件执行过程

  1. 译码:指令控制单元解析出操作码(CONV)、源Buffer地址、目标Buffer地址、卷积核尺寸(KH、KW)、通道数(C)。
  2. 配置:将卷积参数写入MAC阵列的配置寄存器,如“权重加载模式”、“数据复用模式”。
  3. 数据加载:输入特征图从输入Buffer按行读取,权重从权重Buffer按列读取,二者在MAC阵列中“碰撞”并完成乘加。
  4. 累加与写出:部分和通过累加器暂存,最终结果写回输出Buffer。
设计要点:指令集中的CONV指令不应只包含“计算”信息,还应包含“数据布局”信息。例如,是否启用权重预加载(权重常驻在MAC阵列内部的寄存器中,避免重复搬运),这直接影响指令的执行周期数。

案例拆解:矩阵乘法指令的两种编码风格

假设需要计算 C[4][4] = A[4][4] * B[4][4]:

// 风格一:显式地址编码(类似CPU)
LOAD R0, A[0][0]
LOAD R1, B[0][0]
MAC R2, R0, R1
// 需要16条MAC指令,效率低

// 风格二:矩阵指令编码(NPU风格)
MATMUL A_buf, B_buf, C_buf, M=4, N=4, K=4
// 一条指令完成16次乘加,硬件自动调度数据流

显然,风格二才是NPU指令集的设计方向。但风格二要求硬件必须支持“二维数据块”的读取,这又反过来约束了片上Buffer的地址生成逻辑必须支持“二维步长(Stride)”寻址。

2.3 存储层次:指令集如何管理数据搬运

核心原理:存储墙问题与片上Buffer

NPU的性能瓶颈往往不在计算,而在数据搬运。因此,现代NPU普遍采用“多级存储”架构:寄存器文件(RF)→ 片上Buffer(SRAM)→ 外部DDR。指令集必须提供显式的数据搬运指令(如DMA指令),或者通过“缓存一致性”协议隐式管理。

存储层级容量访问延迟指令集管理方式
寄存器文件几KB1周期由标量指令直接读写
片上Buffer几MB10-20周期通过LOAD/STORE指令或DMA指令
外部DDR几GB200-400周期通过异步DMA指令,需同步等待

判断方法:何时使用同步指令,何时使用异步指令?

  • 同步LOAD:当后续计算指令立即需要该数据时,使用同步LOAD,硬件会阻塞流水线直到数据就绪。
  • 异步DMA:当数据可以提前预取时(如双缓冲场景),使用异步DMA指令,并配合WAIT指令进行同步。
实战经验:在编写NPU汇编时,应尽量将DMA搬运与计算重叠。例如,在计算第N层卷积时,同时预取第N+1层的输入数据。这要求指令集支持“异步DMA + 事件同步”机制。

案例拆解:双缓冲(Ping-Pong)调度

假设输入Buffer分为两个区域:Buf_A 和 Buf_B。调度流程如下:

  1. DMA_LOAD Buf_A, DDR_addr_0, size (异步)
  2. DMA_LOAD Buf_B, DDR_addr_1, size (异步)
  3. WAIT Buf_A_ready (等待第一次搬运完成)
  4. CONV Buf_A, weight, out (计算第一块)
  5. WAIT Buf_B_ready (等待第二次搬运完成)
  6. CONV Buf_B, weight, out (计算第二块)

通过这种方式,DMA搬运时间被计算时间掩盖,硬件利用率可接近100%。指令集必须提供DMA_LOAD、WAIT、CONV三类指令,且WAIT指令需要能指定“等待哪个事件”。

2.4 数据通路与指令集:从硬件连线到指令编码

核心原理:数据通路决定指令字段

数据通路(Data Path)是指数据从存储单元到计算单元再到存储单元的物理路径。指令集的编码格式必须与数据通路的宽度和方向严格匹配。例如:

  • 若数据总线宽度为128bit(一次可传输16个INT8数据),则LOAD指令的“字节数”字段必须是16的倍数。
  • 若MAC阵列支持“权重固定”模式(权重在阵列中复用),则指令需包含“权重保持”标志位。

操作流程:指令译码如何控制数据通路

// 假设一条32位NPU指令编码如下:
// [31:28] 操作码 (OPCODE)
// [27:24] 目标Buffer编号 (DST_BUF)
// [23:16] 源Buffer编号 (SRC_BUF)
// [15:0]  立即数或地址偏移 (IMM)

// 硬件译码逻辑(简化):
if (OPCODE == 0b0001) { // CONV
  MAC_CTRL.enable = 1;
  MAC_CTRL.src = SRC_BUF;
  MAC_CTRL.dst = DST_BUF;
  MAC_CTRL.size = IMM;
}

上述代码展示了指令字段如何直接映射到硬件控制信号。如果硬件数据通路中,MAC阵列只能从Buffer0读取输入,那么指令编码中“源Buffer”字段就只有1bit(0或1),而不是2bit。

风险提示:在自定义NPU指令集时,切忌“先定指令格式,再设计硬件”。正确的做法是:先画出数据通路框图,标注每条连线的宽度和方向,再根据连线定义指令字段。否则,会出现“指令无法控制硬件”或“硬件资源浪费”的问题。

总结复盘:硬件与指令集的协同设计要点

硬件模块指令集设计要点常见错误
MAC阵列支持矩阵尺寸参数、权重预加载标志未暴露阵列尺寸,导致编译器无法分块
向量单元支持向量长度、操作类型(ReLU/Add/Mul)向量长度固定,无法适应不同通道数
片上Buffer支持Buffer编号、二维寻址(行/列步长)仅支持一维线性寻址,导致数据重排开销大
DMA控制器支持异步搬运、事件ID、突发长度仅支持同步搬运,计算与搬运无法重叠