跳到主要内容

ICache、ITLB 与持续供给

分支预测器解决“下一拍应该从哪里取”,ICache 解决“那个地址的指令字节何时可靠地交给 IFU”。在昆明湖 V2R2 中,ICache 不是一个收到 PC 后简单读 SRAM 的模块,而是一套把预测预取、地址翻译、权限检查、跨行取数、缺失处理、错误检测和流水撤销对齐起来的前端供给系统。

本页先建立完整数据流;实现级细节见 ICache 深入:VIPT、流水与 Miss。

1. ICache 在前端中的位置​

                    预测地址/FTQ 项
┌────────────────────────┐
│ BPU + FTQ │
└───────┬────────┬───────┘
│ │
预取地址 │ │ 真正取指请求
v v
┌───────────┐ ┌───────────┐
│IPrefetch │ │ MainPipe │
│Pipe │ │ │
└─────┬─────┘ └─────┬─────┘
│ │
ITLB/PMP + Meta │ Data + PMP/ECC
│ │
v │
┌───────────┐ │
│ WayLookup │────────┘
└─────┬─────┘
│ miss/prefetch miss
v
┌───────────┐ TileLink ┌──────┐
│ MissUnit │<──────────────────>│ L2 │
└─────┬─────┘ └──────┘
│ refill Meta/Data Array
v
┌───────────┐
│ Meta/Data │──────命中数据──────> IFU ──> IBuffer
│ Array │
└───────────┘

这里最容易混淆的是两条请求流:

  • IPrefetchPipe 接收 FTQ 提前给出的硬件预取地址,也接收后端 prefetch.i 软件预取。它查询 ITLB 和 MetaArray,提前算出 way hit、异常和 ECC 信息,并写入 WayLookup。
  • MainPipe 接收 FTQ 已经决定要消费的真正取指请求。它从 WayLookup 取得前面准备好的元数据,只读命中的 DataArray way,最后把指令块、异常和请求身份一起交给 IFU。

也就是说,正常稳态下并不是 MainPipe 临时做一次完整 tag lookup;预取流水跑在真正取指流水前面,用 WayLookup 把“地址翻译和选路结果”传给 MainPipe。

2. 默认组织参数​

V2R2 源码 ICacheParameters 的默认值如下:

项目默认值含义
Sets256每路有 256 个集合
Ways4四路组相联
Cache line64 Brefill 和 tag 管理的最小单位
总容量64 KiB256 × 4 × 64 B
Data banks8每行分成 8 个 8 B bank
Meta ports/banks2偶、奇 line 可并行查 tag,支持跨行取指
Fetch MSHR4服务真正取指 miss
Prefetch MSHR10服务硬件/软件预取 miss
WayLookup entries32暂存预取流水产生的翻译、命中与异常信息
默认替换策略set-PLRUMainPipe 命中时 touch,MissUnit 申请 victim
默认校验码parity检错而不纠错;配置上也可选择更强编码

这些是当前 V2R2 默认配置,不应被理解为 ICache 微架构的不可变常数。

3. 一次命中请求如何完成​

3.1 预取流水先行​

当 FTQ 的 prefetch pointer 领先于 fetch pointer 时,IPrefetchPipe 先处理将来可能真正取用的地址:

  1. 用虚拟地址索引 MetaArray;
  2. 同时访问 ITLB,得到物理 tag、页异常与 PBMT 属性;
  3. 比较各 way 的 tag,形成最多两条 Cache line 的 waymask;
  4. 进行预取侧 PMP 检查和 Meta ECC 检查;
  5. 把虚拟地址、物理 tag、waymask、异常等写入 WayLookup;
  6. 若 tag miss 且请求可缓存,则向 MissUnit 发出 prefetch miss。

预取流水的关键产物不是指令数据,而是 MainPipe 稍后要消费的访问凭据。

3.2 MainPipe 消费真正请求​

真正取指到达时,MainPipe 按以下顺序工作:

  1. S0 发射:取 FTQ 请求,并读取与之对应的 WayLookup 项;根据 waymask 只选择命中的 DataArray way;
  2. S1 对齐:收到 data bank,完成 PMP 检查、替换状态 touch,并监听同地址 MSHR 的返回;
  3. S2 决策:检查 data/meta parity、TileLink corrupt、异常和 miss 状态;命中则组装取指块返回 IFU,miss 则向 MissUnit 请求并等待 refill。

源码中能看到 S0/S1/S2 三组名称;官方文档称其为“两级流水”,是把 S0 看作请求发射,把 S1、S2 看作两级寄存处理。分析波形时应直接按信号上的 s0_* / s1_* / s2_* 区分,不要只凭“二级/三级”这个叫法推断延迟。

4. 为什么一次取指可能读两条 Cache line​

