分支预测¶
香山前端在分支真正执行前就需要连续生成下一段 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 行 定义。
这里有三类不同的纠错:
- S3 override:深预测纠正 S1,尚未等到指令解码或执行;
- IFU redirect:预译码识别到漏预测 JAL/JALR/return、错误 CFI 位置等;
- 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 目标 |
直接参数来源:
- uBTB Parameters.scala
- aBTB Parameters.scala
- uTAGE Parameters.scala
- mBTB Parameters.scala
- TAGE Parameters.scala
- ITTAGE Parameters.scala
- RAS Parameters.scala
不要用 entry 数直接推面积或命中率
参数中的 Size、NumWays、NumBanks 还会经过索引、bank 和 SRAM packing。 容量相同的表在不同 PC/历史散列下也可能有完全不同的 alias 行为。面积与 最终实例数要在指定 CONFIG 的生成 RTL/SRAM 报告中确认。
S1:优先维持取指连续性¶
S1 的选择逻辑位于 Bpu.scala 第 294–340 行:
- uBTB 提供快速 CFI/target;若识别为 return,可使用 uRAS target;
- aBTB 给出更多 CFI 候选;
- uTAGE 产生 taken mask;
- 若块内有多个 taken 候选,选择地址最早的一个;
- 若没有 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 环境的示意;先保存原值,实验后恢复:
这不是标准 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 行。 推荐首先计算:
事件 raw ID 取决于该次配置的拼接顺序。必须保存 Backend elaboration 打印的 event set,不能把本次 ID 固化成跨版本脚本。
仿真 XSPerf¶
- 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 或后端背压。
波形检查点¶
一次完整误预测分析至少串起:
- BPU 的 S0–S3 PC、valid/ready、S1 source、S3 source、s3Override;
- FTQ 的 bpuPtr/pfPtr/fetchPtr/commitPtr 和对应 ftqIdx;
- IFU PredChecker 的 fault 类型与 wb redirect;
- Branch/Jump Unit 的 actual taken、target、mispredict;
- backend/IFU redirect 仲裁;
- 正确路径第一条指令重新进入 IFU、IBuffer、Decode 的时刻。
只看到“流水被 flush”无法判断根因,因为同一个 flush 可能来自 S3、IFU 或后端。
更新检查表¶
升级 commit 或更换 CONFIG 后重新确认:
- BPU 子预测器实例和默认使能;
- uBTB/aBTB/mBTB/TAGE/ITTAGE/RAS 的参数与散列;
- S1/S3 source 选择、override 比较字段和 PC 优先级;
- FTQ 前跑上限、训练阻塞和 resolve/commit 队列;
- PredChecker 与 Branch/Jump Unit 的 redirect 条件;
- Sbpctl 地址、权限和 bit 定义;
- HPM event set、XSPerf 名称和 Top-down TODO。
在目标配置重新生成并验证前,预测表容量、raw event ID、CSR 可访问性和精确恢复 周期都应标为待验证。