Scratchpad and NoC
导言
解释 Scratchpad 时,我们很快会遇到一个新问题:数据由 DMA 显式搬入片上 SRAM,但它从 HBM 到计算核心附近的 Scratchpad,究竟经过了什么?答案里反复出现的 NoC,又是什么概念?
这篇文章沿着一次数据搬运,把几个容易混在一起的对象摆回各自的位置:Scratchpad 负责本地存取,NoC 负责片上运输,DMA 负责执行显式搬运。
从工作台到道路¶
可以先把一颗复杂芯片想成一座城市。不同的计算核心、Scratchpad 和内存控制器是分散在城市里的建筑,NoC Router 是路口,连接 Router 的链路是道路,数据包则是行驶在路上的车辆。
沿着这个类比继续看:
- HBM 或 DRAM 是远处的仓库,容量大,但计算核心不能把每次细小访问都当成本地操作。
- Scratchpad 是计算核心旁边的工作台,空间有限,却适合摆放马上要反复使用的数据。
- DMA 像搬运调度者,按照程序、编译器或运行时给出的地址和大小执行搬运。
- NoC 是芯片内部的道路系统,把请求和数据送到对应模块。
这个类比先给出了一条主线:Scratchpad 与 NoC 解决的不是同一个问题。前者回答“数据在计算时放在哪里”,后者回答“数据怎样在芯片内部到达那里”。
Scratchpad 解决本地访存¶
Scratchpad Memory,简称 SPM,通常是一块靠近计算单元的片上 SRAM。它的核心特征不是单纯的“快”,而是由软件显式管理。
三个显式步骤¶
一次典型使用过程可以拆成三步:
- 搬入。程序先确定接下来需要的 Tile,再让 DMA 把这一块数据从 HBM 搬入 Scratchpad。
- 本地访问。数据到达后,计算核心通过普通的
load/store访问 Scratchpad,并在寄存器和计算单元中完成计算。 - 写回。结果先写入 Scratchpad,再由 DMA 搬回 HBM。
用伪代码表示,大致是:
copy_async(HBM[A_global], SPM[A_local], tile_size)
wait_or_barrier()
value = load(SPM[A_local + offset])
SPM[C_local] = compute(value)
copy_async(SPM[C_local], HBM[C_global], tile_size)
搬运完成前,计算核心通常不能安全读取对应区域,因此程序需要等待 DMA 完成,或者通过事件、屏障和双缓冲安排好先后关系。
没有自动补充
Scratchpad 中没有所需数据时,硬件通常不会像 Cache miss 那样自动从 HBM 加载。程序必须提前完成搬运,否则可能读到旧数据、未初始化数据,或者触发非法访问。
它为什么不是 Cache¶
Cache 和 Scratchpad 都可能使用片上 SRAM,也都试图缩短访存路径,因此很容易被看成同一种东西。真正的分界在于谁管理数据的进入、停留和离开。
| 维度 | Cache | Scratchpad |
|---|---|---|
| 数据搬运 | 硬件自动完成 | 软件、编译器或 DMA 显式完成 |
| 数据是否存在 | 通过 Tag 判断是否命中 | 程序必须自己保证 |
| 替换与生命周期 | 主要由硬件决定 | 主要由程序安排 |
| 延迟表现 | 受命中和缺失影响 | 通常更加确定 |
| 使用代价 | 编程负担较低 | 需要规划分块、同步和数据布局 |
所以,Scratchpad 的可预测性来自明确控制,代价也同样来自明确控制。
但“由 DMA 显式搬运”只说明了谁来执行,还没有解释数据怎样跨过芯片内部的距离。接下来就轮到 NoC 了。
NoC 解决片上运输¶
NoC(Network-on-Chip,片上网络)是芯片内部连接计算单元、缓存、Scratchpad、DMA 和内存控制器的通信网络。它不是内存,而是片上通信基础设施。
一个 NoC 通常包含几个基本对象:
- Router:根据目标地址选择下一条链路。
- Link:连接相邻 Router,实际传输数据。
- Network Interface:把 DMA 或计算核心的请求转换成 NoC 数据包。
- Buffer:暂存正在等待转发的数据。
- Routing Algorithm:决定数据经过哪条路径。
- Flow Control:接收端繁忙时施加背压,避免数据丢失。
数据包还可能被切成更小的 Flit(流控单元),在 Router 之间流水传输。
简单芯片可以让多个模块共享一条总线,但随着连接对象增加,大家会争用同一条通路。NoC 通过多条链路和多个 Router,让多组数据有机会并行传输,并处理路由、拥塞和带宽分配问题。
一次搬入发生什么¶
下面这张图只回答一个问题:一块数据怎样从 HBM 到达计算核心附近的 Scratchpad?
沿着图中的箭头,一次搬入通常经历:
- DMA 发出读请求。
- NoC 把请求送到内存控制器。
- 内存控制器从 HBM 读取数据。
- 数据经过 NoC 返回 DMA。
- DMA 把数据写入 Scratchpad。
- 计算核心再通过本地
load/store访问它。
这里最值得记住的不是路径上有多少个方框,而是两个动作主体:DMA 决定搬什么、搬到哪里;NoC 负责把请求和数据运送过去。
快从哪里来¶
Scratchpad 本身靠近计算核心,但“有一块很快的 SRAM”并不会自动带来高性能。真正的收益还依赖数据怎样被分块、复用和搬运。
分块与复用¶
完整数据放不进 Scratchpad 时,可以把它划分成多个 Tile。每次只搬入一个 Tile,在本地重复使用,再写回结果并处理下一块。
关键不是只把数据搬近,而是让一份数据搬进来后被使用多次。如果每份数据只访问一次,DMA 的搬运成本可能抵消 Scratchpad 的收益。
双缓冲¶
Scratchpad 可以划分为两个 Buffer:计算单元处理 Buffer 0 时,DMA 同时向 Buffer 1 搬入下一块数据。理想情况下,每个 Tile 的时间近似为:
如果搬运和计算没有重叠,则更接近:
双缓冲的价值,就是尽量把后一个加法变成前一个最大值。
两种拥塞¶
本地和片上网络都可能成为瓶颈:
- Scratchpad Bank 冲突。多个访问落到不同 Bank 时可以并行;同时落到同一 Bank 时则可能串行化。
- NoC 拥塞。多个核心同时访问同一内存控制器,或者多条路径经过同一个 Router,都可能引入排队和背压。
因此,一次搬运耗时可以粗略理解为:
Scratchpad 解决了本地访问距离,NoC 却仍决定数据能否顺畅到达。两者必须放在同一条数据路径上理解。
把角色摆正¶
回到最初的问题,可以用四句话区分这些概念:
| 对象 | 它主要解决什么 | 谁在管理 |
|---|---|---|
| Cache | 自动搬运数据并判断是否命中 | 硬件 |
| Scratchpad | 保存当前计算显式选择的本地工作集 | 软件、编译器或运行时 |
| DMA | 按给定地址和大小执行数据搬运 | 软件发起,专用硬件执行 |
| NoC | 在芯片内部运输请求和数据 | Router、链路与流控机制 |
于是,“Scratchpad 为什么快”和“数据怎样到达 Scratchpad”就不再是同一个问题:前者来自片上本地 SRAM,后者依赖 DMA 的显式安排和 NoC 的片上运输。
这也是理解 Scratchpad 访存机制时最有用的一条边界。具体实现可以不同,但只要先分清存储、搬运和运输三个角色,后续看到更复杂的数据路径时,就不会再把工作台、搬运者和道路混为一谈。