IFU 每次需要的不只是 PC 所在的一个 8 B bank,而是从任意起始位置取得一段连续字节。V2R2 的一个 64 B line 被切成 8 个 8 B bank;一次取指窗口访问连续 5 个 bank(共覆盖 40 B,供后续截取有效的 34 B 范围)。

line N:   bank0 bank1 bank2 bank3 bank4 bank5 bank6 bank7
line N+1: bank0 bank1 bank2 bank3 bank4 bank5 bank6 bank7

起点 bank2: [ b2 | b3 | b4 | b5 | b6 ] 单行
起点 bank6: [ b6 | b7 | b0 | b1 | b2 ]
跨到下一行

因此:

  • 起始 bank 为 0~3 时,窗口不跨 64 B 边界;
  • 起始 bank 为 4~7 时,需要同时处理当前 line 和下一 line;
  • MetaArray 分成奇偶两组,使相邻两 line 的 tag 可以同时读取;
  • MainPipe 分别判断两条 line 是否命中、异常或需要 miss,再按起始偏移拼成 IFU 所需字节。

“跨行”是 ICache 自身的访问问题;“一条 32 位指令跨取指块边界”则由 IFU 的预译码/拼接逻辑继续处理,两者不要混为一谈。

5. WayLookup 为什么存在​

如果没有 WayLookup,真正取指到来后必须重新等待 ITLB、tag 比较,再启动 DataArray;这会把地址翻译和 tag lookup 延迟直接放到供给关键路径上。

WayLookup 是预取流水和 MainPipe 之间的 FIFO 契约,主要保存:

  • 对应 FTQ 请求的虚拟地址和物理 tag;
  • 两条候选 line 的命中 waymask;
  • ITLB/PMP/PBMT/后端携带的异常属性;
  • Meta ECC 信息以及跨行相关状态。

当 MissUnit 完成 refill 时,WayLookup 会监听写回并更新尚未被 MainPipe 消费的项。这样一个“预取时 miss、真正消费前已 refill”的请求可以直接成为 hit。

如果 redirect 后预取指针来不及领先,IPrefetchPipe 和 MainPipe 可能在同一地址重新汇合;此时 MainPipe 必须等待新的 WayLookup 项,所以 redirect 后首个正确取指通常会多付出一拍元数据准备代价。

6. Miss、MSHR 与 refill​

MainPipe 或 IPrefetchPipe 判定 miss 后,不会自己直接访问 L2,而是交给 MissUnit:

miss request
│
├─ 地址已在某个 MSHR 中 ──> 合并/丢弃重复下行请求
│
└─ 新 line ──> 分配 MSHR ──> TileLink Acquire
│
两个 32 B beat 返回
│
写 DataArray + MetaArray
│
更新 WayLookup / 唤醒 MainPipe

V2R2 将 MSHR 分成两组:

  • 4 个 fetch MSHR,只服务真正取指,优先级更高;
  • 10 个 prefetch MSHR,只服务预取,避免预取完全挤占需求请求资源。

L2 侧数据通路为 32 B,而 Cache line 为 64 B,所以一个 refill 由两个 beat 构成。当前设计不交错不同 line 的返回 beat,所有 MSHR 可以共享一组 refill data register;source ID 用来找到最终应释放的 MSHR。

7. ITLB、PMP、PBMT 与 MMIO​

一次访问是否能作为普通 ICache hit 返回,不只由 tag 决定:

  • ITLB 给出物理地址,并可能产生 instruction page fault、guest page fault 等异常;
  • PMP 检查物理地址的取指权限;
  • PBMT 可把页面标成不可缓存或 I/O 属性;
  • MMIO/不可缓存取指 不走普通 ICache refill,由 IFU 的非缓存取指路径处理;
  • 后端对非法高地址等情况形成的异常,也会随 FTQ 请求带到 ICache。

异常优先级按“已经确定的上游异常优先”组织,整体可理解为:后端携带异常优先于 ITLB,ITLB 优先于 PMP,再之后才考虑 L2 返回的 corrupt。mmio、PBMT 的 NC/IO 属性是路由与访问类型,不应直接当成一种架构异常。

8. 预取请求从哪里来​

V2R2 有两种 ICache 预取来源:

来源产生者目的
硬件预取FTQ/FDIP让地址翻译、tag lookup 和 refill 跑在真正 fetch 之前
软件预取LoadUnit 执行 prefetch.i由软件提示未来会执行的代码地址

IPrefetchPipe 每拍只接收一个请求,软件预取优先。软件预取只负责检查目标是否 miss/异常并尝试填充,不改变控制流;多个同拍软件请求不会全部排队,已有待处理请求在特定阻塞条件下也可能被后来的请求覆盖,因此它是性能提示而不是功能正确性依赖。

