跳转至

分支预测

香山前端在分支真正执行前就需要连续生成下一段 PC。Kunminghu V3 的 BPU 因此 不是一个单独的 BTB,而是“快速预测先开路、深预测随后纠正”的组合:S1 尽快让 FTQ 和 ICache 前进,S3 用容量更大、历史更长的预测器复核;IFU 预译码和后端执行 再分别形成后两道纠错。

对软件性能工程师,关键问题不是背下每张表,而是判断损失来自:

  • 方向错误、目标错误,还是根本没有识别到控制流指令;
  • S3 已及时修正,还是直到 IFU/执行阶段才 redirect;
  • 单个分支不可预测,还是代码布局、别名、调用约定或训练延迟造成系统性退化。
文档基线 kunminghu-v3 @ c7b373b · top.DefaultConfig 预测器容量、使能位、事件顺序和恢复周期不是跨版本 ABI。

本文口径

“源码事实”由固定 commit 直接支持;“软件推导”用于建立可证伪的性能假设; “待验证”要求使用目标 CONFIG 的 elaboration、统计输出或波形。本文不把 DefaultConfig 参数当作产品微架构规格。

两层预测、三次纠错

flowchart LR
  S0["S0<br/>接收起始 PC"]
  S1["S1 快速预测<br/>uBTB + aBTB + uTAGE + uRAS"]
  S2["S2<br/>深预测器 SRAM/历史传播"]
  S3["S3 深预测<br/>mBTB + TAGE + SC + ITTAGE + RAS"]
  FTQ["FTQ<br/>保存 S1 结果与 metadata"]
  IFU["IFU PredChecker<br/>预译码纠错"]
  EXE["Branch / Jump Unit<br/>执行期 resolve"]

  S0 --> S1 --> S2 --> S3
  S1 -->|"先写入"| FTQ
  S3 -->|"s3_override"| FTQ
  FTQ --> IFU --> EXE
  IFU -.->|"IFU redirect / train"| FTQ
  EXE -.->|"backend redirect / resolve / commit"| FTQ
  FTQ -.->|"历史与训练"| S0

Bpu.scala 第 49–84 行 实例化 FallThrough、MicroBtb、AheadBtb、MicroTage、MainBtb、Tage、Ittage、SC、 Ras、Phr、CommonHR 和 MicroRas。流水 valid/ready/fire/flush 在 Bpu.scala 第 116–139 行 定义。

这里有三类不同的纠错:

  1. S3 override:深预测纠正 S1,尚未等到指令解码或执行;
  2. IFU redirect:预译码识别到漏预测 JAL/JALR/return、错误 CFI 位置等;
  3. backend redirect:分支或跳转执行后发现方向、目标或属性错误。

三者都会浪费错误路径工作,但发现越晚,通常需要丢弃的年轻状态越多。准确周期 仍是待验证项,不能从 Scala stage 名直接推成产品恢复延迟。

当前预测器组成与参数

组件 当前参数 主要角色
uBTB 32 entries,22-bit tag/target S1 极快目标与属性预测
aBTB 1024 entries,4 banks,4 ways S1 更大容量的 ahead BTB
uTAGE 2 张 512-entry 表,history 5/9 S1 快速条件分支方向
mBTB 8192 entries,4-way,4 internal banks S3 主 BTB 与目标候选
TAGE 8 张 4096-entry、2-way 表 S3 条件分支方向
TAGE history 4、9、17、29、56、109、211、397 覆盖短到长相关性
SC path/global/backward/IMLI/bias 组件 修正 TAGE 方向选择
ITTAGE 5 张表,256/256/512/512/512 entries 间接跳转目标预测
ITTAGE history 4、8、13、16、32 目标与历史相关
RAS 16-entry commit stack,32-entry speculative queue call/return 目标

直接参数来源:

不要用 entry 数直接推面积或命中率

参数中的 Size、NumWays、NumBanks 还会经过索引、bank 和 SRAM packing。 容量相同的表在不同 PC/历史散列下也可能有完全不同的 alias 行为。面积与 最终实例数要在指定 CONFIG 的生成 RTL/SRAM 报告中确认。

S1:优先维持取指连续性

S1 的选择逻辑位于 Bpu.scala 第 294–340 行

  1. uBTB 提供快速 CFI/target;若识别为 return,可使用 uRAS target;
  2. aBTB 给出更多 CFI 候选;
  3. uTAGE 产生 taken mask;
  4. 若块内有多个 taken 候选,选择地址最早的一个;
  5. 若没有 taken,使用 fall-through。

S1 prediction 只有在 FTQ ready 时才能发射;源码注释指出 S0 stall 的常见来源是 FTQ full。复位后 BPU 还会等待各预测器 SRAM reset 完成,见 Bpu.scala 第 262–292 行

