第四章:NPU微架构入门:计算单元(MAC阵列)、控制单元、存储单元(SRAM/Scratchpad)的基本概念

好,我们进入正题。前面几章聊了指令集的设计哲学,这一章咱们得落地了——看看这些指令到底在芯片里是怎么跑起来的。

NPU的微架构,说白了就是三大块:算的、管的、存的。也就是计算单元、控制单元、存储单元。我当年刚接触NPU时,总觉得这玩意儿很神秘,后来拆开一看,嗯,其实核心逻辑并不复杂。

4.1 计算单元:MAC阵列——NPU的心脏

计算单元,尤其是MAC(乘加)阵列,是NPU最核心的部分。你想想看,神经网络里90%以上的运算都是矩阵乘法和卷积,而这些运算归根结底就是一堆乘法和加法。

MAC阵列的基本结构

一个MAC单元,就是做一次乘加操作:d = a × b + c。多个MAC单元排列成阵列,就能并行处理大量数据。

举个例子,一个4×4的MAC阵列:

// 伪代码:4x4 MAC阵列的一次运算
for (i = 0; i < 4; i++) {
    for (j = 0; j < 4; j++) {
        C[i][j] = A[i][0]*B[0][j] + A[i][1]*B[1][j] + ... + A[i][3]*B[3][j];
    }
}

这其实就是矩阵乘法的硬件实现。我在项目中遇到过一个问题:阵列大小怎么定?

关键参数:MAC阵列规模

  • 小阵列(8×8或16×16):适合低功耗、边缘设备,面积小,但吞吐量有限
  • 中阵列(32×32或64×64):主流选择,平衡性能和功耗
  • 大阵列(128×128以上):云端NPU,算力猛,但功耗和面积也大

我个人习惯,先看目标应用。如果是手机NPU,32×32起步;如果是自动驾驶芯片,至少64×64。别一上来就追求最大,流片失败的成本你扛不住。

MAC阵列的数据流

数据怎么喂给MAC阵列?主要有三种方式:

数据流方式 特点 典型应用
权重固定(Weight Stationary) 权重数据留在MAC内,输入数据流动 卷积层,权重复用率高
输入固定(Input Stationary) 输入数据留在MAC内,权重流动 全连接层,输入复用率高
输出固定(Output Stationary) 部分和留在MAC内,减少写回 深度可分离卷积

小提示:实际设计中,我建议用混合数据流。比如卷积层用权重固定,全连接层用输入固定。别死板,灵活点。

4.2 控制单元:NPU的“大脑”

控制单元负责指令译码、数据调度、状态管理。它不像MAC阵列那样吃算力,但设计不好,整个NPU都会卡住。

控制单元的核心模块

  • 指令译码器:把咱们之前设计的指令,翻译成控制信号。比如一条MAC R1, R2, R3,译码器要告诉MAC阵列:R1和R2是输入,R3是输出。
  • 地址生成器:计算数据在存储单元中的地址。我见过一个坑:地址生成器没考虑对齐,结果读出来的数据全是错的。
  • 状态机:管理NPU的运行状态——空闲、取指、译码、执行、写回。嗯,这里要注意,状态机设计要防死锁。

控制单元的流水线

NPU控制单元通常采用多级流水线。我习惯用5级流水:

// 5级流水线示意
Stage 1: IF (取指)   - 从指令存储器读取指令
Stage 2: ID (译码)   - 解析指令,生成控制信号
Stage 3: EX (执行)   - MAC阵列运算
Stage 4: MEM (访存)  - 读写数据存储器
Stage 5: WB (写回)   - 结果写回寄存器

为什么会用5级?因为MAC运算本身就需要多个时钟周期,流水线能提高吞吐量。但要注意,流水线越深,冒险问题越严重。

避坑指南:我曾经设计过一个8级流水线的控制单元,结果数据冒险和结构冒险搞得我焦头烂额。后来我学乖了:流水线深度不是越深越好,够用就行。对于NPU,4-6级是比较合理的范围。

4.3 存储单元:SRAM与Scratchpad

存储单元是NPU的“粮仓”。数据喂不饱MAC阵列,再强的算力也是白搭。

SRAM vs. Scratchpad

很多人搞不清这两个概念。我简单解释一下:

  • SRAM(静态随机存取存储器):片上缓存,速度快,但面积大。通常用作L1/L2缓存。
  • Scratchpad(便签存储器):也是片上存储,但由软件直接管理,没有硬件缓存一致性逻辑。

你想想看,SRAM是硬件自动管理的,你只管读写,缓存一致性由硬件保证。Scratchpad呢,你得自己管——数据什么时候搬进来,什么时候写回去,全由软件控制。

存储层次设计

NPU的存储层次一般是这样:

层级 容量 延迟 用途
寄存器文件 几百字节 1个周期 MAC阵列的临时数据
Scratchpad 几十KB到几MB 2-4个周期 权重、输入特征图
SRAM(L2缓存) 几MB 10-20个周期 中间结果、大块数据
DRAM(片外) 几GB 几百个周期 模型参数、数据集

重点:NPU的性能瓶颈往往不在MAC阵列,而在存储带宽。我见过一个项目,MAC阵列利用率只有30%,就是因为数据搬不过来。所以,存储带宽设计比算力设计更重要

Scratchpad的软件管理

用Scratchpad,你得写DMA(直接存储器访问)代码。比如:

// 伪代码:从DRAM搬运权重到Scratchpad
DMA_Config config;
config.src_addr = DRAM_BASE + weight_offset;
config.dst_addr = SCRATCHPAD_BASE;
config.size = 256 * 1024;  // 256KB
config.direction = DMA_DRAM_TO_SCRATCHPAD;

DMA_Start(&config);
// 等待DMA完成
while (!DMA_IsDone()) {
    // 可以干点别的,比如处理上一批数据
}

嗯,这里要注意:DMA传输和MAC计算要尽量重叠,别让MAC闲着等数据。

4.4 三大单元的协同工作

计算、控制、存储,这三者怎么配合?我画个简单的流程:

  1. 控制单元从指令存储器取指令,译码后生成控制信号
  2. 控制单元通知存储单元:把数据从DRAM搬到Scratchpad
  3. 控制单元通知计算单元:从Scratchpad读取数据,开始MAC运算
  4. 计算单元运算完成后,结果写回存储单元
  5. 控制单元检查是否还有下一条指令,有则重复

说白了,控制单元就是“项目经理”,计算单元是“干活的人”,存储单元是“仓库”。项目经理指挥仓库备料,然后让工人干活,干完再把成品存回仓库。

我的经验:设计微架构时,先画数据流图,再画控制流图。数据流决定性能上限,控制流决定能否达到这个上限。很多新手一上来就画电路,结果数据流不通,白费功夫。

4.5 本章小结

这一章我们聊了NPU微架构的三大单元:

  • 计算单元(MAC阵列):NPU的心脏,决定了算力上限。阵列规模、数据流方式要仔细权衡。
  • 控制单元:NPU的大脑,负责指令译码、数据调度。流水线设计要防冒险。
  • 存储单元(SRAM/Scratchpad):NPU的粮仓,带宽往往比算力更关键。Scratchpad需要软件管理,灵活性高但编程复杂。

下一章,我们会深入MAC阵列的微架构设计,包括脉动阵列、树形加法器等具体实现。到时候我会分享一些我在实际项目中踩过的坑,嗯,保证让你少走弯路。