From ada674df0cc9a6b6ca57e21e3022376b13030790 Mon Sep 17 00:00:00 2001 From: ngc7331 Date: Wed, 24 Sep 2025 17:11:18 +0800 Subject: [PATCH] docs: init v3 bpu & fallthrough --- docs/zh/frontend/BPU/.index.md | 7 +- docs/zh/frontend/BPU/Composer.md | 65 ------ docs/zh/frontend/BPU/FTB.md | 208 ----------------- docs/zh/frontend/BPU/FallThrough.md | 89 ++++++++ docs/zh/frontend/BPU/ITTAGE.md | 86 ------- docs/zh/frontend/BPU/RAS.md | 152 ------------- docs/zh/frontend/BPU/TAGE-SC.md | 285 ----------------------- docs/zh/frontend/BPU/index.md | 337 +++------------------------- docs/zh/frontend/BPU/uFTB.md | 47 ---- 9 files changed, 124 insertions(+), 1152 deletions(-) delete mode 100644 docs/zh/frontend/BPU/Composer.md delete mode 100644 docs/zh/frontend/BPU/FTB.md create mode 100644 docs/zh/frontend/BPU/FallThrough.md delete mode 100644 docs/zh/frontend/BPU/ITTAGE.md delete mode 100644 docs/zh/frontend/BPU/RAS.md delete mode 100644 docs/zh/frontend/BPU/TAGE-SC.md delete mode 100644 docs/zh/frontend/BPU/uFTB.md diff --git a/docs/zh/frontend/BPU/.index.md b/docs/zh/frontend/BPU/.index.md index d3045dea..3263a735 100644 --- a/docs/zh/frontend/BPU/.index.md +++ b/docs/zh/frontend/BPU/.index.md @@ -4,10 +4,5 @@ ``` {.include} index.md -Composer.md -FTB.md -uFTB.md -TAGE-SC.md -ITTAGE.md -RAS.md +FallThrough.md ``` diff --git a/docs/zh/frontend/BPU/Composer.md b/docs/zh/frontend/BPU/Composer.md deleted file mode 100644 index 53cc742e..00000000 --- a/docs/zh/frontend/BPU/Composer.md +++ /dev/null @@ -1,65 +0,0 @@ -# BPU 子模块 Composer - -## 功能概述 - -Composer 是一个用于组合多个预测器的模块。在南湖中,其组合了 uFTB、FTB、TAGE-SC、ITTAGE 和 RAS 五个预测器,并对外抽象成了一个 三级流水覆盖预测器。Composer 中的各个预测器可以通过写自定义寄存器 sbpctl 来实现开关,可以按需使用预测器。在检测到来自外部的重定向后,Composer 会把重定向请求发送给各预测器,以用于恢复推测更新的元素。在预测块所有指令提交后,Composer 中的各预测器会进行训练。最终,Composer 将三级预测结果输出至 Predictor。 - -三级 BPU 流水级内部重定向的时候如果有预测错误只会恢复那些推测更新的状态,比如说分支历史和 RAS,其它的预测器更新都是在提交后做。 - -此时如果不刷新预测器,只是刷新流水线,那下次这个地方不是还会预测错误?刷新流水线的同时是从纠正过的正确路径开始预测,如果接下来的路径又经过了同一个地方,是可能再次预测同一个结果的,但是也有可能因为分支历史不同,从而在 TAGE 等预测器里索引不同的表项。 - -如果在执行时发现目标地址错误,不会发起重定向,而是统一等到指令提交时再重定向。这样设计的一个原因是本身误预测的重定向就是在错误路径上的,它的执行结果可能也是错误的,这种情况下去训练,可能对预测器造成污染。 - -### 起始 PC 的配置 - -Composer 的 IO 接口 io_reset_vector 可以实现起始 PC 的配置。只需要将期望的起始 PC 传递给该 IO 即可。 - -### 与预测器的连接 - -Composer 将 uFTB、FTB、TAGE-SC、ITTAGE 和 RAS 五个预测器连接起来。因为共有三个分支预测器的流水级,且每个预测器是固定延迟的, 到那个流水级就一定完成预测,所以 Composer 只需要在对应流水级输出对应预测器的预测结果即可。 - -meta 是预测器预测的时候的数据,update 的时候拿回来更新用。都叫 meta 是因为 composer 将所有预测器整合起来,用共同的接口 meta 和外界交互。 - -### 预测器的开关 - -通过 Zicsr 指令,我们可以读写 sbpctl 这一自定义 CSR 来控制 Composer 中的各预测器的使能。sbpctl[6:0]代表了{LOOP, RAS, SC, TAGE, BIM, BTB, uFTB}这七个预测器的使能。其中,高电平代表使能,低电平代表未使能。具体地,spbctl 这一 CSR 的值通过 Composer 的 IO 接口 io_ctrl_*传入各个预测器,并由各预测器负责使能的实现。 - -### 重定向的恢复 - -Composer 通过 io_s2_redirect、io_s3_redirect 和 io_redirect_*等 IO 端口接收重定向请求。这些请求被发送给其各个预测器,用于恢复推测更新的元素,如 RAS 栈顶项等。 - -### 预测器训练 - -Composer 通过 IO 端口 io_update_*将训练信号发送给其各个预测器。总的来说,为防止错误执行路径对预测器内容的污染,各部分预测器在预测块的所有指令提交后进行训练。它们的训练内容来自自身的预测信息和预测块中指令的译码结果和执行结果,它们会被从 FTQ 中 读出,并送回 BPU。其中,自身的预测信息会在预测后打包传进 FTQ 中存储;指令的译码结果来自 IFU 的预译码模块,在取到指令后写回 FTQ;而执行结果来自各个执行单元。 - -## 整体框图 - -![Composer 模块整体框图](../figure/BPU/Composer/structure.png) - -## 接口时序 - -### 控制信号 Ctrl 接口时序 - -![控制信号 Ctrl 接口时序](../figure/BPU/Composer/port1.png) - -上图示意了 Composer 模块控制信号 Ctrl 接口的时序示例,io_ctrl 信号在传入 Composer 模块后,被 delay 一拍传给内部 components 子模块 。 - -### 重定向接口时序 - -![重定向接口时序](../figure/BPU/Composer/port2.png) - -上图展示了 Composer 模块重定向请求的接口,在 BPU 接收到来自后端的重定向请求后,会延迟一拍发往 Composer,因此 Composer 内预测器 会晚一拍收到相应请求。 - -### 分支预测块训练接口时序 - -![分支预测块训练接口时序](../figure/BPU/Composer/port3.png) - -类似重定向,为优化时序,分支预测块训练的 update 接口同样在 BPU 内部被延迟一拍发往 Composer 及其内部各预测器。 - -## 关键电路 - -下图分别展示了 Composer meta 拼接和重定向/分支历史更新来源仲裁逻辑 - -![Composer meta 拼接](../figure/BPU/Composer/key_structure1.png) - -![重定向/分支历史更新来源仲裁逻辑](../figure/BPU/Composer/key_structure2.png) diff --git a/docs/zh/frontend/BPU/FTB.md b/docs/zh/frontend/BPU/FTB.md deleted file mode 100644 index f7dd0d0f..00000000 --- a/docs/zh/frontend/BPU/FTB.md +++ /dev/null @@ -1,208 +0,0 @@ -# BPU 子模块 FTB - -## 功能概述 - -FTB 暂存 FTB 项,为后续高级预测器提供更为精确的分支指令位置、类型等信息。FTB 模块内有一 FTBBank 模块负责 FTB 项的实际存储,模块 内使用了一块多路 SRAM 作为存储器。 - -### 请求接收 - -0 阶段时,FTB 模块向内部 FTBBank 发送读请求,其请求 pc 值为 s0 传入的 PC,。 - -数据读取与返回 - -在发送请求的下一拍也就是预测器的 1 阶段,将暂存从 FTB SRAM 中读出的多路信号。 - -再下一拍也就是预测器的 2 阶段,从暂存数据中根据各路的 tag 和实际请求时 tag 的匹配情况生成命中信号并在命中时选出命中 FTB 数据。若存在 hit 请求,则返回值为选出的 FTB 项及命中的路信息,若未 hit,则输出数据无意义。tag 为 PC 的 29 到 10 位。 - -FTBBank 模块读出的数据在 FTB 模块内作为 2 阶段的预测结果以组合逻辑连线形式在当拍传递给后续预测器,此外这一读出的结果还会被暂 存到 FTB 模块内,在 3 阶段作为预测结果再次以组合逻辑连线传递给后续预测器。若 FTB 命中,则读出的命中路编号也会作为 meta 信息在 s3 与命中信息、周期数一起传递给后续 FTQ 模块。 - -此外,若 FTB 项内存在 always taken 标志,则 2 阶段的预测结果中对应 br_taken_mask 也在本模块内拉高处理。 - -### 数据更新 - -收到 update 请求后,FTB 模块会根据 meta 信息中是否 hit 决定更新时机。若 meta 中显示 hit,则在本拍立刻更新,否则需要延迟 2 周期等待读出 FTB 内现有结果后才可更新。 - -在 FTBBank 内部,当存在更新请求时,该模块行为也因立即更新和推迟更新两情况而有所不同。立即更新时,FTBBank 内的 SRAM 写通道拉高,按照给定的信息完成写入。推迟更新时,FTBBank 首先收到一个 update 的读请求且优先级高于普通预测的读请求,而后下一拍读出数据 ,选出给定地址命中的路编码传递给外部 FTB 模块。而若这一拍未命中,则下一拍需要写入到分配的路中。路选取规则为,若所有路均已 写满,则使用替换算法(此处为伪 LRU,详见 ICache 文档)选取要替换的路,否则选取一空路。 - -### SRAM 规格 - -单 bank,512 set,4 way,使用单口 SRAM,无读保持,有上电复位。 - -20 bit tag,60 bit FTB 项。 - -其中 FTB 项 - -1 bit valid - -20 bit br slot(4 bit offset,12 bit lower 2 bit tarStat, 1bit sharing, 1 bit valid) - -28 bit tail slot (4 bit offset , 20 bit lower, 2 bit tarStat, 1 bit sharing, 1 bit valid) - -4 bit pftAddr - -1 bit carry - -1 bit isCall - -1 bit isRet - -1 bit isJalr - -1 bit 末尾可能为 rvi call - -2 bit always taken - -## 整体框图 - -![整体框图](../figure/BPU/FTB/structure.png) - -## 接口时序 - -### 结果输出接口 - -![结果输出接口](../figure/BPU/FTB/port1.png) - -上图展示了分支预测器中 FTB 模块针对 fallThrough 地址为 0x2000001062 的请求连续三拍在分支预测器不同阶段输出预测结果的接口。 - -### 更新接口 - -![更新接口](../figure/BPU/FTB/port2.png) - -上图展示了 FTB 模块的一次针对 0x2000000E00 地址的更新操作,所有更新数据在一拍内全部传递。 - -## FTBBank - -### 接口时序 - -#### 读数据接口 - -![读数据接口](../figure/BPU/FTB/port3.png) - -上图展示了 FTBBank 读数据接口,FTBBank 在收到请求一拍后回复数据,即 16303ps 处回复的为 16301ps 的 0x2000001060 地址请求。 - -#### 更新读数据接口 - -![更新读数据接口](../figure/BPU/FTB/port4.png) -上图展示了 FTBBank 更新读数据接口,FTBBank 在收到更新读请求一拍后回复数据,回复的数据被外 部在一拍后用于更新写数据,可以注意到请求一拍后的 pftAddr 被用于结果读出一拍后的数据写入。 - -#### 更新写数据接口 - -![更新写数据接口](../figure/BPU/FTB/port5.png) -上图展示了 FTBBank 更新写数据接口,在收到写请求后一拍,数据完成写入。 - -### 功能概述 - -如上所述,FTBBank 主要存储 FTB 项,为 SRAM 模块的简单封装。 - -## FTB 项的生成条件简述 - -FTB 是 BPU 的核心。BPU 的其他预测部件所作出的预测全部依赖于 FTB 提供的信息。FTB 除了提供预测块内分支指令的信息之外,还提供预测块的结束地址。对于 FTB 来说,FTB 项的生成策略至关重要。南湖架构在原始论文 1 的基础上,结合这篇论文 2 的思想形成了现有的策略,记 FTB 项的起始地址为 start ,结束地址为 end ,具体策略如下: - -- FTB 项由 start 索引, start 在预测流水线中生成,实际上, start 基本遵循如下原则之一: - - start 是上一个预测块的 end - - start 是来自 BPU 外部的重定向的目标地址; -- FTB 项内最多记录两条分支指令,其中第一条一定是条件分支; -- end 一定满足三种条件之一: - - end - start = 预测宽度 - - end 是从 start 开始的预测宽度范围内第三条分支指令的 PC - - end 是一条无条件跳转分支的下一条指令的 PC,同时它在从 start 开始的预测宽度范围内 - -这种训练策略下,同一条分支指令可能存在于多个 FTB 项内。 - -和论文中的实现[1](https://docs.xiangshan.cc/zh-cn/latest/frontend/bp/#fn:ftbcite)一样,我们只存储结束地址的低位,而高位用起始地址的高位拼接得到。和 AMD[3](https://docs.xiangshan.cc/zh-cn/latest/frontend/bp/#fn:amd)的做法相似,我们还对[FTB](https://docs.xiangshan.cc/zh-cn/latest/frontend/bp/#ftb)项中的条件分支指令记录“总是跳转”位,该位在第一次遇到该条件分支跳转时置 1,在它值为 1 的时候,该条件分支的方向总是预测为跳转,也不用它的结果训练条件分支方向预测器;当该条件分支遇到一次执行结果为不跳转的时候,将该位置 0,之后它的方向由条件分支方向预测器预测。 - -## FTB 存储结构 - -FTB 项结构如下 - -| total | valid | brSlot | tailSlot | pftAddr | carry | isCall, isRet, isJalr | last_may_be_rvi_call | strong_bias | -| ----- | ------ | -------------- | -------------- | -------------- | -------------------- | --------------------- | -------------------- | ----------- | -| | 有效位 | 第一条分支信息 | 第二条分支信息 | 预测块结束地址 | 结束地址高位是否进位 | tailSlot 分支类型 | RAS 标识特殊位 | 强 bias | -| 62 | 1 | 21 | 29 | 4 | 1 | 3 | 1 | 2 | - -FTB slot 的组成,每个 slot 对应一条分支指令 - -| total | valid | offset | lower | tarStat | sharing | isRVC | -| ----- | ------ | ------------------ | ------------ | -------------------- | ------------------------------------ | -------------- | -| | 有效位 | 相对起始 PC 的偏移 | 目标地址低位 | 目标地址高位是否进位 | (对 tailSlot 来说)是否装了条件分支 | 是否是压缩指令 | -| 21/29 | 1 | 4 | 12/20 | 2 | 1 | 1 | - -FTB 共有 2048 项,4 路组相联,每项最多记录 2 条分支,其中第一条一定是条件分支,第二条可能是任意类型分支指令 - -## 目标地址生成逻辑 - -对于每个 slot,根据三种可能的高位进位情况(进位/退位/不变),在(PC 高位+1, PC 高位-1, PC 高位)三种情况中选择一个,和存储的目标地址低位信息进行拼位 - -## 更新流程 - -1. 表项生成 - - 1.1 从FTQ读取必要信息: - - 起始地址 startAddr - - 预测时读出的旧FTB项 old_entry - - 包含FTQ项内32Byte内所有分支指令的预译码信息 pd - - 此FTQ项内有效指令的真实跳转结果 cfiIndex,包括是否跳转,以及跳转指令相对startAddr的偏移 - - 此FTQ项内分支指令(如跳转)的跳转地址(执行结果) - - 预测时FTB是否真正命中(旧FTB项是否有效) - - 对应FTQ项内所有可能指令的误预测 mask - - 1.2 FTB项生成逻辑: - - 情况1:FTB未命中或存在错误 - 1) 无条件跳转指令处理: - - 不论是否被执行,都一定会被写入新FTB项的tailSlot - - 如果最终FTQ项内跳转的指令是条件分支指令,写入新FTB项的第一个brSlot,将对应的always_taken位置1 - 2) pftAddr设置: - - 存在无条件跳转指令时:以第一条无条件跳转指令的结束地址设置 - - 无无条件跳转指令时:以startAddr+取指宽度(32B)设置 - - 特殊情况:当4Byte宽度的第一条无条件跳转指令起始地址位于startAddr+30时,虽然结束地址超出取指宽度范围,仍按startAddr+32设置 - 3) carry位根据pftAddr的条件同时设置 - 4) 设置分支类型标志: - - isJalr、isCall、isRet按照第一条无条件跳转指令的类型设置 - - 特殊标志:当且仅当4Byte宽度的第一条无条件跳转指令起始地址位于startAddr+30,且该指令是call类型时,置last_may_be_rvi_call位 - - - 情况2:FTB命中且无错误 - 1) 插入新条件分支: - - 有空闲位置时: - a) tailSlot有无条件跳转:新条件分支一定指令序先于该无条件跳转,直接插入brSlot - b) brSlot有条件分支:按指令序排列,保持FTB项内brSlot的分支在指令序上先于tailSlot - c) 以上情况pftAddr均无需修改 - - 无空闲位置时: - a) tailSlot有无条件跳转: - - 新条件分支指令序一定比该无条件跳转靠前 - - 新条件分支替代无条件跳转的位置 - - pftAddr按无条件跳转的PC设置 - b) tailSlot有条件分支: - i) 新条件分支指令序比tailSlot内已有的条件分支指令序靠前: - - 将brSlot内的条件分支和新条件分支按指令序排布 - - pftAddr按tailSlot内原有分支的PC设置 - ii) 新条件分支指令序位于已有的所有分支指令后面: - - slot不发生任何改变 - - 只把pftAddr按新条件分支的PC设置 - 2) 更新jalr跳转地址信息: - - 当tailSlot内原来记载了jalr(RISC-V的无条件间接跳转指令) - - 跳转地址改变时,修改tailSlot内记载的相应目标地址低位和高位进位信息 - 3) 更新always_taken位: - - 如果always_taken位置1,且对应的条件分支此次执行结果是不跳转,拉低always_taken位 - -2. 写入SRAM - - 2.1 写入条件: - - - 新FTB项完全没有变化,或者虽然FTB未命中但uFTB命中:不需写入 - - 新FTB项有变化且非uFTB命中、FTB未命中的情况:需要写入 - - 2.2 写入流程: - - 情况1:预测未命中 - 1) 先用一拍做一次FTB读,判断此时命中情况 - 2) 如命中,写入对应路 - 3) 如仍未命中,根据替换算法选择一路写入 - 4) 总流程需要3个时钟周期 - 5) 过程中由于FTB读写口占用,需要FTQ配合不发出新的更新请求 - - 注:分bank可能提高更新带宽 - - 情况2:预测时命中(包括命中项内有错误信息的情况) - 1) 直接写入对应路,无需再次读出 - -3. 写入SRAM的流水线示意图如下: - -![写入SRAM的流水线](../figure/BPU/FTB/update.svg) - diff --git a/docs/zh/frontend/BPU/FallThrough.md b/docs/zh/frontend/BPU/FallThrough.md new file mode 100644 index 00000000..64fd8edf --- /dev/null +++ b/docs/zh/frontend/BPU/FallThrough.md @@ -0,0 +1,89 @@ +# FallThrough Predictor + +这是一个 always-not-taken 的 S1 预测器,用于在其余所有预测器都不命中/预测为不跳转时提供预测结果。 + +如果不考虑 V3 的各种特性,它只需要将输入的当前预测块的起始地址加上预测块大小(默认为 64 字节,下面讨论时为了方便直接使用 64 这个数字)即可作为预测的目标地址(即下一个预测块的起始地址)。 + +然而实际上,V3 的 S3 预测器组和 Ifu/ICache 对 S1 预测器组存在一些要求,这些要求可以通过 FallThrough 实现,避免给 ubtb 和 abtb 引入额外的复杂度。 + +## Half-align + +Half-align 的细节请参考 mbtb 文档,由于 S1 预测器组直接使用起始地址作为索引(即 not-align 的),所以若不做任何限制,S1 预测器组可以预测 64B 内的任意分支,而对于那些(64B 内,但跨越了两个 32B 边界)的分支,S3 预测器组无法进行预测或校验。 + +为方便描述,我们定义 aligned 函数伪代码如下,将地址对齐到 32B 边界,即清除地址的低 5 位: + +```text +define aligned(addr): + return addr & ~0x1F // 对齐到 32B +``` + +```text + start = 0x0a +0x00 | 0x20 0x40 0x60 0x80 + |----------------------------------| S1 预测器极限范围 [0x0a, 0x4a] + |-------------------------| S3 预测器极限范围 [0x0a, aligned(0x4a)=0x40] +``` + +如果分别在 ubtb 和 abtb 内对超范围的分支进行过滤,实现复杂度会比较高。考虑到所有训练进 ubtb/abtb 的分支都一定包含在曾经某次 fallThrough 预测的范围内,所以我们直接禁止 FallThrough 预测跨越两个 32B 边界的范围就可以避免 ubtb/abtb 预测超范围。 + +伪代码类似于: + +```text +target = aligned(start + 0x40) +``` + +另外考虑 cfiPosition,具体描述见 Bpu 整体设计文档,简单重复其定义:被预测为分支指令的地址相对于取指块起始地址对齐到 32B 后的偏移量,以指令为单位,即: + +```text +cfiPosition = (cfiAddr - aligned(start))[5:1] +``` + +在不考虑 half-align 的限制时,计算 cfiPosition 需要计算上述减法,进行限制以后反而简单了很多:fallThrough 预测的 cfi 为下一个 32B 对齐地址前的最后一条指令,其相对于取指块起始地址对齐到 32B 后的偏移量显然是个常数: + +```text +cfiPosition = (cfiAddr - aligned(start))[5:1] + = ((target - 2) - aligned(start))[5:1] + = (aligned(start) + 0x40 - 2 - aligned(start))[5:1] + = 31 +``` + +从语义上,fallThrough 预测的 cfiPosition 似乎没有什么用,因为它不跳转、甚至不是一个真的 cfi,但目前 ICache/Ifu 使用它计算需要取指的位置(需要访问的 data SRAM bank),因此它必须给出一个合理的值来最小化需要访问的 bank 数量,从而节省功耗、降低 2-fetch 的 bank 冲突[^1]。 + +[^1]: 在 V2 的设计中,ICache 假设永远读取 34B,不需要使用 Bpu 预测的 position,而 Ifu 则根据 Bpu 预测的 taken 进行选择,taken 时根据 Bpu 预测的 position 计算预测块大小,而不 taken 时则根据 target 计算。故 V2 的 fallThrough 预测不需要考虑这一点。V3 相当于把“不 taken 时使用 target-start 计算”这部分逻辑从 Ifu 挪到了 Bpu 内部。 + +## 禁止取指块跨页 + +在 V3 设计中,为了节省 Itlb 面积,Ifu/ICache 假设单次取指请求不会跨过页边界(4KB),即需要 Bpu 保证预测的 cfiPosition 和 start 在同一页内。 + +类似 half-align,我们同样可以通过限制 FallThrough 预测来在不给 ubtb/abtb 引入额外的复杂度的情况下满足这一点。 + +在具体做法上也比较类似 half-align,我们首先比较 start + 64 和 start 的 pfn(物理页号,即地址第 12 位及以上的位)[^2],若不同,则将对齐到 4KB 的结果作为 target: + +```text +define pageAligned(addr): + return addr & ~0xFFF // 对齐到 4KB +``` + +```text +if pfn(start + 0x40) != pfn(start): + target = pageAligned(start + 0x40) +else: + target = aligned(start + 0x40) +``` + +[^2]: 事实上,由于 + 64 最多跨越一个页边界,我们只需要比较 pfn 的最低位即可。 + +接下来考虑 cfiPosition,当跨页时 cfiPosition 不再是个常数,只能根据 target 和 start 计算: + +```text +if pfn(start + 0x40) != pfn(start): + cfiPosition = ((target - 2) - aligned(start))[5:1] + = (pageAligned(start + 0x40) - 2 - aligned(start))[5:1] + // 注意到取[5:1]时忽略了高位,因此 - aligned(start) 和 - aligned(start) - 0x40 效果相同 + // 而 aligned 只 mask 低 5 位,因此 - aligned(start) - 0x40 和 - aligned(start + 0x40) 效果相同 + = (pageAligned(start + 0x40) - 2 - aligned(start + 0x40))[5:1] + // 补码表示时取负数和按位取反再加一效果相同,故 + = ~(aligned(start + 0x40) - pageAligned(start + 0x40))[5:1] +else: + cfiPosition = 31 +``` diff --git a/docs/zh/frontend/BPU/ITTAGE.md b/docs/zh/frontend/BPU/ITTAGE.md deleted file mode 100644 index e34fa039..00000000 --- a/docs/zh/frontend/BPU/ITTAGE.md +++ /dev/null @@ -1,86 +0,0 @@ -# BPU 子模块 ITTAGE - -## 功能 - -ITTAGE 接收来自 BPU 内部的预测请求,其内部由一个基预测表和多个历史表组成,每个表项中都有一个用于存储间接跳转指令目标地址的 字段。基预测表用 PC 索引,而历史表用 PC 和一定长度的分支历史折叠后的结果异或索引,不同历史表使用的分支历史长度不同。在预测时,还会用 PC 和每个历史表对应的分支历史的另一种折叠结果异或计算 tag,与表中读出的 tag 进行匹配,如果匹配成功则该表命中。最终的结果取决于命中的历史长度最长的预测表的结果。最终,ITTAGE 将预测结果输出至 composer。 - -### 间接跳转指令的预测 - -ITTAGE 用于预测间接跳转指令。普通分支指令和无条件跳转指令的跳转目标直接编码于指令中,便于预测,而间接跳转指令的跳转地址来自运行时可变的寄存器,从而有多种可能选择,需要根据分支历史对其作出预测。 - -为此,ITTAGE 的每个表项在 TAGE 表项的基础上加入了所预测的跳转地址项,最后输出结果为选出的命中预测跳转地址而非选出的跳转方 向。 - -由于每个 FTB 项仅存储至多一条间接跳转指令信息,ITTAGE 预测器每周期也最多预测一条间接跳转指令的目标地址。 - -香山南湖架构中的 ITTAGE 提供了 5 个带 tag 的预测表 T1-T5,基准预测器和带 tag 的预测表的基本信息见下表。 - -| **预测器** | **是否带 tag** | **作用** | **表项构成** | **项数** | -| ------------- | -------------- | ------------------------------------------------ | -------------------------------------------------------------------------------------------------------------- | ------------------------------- | -| 基准预测器 T0 | 否 | 用于在带 tag 预测表的 tag 均不匹配时提供预测结果 | ITTAGE 中并没有实现 T0,而是直接使用 ftb 的预测结果作为基准预测结果 | | -| 预测表 T1-T5 | 是 | 存在 tag 匹配时,选历史长度最长者提供预测结果 | valid 1bittag 9bitsctr 2bits(最高位表示要不要输出这 个预测结果 )us: 1bit(usefulness 计数器)target:39 bits | T1-T2 各 256 项 T3-T5 各 512 项 | - -在 BPU 模块中维护了一个 256 比特的全局分支历史 ghv,并为 ITTAGE 的 5 个带 tag 的预测表分别维护了各自的折叠分支历史,折叠算法同 TAGE 。折叠历史具体配置见下表,其中 ghv 是一个循环队列,"低"n 位指的是从 ptr 指示的位置算起的低位: - -| **历史** | **index 折叠分支历史长度** | **tag 折叠分支历史 1 长度** | **tag 折叠分支历史 2 长度** | **设计原理** | -| ------------------- | -------------------------- | --------------------------- | --------------------------- | ------------------------------- | -| 全局分支历史 ghv | 256 比特 | 无 | 无 | 每比特代表对应分支跳转与否 | -| T1 对应折叠分支历史 | 4 比特 | 4 比特 | 4 比特 | ghv 取 ptr 起低 4 比特折叠异或 | -| T2 对应折叠分支历史 | 8 比特 | 8 比特 | 8 比特 | ghv 取 ptr 起低 8 比特折叠异或 | -| T3 对应折叠分支历史 | 9 比特 | 9 比特 | 8 比特 | ghv 取 ptr 起低 13 比特折叠异或 | -| T4 对应折叠分支历史 | 9 比特 | 9 比特 | 8 比特 | ghv 取 ptr 起低 16 比特折叠异或 | -| T5 对应折叠分支历史 | 9 比特 | 9 比特 | 8 比特 | ghv 取 ptr 起低 32 比特折叠异或 | - -ITTAGE 需要 3 拍延迟: - -* 0 拍生成寻址 index -* 1 拍读出数据 -* 2 拍选出命中结果 -* 3 拍输出 - - ### Wrbypass - -Wrbypass 里面有 Mem,也有 Cam,用于给更新做定序,每次 ITTAGE 更新时都会写进这个 wrbypass,同时写进对应预测表的 sram。每次更新的时候会查这个 wrbypass,如果 hit 了,那就把读出的 ITTAGE 的 ctr 值作为旧值,之前随 branch 指令带到后端再送回前端的 ctr 旧值就不要了 。这样如果一个分支重复更新,那 wrbypass 可以保证某一次更新一定能拿到相邻的上一次更新的最终值。 - -ITTAGE 的每一个预测表 T1~T5 都有着一个对应的 wrbypass,每个预测表的 wrbypass 中,Mem 都有 4 个 entry,每个 entry 存储 1 个 ctr;Cam 有 4 个 entry,输入更新的 idx 和 tag 就可以读到对应数据在 Cam 中的位置。Cam 和 Mem 是同时写的,所以数据在 Cam 中的位置就是在 Mem 中的位置。于是利用这个 Cam,我们就可以在更新的时候查看对应 idx 的数据是否在 wrbypass 中。 - -#### 预测器训练 - -首先,定义所有产生 tag 匹配的预测表中所需历史长度最长者为 provider,而其余匹配的预测表(若存在的话)被称为 altpred。 当 provider 的 ctr 为 0 时,会选择 altpred 的结果作为预测结果。 - -ITTAGE 表项中包含一个 usefulness 域,当 provider 预测正确而 altpred 预测错误时 provider 的 usefulness 置 1,表示该项是一个有用的项,便不会被训练时的分配算法当作空项分配出去。当 provider 产生的预测被证实为一个正确的预测,且此时的 provider 与 altpred 的预测结 果不同,则 provider 的 usefulness 域被置 -若预测地址与实际一致,则将对应 provider 表项的 ctr 计数器自增 1;若预测地址与实际不一致,则将对应 provider 表项的 ctr 计数器自减 1。ITTAGE 中,会根据 ctr 判断是否采取这个预测的跳转目标结果。当 ctr 为 0 时,会选择 altpred 的结果。 - -接下来,若该 provider 所源自的预测表并非所需历史长度最高的预测表,则此时执行如下的表项增添操作。表项增添操作会首先读取所有历史长度长于 provider 的预测表的 usefulness 域。若此时有某表的 usefulness 域值为 0,则在该表中分配一对应的表项;若没有找到满足 usefulness 域值为 0 的表,则分配失败。当有多个预测表(如 Tj,Tk 两项)的 usefulness 域均为 0 时,表项的分配概率是随机的,分配的时 候随机把某些 table 给 mask 掉,让它不会每次都分配同一个。这里的表项分配的随机性是通过 chisel 的 util 包里的 64 位线性反馈移位寄存 器原语 LFSR64 生成伪随机数来实现的,在 verilog 代码中对应 allocLFSR_lfsr 寄存器。在训练时,用 8 位 饱和计数器 tickCtr 统计分配失败次数-成功次数,当分配失败的次数足够多,tickCtr 计数器计数到满达到饱和时,触发全局 useful bit reset,把所有的 usefulness 域清零。 - -注:ITTAGE 的清零 usefulness 域的饱和计数器名字是 tickCtr,长度为 8 比特,名字和长度均与 TAGE 不同。 - -最后,在初始化时/TAGE 表分配新项时,所有的表项中的 ctr 计数器均被设置为 0,所有的 usefulness 域均被设置为 0。 - -## 存储结构 - -* 5 张历史表,项数分别为 256、256、512、512、512,每张表根据 pc 低位分了共 2 个 bank,每个 bank 有 128 个 set,每个 set 对应一个 FTB 项的最多 1 条间接跳转。 -* 每个表项含有 1bit valid,9bit tag,2bit ctr,39bit target,1bit useful;其中 useful bit 独立存储,valid 使用寄存器堆独立存储 -* 以 FTB 结果作为 base table,等效于 2K 项(但 FTB target 位宽不够,无法有效存储远跳转地址) -* 历史表每个 bank 有 4 项的写缓存 wrbypass - -## 索引方式 - -* index = pc[8:1] ^ folded_hist(8bit) 或 pc 和 folded_hist 各 9bit -* tag = pc[17:9](或 pc[19:10]) ^ folded_hist(9bit) ^ (folded_hist(8bit) << 1) - * 此处大概还是应该用 pc 低位比较好,而不是用 index 没用过的另一段 pc,或者 -* 历史采取基本的分段异或折叠 - -## 预测流程 - -* s0 进行索引计算,地址送入 SRAM -* s1 读出表项,进行 bank 选取,以及判断是否命中,结果寄存到 s2 -* s2 计算最长历史匹配和次长历史匹配: - * 当存在历史表命中,且 ctr!=0 的时候,尝试使用最长历史结果目标地址 - * 当 provider 信心不足(ctr==0)时,若次长历史命中,尝试使用次长历史结果目标地址 - * 当没有历史表命中的时候,使用 FTB 结果 - * 尝试使用历史表的结果时,只有 ctr>1 才会真正使用,若 ctr<=1,则不使用结果 -* s3 使用目标地址,在 BPU 内和 s2 结果进行比对,判断是否需要冲刷流水线 - -## 训练流程 - -基本和 TAGE 相同,对于 target 字段,仅当分配新表项或者原 ctr 为最小值 0 时,才会替换新值,否则保持原样 diff --git a/docs/zh/frontend/BPU/RAS.md b/docs/zh/frontend/BPU/RAS.md deleted file mode 100644 index a1d2e332..00000000 --- a/docs/zh/frontend/BPU/RAS.md +++ /dev/null @@ -1,152 +0,0 @@ -# BPU 子模块 RAS - -## 术语说明 - -| 缩写 | 全称 | 描述 | -| ------------ | ------------------------------------------ | ------------------------------------------ | -| TOSW | Top Of Speculative Queue Write Pointer | 推测队列写指针 -| TOSR | Top Of Speculative Queue Read Pointer | 推测队列读指针 -| NOS | Next Older Speculative Queue entry Pointer | 推测队列更旧项指针 -| ssp | Speculative Stack Pointer | 虚拟推测栈指针 -| nsp | Next Stack Pointer | 提交栈指针 - -## 功能 - -RAS 预测器使用栈结构来预测函数调用与返回这类具有成对匹配特性的执行流。其中调用(push/call)类指令的特征为类指令的特征为目标寄存器地址为 1 或 5 的 jal/jalr 指令。返回(ret)类指令的特征为源寄存器为 1 或 5 的 jalr 指令。 - -在实现中,RAS 预测器在 s2 和 s3 两阶段提供预测结果。 - -昆明湖架构的 RAS 预测器与南湖架构差异较大。通过引入持久化队列,新的 RAS 预测器结构解决了局部预测器因推测执行所导致的预测器数据污染问题。同时,新结构也保留了和南湖架构类似的栈结构作为提交栈,存储提交后的压栈信息,以弥补持久化队列存储密度不高的缺点。 - -### 基于持久化队列设计的 RAS 预测器架构整体概述 - -RAS 预测器利用分支预测 FTB 块中的 call/return 指令局部的配对信息对 return 指令作出预测。由于正常调用的函数执行完毕会返回到调用 指令的下一条指令,RAS 预测器可在遇到 call 指令时根据当前指令 PC 及当前指令是否为 RVC 指令(确定指令宽度为 2 还是 4)生成其函数调用返回地址的预测结果并压栈。 - -由于现代超标量乱序处理器通常采用深流水线推测执行技术,分支预测器会在之前所预测指令执行结果确定前为后续指令生成预测结果,也即,若面临 ABC 三个连续的分支指令预测请求,RAS 预测器并不能在预测 B 时获取到 A 块内指令的最终执行结果,而只能获取到 A 块的 Z 块内指令的最终执行结果和基于分支预测结果的 Z 到 B 块间推测结果。若 Z 到 B 块间存在错误的分支预测结果,则需要对其涉及的分支预测器状态进行误预测恢复。如前文所述,RAS 预测器基于栈结构对 call-return 指令对作出预测,配对信息准确性对其准确率至关重要。要精确地恢复 RAS 预测器在发生错误预测点的状态,最直观的做法是从当前最新预测点回溯到发生误预测点,撤销其间对 RAS 栈作出的所有改动。而为提升误预测恢复及时性,现代处理器通常难以允许误预测恢复时进行如此高复杂度的操作。在南湖架构设计中,RAS 预测器在发生误预测时仅恢复发生误预测预测块对应栈顶项和 RAS 栈顶指针。这一恢复策略可以应对因推测执行导致的误预测点 RAS 栈顶及其更上方数据的污染,但无法应对推测执行中因先 pop 再 push 对 RAS 栈顶下方数据的污染。 - -这类污染会导致后续的 return 指令跳转目标出错,从而产生误预测。为解决这一问题,昆明湖架构的新 RAS 预测器通过引入持久化队列, 保存 RAS 推测执行过程中的所有局部状态,实现了抗上述污染的预测。具体地,持久化队列模拟的栈结构有读指针 TOSR、写指针 TOSW 和栈底指针 BOS 三个指针,每一项除记录自身数据外,还记录其前一项在持久化队列中的位置指针 NOS;在每次栈 push 操作时均前移一次 TOSW 为当前压栈数据新分配一项存储空间,并将当前 TOSR 指向 TOSW 原位置(即新分配空间),新压栈的项记录的 NOS 存储 push 操作前的读指针位置;在每次栈 pop 操作时,将 TOSR 移动到当前 TOSR 指向的项内 NOS 指针位置。通过上述方式,RAS 能够通过前向链表遍历当前版本(对应一个推测执行路径)栈的所有项,这一设计也不会覆盖任何非本版本(对应其他推测执行路径)的 RAS 栈数据。 - -为提升 RAS 有效存储容量,并非所有 RAS 项均采用持久化队列形式存储。根据实践数据,为满足推测执行路径需要,持久化队列最大约需要 28 项,因而 RTL 实现中使用了 32 项持久化队列。在 RAS 项对应预测块(即含 call 指令的预测块)指令提交后,我们可以将该块从持久化队列释放移入提交栈中,释放操作通过改变 BOS 指针到提交预测块对应的 TOSW 指针。提交栈采用与南湖架构相同的设计,压栈时增加提交栈指针 nsp 并将压入数据写入新栈顶;出栈时减少栈指针 nsp。由于其仅存放提交后的确定性信息,不存在推测执行污染问题。原 BOS 到新 TOSW 这一区间在提交栈中可能存在其他错误路径上的压栈结果,而这些结果可以在这一 BOS 移动过程中被自然释放。 - -由于引入了持久化队列和提交栈两个结构,栈顶项可能在二者之一,在提供预测结果时会需要动态判断。持久化队列是一个环形队列,其指针除用于寻址的 value 外还有一 flag 位,这一位可协助判定 BOS 和 TOSW、TOSR 的位置关系。当 TOSR 位于 BOS 之上 TOSW 之下时,栈顶项位于持久化队列内部;当 TOSR 位于 BOS 之下时,栈顶项位于持久化队列外部,即位于提交栈内。因而,我们能够在运行时动态选出栈顶项。注意到,我们从提交栈获取的栈顶项并非始终与已提交指令的栈顶一致,因而,我们需要为 RAS 预测器维护另一个提交栈指针 ssp,其含义为,持久化队列中该项在被压入提交栈后所处的位置。在从提交栈访问栈顶项时利用 ssp 而非 nsp 完成数据读取。 - -上述讨论全部基于持久化队列容量充足而不发生溢出的情况。若当前程序段内 push 操作过于密集而后端执行速度不够快,可能会出现持久化队列容量不足发生溢出的情况。这一情况有两种可能的处理方案:强行覆盖或者动态关闭返回堆栈的预测。目前昆明湖架构选择后一种实现策略。在当前 BOS 和 TOSW 即将重叠时,将 BOS 强制前进一项来避免持久化队列被意外清空。由于 BOS 记录项并不一定需要在此期间使用,此策略能够略微减少前端的阻塞。不足在于密集的push操作可能在错误路径上,被覆写的项如果需要使用也会导致少量出栈错误,且出栈错误原因复杂。动态关闭方案不足在于密集的push操作可能在正确路径上,因为存在未被写入返回堆栈的项,将可能导致栈内数据错误。如果是递归这种错误的影响可能减轻。出于可控性考虑,目前昆明湖使用后一种方案。 - -为时序优化考虑,栈顶项的读取/更新并非在收到读取请求的当拍完成,而是在其上一拍或上 N 拍根据当拍的压/出栈操作更新。为减少对 推测队列的写操作,在 BPU 2 流水级的 push/pop 结果也不会直接写入推测队列,而是延迟一拍后写入。考虑到存在本拍写入的数据在下一 拍需要获取读数据的情况,设计了写 bypass 机制,准备写入的数据将在本拍首先用于更新写 bypass 的 writeEntry 项。下一拍要求读取的指针若与写 bypass 记录的指针位置匹配,则使用 bypass 值,否则使用从栈顶读取的值(实际因时序优化考虑这一逻辑被提前到读取栈顶的上一拍)。 - -### 2 阶段结果 - -在 BPU 2 阶段,由于其他分支预测器的预测结果还未完全确定,可能存在需要更新的预测结果,当前的预测结果并非最终确定的推测执行路径。当前的预测结果对同一时刻位于 3 阶段的分支预测块不会改变当前 2 阶段预测块的起始地址和现在位于 call/ret 指令前的其他分支指令不会再被预测跳转作出了成立假设。若 2 阶段从 FTB 传来的 FTB 项有效且其中存在 push 类指令,则将该指令之后下一指令的 PC 值压入 RAS 栈;若 2 阶段从 FTB 预测器传来的 FTB 项有效且其中存在 pop 类指令,则将当前栈顶的地址作为结果返回并对结果出栈 - -在 RAS 内,上述行为具体分解如下 - -在 2 阶段的 FTB 项内看到预测跳转的 call 指令,则拉高 s2_spec_push 信号并根据当前 call 指令 pc 生成压栈的地址信息,指示内部 RASStack 模块动作。RASStack 模块在检测到 push 动作时,会利用传入的栈顶地址作为新写入持久化队列 entry 的预测地址,若新写入地址与原地址相同且原栈顶 counter 未饱和,则基于原栈顶项 counter+1 作为新项 counter,否则新项 counter 取 0。生成的新 entry 被用于以下三个用途:1,下一拍更新 writeByassEntry 寄存器,供连续预测读取栈顶项使用;2,当拍内更新栈顶项,供连续预测读取使用;3,打拍后用于在下下拍更新持久化队列。与此同时,如上所述,TOSR 被更新为当前的 TOSW,TOSW 更新为当前 TOSW+1,全局的 ssp、sctr 类似上述 counter 更新算法,若新旧栈顶地址相同且原 sctr(与原栈顶 entry 的 ctr 相同)未饱和,则 ssp 不变,sctr=sctr+1;否则 ssp=ssp+1,sctr=0。为 处理可能的持久化队列溢出情形,若此时持久化队列接近溢出,返回堆栈预测器将被暂停。 - -在 2 阶段的 FTB 项内看到预测跳转的 call 指令,则拉高 s2_spec_pop 信号,指示内部 RASStack 模块动作。RASStack 模块在检测到 pop 动作时,若当前 sctr 非 0,则 sctr=sctr-1,ssp 不变;否则 ssp=ssp-1,sctr=新栈顶项的 sctr。TOSR=原 top 的 NOS,TOSW 保持不变。单独维护的栈顶项也会利用更新后的 ssp、sctr、TOSR 和 TOSW 来更新 - -### 3 阶段结果 - -在 3 阶段的 FTB 项内看到预测跳转的 return 指令,则拉高 s3_push 信号;在 3 阶段的 FTB 项内看到预测跳转的 call 指令,则拉高 s3_pop 信号。当前预测块在 2 阶段作出的预测结果也分别被打拍到 3 阶段(s3_pushed_in_s2 和 s3_poped_in_s2)。若 2 阶段与 3 阶段判断要 采取的动作不同,则需要在 3 阶段进行恢复,无论是否恢复,3 阶段均使用 2 阶段读出的 RAS 栈顶项作为预测结果 - -因为 2 阶段的 push/pop 和 3 阶段的 push 和 pop 仅存在如下几种情况,因而 3 阶段可通过 push/pop 操作撤销 2 阶段的操作。 - -| | | | -| ------- | ------- | ------------- | -| S2 push | S3 push | No fix needed | -| S2 push | S3 keep | Fix by pop | -| S2 keep | S3 push | Fix by push | -| S2 keep | S3 keep | No fix needed | -| S2 keep | S3 pop | Fix by pop | -| S2 pop | S3 keep | Fix by push | -| S2 pop | S3 pop | No fix needed | - -3 阶段 push/pop 操作在 RASStack 内的具体动作与 2 阶段相同,此处不再重复 - -### 误预测状态恢复 - -在预测块离开 BPU 后,在 IFU 或后端执行时可能会触发重定向,在遇到重定向时,RAS 预测器需要进行状态恢复。具体地,RAS 的 TOSR、TOSW、ssp 和 sctr 都需要根据误预测发生前的对应 meta 信息恢复,随后,根据误预测的指令自身是否为 call/return 指令,还需要分别通过 push 和 pop 操作来调整栈结构。Push 和 pop 操作的具体动作同 2、3 预测流水级,此处略。 - -### 提交 entry 迁移 - -如上所述,在含 call 指令的预测块提交时,BOS 会被更新为预测时的 TOSW,同时将预测块对应的 entry 写入 commit stack 栈顶(以 nsp 寻址 )并更新 nsp。Nsp 更新算法类似 ssp,若存在递归且栈顶 counter 未满则 counter=counter+1,nsp 不变,否则 nsp=nsp+1,counter=0。 - -## 整体框图 - -![整体框图](../figure/BPU/RAS/structure.svg) - -## 接口时序 - -### RAS 模块 2 阶段更新输入输出接口 - -![2 阶段更新输入输出接口](../figure/BPU/RAS/port1.png) - -上图展示了 RAS 模块 2 阶段更新的一次 pop 和一次 push,由于 push 和 pop 的块前一个分支预测 slot 均无效,在本流水级看到 return 和 call 指令跳转,因而分别指示 RASStack 模块出/压栈。在压栈时,使用 FTB 的 fallThrough 地址作为跳转返回地址。若最后一条指令为被截断的 RVI call 指令,则该地址+2 才是正确的返回地址 - -### RAS 模块 3 阶段更新输入输出接口 - -![3 阶段更新输入输出接口](../figure/BPU/RAS/port2.png) - -### RASStack 模块输入接口 - -![Stack 模块输入接口](../figure/BPU/RAS/port3.png) - -上图展示了 RASStack 模块 2 阶段更新的一次 push 和一次 pop 操作,可以注意到,在 push 操作后,从 RASStack 模块中读出的栈顶被更新为新 push 的值,而 pop 操作后,栈顶恢复为 push 操作前的值 - -### RAS 模块重定向恢复接口 - -![重定向恢复接口](../figure/BPU/RAS/port4.png) - -上图展示了 RAS 和 RASStack 模块重定向恢复且恢复点指令为 call 指令的情形,BPU 传入的重定向信号在 RAS 预测器内部被打一拍后送入 RASStack,用于恢复持久化队列中各项指针。由于该误预测点指令为 call 指令,还需压入新项 - -![重定向恢复接口](../figure/BPU/RAS/port5.png) - -类似地,若误预测点指令为 return 指令,则需基于当时的栈顶 pop 出一项 - -### RAS 模块指令提交训练接口 - -![指令提交训练接口](../figure/BPU/RAS/port6.png) - -如图展示了一次含 return 和一次含 call 指令的指令提交,可以看到,在提交时提交栈顶发生变化,且在 call 指令提交后 BOS 指针也相 应调整 - -## RAS存储结构 - -返回堆栈预测器分为推测队列(持久化队列)和提交栈。推测队列有32项,提交栈有16项。 - -推测队列项结构如下 - -| retAddr | sctr | nos | -| --------- | ---------------------------- | ------------------------------ | -| 返回地址 | 连续出现相同返回地址次数 | 推测队列更旧一项位置 | - -提交栈项结构如下 - -| retAddr | sctr | -| --------- | ------------------------------------------------------------ | -| 返回地址 | 连续出现相同返回地址次数 | - -## 预测与更新 - -返回堆栈与其他预测器有所区别,对于推测队列来说其在预测的同时实际上也作出了相应的更新。提交栈的更新则是在指令退休的时候。下图分别展示了返回堆栈预测地址的获取,推测Pop以及推测Push时对推测队列的更新操作。 - -### 栈顶地址获取 - -![getTop逻辑细节](../figure/BPU/RAS/get_top.png) - -返回堆栈的栈顶数据可能在推测栈中,也有可能在提交栈中。当推测栈为空时,栈顶数据就在提交栈中。推测栈和提交栈采用不同的策略进行维护。推测栈是推测执行的,允许回退。提交栈的数据是真实有效的,不允许回退。推测栈判不为空的逻辑是 BOS<=TOSR>1)的低位) | -| Tag | (tag 折叠分支历史 1)^(tag 折叠分支历史 2<<1)^((pc>>1)的低位) | - -若 T1-T4 中索引所得到的 valid 为 1,且 tag 与计算 tag 哈希函数得到的结果匹配,则该预测表中给出的 pred 最高位被加入最终的分支预判序 列。最终,通过多级 MUX,可以从所有 tag 匹配的分支预判中选择历史长度最长者作为最终预测结果。 - -若 T1-T4 无匹配,则采用 T0 为最终的预测结果。T0 的索引方式为直接用 pc 的低 11 位进行索引。 - -TAGE 需要 2 拍延迟: - -- 0 拍生成 SRAM 寻址用 index。index 的生成过程就是把折叠历史和 pc 异或,折叠历史的管理不在 ITTAGE 和 TAGE 内部,而在 BPU 里 -- 1 拍读出结果 -- 2 拍输出预测结果 - - ### TAGE:预测器训练 - -首先,定义所有产生 tag 匹配的预测表中所需历史长度最长者为 provider,而其余产生 tag 匹配的预测表(若存在的话)被称为 altpred。 - -TAGE 表项中包含一个 usefulness 域,当 provider 预测正确而 altpred 预测错误时 provider 的 usefulness 置 1,表示该项是一个有用的项,便不会被训练时的分配算法当作空项分配出去。当 provider 产生的预测被证实为一个正确的预测,且此时的 provider 与 altpred 的预测结果 不同,则 provider 的 usefulness 域被置 -若分支指令实际是跳转,则将对应 provider 表项的 ctr 计数器自增 1;若分支指令实际是不跳转,则将对应 provider 表项的 ctr 计数器自减 -若由于误预测导致 TAGE 表项需要更新,且误预测不是由使用 altpred 而抛弃了正确的 provider 导致的,则说明需要增添表项。但此时并不 一定真的能够增添表项。还需要满足 provider 所源自的预测表并非所需历史长度最长的预测表,则此时执行表项增添操作。 - -这里从逻辑上举一个判断是否满足 provider 所源自的预测表并非所需历史长度最长的预测表的例子: - -s1_providers(i)表示预测块中第 i 条分支的 provider 对应的预测表序号,假设 provider 在预测表 T2 中,则 LowerMask(UIntToOH(s1_providers(i)), TageNTables)为 0b0011。 s1_provideds(i)表示预测块中第 i 条分支的 provider 是否在 T1~T4,根据刚刚的假设,Fill(TageNTables, s1_provideds(i).asUInt)为 0b1111,二者相与,得到结果为 0b0011,再取反得到 0b1100,于是可以看出 T3、T4 都是比 provider 历史长度更长的预测表。 - -再举一个例子。在最开始,预测表都是空的,不存在 provider,此时对应的 s1_providers(i)为 0, s1_provideds(i)为 false,则此时 Fill(TageNTables, s1_provideds(i).asUInt)为 0b0000,二者相与取反一定得到 0b1111,说明 T1~T4 都属于所谓的比 provider 历史长度 更长的预测表。 - -具体的表项增添操作如下: - -表项增添操作会首先读取所有历史长度长于 provider 的预测表的 usefulness。若此时有某表的 usefulness 域值为 0,则在该表中分配一对 应的表项;若没有找到满足 usefulness 域值为 0 的表,则分配失败。当有多个预测表(如 Tj,Tk 两项)的 usefulness 域均为 0 时,表项的分配概率是随机的,分配的时候随机把某些 table 给 mask 掉,让它不会每次都分配同一个。这个 mask 的具体实现是:待选的历史表中(长度 大于 provider 且 u 为 0 的所有历史表),使用产生的随机数随机将一些表屏蔽掉,如果 maskedEntry 的第一个不可用,那么选没 mask 的第一个。这里的表项分配的随机性是通过 chisel 的 util 包里的 64 位线性反馈移位寄存器原语 LFSR64 生成伪随机数来实现的,在 verilog 代码中 对应 allocLFSR_lfsr 寄存器。在训练时,用 7bit 饱和计数器 bankTickCtrs 统计分配失败次数-成功次数,当分配失败的次数足够多,bankTickCtrs 计数器计数到满达到饱和时,触发全局 useful bit reset,把所有的 usefulness 域清零。 - -最后,在初始化时/TAGE 表分配新项时,所有的表项中的 ctr 计数器均被设置为 0,所有的 usefulness 域均被设置为 0。 - -注:同一个预测块中的两条分支指令对应的 usefulness 不一定是相等的,如果 altpred 的第一条分支预测跳转,第二条分支预测不跳转,provider 的都预测跳转,就只有第一个 u 要置一。 - -注:meta 是预测器预测的时候的数据,update 的时候拿回来更新用。都叫 meta 是因为 composer 将所有预测器整合起来,用共同的接口 meta 和外界交互。TAGE 的 meta 具体构成见下图: - -![TAGE meta 构成](../figure/BPU/TAGE-SC/tage_meta.png) - -### TAGE:备选预测逻辑(USE ALT ON NA) - -当 tag 匹配的项的置信度计数器 ctr 为 0 时,altpred 有时比正常预测更准确。因此在实现中使用一个 4-bit 计数器 useAltCtr,动态决定是否在最长历史匹配结果信心不足时使用备选预测。在实现中处于时序考虑,始终用基预测表的结果作为备选预测,这带来的准确率损失很小。 - -备选预测逻辑的具体实现如下: - -ProviderUnconf 表示最长历史匹配结果的信心不足。当 provider 对应的 ctr 值为 0b100、0b011 时,说明最长历史匹配结果的信心很足,此 时 providerUnconf 为 false;当 provider 对应的 ctr 值为 0b01、0b10 时,说明最长历史匹配结果的信心不足,此时 providerUnconf 为 true。 - -useAltOnNaCtrs 是 128 个 4-bit 饱和计数器构成的计数器组,每个计数器都被初始化为 0b1000。在 TAGE 收到训练更新请求时,如果拿来训练的预测中,发现 provider 的预测结果与 altpred 不同,且 provider 的预测结果信心不足,则讨论备选预测结果是否正确。如果备选预测结 果正确而 provider 错误,则对应 useAltOnNaCtrs 计数器的值+1;若备选预测结果错误而 provider 正确,则对应 useAltOnNaCtrs 计数器的值-1。因为 useAltOnNaCtrs 是饱和计数器,所以当 useAltOnNaCtrs 值已经为 0b1111 且正确或已经为 0b0000 且错误时,useAltOnNaCtrs 的值不变。 - -useAltOnNa 是利用 pc(7, 1),即 pc 的对应低位索引 useAltOnNaCtrs 计数器组后得到的计数结果的最高位。 - -当 providerUnconf && useAltOnNa 为真时,使用备选预测结果(即基预测表的结果)为 TAGE 最终的预测结果,而不是 provider 的预测结果。 - -### TAGE:wrbypass - -Wrbypass 里面有 Mem,也有 Cam,用于给更新做定序,每次 TAGE 更新时都会写进这个 wrbypass,同时写进对应预测表的 sram。每次更新的时候会查这个 wrbypass,如果 hit 了,那就把读出的 TAGE 的 ctr 值作为旧值,之前随 branch 指令带到后端再送回前端的 ctr 旧值就不要了。这 样如果一个分支重复更新,那 wrbypass 可以保证某一次更新一定能拿到相邻的上一次更新的最终值。 - -T0 有一个对应的 wrbypass,T1~T4 各有 16 个。每个预测表对应的 wrbypass 中,Mem 都有 8 个 entry,T0 每个 entry 存储 2 个(对应两个 bank)预测表的表项,T1~T4 每个 entry 存储 1 个(因为 2 个 bank 存在不同的 wrbypass 里)预测表的表项 ;Cam 有 8 个 entry,输入更新的 idx 和 tag(T0 没有 tag)就可以读到对应数据在 Cam 中的位置。Cam 和 Mem 是同时写的,所以数据在 Cam 中的位置就是在 Mem 中的位置。于是利用这个 Cam, 我们就可以在更新的时候查看对应 idx 的数据是否在 wrbypass 中。 - -### 存储结构 - -- 4 张历史表,每张表根据 pc 低位分了共 8 个 bank,每个 bank 有 256 个 set,每个 set 对应一个 FTB 项的最多 2 条分支。每个 bank 共 512 项,每张表共 4096 项,所有表共 16K 项 -- 每个表项含有 1bit valid,8bit tag,3bit ctr,1bit useful;其中 useful bit 独立存储 -- base table 有 2048 set,每 set 两条分支各一个 2bit 饱和计数器,共记录 4K 条分支 -- 历史表每个 bank 有 8 项的写缓存 wrbypass,base table 共有 8 项的 wrbypass -- use_alt_on_na 预测共 128 个 4 bit 饱和计数器 - -### 索引方式 - -- index = pc[11:1] ^ folded_hist(11bit) -- tag = pc[11:1] ^ folded_hist(8bit) ^ (folded_hist(7bit) << 1) -- 历史采取基本的分段异或折叠 -- 每项两条分支和 FTB 两个 slot 之间的两种对应关系,用 pc[1]选取,由于 FTB 项的建立机制,第一个 slot 的使用率会大于第二个,此举可以缓解这种分布不均的情况 -- base table 和 use_alt_on_na 都直接使用 pc 低位索引 - -### 预测流程 - -- s0 进行索引计算,地址送入 SRAM,仅有对应的 bank 被使能 -- s1 进行 tag 匹配和 bank 数据选取,以及两个 slot 间的重排序,得到每张历史表的总结果,传到顶层进行最长历史选取,结合 use_alt_on_na 机制,在最长历史匹配和 base table 之间进行选取(将原算法简化为始终用 base table 作为备选预测),得到每条分支的预测结果后暂存至 s2 -- s2 使用预测结果,在 BPU 内和 s1 结果进行比对,判断是否需要冲刷流水线 - -### 训练流程 - -![训练流程](../figure/BPU/TAGE-SC/tage_update.svg) - -收到来自 FTQ 的训练请求后,根据预测时记录的信息进行更新,更新流程分为两个流水级,其中第一个流水级在历史表外部判断一些更新条件,细节如下: - -- provider 更新:预测时命中的最长历史表会进行更新 - - 当备选预测和它不同时,按 provider 是否正确,决定新的 useful bit - - 根据实际方向决定 ctr 的加减 -- alloc:当误预测,且排除以下情况(provider 正确,但预测时选择了错误的备选预测结果)时进行分配 - - 分配时根据预测时记录的所有历史表的 useful bit 信息生成 mask,用随机数 LFSR 随机 mask 掉一些 bit,判断是否仍有比 provider 更长的历史表空余项? - - 如有,使用其中最短历史的项 - - 如无,用 LFSR mask 前,历史长于 provider 的最短历史的可分配项 -- useful reset 计数器:根据分配成功/失败的净次数增减一个 8bit 饱和计数器,如失败次数过多以至饱和,清除所有的 useful bit - - 记比 provider 长的历史表个数为 n,其中 useful bit 为 0 的视为分配成功,反之视为失败,两者次数之差作为此次增减的绝对值 -- use_alt_on_na 训练:当 provider 的 ctr 为两个最弱的值,且备选预测和 provider 方向不同时,根据备选预测的正确与否,增减 pc 低 -位索引的的 use_alt_on_na 饱和计数器 - -第二个流水级将更新请求发入各个预测表内部,尝试写入 SRAM - -- 其中 ctr 的旧值可能来自很久以前的预测内容,也许和现在表内 ctr 的情况已经大相径庭,为解决此问题,引入 wrbypass 机制: - - wrbypass 是 TAGE table 的全相联写缓存,每当尝试写入时,首先根据历史表的索引查询此全相联表,若命中,将对应的内容当作旧 ctr,根据对应的跳转方向更新后,同时写入 SRAM 和 wrbypass,如果不命中,根据替换算法选取一项替换 - - 每个 bank 有一个对应的 8 项全相联 wrbypass,每个 table 共 64 项 -- 由于使用了单口 SRAM,不能同时处理读写请求,故为了避免读写冲突: - - 使用分 bank 的做法 - - 当饱和且新值等于旧值时,不写入 -- 当仍然发生了读写冲突时,处理写请求,但不阻塞读请求,使用读结果时默认当作 not hit,此举可能降低准确率 - -### SC:设计思路 - -一些应用上,一些分支行为与分支历史或路径相关性较弱,表现出一个统计上的预测偏向性。对于这些分支,相比 TAGE,使用计数器捕捉统计偏向的方法更为有效。TAGE 在预测非常相关的分支时非常有效,TAGE 未能预测有统计偏向的分支,例如只对一个方向有小偏差,但 与历史路径没有强相关性的分支。 - -SC 统计校正的目的是检测不太可靠的预测并将其恢复。SC 负责预测具有统计偏向的条件分支指令并在该情形下反转 TAGE 预测器的结果。 - -### SC:硬件实现 - -SC 共维护了 4 个预测表,预测表的具体参数见下表。 - -| 序号 | 项数 | ctr 长度 | 折叠后历史长度 | 设计原理 | -| ---- | ---- | -------- | -------------- | ------------------------------- | -| T1 | 512 | 6 | 0 | ghv 取 ptr 起低 0 比特折叠异或 | -| T2 | 512 | 6 | 4 | ghv 取 ptr 起低 4 比特折叠异或 | -| T3 | 512 | 6 | 8 | ghv 取 ptr 起低 10 比特折叠异或 | -| T4 | 512 | 6 | 8 | ghv 取 ptr 起低 16 比特折叠异或 | - -### SC:预测时序 - -SC 收到来自 TAGE 的预测结果 pred 和 ctr、来自 BPU 的折叠分支历史信息和 pc,根据((index 折叠分支历史)^((pc>>1)的低位))索 引 SC 预测表,根据 SC 预测表的结果 ctr 和 TAGE 的预测结果 pred 和 ctr 动态判定(见 HasSC)是否反转 TAGE 的预测。 - -SC 的动态判定具体逻辑如下: - -SC 中实现了 2 个 bank 的 scThresholds 寄存器组,2 个 bank 是为了和 TAGE 的 2 个 bank 对应,都是因为一个预测块内最多会有两条分支指令。SC 中动态判定的寄存器组一共就是这两个。每个 bank 的 scThresholds 中有一个 5 比特的饱和计数器 ctr(初始值为 0b10000)和一个 8 比特的 thres(初始值为 6)。为了以示区别,我们后面用 tage_ctr、sc_ctr、thres_ctr 来分别表示 TAGE 传给 SC 的 ctr、SC 自己的 ctr、scThresholds 中的 ctr。 - -scThresholds 的更新:当 SC 收到训练数据时,如果当时 SC 翻转了 TAGE 的预测结果,且(sc_ctr_0*2+1)+(sc_ctr_1*2+1)+(sc_ctr_2*2+1)+(sc_ctr_3*2+1)+(2*(tage_ctr-4)+1)*8(有符号进位加)后取绝对值后在[thres-4, thres-2]的范围内,则 scThresholds 会被更新。 - -- scThresholds 中 ctr 的更新:若预测正确,则 thres_ctr+1;若预测错误,则 thres_ctr-1;若 thres_ctr 已为 0b11111 且预测正确,或 thres_ctr 已为 0b00000 且预测错误,则 thres_ctr 保持不变。 -- ScThresholds 中 thres 的更新:若 thres_ctr 更新后的值已达 0b11111 且 thres<= 31,则 thres+2;若 thres_ctr 更新后的值为 0 且 thres>=6,则 thres-2。其余情况 thres 不变。 -- Thres 更新判断结束后,会对 thres_ctr 再做一次判断,若更新后的 thres_ctr 若为 0b11111 或 0,则 thres_ctr 会被置回初始值 0b10000。 - -设定 scTableSums 为 (sc_ctr_0*2+1)+(sc_ctr_1*2+1)+(sc_ctr_2*2+1)+(sc_ctr_3*2+1) (有符号进位加),tagePrvdCtrCentered 为 (2*(tage_ctr-4)+1)*8(有符号进位加),totalSum 为 scSum+tagePvdr(有符号进位加)。若 scTableSums>(有符号的 thres - tagePrvdCtrCentered)并且 totalSum 为正,或者 scTableSums<(-有符号的 thres - tagePrvdCtrCentered)并且 totalSum 为负,都 说明已经超过了阈值,翻转 TAGE 的预测结果,否则仍使用 TAGE 的预测结果不变。 - -SC 的预测算法依赖 TAGE 里面的是否有历史表 hit 的信号 provided,以及 provider 的预测结果 taken,从而来决定 SC 自己的预测。provided 是使用 SC 预测的必要条件之一,provider 的 taken 作为 choose bit,选出 SC 最终的预测,这是因为 SC 在 TAGE 预测结果不同的场景下可能有不同的预测。 - -sc 反转 TAGE 的预测结果会造成 TAGE 表增添新项。 - -SC 需要 3 拍延迟: - -- 0 拍生成寻址 index 得到索引 s0_idx -- 1 拍读出 SCTable 对应 s0_idx 的计数器数据 s1_scResps -- 2 拍根据 s1_scResps 选择是否需要反转预测结果 -- 3 拍输出完整的预测结果 - -### SC:Wrbypass - -Wrbypass 里面有 Mem,也有 Cam,用于给更新做定序,每次 SC 更新时都会写进这个 wrbypass,同时写进对应预测表的 sram。每次更新的时候会查这个 wrbypass,如果 hit 了,那就把读出的 SC 的 ctr 值作为旧值,之前随 branch 指令带到后端再送回前端的 ctr 旧值就不要了。这样如 果一个分支重复更新,那 wrbypass 可以保证某一次更新一定能拿到相邻的上一次更新的最终值。 - -SC 的 T1~T4 各有 2 个 wrbypass,每个预测表的 wrbypass 中,Mem 都有 16 个 entry,每个 entry 存储 2 个预测表的表项;Cam 有 16 个 entry,输入更新的 idx 就可以读到对应数据在 Cam 中的位置。Cam 和 Mem 是同时写的,所以数据在 Cam 中的位置就是在 Mem 中的位置。于是利用这个 Cam,我 们就可以在更新的时候查看对应 idx 的数据是否在 wrbypass 中。 - -### SC:预测器训练 - -sc_ctr(见 signedSatUpdate)是一个 6 比特的有符号饱和计数器,在指令实际 taken 后+1,nottaken-1,计数范围是[-32,31]。 - -注:meta 是预测器预测的时候的数据,update 的时候拿回来更新用。都叫 meta 是因为 composer 将所有预测器整合起来,用共同的接口 meta 和外界交互。SC 的 meta 具体构成见下图: - -![SC meta 具体构成](../figure/BPU/TAGE-SC/sc_meta.png) - -## 整体框图 - -![整体框图](../figure/BPU/TAGE-SC/structure.png) - -## 接口时序 - -s0 输入 pc 和折叠历史时序示例 - -![pc 和折叠历史时序](../figure/BPU/TAGE-SC/port1.png) - -上图示意了 s0 输入 pc 和折叠历史时序的示例,当 io_s0_fire 为高时,输入的 io_in_bits 数据有效。 - -### 存储结构 - -- 4 张表,历史长度分别为 0、4、10、16,每张表有 256 项,每项中是 4 个 6 bit 宽的饱和计数器,其中 4 个饱和计数器分别对应 2 条分支*TAGE 的 2 种预测结果(T/NT)。每张表共 1024 个计数器,存储开销 3KB -- 每张表有 16 项的写缓存 wrbypass -- 2 个 threshold 寄存器,分别对应一个 slot 的分支。每个 threshold 有 8bit 宽的阈值饱和计数器,以及 5bit 的增减缓冲饱和计数器 - -### 索引方式 - -- index = pc[8:1] ^ folded_hist(8bit) -- 历史采取基本的分段异或折叠 -- 每项两条分支和 FTB 两个 slot 之间的两种对应关系,用 pc[1]选取,由于 FTB 项的建立机制,第一个 slot 的使用率会大于第二个,此举可以缓解这种分布不均的情况 - -### 预测流程 - -- s0 进行索引计算,地址送入 SRAM -- s1 读出饱和计数器,进行 slot 间的重排序,对应 TAGE 两种预测结果的 SC ctr 传到顶层分别做加法,结果寄存到 s2 -- s2 将 SC 的 ctr 和与 TAGE 的 provider ctr 做加法,得到最终结果,取绝对值,若大于阈值,且 TAGE 也有 hit,用加法结果的符号作为最终预测的方向,寄存到 s3 -- s3 使用方向结果,在 BPU 内和 s2 结果进行比对,判断是否需要冲刷流水线 - -### 训练流程 - -收到来自 FTQ 的训练请求后,根据预测时记录的信息进行更新,更新流程分为两个流水级,其中第一个流水级在历史表外部判断一些 -更新条件,细节如下: - -- 若预测时 TAGE 命中,根据预测时存下的旧 SC ctr 与旧 TAGE ctr 再次做加法,和更新所用的阈值(预测阈值*8+21)比对,若小于阈值,或者此时预测结果和执行方向不符,则根据 perceptron 训练规则训练每个计数器,请求寄存到下一拍发送给每张表 - -第二个流水级将更新请求发入各个预测表内部,尝试写入 SRAM,仍然尝试查询写缓存 wrbypass 获取最新计数器值(应该可以在上一 -个流水级先查询,而不是直接使用旧 ctr) - -![训练流程](../figure/BPU/TAGE-SC/sc_update.svg) diff --git a/docs/zh/frontend/BPU/index.md b/docs/zh/frontend/BPU/index.md index 98972fb1..3921997f 100644 --- a/docs/zh/frontend/BPU/index.md +++ b/docs/zh/frontend/BPU/index.md @@ -2,312 +2,43 @@ ## 术语说明 -表 1.1 术语说明 - -| **缩写** | **全称** | **描述** | -| -------- | ------------------------------------------------- | ------------------------------------------------------ | -| BPU | Branch Prediction Unit | 分支预测单元 | -| IFU | Instruction Fetch Unit | 取指单元 | -| FTQ | Fetch Target Queue | 取指目标单元 | -| uFTB | Micro Fetch Target Buffer | 分支目标缓冲 | -| FTB | Fetch Target Buffer | 取指目标缓冲 | -| TAGE | TAgged GEometric length predictor | 一种条件分支预测器 | -| SC | statistical corrector predictor | 一种用于在统计偏向情况下纠正 TAGE 预测的条件分支预测器 | -| ITTAGE | Indirect Target TAgged GEometric length predictor | 一种用于预测间接跳转指令目标地址的分支预测器 | -| RAS | Return Address Stack | 一种用于预测调用指令对应返回指令目标地址的分支预测器 | - ## 设计规格 -1. 支持一次生成一个分支预测块及其对应预测器附加信息 -2. 支持无空泡的简单预测 -3. 支持多种精确预测器及覆盖机制 -4. 支持训练预测器 -5. 支持分支历史信息维护及误预测恢复 -6. 支持 topdown 性能事件的统计 - ## 功能描述 -### 功能概述 - -BPU 模块接收来自模块外部后端执行单元及后续流水级的重定向信号,按照地址采用多种预测器为当前 PC 值开始的位置生成预测块及生成 该预测块时各预测器内部的 meta 信息,传递给后续取指目标队列(FTQ)存储,预测块供取指单元(IFU)使用,meta 信息供未来训练恢复预测器使用。其中,BPU 模块使用全相连 uFTB 作为 next line predictor,生成理想条件下延迟仅 1 周期的无空泡简单预测结果,该结果会 被直接作为输出传递到 FTQ。与此同时,这一基础预测结果还将在 BPU 后续流水线内流动,供高级预测部件使用以提供更为精确的预测结果。一旦高级预测器在后续流水级的预测结果与已有结果不一致,就将会使用高级预测器结果作为新的输出更新后续 FTQ 中存储预测块结果 并重定向 s0 级 PC,清空新结果流水级之前的流水级的错误路径结果。针对不同种类的指令预测的信息也有不同,条件分支指令的目标地址由 uFTB 提供,需要预测其方向;无条件直接跳转指令的目标地址由 uFTB 提供,不需要做任何特别的结果预测;间接跳转指令的跳转方向不需要预测,但 uFTB 提供的跳转地址结果并不一定正确,需要预测。 - -BPU 内的高级预测器包括 FTB、TAGE-SC、ITTAGE 和 RAS。其中,FTB 负责维护预测块的起始地址,终止地址,所含分支指令 PC 地址、类型( 是否 branch、是否 jalr、是否 jal、是否 call、是否 return)、基础的方向结果。TAGE-SC 是条件分支指令的主预测器,ITTAGE 用于预测 间接跳转指令。RAS 负责预测 return 类型的间接跳转指令跳转地址。 - -预测单元内多种预测器使用了分支预测历史作为预测条件,为提高历史与执行真实轨迹的匹配度,分支预测历史也会随预测结果做推测更新。全局分支历史在 BPU 顶层使用多个更新源进行更新维护,维护时会按照 TAGE、SC 和 ITTAGE 预测需要的长度进行维护,不同分支历史长 度的分支历史维护算法一致。具体地,TAGE 使用的分支历史长度有 8、13、32、119;ITTAGE 使用的历史长度有 4,8,13,16,32;SC 使用的历史长度有 0,4,10,16。分支预测历史在 BPU 模块顶层统一维护,更新源按照优先级从低到高依次为 s0 阻塞暂存的分支历史、利用 s1 预测结果更新的分支历史、利用 s2 预测结果更新的分支历史、利用 s3 预测结果更新的分支历史和 BPU 外部重定向的分支历史。每个分支历 史的具体维护策略为: - -s0 阻塞暂存的分支历史:不进行任何主动的更新,始终与最新全局折叠历史保持一致。 - -s1 预测结果更新的分支历史:利用 s1 阶段的分支预测结果在随流水线传递来的 - -s0 全局折叠历史上更新。具体地,将预测结果根据位于的分支预测 slot shift 进全局分支历史,在 0 号 slot 则直接 shift 进去,在 1 号 slot 则 shift 进去 0(slot 0 没有 taken)和当前预测结果。 - -s2 预测结果更新的分支历史:利用 s2 阶段的分支预测结果在随流水线传递来的 - -s1 全局折叠历史上更新。更新算法与 s1 相同。s2 的更新仅在 s2 预测结果与之前 s1 不同时生效。 - -s3 更新策略与 s1、s2 相同,更新仅在 s3 结果与之前 s1 或 s2 不同时生效。 - -重定向的分支历史更新仅在发生重定向时进行,根据重定向信息中 addIntoHist 信号情况,分别将传回的分支历史直接或添加重定向对应 分支指令的方向结果后用于更新 BPU 的全局分支历史。 - -为保证较为准确的预测结果,各分支预测器都需要不断使用最新的执行结果训练预测器。具体地,更新的预测块及做出该预测时各预测器内部状态的 meta 信息将会在 FTQ 模块内生成并传递回 BPU 单元供各预测器更新内部状态。 - -分支预测并不能保证结果正确性,在预测结果与真实状态不符时,需要将状态恢复到使用错误预测而更新的状态之前,主要为分支历史的恢复及预测块起始地址的重定向。 - -### 分支预测块及 meta 信息生成 - -#### 分支预测块思想 - -分支预测的目的是对执行流中存在的分支指令跳转方向与目标进行预测以在真实执行当前指令前推测地生成后续取指的 PC 范围信息以保证指令的连续供应。 - -一个分支预测块内包含了本分支预测块有效位(BranchPredictionBundle.valid)、起始地址、完整预测结果、FTB 项、折叠的分支历史 、RAS 预测器栈顶等信息。其中,完整预测结果在第一流水级来自 uFTB,后续来自 FTB 等高级预测器,FTB 项来自 uFTB 与 FTB 读出结果。 - -完整预测结果内记录了分支指令跳转方向、块内记录的分支指令信息是否有效(slot_valids,即是否存在该分支指令,一个 valid 对应一个 slot,一个 slot 对应一条预测块内的分支指令,共计 2 个 slot,其中最后一个 slot 可能记录块内第二条分支指令或一条无条件跳转/间接跳转指令)、分支指令目标、jalr 指令目标、分支指令块内偏移、无跳转时指令块结束地址、结束地址是否有误(块起始地址大于结束地 址,表明存在 false hit)、最后一条分支指令类型、最后一条指令是否为 RVI 的 call 指令、第二个分支指令 slot 是否记录分支指令而非无条件/间接跳转指令、是否命中等信息。如前所属,分支预测块内最多出现 2 条分支指令/1 条分支指令+1 条无条件跳转指令,当预测块内实际记录指令数超出该限制时,后续 FTQ 模块会将其拆分。此外,若分支预测块内不存在分支指令或未超出分支指令数量限制但达到了分支 预测块的最大宽度(32B)也将被截断。 - -FTB 项内记录了项是否有效(FTB 采用直接映射方式,若读地址不曾被写入则该标记无效)、第一个分支指令 slot、结尾的分支/跳转共用 slot、结束地址、指令类型、最后一条指令为 RVI call 指令、是否总是跳转等信息。FTB 项可被用于生成完整预测结果。 - -#### 分支预测块生成 - -分支预测块中基础的 PC 信息最初由模块外传入的复位地址指定,随后处理器运行过程中正常情况下不断按照预测块的跳转地址推测更新,在遇到误预测时,PC 值将依据 redirect 通道所给值更新。next line 的完整预测结果由 uFTB 模块读出。完整预测结果中,FTB 项由 FTQ 模块 根据以往训练结果生成,条件分支指令的跳转方向由 uFTB 生成并在后续由 TAGE-SC 预测器更新,间接跳转指令的跳转地址由 uFTB 生成并在 后续由 ITTAGE 预测器更新,RAS 预测器会针对 return 类型的间接跳转指令覆盖 ITTAGE 的预测结果。被覆盖的结果仅体现在预测器输出,不 会立刻反向反馈给结果被覆盖的预测器更新内部状态。 - -#### meta 信息生成 - -各预测器为便于自身的更新,会将做出预测时预测器内部状态信息(例如作出该预测时命中预测表序号、命中 index)作为 meta 信息和预 测结果一同随流水线传递。 - -### 无空泡简单预测 - -uFTB 作为 BPU 的 next line predictor,为处理器作出无空泡的基础预测以连续生成下一个推测 PC 值。 - -#### uFTB 请求接收 - -每次 1 阶段请求有效时,截取传入预测块起始 PC 的 16 到 1 位生成 tag 发送给本模块内的全相连 uFTB 用于读取 FTB 项,FTB 项记录内容如前所述 。uFTB 内有 32 项使用寄存器搭建的全相连结构。由于使用寄存器实现,各项可在当拍根据其中存储数据是否有效及存储的 tag 值是否与传 入信息匹配来生成本项是否命中的信号及读出的 FTB 项数据并返回到 uFTB 层次。 - -#### uFTB 数据读取与返回 - -在当拍,uFTB 存储体已返回命中信号及读出数据。在本阶段将会从返回的命中信号中选出至多一命中项并利用该命中项生成预测结果,生成完整预测结果的算法在后续 FTB 模块有详细叙述,这里 uFTB 有一额外补充的 counter 机制,为 uFTB 内每一项内至多 2 条分支指令增加一个 2 位宽 counter,若 counter 大于 1 或 FTB 项内 always_taken 有效(后者机制也存在于 FTB 模块中)则预测结果为跳转。此外,本级的命中信号及选出的命中路编号还被作为本预测器的 meta 信息等待其他预测器一起进入 s3 阶段时送出预测器,随最终预测结果一起存储到 FTQ。本预 测器在 2、3 阶段没有其他额外动作。 - -#### uFTB 数据更新 - -当该预测块对应指令全部提交时,由 FTQ 传入 BPU 一直连到本模块的 update 通道中将包含 FTQ 模块根据指令提交信息更新的 FTB 项。由于全相连 uFTB 全部采用寄存器搭建存储体,写操作不会影响并行的读操作,传来的更新信息将始终用于更新。在更新通道有效时,在当拍将利用传入的更新 pc 值生成 tag 与 uFTB 内现有各项匹配并生成是否匹配及匹配的路信号。在下一拍,若存在已有匹配,将拉高匹配路的写入信号 ,否则利用伪 LRU 替换算法选出一待替换路拉高对应路写入信号,写入数据即为更新的 FTB 项。 - -针对每个分支指令的 counter 维护也在 update 通道拉高时一并更新,在 update 通道拉高下一拍,更新 FTB 项内跳转的分支指令及其之前的分支指令对应的 counter。若 taken 则 counter+1,若不 taken 则 counter-1,如达到饱和(0 或全 1)则维持当前值不变。 - -伪 LRU 算法也需要数据更新,其共有两个数据源,其一为作出预测时命中的路编码,其二为 uFTB 更新时要写入的路编码,若其中任意有效 ,则利用其信息更新伪 LRU 状态,当都有效时一拍内使用组合逻辑依次使用两信息更新。 - -### 多种高级预测器及覆盖机制 - -各高级预测器输出的结果将与之前流水级生成并随流水线传递来的结果(uFTB 的 minimal 结果或之后流水级由 FTB 提供的完成结果)比较,如有不同将以较新结果冲刷流水线。 - -#### Composer - -Composer 是一个用于组合多个预测器的模块。在本项目中,其组合了 uFTB、FTB、TAGE-SC、ITTAGE 和 RAS 五个预测器,并对外抽象成了一 个三级流水覆盖预测器。Composer 中的各个预测器可以通过写自定义寄存器 sbpctl 来实现开关,可以按需使用预测器。在检测到来自外部的重定向后,Composer 会把重定向请求发送给各预测器,以用于恢复推测更新的元素。在预测块所有指令提交后,Composer 中的各预测器会进行训练。最终,Composer 将三级预测结果输出至 Predictor。各预测器的 meta 信息在本模块拼接在一起传递给 FTQ,FTQ 返回的训练用 meta 信息也在本模块内拆分后发送给各模块。 - -##### 起始 PC 的配置 - -Composer 的 IO 接口 io_reset_vector 可以实现起始 PC 的配置。只需要将期望的起始 PC 传递给该 IO 即可。 - -##### 与预测器的连接 - -Composer 将 uFTB、FTB、TAGE-SC、ITTAGE 和 RAS 五个预测器连接起来。共有三个分支预测器的流水级,每个预测器的相同流水级从前向后 以组合逻辑连接,且每个预测器是固定延迟的,到那个流水级就一定完成预测,所以 Composer 的只需要在对应流水级输出对应预测器的预测结果即可。 - -##### 预测器的开关 - -通过 Zicsr 指令,我们可以读写 sbpctl 这一自定义 CSR 来控制 Composer 中的各预测器的使能。sbpctl[6:0]代表了{LOOP, RAS, SC, TAGE, BIM, BTB, uFTB}这七个预测器的使能。其中,高电平代表使能,低电平代表未使能。具体地,spbctl 这一 CSR 的值通过 Composer 的 IO 接口 io_ctrl_*传入各个预测器,并由各预测器负责使能的实现。当前架构中未加入 Loop 和 BIM 两预测器,因此对应位无效。 - -##### 重定向的恢复 - -Composer 通过 io_s2_redirect、io_s3_redirect 和 io_redirect_*等 IO 端口接收重定向请求。这些请求被发送给其各个预测器,用于恢复推测更新的元素,如 RAS 栈顶项等。 - -#### FTB - -FTB 暂存 FTB 项,为后续的 TAGE、ITTAGE、SC、RAS 等高级预测器提供更为精确的分支指令位置、类型、目标地址等分支预测块关键信息, 也为总是跳转的分支指令提供基础的方向预测。FTB 模块内有一 FTBBank 模块负责 FTB 项的实际存储,模块内使用了一块多路 SRAM 作为存储 器。SRAM 规格格式详见后续。 - -##### 请求接收 - -0 阶段时,FTB 模块向内部 FTBBank 发送读请求,其请求 pc 值为 s0 传入的 PC。 - -##### 数据读取与返回 - -在发送请求的下一拍也就是预测器的 1 阶段,将暂存从 FTB SRAM 中读出的多路信号。 - -再下一拍也就是预测器的 2 阶段,从暂存数据中根据各路的 tag 和实际请求时由 PC 高位生成 tag 的匹配情况生成命中信号并在命中时选出命 中 FTB 数据。若存在 hit 请求,则返回值为选出的 FTB 项及命中的路信息,若未 hit,则输出数据无意义。 - -FTBBank 模块读出的数据在 FTB 模块内作为 2 阶段的预测结果传递给 BPU 后续预测器的 s2 阶段以获取分支指令类型、PC 信息,此外这一读出的结果还会被暂存到 FTB 模块内,在 3 阶段作为预测结果以组合逻辑传递给后续预测器。若 FTB 命中,则读出的命中路编号与命中信息、周期 数等也会随流水线向后传递,最终若该预测块未被流水线中途冲刷,则在 s3 作为 meta 信息传递给后续 FTQ 模块,其中周期数仅在仿真环境 用于性能统计,在 FPGA 等环境不存在。 - -此外,若 FTB 项内记录的有效分支指令存在 always taken 标志,表示该分支指令历史上不曾有非跳转情况,则 2 阶段的预测结果中对应 br_taken_mask 也在本模块内直接拉高处理,直接预测该分支指令跳转,不再使用其他高级预测器的预测结果。 - -#### TAGE-SC - -TAGE-SC 是南湖架构条件分支的主预测器,属于精确预测器(Accurate Predictor,简称 APD)。 - -其中 TAGE 利用历史长度不同的多个预测表,可以挖掘极长的分支历史信息;SC 是统计校正器。 - -TAGE 由一个基预测表和多个历史表组成,基预测表用 PC 索引,而历史表用 PC 和一定长度的分支历史折叠后的结果异或索引,不同历 史表使用的分支历史长度不同。在预测时,还会用 PC 和每个历史表对应的分支历史的另一种折叠结果异或计算 tag,与表中读出的 tag 进行匹配,如果匹配成功则该表命中。最终的结果取决于命中的历史长度最长的预测表的结果。 - -当 SC 认为 TAGE 有较大的概率误预测时,它会反转最终的预测结果。 - -在南湖架构中,每次预测最多同时预测 2 条条件分支指令。在访问 TAGE 的各个历史表时,用预测块的起始地址作为 PC,同时取出两个预测结果,它们所用的分支历史也是相同的。 - -##### TAGE 预测时序 - -TAGE 是高精度条件分支方向预测器。使用不同长度的分支历史和当前 PC 值寻址多个 SRAM 表,当在多个表中出现命中时,优先选择最 命中的历史长度最长的对应表项的预测结果作为最终结果。 - -TAGE 需要 2 拍延迟: - -* 0 拍生成 SRAM 寻址用 index。index 的生成过程就是把折叠历史和 pc 异或,折叠历史的管理不在 ITTAGE 和 TAGE 内部,而在 BPU -* 1 拍读出结果 -* 2 拍输出预测结果 - -##### TAGE:折叠历史 - -TAGE 类预测器的每一个历史表都有一个特定的历史长度,为了与 PC 异或后进行历史表的索引,很长的分支历史序列需要被分成很多段,然后全部异或起来。每一段的长度一般等于历史表深度的对数。由于异或的次数一般较多,为了避免预测路径上多级异或的时延,我们会直接存储折叠后的历史。由于不同长度历史折叠方式不同 ,所需折叠历史的份数等于 (历史长度,折叠后长度) 元组去重后的个数。在更新一位历史时只需要把折叠前的最老的那一位和最新的一位异或到相应的位置,再做一个移位操作即可。 - -##### TAGE:备选预测逻辑 - -实现了 USE_ALT_ON_NA 寄存器,动态决定是否在最长历史匹配结果信心不足时使用备选预测。在实现中处于时序考虑,始终用基预测表的结果作为备选预测,这带来的准确率损失很小。 - -##### SC:时序 - -一些应用上,一些分支行为与分支历史或路径相关性较弱,表现出一个统计上的预测偏向性。对于这些分支,相比 TAGE,使用计数器捕捉统计偏向的方法更为有效。TAGE 在预测非常相关的分支时非常有效,TAGE 未能预测有统计偏向的分支,例如只对一个方向有小偏差,但 与历史路径没有强相关性的分支。 - -统计校正的目的是检测不太可靠的预测并将其恢复,来自 TAGE 的预测以及分支信息(地址、全局历史、全局路径、局部历史)被呈现给统计校正预测器,其决定是否反转预测。SC 负责预测具有统计偏向的条件分支指令并在该情形下反转 TAGE 预测器的结果。 - -SC 的预测算法依赖 TAGE 里面的是否有历史表 hit 的信号 provided,以及 provider 的预测结果 taken,从而来决定 SC 自己的预测。provided 是使用 SC 预测的必要条件之一,provider 的 taken 作为 choose bit,选出 SC 最终的预测,这是因为 SC 在 TAGE 预测结果不同的场景下可能有不同的预测。 - -SC 需要 3 拍延迟: - -* 0 拍生成寻址 index 得到 s0_idx,index 的生成过程就是把折叠历史和 pc 异或,折叠历史的管理不在 ITTAGE 和 TAGE 内部,而在 BPU 里 -* 1 拍读出 SCTable 对应 s0_idx 的计数器数据 s1_scResps -* 2 拍根据 s1_scResps 选择是否需要反转预测结果 -* 3 拍输出完整的预测结果 - -#### ITTAGE - -ITTAGE 接收来自 BPU 内部的预测请求,其内部由一个基预测表和多个历史表组成,每个表项中都有一个用于存储间接跳转指令目标地址的 字段。基预测表用 PC 索引,而历史表用 PC 和一定长度的分支历史折叠后的结果异或索引,不同历史表使用的分支历史长度不同。在预测时,还会用 PC 和每个历史表对应的分支历史的另一种折叠结果异或计算 tag,与表中读出的 tag 进行匹配,如果匹配成功则该表命中。最终的结果取决于命中的历史长度最长的预测表的结果。最终,ITTAGE 将预测结果输出至 composer。 - -##### 间接跳转指令的预测 - -ITTAGE 用于预测间接跳转指令。普通分支指令和无条件跳转指令的跳转目标直接编码于指令中,便于预测,而间接跳转指令的跳转地址 来自运行时可变的寄存器,从而有多种可能选择,需要根据分支历史对其作出预测。为此,ITTAGE 的每个表项在 TAGE 表项的基础上加 入了所预测的跳转地址项,最后输出结果为选出的命中预测跳转地址而非选出的跳转方向。由于每个 FTB 项仅存储至多一条间接跳转指 令信息,ITTAGE 预测器每周期也最多预测一条间接跳转指令的目标地址。 - -ITTAGE 需要 3 拍延迟: - -* 0 拍生成寻址 index -* 1 拍读出数据 -* 2 拍选出命中结果 -* 3 拍输出 - -##### 折叠分支历史 - -历史表有特定的历史长度,为了与 PC 异或后进行历史表的索引,很长的分支历史序列需要被分成很多段,然后全部异或起来。每一段的长度一般等于历史表深度的对数。由于异或的次数一般较多,为了避免预测路径上多级异或的时延,我们会直接存储折叠后的历史。由于不同长度历史折叠方式不同,所需折叠历史的份数等于 (历史长度,折叠后长度) 元组去重后的个数。在更新一位历史时只需要把折叠前 的最老的那一位和最新的一位异或到相应的位置,再做一个移位操作即可。 - -#### RAS - -RAS 使用栈结构来预测函数调用与返回这类具有成对匹配特性的执行流的返回地址。其中调用(push/call)类指令的特征为目标寄存器地址为 1 或 5 的 jal/jalr 指令。返回(ret)类指令的特征为源寄存器为 1 或 5 的 jalr 指令。这类指令为无条件跳转指令,其类型、所在块内偏 移量已在 FTB 中读出。 - -在实现中,RAS 预测器在 s2 和 s3 两阶段提供预测结果。 - -##### 2 阶段结果 - -在 2 阶段,由于 s3 阶段还可能存在预测结果需要更新,当前 FTB 项并不一定为最终执行路径,此时作出的预测推测了此时 3 阶段预测结果( 也即前一个预测块)不会刷新当前流水级内的预测起始地址。若 2 阶段从 FTB 传来的 FTB 项有效且其中存在 push 类(call)指令,则将该指 令之后下一指令的 PC 值压入 RAS 栈;若 s2 阶段传来 FTB 项有效且其中存在 pop 类(return)指令,则将当前栈顶的地址作为结果返回并对结 果出栈。 - -在 RAS 栈模块内,上述行为分别体现为,在 push 操作时,如当前地址和栈顶地址不同,则压栈一个新项目,其对应计数器为 0,否则将栈顶项的计数器增加 1。两操作都需将这一顶部信息设置为写 bypass 项以供当前读操作使用。在 pop 操作时,若当前栈顶项计数器为 0,则栈顶 指针减 1,若计数器大于 0,则将计数器减 1。为时序优化考虑,写入到 RAS 栈内的数据将会延迟一拍后写入,考虑到可能存在本拍写入的数据下一拍需要获取读数据的情况,设计了写 bypass 机制,准备写入的数据将在本拍首先用于更新写 bypass 相关的项,包括写操作指针及写操作数据。下一拍要求读取的指针若与写 bypass 记录的指针位置匹配,则使用 bypass 值,否则使用真正的栈顶值。 - -##### 3 阶段结果 - -在 3 阶段,2 阶段曾发生过的推测 push/pop 操作记录会随流水线传递过来,3 阶段会根据 3 阶段 FTB 项(此时不再存在后续流水级,也即能进 入 3 阶段的 FTB 项不会被后续流水级冲刷)结果以与 2 阶段相同逻辑生成 push/pop 控制信号,若发现 2 阶段推测结果与 3 阶段判定结果不同, 即 RAS 位于 2 阶段时,当时 3 阶段预测冲刷过 BPU 流水线,则 2 阶段做出的预测结果所基于的情况已经发生变化,对 RAS 栈的操作不正确,需要仿照误预测进行状态恢复,具体细节见后。 - -### 预测器训练 - -预测器作出的每一个预测,在其中所有指令都成功提交后会由 FTQ 生成预测块更新信息,与传递到 FTQ 的各预测器 meta 信息一起送回预测器进行训练。 - -#### 预测器训练数据接收 - -从 FTQ 传入的预测器训练数据在 BPU 模 FTB 项、更新 PC 等其他信号一起传递给各预测器。各预测器视其时序压力再暂存 update 信号或立刻进 行 - -#### FTB - -FTB 项的更新具体逻辑详见 FTQ 模块。 - -收到 update 请求后,FTB 模块会根据 meta 信息中记录的这一预测做出时原来的读取结果是否 hit 决定更新时机。若 meta 中显示做出预测时 hit,则在本拍立刻更新将新的 FTB 数据写入 SRAM,否则需要延迟 2 周期等待读出 FTB 内现有结果决定写入路后才可更新。 - -在 FTBBank 内部,当存在更新请求时,该模块行为也因立即更新和推迟更新两情况而有所不同。立即更新时,FTBBank 内的 SRAM 写通道拉高,按照给定的信息完成写入。推迟 2 周期更新时,FTBBank 首先收到一个 update 的读请求且优先级高于普通预测的读请求,而后下一拍读出数据,选出给定地址命中的路编码传递给外部 FTB 模块(命中场景:两次针对这一 FTB 项的请求,第一次请求未命中,它更新之前发生了第 二次访问,当前第一次的更新已完成,此更新为第二次对应的更新)。而若这一拍未命中,则下一拍需要写入到在读出 FTB 项后一拍由路选取算法分配的路中。路选取规则为,若所有路均已写满,则使用替换算法(此处为伪 LRU,详见 ICache 文档)选取要替换的路,否则选取 一空路。 - -#### Composer - -Composer 通过 IO 端口 io_update_*将训练信号发送给其各个预测器。总的来说,为防止错误执行路径对预测器内容的污染,各部分预测器在预测块的所有指令提交后进行训练。它们的训练内容来自自身的预测信息和预测块中指令的译码结果和执行结果,它们会被从 FTQ 中 读出,并送回 BPU。其中,自身的预测信息会在预测后打包传进 FTQ 中存储;指令的译码结果来自 IFU 的预译码模块,在取到指令后写回 FTQ;而执行结果来自各个执行单元。 - -#### TAGE-SC & ITTAGE - -表项中包含一个 useful 域,它的值不为 0 表示该项是一个有用的项,便不会被训练时的分配算法当作空项分配出去。在训练时,用一个饱 和计数器动态监测分配的成功/失败次数,当分配失败的次数足够多,计数器达到饱和时,把所有的 useful 域清零。 - -#### RAS - -当 RAS 在 2 阶段的推测结果与 3 阶段不同或之前的预测结果遇到了 redirect,需要恢复状态。其中,redirect 信息在实际恢复前被暂存到 RAS 内寄存器,延迟一拍再更新。3 阶段检测到不同的下一拍完成更新。若为 redirect 中的 call 误预测(出错指令预译码为 call 类型指令)或 3 阶段的 push 操作不匹配,则进行 recover_push 操作,将原来错误 pop 的 RAS 栈顶重新 push 进去。若为 redirect 中的 ret 误预测(出错指令预译码为 pop 指令)或 3 阶段的 pop 操作不匹配,则进行 recover_pop 操作,弹出 RAS 栈顶本应弹出的地址。栈指针在 redirect 时恢复为 redirect 传来的栈指针,否则为当前值。栈顶在 redirect 恢复时恢复为 redirect 传来的栈顶项,否则为当前值,恢复的新地址在 redirect 时为 redirect 信号下一条指令的值,否则为 2 阶段推测值。 - -在 RAS 栈内部,状态恢复时同样生成 push、pop 等操作,这类操作的处理方式与前述 2 阶段相同,此处仅列举存在不同的情况。在 push 操作 且非分配新项的情况下,若处于 recover 状态,sp、栈顶指针、顶部返回地址要设置为更新值。在 pop 操作且当前栈顶计数器非 0 时,so、 栈顶指针、顶部返回地址要设置为更新值。在既非 push 也非 pop 时,需要恢复 sp、栈顶指针、顶部返回地址同时处理写 bypass。 - -### 分支历史信息维护 - -#### 推测更新 - -在流水级中预测器生成推测结果后,后续请求所用的分支历史也将包含这一推测值以提高预测准确率。 - -#### redirect 恢复 - -在遇到分支预测错误时,分支历史也被一并恢复到出错状态前,这样可以保证分支历史的准确度。 - -### topdown 性能分析事件统计 - -在 BPU 流水级中预测器生成推测结果可能因为各种原因阻塞,而阻塞可能最终导致处理器整体的流水线空泡。为对性能瓶颈进行较为准确 的分析定位,昆明湖架构增加了 topdown 性能计数器,收集流水线中各流水级的空泡/阻塞信息并将指令提交时空泡(实际提交指令数与发射数理想值间差值)归因到具体的模块,从而实现对瓶颈部件的定位。上述性能分析建模方法细节详见 Intel 发表的论文《A Top-Down method for performance analysis and counters architecture》。在 BPU 可以统计到的阻塞事件包括因各预测器误预测恢复所引入的流水线空泡、因后端访存违例恢复所引入的流水线空泡、因 BPU 内部 override 预测刷新较老预测结果所引入的流水线空泡、因分支指令训练预 测器而阻塞 BPU 所引入的流水线空泡和因 FTQ 满无法接收新的分支预测块阻塞 BPU 所引入的流水线空泡。在 BPU 内不处理各空泡原因间的优先级,而只是在符合对应的统计条件时将空泡控制信号拉高并随处理器流水线传递。 - -目前 BPU 内有 FTB(含 uFTB 和主 FTB)、TAGE、SC、ITTAGE 和 RAS 共计 5 种预测器。Topdown 将分支误预测原因细分到以上 5 个预测器。具体地 ,将每个误预测空泡分解到各预测器的条件为: - -1. FTB:发生了分支指令相关的重定向且误预测的指令在对应预测块的 FTB 内并没有记录 -2. TAGE:发生了分支指令相关的重定向且误预测的指令在对应预测块的 FTB 内有记录,但 SC 预测器并未给出对应的预测 -3. SC:发生了分支指令相关的重定向且误预测的指令在对应预测块的 FTB 内有记录,同时 SC 预测器给出了对应的预测结果 -4. ITTAGE:发生了分支指令相关的重定向,误预测的指令为 jalr 指令但不是 return 指令且在 FTB 项中命中 -5. RAS:发生了分支指令相关的重定向。误预测指令为 return 指令(一类特殊的 jalr 指令,详见后续 RAS 模块说明)。 - -后端访存违例恢复所引入的流水线空泡判断条件为后端发来的重定向信号指示重定向来自访存违例。 - -BPU 内部 override 引入的流水线空泡有两个可能的来源,分别为 BPU 第 2 和第 3 流水级的重定向信号。 - -因分支预测器训练而引入的流水线空泡有 3 个可能的来源,分别为 BPU 第 1,2 和 3 流水线的 ready 信号。上述 ready 信号为 BPU 内各预测器的 ready 信号取或操作的结果。 - -因 FTQ 满而引入的流水线空泡判断条件为 BPU 发往 FTQ 模块的握手接口 ready 信号拉低。 - -对分支误预测和访存违例所导致的空泡,其将被标记在当前 BPU 各流水级的 topdown 信号中。对 BPU 内部 overrride 引入的空泡,其将被标记在 override 当前所在流水级和更早流水级,而不影响比这一 override 更早的预测块所在流水级。对分支预测器训练所导致的空泡处理类似 BPU override。 - ## 总体设计 -### 整体框图 - -![整体框图](../figure/BPU/BPU/structure.svg) - -### 接口时序 - -#### BPU 到 FTQ 接口时序 - -![BPU 到 FTQ 接口时序](../figure/BPU/BPU/port1.png) - -上图展示了 BPU 到 FTQ 的预测结果接口时序。图中针对 0x1FFFF80020 起始地址的预测结果在流水线内 1、2、3 阶段分别输出,若结果与之前 流水级不一致则 redirect 信号拉高表明需要刷新预测流水线。 - -#### FTQ 到 BPU redirect 接口时序 - -![FTQ 到 BPU redirect 接口时序](../figure/BPU/BPU/port2.png) - -上图展示了 FTQ 到 BPU 的 redirect 接口时序,redirect 核心信号为 cfiUpdate_target,其指定了 redirect 的目标地址为 0x200000109a。 - -#### FTQ 到 BPU update 接口时序 - -![FTQ 到 BPU update 接口时序](../figure/BPU/BPU/port3.png) - -上图展示了 FTQ 到 BPU 的 update 接口时序,这一更新是为 0x2000000e00 开始预测块准备的,其目标跳转地址为 0x200000e0e。 - ## 寄存器配置 -| **寄存器** | **地址** | **复位值** | **属性** | **描述** | -| ---------- | -------- | ---------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| sbpctl | 0x5C0 | 64'd0 | RW | bit0: uFTB 使能信号
bit1: FTB 使能信号
bit2: BIM 使能信号(保留)
bit3: TAGE 使能信号
bit4: SC 使能信号
bit5: RAS 使能信号
bit6: loop 预测器使能信号(保留) | +Table: BPU 相关 CSR 列表 + ++---------+-------+----------+------+----------------------------------------------------------------------+ +| 寄存器 | 地址 | 复位值 | 属性 | 描述 | ++=========+=======+==========+======+======================================================================+ +| sbpctl | 0x5C0 | 64'd0 | RW | bit0: ubtb 使能信号 | +| | | | | | +| | | | | bit1: abtb 使能信号 | +| | | | | | +| | | | | bit2: mbtb 使能信号 | +| | | | | | +| | | | | bit3: Tage 使能信号 | +| | | | | | +| | | | | bit4: Sc 使能信号 | +| | | | | | +| | | | | bit5: Ittage 使能信号 | +| | | | | | +| | | | | bit6: Ras 使能信号 | +| | | | | | +| | | | | 需要注意:虽然这个寄存器可以任意配置,但预测器间存在依赖关系,例如: | +| | | | | | +| | | | | - Tage、Sc、Ittage、Ras 依赖 mbtb; | +| | | | | | +| | | | | - Sc 依赖 Tage。 | +| | | | | | +| | | | | 当预测器依赖的预测器被禁用时,其自身也会被禁用, | +| | | | | | +| | | | | 这**不会**反映在该 CSR 的值上。 | ++---------+-------+----------+------+----------------------------------------------------------------------+ 注:RO——只读寄存器;RW——可读可写寄存器。 @@ -317,8 +48,8 @@ BPU 内部 override 引入的流水线空泡有两个可能的来源,分别为 2. Perais A, Sheikh R, Yen L, et al. Elastic instruction fetching[C]//2019 IEEE International Symposium on High Performance Computer Architecture (HPCA). IEEE, 2019: 478-490. 3. Software Optimization Guide for AMD Family 19h Processors (PUB), Chap. 2.8.1.5, 4. Seznec A, Michaud P. A case for (partially) TAgged GEometric history length branch prediction[J]. The Journal of Instruction-Level Parallelism, 2006, 8: 23. -6. Seznec A. A 256 kbits l-tage branch predictor[J]. Journal of Instruction-Level Parallelism (JILP) Special Issue: The Second Championship Branch Prediction Competition (CBP-2), 2007, 9: 1-6. -7. Seznec A. A new case for the tage branch predictor[C]//Proceedings of the 44th Annual IEEE/ACM International Symposium on Microarchitecture. 2011: 117-127. -8. Seznec A. The O-GEHL branch predictor[J]. The 1st JILP Championship Branch Prediction Competition (CBP-1), 2004. -9. Jiménez D A, Lin C. Dynamic branch prediction with perceptrons[C]//Proceedings HPCA Seventh International Symposium on High-Performance Computer Architecture. IEEE, 2001: 197-206. -10. Seznec A. A 64-Kbytes ITTAGE indirect branch predictor[C]//JWAC-2: Championship Branch Prediction. 2011. +5. Seznec A. A 256 kbits l-tage branch predictor[J]. Journal of Instruction-Level Parallelism (JILP) Special Issue: The Second Championship Branch Prediction Competition (CBP-2), 2007, 9: 1-6. +6. Seznec A. A new case for the tage branch predictor[C]//Proceedings of the 44th Annual IEEE/ACM International Symposium on Microarchitecture. 2011: 117-127. +7. Seznec A. The O-GEHL branch predictor[J]. The 1st JILP Championship Branch Prediction Competition (CBP-1), 2004. +8. Jiménez D A, Lin C. Dynamic branch prediction with perceptrons[C]//Proceedings HPCA Seventh International Symposium on High-Performance Computer Architecture. IEEE, 2001: 197-206. +9. Seznec A. A 64-Kbytes ITTAGE indirect branch predictor[C]//JWAC-2: Championship Branch Prediction. 2011. diff --git a/docs/zh/frontend/BPU/uFTB.md b/docs/zh/frontend/BPU/uFTB.md deleted file mode 100644 index 47d3ee32..00000000 --- a/docs/zh/frontend/BPU/uFTB.md +++ /dev/null @@ -1,47 +0,0 @@ -# BPU 子模块 uFTB - -## 功能概述 - -uFTB 作为 BPU 的 next line predictor,为处理器作出无空泡的基础预测以连续生成下一个推测 PC 值。 - -### uFTB 请求接收 - -每次 0 阶段请求有效时,截取传入预测块起始 PC 的 16 到 1 位生成 tag 发送给本模块内的全相连 uFTB 用于读取 FTB 项,FTB 项记录内容如前所述 。uFTB 各 bank 内均有 32 项使用寄存器搭建的全相连结构。由于使用寄存器实现,各项可在当拍根据其中存储数据是否有效及存储的 tag 值 是否与传入信息匹配来生成本项是否命中的信号及读出的 FTB 项数据并返回到 uFTB 层次,但这一数据并不在本拍而是在下一拍使用。 - -### uFTB 数据读取与返回 - -在下一拍,uFTB 存储体已返回命中信号及读出数据。预测器进入 1 阶段。在本阶段将会从返回的命中信号中选出至多一命中项并利用该命 中项生成预测结果,生成完整预测结果的算法在后续 FTB 模块有详细叙述,这里 uFTB 有一额外补充的 counter 机制,为 uFTB 内每一项内至多 2 条分支指令增加一个 2 位宽 counter,若 counter 大于 1 或 FTB 项内 always_taken 有效(后者机制也存在于 FTB 模块中)则预测结果为跳转。此外,本级的命中信号及选出的命中路编号还被作为本预测器的 meta 信息等待其他预测器一起进入 s3 阶段时送出预测器,随最终预测结果一起存储到 FTQ。本预测器在 2、3 阶段没有其他额外动作。 - -### uFTB 数据更新 - -当该预测块对应指令全部提交时,由 FTQ 传入 BPU 一直连到本模块的 update 通道中将包含 FTQ 模块根据指令提交信息更新的 FTB 项。由于全相连 uFTB 全部采用寄存器搭建存储体,写操作不会影响并行的读操作,传来的更新信息将始终用于更新。在更新通道有效时,在当拍将利用传入的更新 pc 值生成 tag 与 uFTB 内现有各项匹配并生成是否匹配及匹配的路信号。在下一拍,若存在已有匹配,将拉高匹配路的写入信号 ,否则利用伪 lru 替换算法选出一待替换路拉高对应路写入信号,写入数据即为更新的 FTB 项。 - -针对每个分支指令的 counter 维护也在 update 通道拉高时一并更新,在 update 通道拉高下一拍,更新 FTB 项内跳转的分支指令及其之前的分支指令对应的 counter。若 taken 则 counter+1,若不 taken 则 counter-1,如达到饱和(0 或全 1)则维持当前值不变。 - -伪 LRU 算法也需要数据更新,其共有两个数据源,其一为作出预测时命中的路编码,其二为 uFTB 更新时要写入的路编码,若其中任意有效 ,则利用其信息更新伪 LRU 状态,当都有效时一拍内使用组合逻辑依次使用两信息更新。 - -### SRAM 规格 - -模块内不使用 SRAM,但存在较多的 reg 拼接结构,列举如下。 - -模块内含有 32 路数据,每一路包含 2 个 2 bit 宽饱和 counter,记录基础的分支方向预测;一个 60 bit FTB 项,具体含义与 FTB 模块相同,详见 FTB SRAM 规格描述;一个 16 bit tag 及一个 1 bit 路有效信号。 - -## 整体框图 - -![整体框图](../figure/BPU/uFTB/structure.png) - -TODO:图与昆明湖不符,待更新 - -## 接口时序 - -### 结果输出接口 - -![结果输出接口](../figure/BPU/uFTB/port1.png) - -上图展示了一次有效的 uFTB 输出,其下一预测块起始地址为 0x80002000。 - -### 更新接口 - -![更新接口](../figure/BPU/uFTB/port2.png) - -上图展示了一次有效的更新请求,更新了 0x80003b9a 地址的 FTB 项。