软件推导。 冷启动最前面的低 IPC 不能直接当稳态预测能力;至少要明确是否 包含 predictor SRAM reset、首次填表和 cache/TLB 冷启动。

S3:用更深的历史和更大容量纠正

S3 的选择逻辑位于 Bpu.scala 第 359–443 行

  • mBTB 提供块内 CFI 候选和普通 direct/indirect target;
  • TAGE 给出主方向预测,SC 可对方向作修正;
  • 对 return 优先使用 RAS target;
  • 对符合条件的 indirect branch 使用 ITTAGE target;
  • 其他控制流目标来自 mBTB;
  • 同样选择块内最早 taken 的 CFI。

S3 将方向、CFI position、branch attribute 和 target 与 S1 比较。任一不同即产生 s3_override,把 BPU 指针回到对应 FTQ entry 的后一项,覆盖旧预测并 flush 年轻的 S2/S1 工作。相关控制见 Bpu.scala 第 262–292 行Ftq.scala 第 174–245 行

软件推导。 S3 override 不是“免费命中”。它避免更晚的执行期误预测,但已经 发往 FTQ、ICache 或 IFU 的年轻工作仍可能作废。评估快速预测器时,应同时看最终 backend mispredict 和 S3 override rate。

FTQ 如何约束前跑与训练

FTQ 不仅是 prediction queue,还保存 redirect metadata、resolve metadata、 commit metadata,以及 ResolveQueue、CommitQueue,见 Ftq.scala 第 91–119 行

预测推进必须同时满足:

  • FTQ 未满;
  • BPU 与 fetch 的距离小于 8;
  • predictor train stall 未连续达到 8 周期。

条件见 Ftq.scala 第 160–172 行。 IFU resolve、backend resolve 和 commit 信息进入训练队列;redirect 会取消更年轻 的训练记录,见 Ftq.scala 第 354–464 行

软件推导。 FTQ full 不一定说明 BPU 慢。commit 受长延迟指令或后端资源阻塞 时,FTQ 也会因无法回收而反压 BPU。判断根因时必须结合 ROB/IBuffer/Decode/Rename 占用。

IFU 与后端分别纠正什么

IFU 预译码纠错

PredChecker.scala 第 80–123 行 检测:

  • direct JAL 没有预测 taken;
  • indirect JALR 没有预测;
  • return 没有预测;
  • 普通非 CFI 被预测为 taken;
  • 预测位置落在 32-bit 指令中间。

它计算 sequential/jump target 并形成 IFU redirect,见 PredChecker.scala 第 128–180 行

执行期纠错

Branch Unit 计算条件分支 taken/target 并产生 mispredict;Jump Unit 解析 JAL/JALR、target、branch type 与 RAS action:

backend redirect 在 FTQ 中优先于 IFU redirect,见 Ftq.scala 第 121–137 行

RAS 与 RISC-V 调用约定

BranchAttribute 根据指令的 rd/rs1 识别 RAS action。当前实现把 x1 和 x5 视作 link register:

  • direct/indirect jump 的 rd 为 x1/x5:push;
  • indirect jump 的 rs1 为 x1/x5 且 rd 不同:pop;
  • rd、rs1 都是 link register 且不同:pop-and-push。

解码见 Bundles.scala 第 108–135 行

软件推导。 遵守标准 x1/x5 call/return hint 的编译代码更容易使用 RAS;手写 汇编用普通寄存器保存返回地址、把 return 写成非典型 JALR,可能把它退化为普通 indirect target 预测。验证时应保持调用深度和代码布局一致,只改变链接寄存器用法。

自定义 CSR 控制与 A/B 实验

当前实现提供非标准 CSR Sbpctl = 0x5c0,定义见 CSRConst.scala 第 24 行。 控制位定义于 CSRCustom.scala 第 70–77 行

bit 预测器
0 uBTB
1 aBTB
2 mBTB
3 TAGE
4 SC
5 ITTAGE
6 RAS

复位值全部使能。下面是仅用于 M-mode bring-up 环境的示意;先保存原值,实验后恢复:

# 例:暂时关闭 TAGE,其他位保持不变
li    t0, (1 << 3)
csrrc t1, 0x5c0, t0

# ...运行固定 workload...

csrw  0x5c0, t1

这不是标准 RISC-V 接口

在 S/U-mode 直接访问可能触发 illegal-instruction trap;平台也可能通过固件、 Constantin 或配置改变实际控制。先在目标 RTL 中确认 CSR 存在、权限、位定义和 写入是否真正传到 BPU。单独关闭组件还会改变冷启动和训练状态,比较前应采用 一致的 predictor warm-up。

性能事件与归因边界

软件 HPM

Backend 的 ROB 事件包含分支/跳转数、branch mispredict、flush 和退休指令,定义见 Rob.scala 第 1695–1723 行。 推荐首先计算:

branch MPKI = 1000 × BR_MIS_PRED / minstret
branch mispredict rate = BR_MIS_PRED / retired branches