9. Flush 与 fence.i​

不同 flush 的语义并不相同:

事件必须清理的状态原因
后端/IFU redirectMainPipe、IPrefetchPipe、WayLookup、相关 MSHR 状态旧路径上的请求身份已经失效
BPU redirect与 redirect 阶段和 FTQ index 对应的预取流水项较早、较精细地取消错误预测路径
fence.i流水、WayLookup、MSHR、Meta valid保证后续取指不再看到旧指令内容
sfence.vmaITLB 相关状态地址翻译失效;通常不等于清空 ICache 数据

fence.i 不要求把 DataArray 的每个 bit 真正写零。只要 Meta valid 被清除,旧 data 就不可命中;随后按新内容 refill 即可。这是“逻辑失效”和“物理擦除”的区别。

10. ECC/Parity 错误如何处理​

默认配置对 Meta 和每个 data bank使用 parity。一个 line 包含 1 份 Meta 校验和 8 份 data-bank 校验:

  • Meta 错误可能使命中 way 本身不可信,因此会失效该 set 的所有 way;
  • Data 错误只失效已经选中的命中 way;
  • 错误会上报 Bus Error Unit;
  • ICache 是只读副本,失效后可以复用普通 miss/refill 路径从下层重新取回正确内容。

因此这里的 parity 并非“报错后继续把坏指令交给 IFU”,也不必像脏 DCache 那样担心丢失唯一数据副本。

11. 性能分析:前端为什么会断粮​

看到 IFU 没有 valid 时,应先按资源层次分类:

类别典型迹象首要检查点
预测供给不足FTQ 没有可发请求BPU redirect、FTQ enqueue/dequeue
预取未领先MainPipe 等 WayLookupprefetchPtr 与 fetchPtr 距离、WayLookup valid
ITLB missMeta 可能已读,但翻译未返回ITLB req/resp、miss queue
Data/Meta 端口冲突S0 ready 拉低refill 写 SRAM、错误注入占端口
ICache missMSHR 分配并发出 Acquirefetch MSHR 占用、重复 line、L2 延迟
跨行半命中两条 line 中仅一条 missline0/line1 waymask 与 MSHR 请求顺序
IFU 反压ICache 已有响应但不能提交resp.ready、IBuffer 空间
flush流水 valid 被取消redirect 类型、FTQ index、fence.i

稳态吞吐高不代表 redirect 延迟低:双流水可以把大部分 tag/翻译延迟藏在预取距离里,但 redirect 会缩短甚至清空这段距离。

12. 推荐的波形观察顺序​

不要一上来盯 DataArray。按请求身份从上游一路追:

  1. FTQ 给 IPrefetchPipe 和 MainPipe 的地址、FTQ index、valid/ready;
  2. ITLB 请求/响应、物理 tag、page fault/PBMT;
  3. Meta 响应与两条 line 的 waymask;
  4. WayLookup enqueue、dequeue、flush、refill update;
  5. MainPipe 的 s0/s1/s2 valid、stall 和 line0/line1 状态;
  6. MissUnit 的 fetch/prefetch MSHR 分配、Acquire、Grant beat、SRAM write;
  7. Data/Meta ECC、corrupt、异常优先级;
  8. 最终送给 IFU 的数据、起始偏移、异常和请求身份。

若地址对了但 FTQ identity 错了,数据内容即使碰巧正确也不能算一次正确取指。

13. 阅读源码的最短路径​

建议按数据流,而不是文件名排序阅读:

  1. frontend/icache/ICache.scala:参数、Meta/Data Array 和顶层连线;
  2. frontend/icache/IPrefetch.scala:ITLB/Meta 查询与 WayLookup 写入;
  3. frontend/icache/WayLookup.scala:预取结果如何排队和被 refill 更新;
  4. frontend/icache/ICacheMainPipe.scala:真正取指的数据访问和最终判定;
  5. frontend/icache/ICacheMissUnit.scala:MSHR、TileLink 与 refill;
  6. frontend/icache/ICacheCtrlUnit.scala:ECC 控制和错误注入。

官方设计文档可对照阅读:ICache 总览、MainPipe、MissUnit 和 WayLookup。

14. 自测题​

  1. 为什么 WayLookup 存的是 waymask 和异常等元数据,而不是完整指令数据?
  2. 起始地址落在 bank6 时,需要哪五个 bank?为什么 MetaArray 需要同时处理两个 line?
  3. 一条请求“ITLB hit、tag hit、PMP fault”时能否把 DataArray 内容交给 IFU?
  4. 为什么 fence.i 清 Meta valid 就足以让旧 DataArray 内容不可见?
  5. 当一个 prefetch miss 与后来的真实 fetch 指向同一 line 时,哪些机制避免重复访问 L2?