事件 raw ID 取决于该次配置的拼接顺序。必须保存 Backend elaboration 打印的 event set,不能把本次 ID 固化成跨版本脚本。

仿真 XSPerf

Bpu.scala 第 676–817 行 记录:

  • toFtqFire、s3Override;
  • S1/S3 各预测来源使用量;
  • taken、position、attribute、target mismatch;
  • 训练阻塞、分支类型和部分误预测原因。

FTQ 还有 resolve/commit mispredict、指针距离、双预取/双取指成功和失败原因,见 Ftq.scala 第 506–720 行

推荐仿真派生指标:

S3 override rate = s3Override / toFtqFire
target mismatch share = target mismatch / all S3 overrides
position mismatch share = position mismatch / all S3 overrides

XSPerf 主要是仿真/调试统计,不保证能从软件 mhpmcounter 读取。

当前 Top-down 局限

  • BPU 细分 top-down reason 仍有 TODO/置零,见 Bpu.scala 第 597–634 行
  • FTQ 暂把 conditional redirect 粗略归到 TAGE、indirect 归到 ITTAGE、return 归到 RAS,BTB/SC 仍有 TODO,见 Ftq.scala 第 469–492 行
  • IFU predecode redirect 当前统一标成 BTB miss。

因此 Top-down 只能先定位大类;方向、目标、位置和来源必须用 XSPerf、trace 或波形 确认。

五组可证伪实验

1. 方向相关性

选取同一条件分支,保持代码地址和输入规模不变,只改变输入模式:

  • 恒真/恒假;
  • 周期模式;
  • 与较长历史相关;
  • 接近随机。

比较 branch MPKI、TAGE/SC 来源和 override 类型。若性能变化主要来自方向可预测性, target/position mismatch 不应同步成为主项。

2. BTB 容量或别名

逐步增加热控制流 PC 数量,但保持每个分支方向模式简单。观察:

  • frontendFlush 和 branch MPKI;
  • mBTB miss 相关 XSPerf;
  • position/attribute/target mismatch;
  • 只改变函数布局或地址散列后问题是否显著变化。

若总分支数相近而重新布局即可恢复,优先怀疑索引别名或 bank 冲突,而不是方向历史。

3. 间接跳转与 ITTAGE

构造固定 PC 的 indirect branch,让 target 数从 1 增至多个,并分别设置与历史 相关、无关的 target 序列。比较 ITTAGE 开/关、target mismatch 和 cycles。恢复 ITTAGE 后若目标错误同时下降,才能支持“间接目标预测是主因”。

4. RAS 与调用深度

固定函数体和调用次数,分别改变:

  • 嵌套深度;
  • 正常返回与非局部跳转;
  • x1/x5 标准 hint 与非典型 JALR。

比较 return mispredict、RAS 来源和 cycles。异常展开、setjmp/longjmp、协程切换会 改变控制流语义,不能与普通 return 混为一组。

5. 分离 S1 质量与最终误预测

同一 workload 同时记录:

toFtqFire
s3Override
taken / position / attribute / target mismatch
backend BR_MIS_PRED
frontendFlush
cycles
  • override 高而 backend MPKI 不高:深预测在执行前挽救了大量 S1 错误;
  • 两者都高:深预测也无法解决,继续查容量、历史或程序本身;
  • MPKI 低但 Front_Bubble 高:瓶颈可能是 FTQ、ICache、ITLB 或后端背压。

波形检查点

一次完整误预测分析至少串起:

  1. BPU 的 S0–S3 PC、valid/ready、S1 source、S3 source、s3Override;
  2. FTQ 的 bpuPtr/pfPtr/fetchPtr/commitPtr 和对应 ftqIdx;
  3. IFU PredChecker 的 fault 类型与 wb redirect;
  4. Branch/Jump Unit 的 actual taken、target、mispredict;
  5. backend/IFU redirect 仲裁;
  6. 正确路径第一条指令重新进入 IFU、IBuffer、Decode 的时刻。

只看到“流水被 flush”无法判断根因,因为同一个 flush 可能来自 S3、IFU 或后端。

更新检查表

升级 commit 或更换 CONFIG 后重新确认:

  1. BPU 子预测器实例和默认使能;
  2. uBTB/aBTB/mBTB/TAGE/ITTAGE/RAS 的参数与散列;
  3. S1/S3 source 选择、override 比较字段和 PC 优先级;
  4. FTQ 前跑上限、训练阻塞和 resolve/commit 队列;
  5. PredChecker 与 Branch/Jump Unit 的 redirect 条件;
  6. Sbpctl 地址、权限和 bit 定义;
  7. HPM event set、XSPerf 名称和 Top-down TODO。

在目标配置重新生成并验证前,预测表容量、raw event ID、CSR 可访问性和精确恢复 周期都应标为待验证