本部分文档将会详细介绍香山分支预测单元的每一个模块,包括 BPU 顶层和五个子预测器。
在每一个模块的文档中,我们将会详细介绍该模块在香山分支预测单元中所发挥的作用,以及该模块的算法原理、模块结构和时序等。
负责一个模块验证的同学需要重点阅读对应文档并结合代码进行理解,其他文档亦可进行阅读以帮助你对香山分支预测单元整体功能的理解。理解过程中,你可能需要时刻回顾之前文档中所描述的基础设计思想及接口信号的含义。
本部分文档将会详细介绍香山分支预测单元的每一个模块,包括 BPU 顶层和五个子预测器。
在每一个模块的文档中,我们将会详细介绍该模块在香山分支预测单元中所发挥的作用,以及该模块的算法原理、模块结构和时序等。
负责一个模块验证的同学需要重点阅读对应文档并结合代码进行理解,其他文档亦可进行阅读以帮助你对香山分支预测单元整体功能的理解。理解过程中,你可能需要时刻回顾之前文档中所描述的基础设计思想及接口信号的含义。
BPU 顶层整体的功能和结构已经在之前的文档中粗略的描述,对于验证 BPU 顶层的同学来说,可能还需要更加细致的描述。由于 BPU 顶层功能较多,本节将 BPU 划分为了几大功能点进行更进一步的描述。但由于 BPU 顶层细节过多,更进一步的细节需参照代码进行理解。
通过香山的基础设计文档,我们知道,BPU 顶层是通过一个个生成器来维护 s0 周期的各类变量的,例如 PC、分支历史等,并且其核心概念是,通过预测结果重定向信号决定采用哪个流水级的结果。
BPU 顶层中所有的生成器共有 6 个:
其中,除了 npcGen
以外,其余生成器都会在本文档中进行介绍。本节中我们着重探讨一下生成器的产生下一次预测的方法。
在代码中你可以看到生成器用类似下面这种方式进行定义:
val npcGen = new PhyPriorityMuxGenerator[UInt]
接下来,代码通过多条语句对生成器的数据来源进行注册:
npcGen.register(true.B, reg, ...)
npcGen.register(s1_valid, s1_target, ...)
npcGen.register(s2_redirect, s2_target, ...)
npcGen.register(s3_redirect, s3_target, ...)
npcGen.register(do_redirect.valid, do_redirect.bits.cfiUpdate.target, ...)
每一行被称作一次注册,在一次注册中第一个信号参数是数据有效信号,第二个信号参数包含具体的数据。 生成器的优先级也是按照注册的顺序来决定,越往后优先级越高,因此,同一时刻的优先级从低到高依次为:
这样一来,我们就可以在预测结果重定向有效时,避免采用较早流水级的预测结果,而采用纠正后的预测结果。也使得我们可以将外部重定向请求作为最高优先级去处理。
我们可以得出所有生成器产生 s0 信号的方法:在所有数据有效信号中,如果只有一个有效的,则选取它对应的数据,如果有多个数据有效信号生效,则选取优先级最高的数据。
我们知道,全局分支历史在 BPU 顶层进行维护,维护的策略与 PC 的维护策略一致。即在每个阶段流水级预测结果产生之后,会根据相应信号对全局分支历史进行更新。
顶层为了维护全局分支历史定义了两组信号
与 s0_pc
, s1_pc
, s2_pc
一样,BPU 顶层为全局历史指针也维护了每一阶段的信号 s0_ghist_ptr
, s1_ghist_ptr
, s2_ghist_ptr
,但 ghv
中的内容是位置固定的,我们仅通过 ghist_ptr
来定位当前的全局分支历史从哪里开始。
ghist_ptr
的使用仅在 BPU 顶层可见,而我们向子预测器传入的,是全局历史寄存器中的数据根据 ghist_ptr
所移位之后的全局分支历史。在子预测器拿到的全局分支历史中,最低位对应全局分支历史的最新位,最高位对应全局分支历史的最老位。
那么是怎样进行移位的呢,我们首先来看一下全局历史是怎样在 ghv
中进行存储的。
|===== ghist =====>| =======>|
n ^ 0
ghist_ptr
如上图所示,序列表示整个 ghv
寄存器,ghist_ptr
指向 ghv
中的某个位置,这个位置代表了全局分支历史的最新位。当需要添加一位新的全局历史记录时,首先将 ghist_ptr
减 1,然后将该位写在其所指向的位置。当 ghist_ptr
减到 0 后,又会循环回来指向最高位,因此会覆盖之前写入的全局分支历史。
但不管怎样,从 ghist_ptr
所指向的位置开始,指针越增加,历史越老。因此,当我们需要计算当前全局分支历史时,只需要将 ghv
寄存器循环右移 ghist_ptr
位即可。
全局分支历史的更新策略与 pc
更新的策略一致,在每一个流水级都需要根据当前流水级的预测结果生成一个 当前流水级的指针及 ghv
的更新说明,最终都送给相关生成器来处理。
ghv
的更新说明即 用于指导 ghv
寄存器的更新的某些信息。香山 BPU 中维护了两个信息来完成这一职责:
ghv_wdata
需要向 ghv 中写入的数据ghv_wens
写入位掩码最终更新时,只需要将 ghv_wens
所标识的位写入 ghv_wdata
的对应位即可。
因此每个流水级需要负责产生三组信息:ghist_ptr
,ghv_wdata
, ghv_wens
。
具体地,预测结果中最多含有两条分支指令,我们只需将实际情况来设置这几个信息即可,举几种情况的例子:
ghv_wens
的下一个位置置 0 ,ghv_wens
的对应位置置 1, 同时 ghist_ptr
减一。ghist_ptr
应该减二,并且其他两个信息应该指示向 ghv
中写入 01。此处在生成器中只维护了一个 ghv_wdata
信息(通过 ghvBitWriteGens
生成器维护),ghv_wens
并没有通过生成器来维护。这是因为此处使用了一个小技巧,使用了生成器的 ghv_wdata
最终输出的是被选中阶段的结果,而 ghv_wens
将所有阶段的 ghv_wens
进行按位或来使用。
这是基于如下考虑的:
ghv_wens
修改了较新位置的历史也没关系。ghv_wens
置位。送入预测器的分支折叠历史也是由顶层 BPU 来维护的,BPU 为了缩短折叠历史的更新延迟,维护了很多变量,来支持分支折叠历史的快速更新,我们将会重点介绍这一策略,并介绍每一个变量的作用。
在开始之前,我们先来看一下分支折叠历史是怎样定义的,结构又是怎样的。
如果你查看了 BPU 全局接口的文档,你就会知道,子预测器拿到的是一个不同长度位向量的数组,代表了各种长度的折叠历史,而这些折叠历史都是由全局分支历史压缩而成。
对于全局分支历史,我们有一个存放全局分支历史的寄存器,长度为 256。为了方便举例,我们假设全局分支历史的长度为 15 位,并且经过移位之后,我们可以拿到一个这样的分支历史:最低位是最新的历史记录,最高位是最老的历史记录。
此时如果我们需要用这 15 位,产生一个 6 位的折叠历史,会使用异或的策略进行压缩,具体过程是这样的:
h[5] h[4] h[3] h[2] h[1] h[0]
h[11] h[10] h[9] h[8] h[7] h[6]
^ h[14] h[13] h[12]
---------------------------------------------------------------
h[5]^h[11] h[4]^h[10] ... h[0]^h[6]^h[12]
即将其按照上面的方式排列之后,将每一位上的值进行异或,结果便是求出的长度为 6 的折叠历史。
此时我们想要对这一分支折叠历史进行更新,当我们向全局分支历史插入一位新历史时,是从最低位插入的,也就是说原来的 h[0] 变为了 h[1],如果我们想求此时的分支折叠历史,只需要再进行一遍异或运算。但这样的效率太低了,因为异或的操作有可能变得特别长,我们可以来探寻一下一次更新对分支折叠历史的影响。
上述例子中,插入一位新历史之前,6 位折叠历史的生成是按照下面这种排列生成的
h[5] h[4] h[3] h[2] h[1] h[0]
h[11] h[10] h[9] h[8] h[7] h[6]
h[14] h[13] h[12]
插入一位新历史之后变成了下面这样
h[4] h[3] h[2] h[1] h[0] h[new]
h[10] h[9] h[8] h[7] h[6] h[5]
(h[14]) h[13] h[12] h[11]
我们可以发现一些规律
插入前:
h[5] {h[4] h[3] h[2] h[1] h[0] }
h[11] {h[10] h[9] h[8] h[7] h[6] }
{ h[14] h[13] h[12]}
插入后:
{h[4] h[3] h[2] h[1] h[0] } h[new]
{h[10] h[9] h[8] h[7] h[6] } h[5]
{ (h[14]) h[13] h[12]} h[11]
大括号中的内容发生了整体的左移,h[5] 和 h[11],由最高位变到了最低位。那么表现在压缩后的历史上不就是我们常见的循环左移吗!
但其中有且仅有两个位的值发生了变化,一个是新加入的 h[new],一个是被舍弃掉的 h[14]。h[new] 肯定在第一位,被舍弃的位置也是固定的。因此我们想要完成一次更新,只需要知道 新插入历史的值 和 前一次历史的最老位即可。循环移位后,将这两个位置根据实际情况进行一次修改便可拿到更新后的折叠历史。
BPU 顶层为了实现这种更新,正是通过维护最老位,这通过两个额外的变量来实现:
在这里有一处为时序所优化的点,因为当流水级的预测结果出来时,全局历史指针才能通过跳转情况进行更新,等到全局历史指针更新完再来更新最老位会增加时延。因此我们将跳转情况维护起来,等到下一周期用的时候再来用跳转情况更新最老位。
此时的最老位也需要多往前维护几位,因为在使用时,利用跳转情况更新后,前面较新的几位就会变成最老位了。
所以与折叠历史相关的生成器共有三个:foldedGhGen
, lastBrNumOhGen
, aheadFhObGen
每次折叠历史更新时需要的信息分别是:
每次折叠历史更新时,都需要根据 last_br_num_oh
和 ahead_fh_oldest_bits
求出真正的最老位,然后通过最老位与本次更新的跳转情况将其中的若干位进行修改,最后进行循环左移,便完成了更新操作。
流水线控制是 BPU 功能的核心,逻辑也最为复杂,BPU 顶层中所有的流水线控制信号如下:
我们会逐步来介绍每个信号的作用,首先我们来看 fire
信号,这一信号表示的含义是流水线握手成功,数据成功传给了下一流水。这标志着本周期结束时,本流水级的预测也随之结束,下周期开始时,下一流水级的预测即将开始。
这需要两个条件:
valid
本流水级的数据是有效的。ready
与 component_ready
分别指示了 BPU 顶层与预测器的下一流水级是否就绪。当这两个信号同时置高时,fire
信号有效,表示握手成功。如果我们单独把一次预测拿出来,那么时序应该是这样的(实际中,大多数时间每个流水线都是一直有效的):
上文中提到的四组信号,除了 component_ready
是由预测器输出,其余信号皆需 BPU 顶层来维护,而最终暴露给子预测器的,只有 fire
一组信号。
我们接下来以 s2 为例分别来看每个信号是如何维护的。
ready 信号
s2_ready := s2_fire || !s2_valid
该赋值语句是一个组合电路赋值,也就是说,s2_ready
信号是与本周期的 s2_fire
和 s2_valid
直接相关联的,分为以下两种情况:
s2_valid
信号在本周期无效,说明 s2 流水级目前是空的,自然可以接受新的数据,则 s2_ready
有效s2_valid
信号在本周期有效,说明 s2 流水级目前有数据还未传递给下一级,但如果 s2_fire
,那么本周期就会传递给下一级。此时 s2_ready
有效,刚好指示数据可以在下一拍流入。valid 信号
s2_valid
信号目前为止维护是相对简单的,与 s1_fire
信号和 s2_ready
信号相关。其关系为:
s1_fire
有效,说明数据传进来,下一周期 s2_valid
有效。s2_fire
有效,说明数据流出去,下一周期 s2_valid
无效。fire 信号
fire 信号相对特殊,但对于中间的流水级来说,维护非常简单,例如
s2_fire := s2_valid && s3_components_ready && s3_ready
只需考虑当前流水级的 valid
和下一流水级的 ready
即可。
但对 s0_fire 来说,没有 valid 信号,因此其直接等于 s1_components_ready && s1_ready
对于 s3_fire 来说,没有下一级的 ready 信号,因此其直接等于 s3_valid
我们知道,当流水线出现预测结果不同时,需要产生预测结果重定向信号,并且将之前的流水线清空。flush
和 redirect
正是在做这两项工作。redirect
表示当前流水级是否需要重定向,flush
则表示当前流水级是否需要冲刷。
redirect 信号
s2_redirect
的产生方式如下:
s2_redirect := s2_fire && s2_redirect_s1_last_pred
也就是说,当 s2_fire
时,并且 s2 的预测结果与上一周期保存的 s1 预测结果不同时,这个信号便有效。之后该信号将会连接到子预测器的输入,与 BPU 预测结果的输出,分别指导子预测器和 FTQ 的状态恢复。
flush 信号
flush 信号是用于指导流水线冲刷的,例如 s3 重定向有效时,说明错误的预测结果已经流入流水线, s1 和 s2 此时全都是基于错误的结果来预测的,因此需要进行流水线冲刷,使之前的流水级都暂停工作,等待新的预测结果流入。
具体地,他们之间有如下关系:
s2_flush := s3_flush || s3_redirect
s1_flush := s2_flush || s2_redirect
也就是说,某个流水级 redirect
有效,之前的流水级的 flush 全都会被置为有效。那么 flush 具有什么作用呢?答案是指导 valid 信号,如果本周期 valid 信号有效,但 fire 信号未生效,说明错误的数据没有被下一流水取走,此时 flush 有效后,在下一周期 valid 就会立即变为无效,以这种方式来避免错误数据长期存储在流水线中。
但 flush 信号对 valid 信号的影响,也根据每一个流水级的不同而有一定差异。例如
s0_fire
有效,说明新数据流入,那么下周期 valid 依然有效。s2_redirect
发生时,s2_flush
并没有被置为有效,此时如果发生 s1_fire
,s1 的错误预测结果也可能流入,此时还需根据 s1_flush
信号来决定 s2_valid
是否有效。flush 的使用较复杂,更详细的细节还需参考代码进行理解。
当 FTQ 发往 BPU 的重定向请求生效时就说明所有流水级的预测结果都是不正确的,此时应该将所有流水级进行冲刷,这可以通过将 s3_flush
置为有效来实现。因此有
s3_flush := redirect_req.valid
在 BPU 中,重定向请求送入后被延迟一周期才正式使用,因此 s1_valid
的信号也需要对 flush
信号的响应做出一些改变。当重定向请求(延迟前)有效时,s1_valid
下周期立即被置为无效,不需要再去参考 s0_fire
信号了。
此时 npcGen
等生成器也需要直接去采用重定向请求中的数据来生成,这就相当于将 BPU 的状态重定向到出错之前状态的过程。但注意 BPU 默认的重定向等级为 flushAfter
,即重定向请求会对应一条预测错误的指令,而 BPU 会认为这条指令虽然预测错了,但是已经被纠正并且交由后端执行了,因此下一次预测可以直接从下一条指令开始。
所以在重定向恢复时,不仅需要将重定向接口中的信息恢复,还需要将这条预测错误的指令的执行情况也更新到历史中去。
uFTB 是香山所有预测器中第一个预测器,也是其他预测器产生预测结果的基石。uFTB 工作在 s1 阶段,它获取到 s1_pc 后能在本周期内产生预测结果,并在 s1 通道输出,其他通道不予更改。分支指令的位置、指令的跳转目标都是由它来提供,后续预测器也将会根据这一预测结果进行进一步的预测。
而其本质则是一个 FTB 项缓存,其中存储了 FTB 项,基础的预测结果将直接由读出的 FTB 项生成。
因此,在你开始文档的阅读之前,请确保你理解了 FTB 项和含义以及预测结果接口的具体细节。
如上所述,uFTB 本质上是一个存储了 FTB 项的小型缓存,其大致结构如下图所示。
在当前版本的香山中,uFTB 共有 32 个缓存行,每个缓存行被称作 FauFTBWay
,一个缓存行中可以存储一个 FTB 项。
当 s1 流水有效时,uFTB 会使用 s1_pc 来决定读出 uFTB 缓存的哪一项。缓存是根据 PC 中的 tag 字段来索引,tag 字段被定义为 pc[16: 1],即在 PC 中截取了 16 比特作为标识来匹配缓存中的某一行。
缓存中的每一行,即 FauFTBWay
中的数据请求接口有三项:
uFTB 会将 tag 连接到缓存的每一行的数据请求接口,并根据 resp_hit
信号,选出命中的那一个 FTB 项。后续将会根据这一 FTB 项生成完整预测结果。
uFTB 中为 FTB 项缓存的每一行都维护了对应的两比特饱和计数器。我们知道,在一个 FTB 项最多可以存储两个条件分支指令,因此每一行对应的两比特饱和计数器也有两个,负责为其中的条件分支指令提供粗略的预测结果。
uFTB 在索引到 FTB 项时,也会索引到对应的两比特饱和计数器。
当预测结果生成时,会根据 FTB 项与其对应的两个两比特饱和计数器中的内容来产生。
uFTB 通过 s1_pc 索引到对应的 FTB 项以及两个两比饱和计数器之后,需要根据他们产生预测结果。uFTB 产生的预测结果,将在 s1 流水有效时,通过 s1 通道进行输出,对于 s2 以及 s3 通道,uFTB 不予更改。
s1 预测结果中的信号产生方式可参考以下列表:
hit FTB表项是否命中
FauFTBWay
中的 resp_hit
信号,存在一个有效slot_valids slot有效位,表示 ftb 项中的每个 slot 是否有效
targets 每个slot对应的跳转目标地址
offsets 每个slot中的指令相对于预测块起始地址的偏移
is_jal 预测块内包含jal指令
is_jalr 预测块内包含jalr指令
is_call 预测块内包含call指令
is_ret 预测块内包含ret指令
last_may_be_rvi_call 预测结果预测块末尾可能为一个RVI类型的call指令信号
is_br_sharing 最后一个slot (tailSlot) 中存储的是条件分支指令信号
fallThroughErr FTB项中记录的 pftAddr
有误
pftAddr
代表的预测块结束地址是否大于预测块的起始地址,如果小于,则代表出现错误,此信号置为有效。这种情况可能会发生在 pc 索引到错误的 FTB 项的情况。fallThroughAddr 预测块的结束地址
fallThroughErr
无效,则根据 pftAddr
生成,否则将其设置为起始地址 + 预测宽度。br_taken_mask 分支预测结果,每个分支(slot)对应一个 bit,表示该分支是否被预测为 taken
always_taken
字段和两比特饱和计数器指示结果生成。jalr_target 本预测块中的 jalr 的跳转目标
uFTB 的更新涉及 FTB 项缓存的更新,以及两比特饱和计数器的更新,而更新的内容都是通过更新接口来获取的。
在 uFTB 预测器中,缓存和两比特饱和计数器的读与写并不冲突,因此我们无需考虑读和更新之间的时序冲突问题,可以将他们看做是独立的两部分。
FTB 缓存的更新过程很简单,更新通道中已经为我们指定好了 pc 以及新生成的 FTB 项,只需将其写入到缓存的指定位置即可。
FTB 缓存的更新需要两个周期:
FauFTBWay
的更新请求通道,根据每一行所返回的 hit 信号计算ftb_entry
信号组包含了新 FTB 项的完整信息,将该信息发送到需要更新的某一个缓存行即可。两位饱和计数器的更新则需要结合后续程序的真实执行情况,以及 FTB 项中记录的分支指令信息来进行更新了,而这些信息都可以从更新通道中进行获取。
两位饱和计数器的更新也需要两个周期:
br_taken_mask
信息进行更新。信号类型 | 信号位 | 信号名 | 信号描述 |
---|---|---|---|
input | clock | 输入时钟 | |
input | reset | 复位信号 | |
input | [35:0] | io_reset_vector | 用于reset时,reset s1_pc_dup_0 提供的值 |
input | [40:0] | io_in_bits_s0_pc_0 | 输入位s0_pc 的 第0个复制 |
input | [40:0] | io_in_bits_s0_pc_1 | 同上 第1个 |
input | [40:0] | io_in_bits_s0_pc_2 | 同上 第2个 |
input | [40:0] | io_in_bits_s0_pc_3 | 同上 第3个 |
output | [40:0] | io_out_s1_pc_0 | 输出s1_pc 的 第0个复制 |
output | [40:0] | io_out_s1_pc_1 | 同上 第1个 |
output | [40:0] | io_out_s1_pc_2 | 同上 第2个 |
output | [40:0] | io_out_s1_pc_3 | 同上 第3个 |
output | io_out_s1_full_pred_0_br_taken_mask_0 | solt 0 是否被预测为 always taken | |
output | io_out_s1_full_pred_0_br_taken_mask_1 | solt 1 是否被预测为 always taken | |
output | io_out_s1_full_pred_0_slot_valids_0 | solt 0 是否启用 | |
output | io_out_s1_full_pred_0_slot_valids_1 | solt 1 是否启用 | |
output | [40:0] | io_out_s1_full_pred_0_targets_0 | solt 0 对应的跳转目标地址 |
output | [40:0] | io_out_s1_full_pred_0_targets_1 | solt 1 对应的跳转目标地址 |
output | [3:0] | io_out_s1_full_pred_0_offsets_0 | solt 0 中分支指令相对于地址块起始pc的偏移 |
output | [3:0] | io_out_s1_full_pred_0_offsets_1 | solt 1 中分支指令相对于地址块起始pc的偏移 |
output | [40:0] | io_out_s1_full_pred_0_fallThroughAddr | 预测块的结束地址 |
output | io_out_s1_full_pred_0_is_br_sharing | solt 1(无条件跳转)是否被共享为有条件跳转指令 | |
output | io_out_s1_full_pred_0_hit | ||
output | io_out_s1_full_pred_1_br_taken_mask_0 | 类似 io_out_s1_pc_1 io_out_s1_full_pred_0的复制 | |
output | io_out_s1_full_pred_1_br_taken_mask_1 | ||
output | io_out_s1_full_pred_1_slot_valids_0 | ||
output | io_out_s1_full_pred_1_slot_valids_1 | ||
output | [40:0] | io_out_s1_full_pred_1_targets_0 | |
output | [40:0] | io_out_s1_full_pred_1_targets_1 | |
output | [3:0] | io_out_s1_full_pred_1_offsets_0 | |
output | [3:0] | io_out_s1_full_pred_1_offsets_1 | |
output | [40:0] | io_out_s1_full_pred_1_fallThroughAddr | |
output | io_out_s1_full_pred_1_is_br_sharing | ||
output | io_out_s1_full_pred_1_hit | ||
output | io_out_s1_full_pred_2_br_taken_mask_0 | 同上 | |
output | io_out_s1_full_pred_2_br_taken_mask_1 | ||
output | io_out_s1_full_pred_2_slot_valids_0 | ||
output | io_out_s1_full_pred_2_slot_valids_1 | ||
output | [40:0] | io_out_s1_full_pred_2_targets_0 | |
output | [40:0] | io_out_s1_full_pred_2_targets_1 | |
output | [3:0] | io_out_s1_full_pred_2_offsets_0 | |
output | [3:0] | io_out_s1_full_pred_2_offsets_1 | |
output | [40:0] | io_out_s1_full_pred_2_fallThroughAddr | |
output | io_out_s1_full_pred_2_is_br_sharing | ||
output | io_out_s1_full_pred_2_hit | ||
output | io_out_s1_full_pred_3_br_taken_mask_0 | 同上 | |
output | io_out_s1_full_pred_3_br_taken_mask_1 | ||
output | io_out_s1_full_pred_3_slot_valids_0 | ||
output | io_out_s1_full_pred_3_slot_valids_1 | ||
output | [40:0] | io_out_s1_full_pred_3_targets_0 | |
output | [40:0] | io_out_s1_full_pred_3_targets_1 | |
output | [3:0] | io_out_s1_full_pred_3_offsets_0 | |
output | [3:0] | io_out_s1_full_pred_3_offsets_1 | |
output | [40:0] | io_out_s1_full_pred_3_fallThroughAddr | |
output | io_out_s1_full_pred_3_fallThroughErr | ||
output | io_out_s1_full_pred_3_is_br_sharing | ||
output | io_out_s1_full_pred_3_hit | ||
output | [222:0] | io_out_last_stage_meta | 输出最后阶段的元信息 io_out_last_stage_meta = {213’h0, resp_meta_pred_way_r_1, resp_meta_hit_r_1} |
input | io_ctrl_ubtb_enable | 控制ubtb是否启用 | |
input | io_s0_fire_0 | 输入s0_fire_0,与 io_out_s1_pc_0 <= io_in_bits_s0_pc_0 的时钟门控相关 | |
input | io_s0_fire_1 | 输入s0_fire_1 | |
input | io_s0_fire_2 | 输入s0_fire_2 | |
input | io_s0_fire_3 | 输入s0_fire_3 | |
input | io_s1_fire_0 | 输入s1_fire_0 | |
input | io_s2_fire_0 | 输入s2_fire_0 | |
input | io_update_valid | 更新有效性 | |
input | [40:0] | io_update_bits_pc | 传回的预测块pc(用于指示更新的预测块) |
input | [3:0] | io_update_bits_ftb_entry_brSlots_0_offset | solt 0 中分支指令相对于地址块起始pc的偏移 |
input | [11:0] | io_update_bits_ftb_entry_brSlots_0_lower | 跳转目标地址的低位 |
input | [1:0] | io_update_bits_ftb_entry_brSlots_0_tarStat | 跳转后的 pc 高位是否进退位 |
input | io_update_bits_ftb_entry_brSlots_0_valid | 是否启用 | |
input | [3:0] | io_update_bits_ftb_entry_tailSlot_offset | solt 1 中分支指令相对于地址块起始pc的偏移 |
input | [19:0] | io_update_bits_ftb_entry_tailSlot_lower | 跳转目标地址的低位 |
input | [1:0] | io_update_bits_ftb_entry_tailSlot_tarStat | 跳转后的 pc 高位是否进退位 |
input | io_update_bits_ftb_entry_tailSlot_sharing | 无条件跳转指令槽中存储条件分支指令 | |
input | io_update_bits_ftb_entry_tailSlot_valid | 是否启用 | |
input | [3:0] | io_update_bits_ftb_entry_pftAddr | Partial Fallthrough Addr 如果预测块中没有跳转,那么程序将会顺序执行到达的地址,预测块的结束地址。 |
input | io_update_bits_ftb_entry_carry | pc+pft时是否产生进位 | |
input | io_update_bits_ftb_entry_always_taken_0 | 是否预测为总是跳转 | |
input | io_update_bits_ftb_entry_always_taken_1 | 是否预测为总是跳转 | |
input | io_update_bits_br_taken_mask_0 | 是否跳转 | |
input | io_update_bits_br_taken_mask_1 | 是否跳转 |
TAGE-SC 是南湖架构条件分支的主预测器,属于精确预测器(Accurate Predictor,简称 APD)。TAGE-SC 可以看作两个功能相对独立的组件:预测部分 TAGE 和 校验部分 SC。
在昆明湖中,由于每个预测块,最多可以有2条跳转指令,因此TAGE在每次预测最多同时预测 2 条条件分支指令。在访问 TAGE 的各个历史表时,用预测块的起始地址作为 PC,同时取出两个预测结果,并基于相同的全局历史进行预测。
TAGE预测器的核心思路是提供不同历史长度的预测结果,并选择最合适的结果进行反馈。在TAGE预测器中,一共有 1+N 个表历史记录表,N 为可配置选项。以昆明湖为例,N=4。
基于 T0 表的预测器为基础预测器。在预测时,直接通过预测块的 PC 地址在 T0 表中查找到该地址对应的 “2 bit 饱和计数器表示的跳转历史信息” ,然后根据历史信息做出预测结果。T0表的表项只有2bit,所以能记录的历史状态有限。
对于 T0 以外的表,我们用 Tn 进行表示。在查表时除了 PC 外,还需要用到全局跳转历史信息H进行查找。查找命中后,根据 “3bit饱和预测器” 进行跳转或者不跳转的预测。Tn 表的n越大,其用到的H中的位数越多。
对于每一次预测,TAGE 从所有 Tn 命中表项中选择全局跳转历史最长的表项。
在实现上,为了节约空间,检索 Tn 表时,输入的跳转历史信息H需要进行压缩,该过程也称为历史折叠。
各预测表的表项包含如下元素:
对于一个预测块,有可能所有的表都会产生预测结果,此时就需要进行选择。如上图所示,一般情况下,Tn表的编号越大,优先级越高。
TAGE 内含两个流水级,第一级计算索引,第二级通过索引读出SRAM表中的结果
第一流水级运行:计算index。通过寄存器输出到s1
第二流水级运行:访存SRAM,读取预测结果。通过寄存器输出到s2
在昆明湖的实现中,T0与Tn的表结构如下所示:
预测器 | 作用 | 表项构成 | 项数 |
---|---|---|---|
基准预测器T0 | 用于在其他预测器的预测结果都无效时输出预测结果 | 2 bit ctr 饱和计数器最高位决定跳转方向 | 2路各2048项,每路对于一条分支指令 |
预测表T1-T4 | 对每个预测块的输入,所有Tn表都进行预测,在所有预测有效的结果中,选择历史记录最长的结果作为最后预测结果。历史记录长度由输入的H决定 | 1 bit valid 有效位 3 bit ctr 饱和计数器8 bit tag 校验命中1 bit us 作为usefulness计数器 | 4096项、奇数项对应第一条分支指令,偶数项对应第二条分支指令 |
对于每个表Tn,在查询时,其输入“全局分支历史数据H”的长度是不同的。假如总的预测历史长度为S,Tn、Tn+1可能用S的低x,低y位 (越低的位是越新的历史)作为查询输入。一般情况下,Tn表的n越大,其用到的历史信息越长,即 x<y 。
在进行 Tn 表的查询时,由于历史数据H进行了“压缩”,可能导致一个PC1^H1的结果与另一个PC2^H2的结果相同(类似Hash表的Hash冲突),进而导致索引到无效数据(预测PC1索引到预测PC2的数据)。所以TAGE为每个表提供了一个tag标识,昆明湖中采用了8bit tag,来减小冲突发生的概率,tag的计算方法和查询的索引方法不同,只有当tag计算相同时查询结果才有效。
在Tn表项中,除了饱和计数器ctr和tag外,还有1bit的usefulness计数器,该计数器为0时,作弱表项,表示可以当作该项可以被重新分配当作他用;不为0时,作强表项,该项不能被分配当作他用。
为了尽力避免所有表项全为1,无法分配新表项的情况发生,TAGE预期使用计数器bankTickCtrs 对所有usefulness进行清零操作。
计算方法 公式 |
---|
index = FH ^ ((pc»1)低位) |
tag = FH1 ^ FH2 ^ ((pc»1)低位) |
其中FH、FH1、FH2表示按一定规范折叠后的全局分支历史。对于Tn而言,其FH、FH1、FH2都有各自的对于折叠位数,不一定相同。在昆明湖实现中,T0和Tn表的配置如下:
表名称 | FH长度 | FH1长度 | FH2长度 | 最近历史长度(用到GH中的位数) |
---|---|---|---|---|
T1 | 8比特 | 8比特 | 7比特 | 低8位,即把最新8位历史,折叠成FH、FH1、FH2 |
T2 | 11比特 | 8比特 | 7比特 | 低13位,即把最新13位历史,折叠成FH、FH1、FH2 |
T3 | 11比特 | 8比特 | 7比特 | 低32位,即把最新32位历史,折叠成FH、FH1、FH2 |
T4 | 11比特 | 8比特 | 7比特 | 低119位,即把最新119位历史,折叠成FH、FH1、FH2 |
注:pc»1是因为使用了RISC-C拓展,2Byte对齐,而PC本身已经省略了1B对齐,所以只用1b
由于 Tn 表使用饱和计数器进行预测,因此其输出结果可能存在“信心不足”的情况。例如在昆明湖中,对3比特饱和计数器,100、011时都是弱预测。为了为该状态提供更多的选择参考,TAGE 预测器还提供了一个“替代预测器”机制,他的作用是告诉 TAGE 在 Tn 表预测的结果 “信心不足” 时,是该选择 Tn 的预测结果,还是选择 T0 的预测结果。
在昆明湖的具体实现中,“替代预测器” 基于 useAltOnNaCtrs 的寄存器组实现。它由两路128个 4-bit饱和计数器 构成,每个计数器都被初始化为 0b1000。在TAGE进行预测时,使用 PC(7,1) 进行索引得到对应的饱和计数器。如果该计数器的值大于等于预设值,且Tn预测结果信心不高,则选择T0的结果,否则选择Tn的结果。
综上所述,昆明湖中TAGE预测器的预测步骤如下:
由于TAGE的预测过程用到了很多计数器和tag,他们需要根据一定的规则进行更新,该过程称为训练。该训练过程发生在BPU的update阶段,该阶段会输入PC,分支历史、以及否预测正确等信息。香山昆明湖的分支预测进行训练的流程根据不同情况,分为如下几个步骤:
SC(Statistics counter)分支预测器是一种基于历史统计信息的分支预测器。
与TAGE类似,在SC中通常有多个表Tn,他们对应了不同历史长度的跳转统计。不同点在于,在预测按PC检索时,每个表Tn都会命中,然后SC把每个命中的表项进行求和,计算总的“饱和计数器”跳转信息,最后根据总的跳转信息判断是否跳转。一般情况下,SC采用“有符号饱和计数器”,计数器值大于0时表示跳转,小于0时表示不跳转。计数器的绝对值越大,表示其预测置信度越高。
在SC预测器中,SC也由多个表组成(例如T1,T2,T3,T4),但相对TAGE预测器少了基础预测表T0。SC中Tn表有6 bit的有符号饱和计数器。SC表的索引方式如下:
计算方式 |
---|
Index = (FH) ^ ((pc»1)低位) |
对应每个表,其表项数和用到的折叠历史长度如下:
表名 | 表项数 | FH长度 | 折叠历史范围 |
---|---|---|---|
T1 | 512 | 0 | 不折叠 |
T2 | 512 | 4 | 把历史信息的低4位,折叠成FH |
T3 | 512 | 8 | 把历史信息的低10位,折叠成FH |
T4 | 512 | 8 | 把历史信息的低16位,折叠成FH |
在计算统计预测结果时,总得统计结果计算公式如下:
其中ctr_sc表示每个表的有符号饱和计数器。对其进行左移加一是进行权重调整。累加后的scCtrSum就是SC的最终预测结果,如果为该值大于零,则预测跳转,小于零则预测不跳转。其绝对值越大,表示预测置信度越高。
典型数据转换结果如下(在计算时为了不溢出,扩展到了9位):
在update阶段进行饱和计数器的更新。
一些应用上,一些分支行为与分支历史或路径相关性较弱,表现出一个统计上的预测偏向性。对于这些分支,相比基于历史的分支预测,使用计数器捕捉统计偏向的方法更为有效。
TAGE在预测与历史非常相关的分支时非常有效,但对有统计偏向的分支则支持不佳。例如只对一个方向有小偏差,但与历史路径没有强相关性的分支。为了避免该问题,可以在传统TAGE预测器上增加SC预测器。
在昆明湖 TAGE-SC 预测器中,会同时得到 TAGE 和 SC 的预测结果 P1 和 P2,然后对他们的结果进行累加 P = P1+P2 ,如果 P 的绝对值大于 8bit 的阈值 sc_bank_thres,则采用预测器结果 P,否则则采用 P1 作为最终预测结果。
为了进行动态自适应,阈值sc_thres是需要是动态变化的。为此在实现上TAGE-SC使用了一个5bit的sc_bank_ctr计数器对阈值sc_bank_thres进行调整。另外,由于昆明湖支持同时预测2条分支指令,因此阈值寄存器和对应的控制计数器也是两份。
TAGE-SC 内含 3 个流水级,TAGE的2级流水已经介绍过,SC部分的流水线如下:
第一流水级:通过pc和FH计算出index,并输出到s0_idx寄存器
第二流水级:找到SCTable中s1_idx对应的计数器数据,输出到s1_scResps寄存器
第三流水级:根据s2_scResps选择是否需要反转预测结果,输出到s2_disagree寄存器
在TAGE-SC预测时,TAGE的预测结果P1用tage_ctr表示,SC的预测结果P2用scCtrSum表示。在预测时,分为如下四步。
执行SC预测器,得到预测结果scCtrSum
并行得到TAGE预测器的预测结果 tage_ctr。
由于TAGE的预测结果是无符号饱和计数器,而SC的预测结果是有符号饱和计数器,如果对他们进行相加,需要进行数据转换。
昆明湖在实现上,采用了对TAGE的结果进行转换。转换后的结果用 tageCtrCentered 表示,具体转换过程如下:
3比特的无符号饱和计数器转换为8比特的有符号饱和计数器结果举例如下所示:
3b100 弱跳转 => 8b00001000 = 8
3b011 弱不跳转 => 8b11111000 = -8
3b111 强跳转 => 8b00111000 = 56
3b000 强不跳转 => 8b11001000 = -56
对TAGE和SC的预测结果进行相加得到最终预测结果P,在实现上用totalSum进行表示。
根据 totalSum 以及 sc_bank_thres 决定最终预测方向
在对 TAGE 和 SC 进行组合后,TAGE-SC 添加了 sc_bank_ctr 计数器用来控制阈值sc_bank_thres。因此在训练时,除了 TAGE 和 SC 本身的训练外,还需要对新增加的计数器进行更新。
在update阶段,其具体更新流程如下:
信号类型 | 信号宽度 | 信号名 | 信号描述 |
---|---|---|---|
input | * | clock | |
input | * | reset | |
input | *[35:0] | io_reset_vector | 用于reset时,reset s1_pc_dup_0 提供的值 |
input | *[40:0] | io_in_bits_s0_pc_0 | 复制的s0_pc的dup数组的第1个,给顶层BPU的PC |
input | *[40:0] | io_in_bits_s0_pc_1 | 复制的s0_pc第2个,给Tage的PC |
input | *[40:0] | io_in_bits_s0_pc_3 | 复制的s0_pc的第4个,给SC的PC |
input | *[10:0] | io_in_bits_folded_hist_1_hist_17_folded_hist | TageTable 2 用到的11bits 折叠历史 从多长历史范围折叠到11bit见前文所述的表 注意TageTable下标+1,此处 T2 是前文 T3 |
input | *[10:0] | io_in_bits_folded_hist_1_hist_16_folded_hist | TageTable 3 用到的11bits 折叠历史 |
input | *[6:0] | io_in_bits_folded_hist_1_hist_15_folded_hist | TageTable 1 用到的7bits 折叠历史 |
input | *[7:0] | io_in_bits_folded_hist_1_hist_14_folded_hist | TageTable 0 用到的8bits 折叠历史 |
input | *[6:0] | io_in_bits_folded_hist_1_hist_9_folded_hist | TageTable 2 用到的7bits 折叠历史 |
input | *[7:0] | io_in_bits_folded_hist_1_hist_8_folded_hist | TageTable 3 用到的8bits 折叠历史 |
input | *[6:0] | io_in_bits_folded_hist_1_hist_7_folded_hist | TageTable 0 用到的7bits 折叠历史 |
input | *[6:0] | io_in_bits_folded_hist_1_hist_5_folded_hist | TageTable 3 用到的7bits 折叠历史 |
input | *[7:0] | io_in_bits_folded_hist_1_hist_4_folded_hist | TageTable 1 用到的8bits 折叠历史 |
input | *[7:0] | io_in_bits_folded_hist_1_hist_3_folded_hist | TageTable 2 用到的8bits 折叠历史 |
input | *[10:0] | io_in_bits_folded_hist_1_hist_1_folded_hist | TageTable 1 用到的11bits 折叠历史 |
input | *[3:0] | io_in_bits_folded_hist_3_hist_12_folded_hist | SCTable 1 用到的 4bit 折叠历史 |
input | *[7:0] | io_in_bits_folded_hist_3_hist_11_folded_hist | SCTable 2 用到的 8bit 折叠历史 |
input | *[7:0] | io_in_bits_folded_hist_3_hist_2_folded_hist | SCTable 3 用到的 8bit 折叠历史 |
output | * | io_out_s2_full_pred_0_br_taken_mask_0 | io_out_s2_full_pred_{i}br_taken_mask{j} Tage 在 s2流水级输出的,复制4份 预测块中第 j 条分支指令TAGE预测结果 这里不该叫mask吧 |
output | * | io_out_s2_full_pred_0_br_taken_mask_1 | |
output | * | io_out_s2_full_pred_1_br_taken_mask_0 | |
output | * | io_out_s2_full_pred_1_br_taken_mask_1 | |
output | * | io_out_s2_full_pred_2_br_taken_mask_0 | |
output | * | io_out_s2_full_pred_2_br_taken_mask_1 | |
output | * | io_out_s2_full_pred_3_br_taken_mask_0 | |
output | * | io_out_s2_full_pred_3_br_taken_mask_1 | |
output | * | io_out_s3_full_pred_0_br_taken_mask_0 | io_out_s3_full_pred_{i}br_taken_mask{j} Tage 在 s3流水级输出的,复制4份 预测块中第 j 条分支指令SC预测结果 |
output | * | io_out_s3_full_pred_0_br_taken_mask_1 | |
output | * | io_out_s3_full_pred_1_br_taken_mask_0 | |
output | * | io_out_s3_full_pred_1_br_taken_mask_1 | |
output | * | io_out_s3_full_pred_2_br_taken_mask_0 | |
output | * | io_out_s3_full_pred_2_br_taken_mask_1 | |
output | * | io_out_s3_full_pred_3_br_taken_mask_0 | |
output | * | io_out_s3_full_pred_3_br_taken_mask_1 | |
output | *[222:0] | io_out_last_stage_meta | 见附表 |
input | * | io_ctrl_tage_enable | |
input | * | io_ctrl_sc_enable | |
input | * | io_s0_fire_0 | s0 阶段流水线控制 相同信号复制多份,0给BPU,1给Tage,3给SC |
input | * | io_s0_fire_1 | |
input | * | io_s0_fire_3 | |
input | * | io_s1_fire_0 | s1 阶段流水线控制 |
input | * | io_s1_fire_1 | |
input | * | io_s1_fire_2 | |
input | * | io_s1_fire_3 | |
input | * | io_s2_fire_0 | s2 阶段流水线控制 |
input | * | io_s2_fire_1 | |
input | * | io_s2_fire_2 | |
input | * | io_s2_fire_3 | |
output | * | io_s1_ready | tage的所有表,可以执行读取结果的操作 |
input | * | io_update_valid | 从FTQ发向BPU的后端执行结果(更新信号)是否有效 |
input | *[40:0] | io_update_bits_pc | (后端执行过的)预测块的PC |
input | *[10:0] | io_update_bits_spec_info_folded_hist_hist_17_folded_hist | TageTable 2 用到的11bits 折叠历史 预测时使用的分支历史结果,没有更新,转了一圈回来了 |
input | *[10:0] | io_update_bits_spec_info_folded_hist_hist_16_folded_hist | TageTable 3 用到的11bits 折叠历史 |
input | *[6:0] | io_update_bits_spec_info_folded_hist_hist_15_folded_hist | TageTable 1 用到的7bits 折叠历史 |
input | *[7:0] | io_update_bits_spec_info_folded_hist_hist_14_folded_hist | TageTable 0 用到的8bits 折叠历史 |
input | *[3:0] | io_update_bits_spec_info_folded_hist_hist_12_folded_hist | SCTable 1 用到的 4bit 折叠历史 |
input | *[7:0] | io_update_bits_spec_info_folded_hist_hist_11_folded_hist | SCTable 2 用到的 8bit 折叠历史 |
input | *[6:0] | io_update_bits_spec_info_folded_hist_hist_9_folded_hist | TageTable 2 用到的7bits 折叠历史 |
input | *[7:0] | io_update_bits_spec_info_folded_hist_hist_8_folded_hist | TageTable 3 用到的8bits 折叠历史 |
input | *[6:0] | io_update_bits_spec_info_folded_hist_hist_7_folded_hist | TageTable 0 用到的7bits 折叠历史 |
input | *[6:0] | io_update_bits_spec_info_folded_hist_hist_5_folded_hist | TageTable 3 用到的7bits 折叠历史 |
input | *[7:0] | io_update_bits_spec_info_folded_hist_hist_4_folded_hist | TageTable 1 用到的8bits 折叠历史 |
input | *[7:0] | io_update_bits_spec_info_folded_hist_hist_3_folded_hist | TageTable 2 用到的8bits 折叠历史 |
input | *[7:0] | io_update_bits_spec_info_folded_hist_hist_2_folded_hist | SCTable 3 用到的 8bit 折叠历史 |
input | *[10:0] | io_update_bits_spec_info_folded_hist_hist_1_folded_hist | TageTable 1 用到的11bits 折叠历史 |
input | * | io_update_bits_ftb_entry_brSlots_0_valid | FTB 表项的第一个slot是否有效(存储了跳转指令) |
input | * | io_update_bits_ftb_entry_tailSlot_sharing | FTB 表项的最后一个slot是否存储了条件分支而非无条件跳转 |
input | * | io_update_bits_ftb_entry_tailSlot_valid | FTB 表项的最后一个slot是否有效 |
input | * | io_update_bits_ftb_entry_always_taken_0 | 历史上slot 0 指令总是跳转 |
input | * | io_update_bits_ftb_entry_always_taken_1 | 历史上slot 1 指令总是跳转 |
input | * | io_update_bits_br_taken_mask_0 | solt 0 是否 taken |
input | * | io_update_bits_br_taken_mask_1 | solt 1 是否 taken |
input | * | io_update_bits_mispred_mask_0 | solt 0 是否预测正确 |
input | * | io_update_bits_mispred_mask_1 | solt 1 是否预测正确 |
input | *[222:0] | io_update_bits_meta | 见附表 |
需要设计参与优化!
信号类型 | 信号位 | 信号名 | 信号描述 |
---|---|---|---|
output | [218:88] | 0 | 占位,全为0,传递到composer时会忽略 |
87 | resp_meta_providers_1_valid_r | ||
[86:85] | resp_meta_providers_1_bits_r | ||
84 | resp_meta_providers_0_valid_r | ||
[83:82] | resp_meta_providers_0_bits_r | ||
[81:79] | resp_meta_providerResps_1_r_ctr | ||
78 | resp_meta_providerResps_1_r_u | ||
77 | resp_meta_providerResps_1_r_unconf | ||
[76:74] | resp_meta_providerResps_0_r_ctr | ||
73 | resp_meta_providerResps_0_r_u | ||
72 | resp_meta_providerResps_0_r_unconf | ||
71 | resp_meta_altUsed_1_r | ||
70 | resp_meta_altUsed_0_r | ||
69 | resp_meta_altDiffers_1_r | ||
68 | resp_meta_altDiffers_0_r | ||
[67:66] | resp_meta_basecnts_1_r | ||
[65:64] | resp_meta_basecnts_0_r | ||
[63:60] | resp_meta_allocates_1_r | ||
[59:56] | resp_meta_allocates_0_r | ||
55 | resp_meta_takens_1_r | ||
54 | resp_meta_takens_0_r | ||
53 | resp_meta_scMeta_tageTakens_1_r | ||
52 | resp_meta_scMeta_tageTakens_0_r | ||
51 | resp_meta_scMeta_scUsed_1_r | ||
50 | resp_meta_scMeta_scUsed_0_r | ||
49 | resp_meta_scMeta_scPreds_1_r | ||
48 | resp_meta_scMeta_scPreds_0_r | ||
[47:42] | r_1_3 | scMeta(预测时的状态)中第2路的第4个sc_ctr的值 | |
[41:36] | r_1_2 | scMeta中第2路的第3个sc_ctr的值 | |
[35:30] | r_1_1 | scMeta中第2路的第2个sc_ctr的值 | |
[29:24] | r_1_0 | scMeta中第2路的第1个sc_ctr的值 | |
[23:18] | r_3 | scMeta中第1路的第4个sc_ctr的值 | |
[17:12] | r_2 | scMeta中第1路的第3个sc_ctr的值 | |
[11:6] | r_1 | scMeta中第1路的第2个sc_ctr的值 | |
[5:0] | r_0 | scMeta中第1路的第1个sc_ctr的值 |
信号类型 | 信号位 | 信号名 | 信号描述 |
---|---|---|---|
input | [218:94] | FTB, ITAGE, RAS 模块传给 FTQ 的 META 信息,忽略 | |
[93:6] | io_out_last_stage_meta[87:0] 偏移 6bit 后的结果 | TAGE 输出给 FTQ 的 META | |
[5:0] | uFTB 输出给 FTQ 的 META |
FTB 是香山 BPU 的第三个子预测器,它也能一并获取到 uFTB 和 TAGE-SC 的输出。在 FTB 的输入接口中,s1 通道含有 uFTB 的基础预测结果,s2 通道和 s3 通道中仅有 br_taken_mask
一组信号被 TAGE-SC 填充,并无 FTB 项生成的基础预测结果。FTB 的工作便是为 s2 和 s3 通道提供基础预测结果。
FTB 在功能和结构上都与 uFTB 类似,其主要区别就是 FTB 能够容纳更多的 FTB 项,并且 FTB 的预测结果是在 s2 与 s3 通道输出。正是由于容量大,其读出的速度上会比 uFTB 慢,无法被放置在第一周期产生预测结果,但大容量也使它能够获得更加精准的预测结果。
br_taken_mask
字段,避免在生成时丢失。FTB 预测器中 FTB 项被放置在了一个专门的存储结构中,叫做 FTBBank
,在进一步查看 FTBBank
的结构之前,我们先来看一下 FTBBank
是怎样使用的。
FTBBank
的读请求接口如下:
Flipped(DecoupledIO(UInt(VAddrBits.W)))
FTBEntry
Valid(UInt(log2Ceil(numWays).W))
其中 req_pc
的接口是 Decoupled 的,也就是说含有 valid 和 ready 信号。 FTB 需要在 s1 阶段开始之前便获取到 pc,因此 s0_pc
被送入 req_pc
接口,s0_fire
信号被连接至 req_pc
的 valid 信号,ready
信号被连接至预测器的流水控制信号 s1_ready
。
当 s0_fire
之后进入 s1 阶段,s0_fire
的下一个周期,也就是在 s1_fire
的同时,FTBBank 已经将读出的 FTB 项输出至 read_resp
接口,并计算好了 read_hits
。但此时因为读出已经浪费了过多时延,无法在 s1 阶段进行输出了,因此该读出结果被保存在内部寄存器中。在 s2 和 s3 阶段会在寄存器中读出该 FTB 项并生成预测结果。
FTBBank 中定义了一个存储器来存储所有的 FTB 项,存储器采用组相联的结构(如果你不清楚组相联的含义,请自行 RTFM),共含 512 个组(Set),每组有 4 路(Way),最多能存储 2048 个 FTB 项,存储 FTB 的同时还会存储与 FTB 项对应的 tag 用于匹配 FTB 项。
具体地,tag 被定义为 pc[29:10]
,即在 pc 中截取了 20 位用于标识 FTB 项,pc 的具体划分方式如下:
pc: | ... |<-- tag(20 bits) -->|<-- idx(9 bits) -->|<-- instOffset(1 bit) -->|
读出时,向存储器提供组序号(idx),读出该组中所有路,然后检测是否有某一路的 tag 与当前 tag 匹配,如果匹配,则表示命中,读出的 FTB 项通过 read_resp
接口送出,并把命中的路编号通过 read_hits
接口送出。
正如我们之前提到的,对于 FTB 预测器,它需要向 s2, s3 通道提供由 FTB 项转化来的基础预测结果,FTB 项已经在 s1 阶段被读出并保存。在 s2 和 s3 阶段只需直接读出便生成即可,但需要注意的一点就是保护 TAGE-SC 在 s2 及 s3 通道中产生的预测结果 br_taken_mask
字段,这一字段为条件分支指令提供了精确的预测结果。对于 s1 通道,FTB 预测器不予更改。
s2 和 s3预测结果中的信号产生方式可参考以下列表:
hit FTB表项是否命中
FTBBank
输出的 read_hits
信号 valid 位有效。slot_valids slot有效位,表示 ftb 项中的每个 slot 是否有效
targets 每个slot对应的跳转目标地址
offsets 每个slot中的指令相对于预测块起始地址的偏移
is_jal 预测块内包含jal指令
is_jalr 预测块内包含jalr指令
is_call 预测块内包含call指令
is_ret 预测块内包含ret指令
last_may_be_rvi_call 预测结果预测块末尾可能为一个RVI类型的call指令信号
is_br_sharing 最后一个slot (tailSlot) 中存储的是条件分支指令信号
fallThroughErr FTB项中记录的 pftAddr
有误
pftAddr
代表的预测块结束地址是否大于预测块的起始地址,如果小于,则代表出现错误,此信号置为有效。这种情况可能会发生在 pc 索引到错误的 FTB 项的情况。fallThroughAddr 预测块的结束地址
fallThroughErr
无效,则根据 pftAddr
生成,否则将其设置为起始地址 + 预测宽度。br_taken_mask 分支预测结果,每个分支(slot)对应一位,表示该分支是否被预测为 taken
always_taken
字段有效,则预测为 taken;否则由相应通道输入接口中提供的 br_taken_mask
信号来决定。jalr_target 本预测块中的 jalr 的跳转目标
在预测的第三周期,FTB 预测器会将本次预测的一些辅助信息输出至 last_stage_meta
中,还会将读出的 FTB 项送到 last_stage_ftrb_entry
接口中。
FTB meta 中含有 hit
和 writeWay
两个信息,分别表示本次预测是否命中,以及在哪一路读出。之后更新通道中会产生本次预测的更新信息,这两个信息也会随之送入,来指导更新后的 FTB 项写入的位置。
update 通道中已经为我们指定好了 pc 以及新 FTB 项,并且还有 meta 信息中的 hit
和 writeWay
。如果 meta 中的 hit
有效,说明之前这一 pc 对应的 FTB 项在存储器中是有存储的,我们只需将它写入到 writeWay 对应的路中即可。
如果无效,说明以前没有存储,但现在是否存储我们并不知道,有可能在此更新请求之前,该 pc 对应的 FTB 项被另一个更新请求写入了。因此我们还需要给 FTBBank 发送一个读请求,来判断其中是否存在对应的 FTB 项。如果存在,下周期可直接写入到该位置,否则,通知 FTBBank 为其重新分配一个位置。
因此 FTB 项的更新需要的周期数需随 hit 情况而定。
我们首先来看一下 FTBBank是怎样处理更新的。
FTBBank 的更新接口如下,分为更新读接口和更新写接口两部分。
u_req_pc 更新读请求 pc
Flipped(DecoupledIO(UInt(VAddrBits.W)))
update_hits 读出的命中情况
Valid(UInt(log2Ceil(numWays).W))
update_access 有更新请求但 meta 信息指示未命中
Bool()
update_pc 更新写请求 pc
UInt(VAddrBits.W))
update_write_data 更新请求写入的数据,valid 有效的时候写
Flipped(Valid(new FTBEntryWithTag))
update_write_way 更新请求写入的路索引
UInt(log2Ceil(numWays).W))
update_write_alloc 是否需要分配FTB项(以前未命中)
Bool()
对于更新读接口,FTBBank 通过 u_req_pc
信号获取更新读请求,该请求要比预测时的读请求优先级更高,在下一个周期,FTBBank 会将命中情况通过 update_hits
接口送出。update_access
仅用于 FTBBank 内部某些状态判断。
对于更新写接口,FTBBank 通过 update_pc
信号获取更新写请求的 pc,并在 update_write_data
有效时向 update_write_way
对应的位置写入其中的数据。如果 update_write_alloc
有效,说明不能直接写入请求中指定的位置,而是需要重新分配一个位置。
分配的策略如下:
update_write_alloc
,并发出写请求。这时的更新过程总共花费了三个周期。信号类型 | 信号位 | 信号名 | 信号描述 |
---|---|---|---|
input | clock | 输入时钟 | |
input | reset | 复位信号 | |
input | [35:0] | io_reset_vector | 用于reset时,reset s1_pc_dup_0 提供的值 |
input | [40:0] | io_in_bits_s0_pc_0 | 输入位s0_pc 的 第0个复制 |
input | [40:0] | io_in_bits_s0_pc_1 | 同上 第1个 |
input | [40:0] | io_in_bits_s0_pc_2 | 同上 第2个 |
input | [40:0] | io_in_bits_s0_pc_3 | 同上 第3个 |
input | io_in_bits_resp_in_0_s2_full_pred_0_br_taken_mask_0 | 预测结果输入 | |
input | io_in_bits_resp_in_0_s2_full_pred_0_br_taken_mask_1 | ||
input | io_in_bits_resp_in_0_s2_full_pred_1_br_taken_mask_0 | ||
input | io_in_bits_resp_in_0_s2_full_pred_1_br_taken_mask_1 | ||
input | io_in_bits_resp_in_0_s2_full_pred_2_br_taken_mask_0 | ||
input | io_in_bits_resp_in_0_s2_full_pred_2_br_taken_mask_1 | ||
input | io_in_bits_resp_in_0_s2_full_pred_3_br_taken_mask_0 | ||
input | io_in_bits_resp_in_0_s2_full_pred_3_br_taken_mask_1 | ||
input | io_in_bits_resp_in_0_s3_full_pred_0_br_taken_mask_0 | ||
input | io_in_bits_resp_in_0_s3_full_pred_0_br_taken_mask_1 | ||
input | io_in_bits_resp_in_0_s3_full_pred_1_br_taken_mask_0 | ||
input | io_in_bits_resp_in_0_s3_full_pred_1_br_taken_mask_1 | ||
input | io_in_bits_resp_in_0_s3_full_pred_2_br_taken_mask_0 | ||
input | io_in_bits_resp_in_0_s3_full_pred_2_br_taken_mask_1 | ||
input | io_in_bits_resp_in_0_s3_full_pred_3_br_taken_mask_0 | ||
input | io_in_bits_resp_in_0_s3_full_pred_3_br_taken_mask_1 | ||
output | io_out_s2_full_pred_0_br_taken_mask_0 | s2 阶段输出的完整预测结果 | |
output | io_out_s2_full_pred_0_br_taken_mask_1 | ||
output | io_out_s2_full_pred_0_slot_valids_0 | ||
output | io_out_s2_full_pred_0_slot_valids_1 | ||
output | [40:0] | io_out_s2_full_pred_0_targets_0 | |
output | [40:0] | io_out_s2_full_pred_0_targets_1 | |
output | [40:0] | io_out_s2_full_pred_0_jalr_target | |
output | [3:0] | io_out_s2_full_pred_0_offsets_0 | |
output | [3:0] | io_out_s2_full_pred_0_offsets_1 | |
output | [40:0] | io_out_s2_full_pred_0_fallThroughAddr | |
output | io_out_s2_full_pred_0_is_br_sharing | ||
output | io_out_s2_full_pred_0_hit | ||
output | io_out_s2_full_pred_1_br_taken_mask_0 | 同上 | |
output | io_out_s2_full_pred_1_br_taken_mask_1 | ||
output | io_out_s2_full_pred_1_slot_valids_0 | ||
output | io_out_s2_full_pred_1_slot_valids_1 | ||
output | [40:0] | io_out_s2_full_pred_1_targets_0 | |
output | [40:0] | io_out_s2_full_pred_1_targets_1 | |
output | [40:0] | io_out_s2_full_pred_1_jalr_target | |
output | [3:0] | io_out_s2_full_pred_1_offsets_0 | |
output | [3:0] | io_out_s2_full_pred_1_offsets_1 | |
output | [40:0] | io_out_s2_full_pred_1_fallThroughAddr | |
output | io_out_s2_full_pred_1_is_br_sharing | ||
output | io_out_s2_full_pred_1_hit | ||
output | io_out_s2_full_pred_2_br_taken_mask_0 | 同上 | |
output | io_out_s2_full_pred_2_br_taken_mask_1 | ||
output | io_out_s2_full_pred_2_slot_valids_0 | ||
output | io_out_s2_full_pred_2_slot_valids_1 | ||
output | [40:0] | io_out_s2_full_pred_2_targets_0 | |
output | [40:0] | io_out_s2_full_pred_2_targets_1 | |
output | [40:0] | io_out_s2_full_pred_2_jalr_target | |
output | [3:0] | io_out_s2_full_pred_2_offsets_0 | |
output | [3:0] | io_out_s2_full_pred_2_offsets_1 | |
output | [40:0] | io_out_s2_full_pred_2_fallThroughAddr | |
output | io_out_s2_full_pred_2_is_jalr | ||
output | io_out_s2_full_pred_2_is_call | ||
output | io_out_s2_full_pred_2_is_ret | ||
output | io_out_s2_full_pred_2_last_may_be_rvi_call | ||
output | io_out_s2_full_pred_2_is_br_sharing | ||
output | io_out_s2_full_pred_2_hit | ||
output | io_out_s2_full_pred_3_br_taken_mask_0 | 同上 | |
output | io_out_s2_full_pred_3_br_taken_mask_1 | ||
output | io_out_s2_full_pred_3_slot_valids_0 | ||
output | io_out_s2_full_pred_3_slot_valids_1 | ||
output | [40:0] | io_out_s2_full_pred_3_targets_0 | |
output | [40:0] | io_out_s2_full_pred_3_targets_1 | |
output | [40:0] | io_out_s2_full_pred_3_jalr_target | |
output | [3:0] | io_out_s2_full_pred_3_offsets_0 | |
output | [3:0] | io_out_s2_full_pred_3_offsets_1 | |
output | [40:0] | io_out_s2_full_pred_3_fallThroughAddr | |
output | io_out_s2_full_pred_3_fallThroughErr | ||
output | io_out_s2_full_pred_3_is_br_sharing | ||
output | io_out_s2_full_pred_3_hit | ||
output | io_out_s3_full_pred_0_br_taken_mask_0 | s3 阶段输出的完整预测结果 | |
output | io_out_s3_full_pred_0_br_taken_mask_1 | ||
output | io_out_s3_full_pred_0_slot_valids_0 | ||
output | io_out_s3_full_pred_0_slot_valids_1 | ||
output | [40:0] | io_out_s3_full_pred_0_targets_0 | |
output | [40:0] | io_out_s3_full_pred_0_targets_1 | |
output | [40:0] | io_out_s3_full_pred_0_jalr_target | |
output | [40:0] | io_out_s3_full_pred_0_fallThroughAddr | |
output | io_out_s3_full_pred_0_fallThroughErr | ||
output | io_out_s3_full_pred_0_is_br_sharing | ||
output | io_out_s3_full_pred_0_hit | ||
output | io_out_s3_full_pred_1_br_taken_mask_0 | 同上 | |
output | io_out_s3_full_pred_1_br_taken_mask_1 | ||
output | io_out_s3_full_pred_1_slot_valids_0 | ||
output | io_out_s3_full_pred_1_slot_valids_1 | ||
output | [40:0] | io_out_s3_full_pred_1_targets_0 | |
output | [40:0] | io_out_s3_full_pred_1_targets_1 | |
output | [40:0] | io_out_s3_full_pred_1_jalr_target | |
output | [40:0] | io_out_s3_full_pred_1_fallThroughAddr | |
output | io_out_s3_full_pred_1_fallThroughErr | ||
output | io_out_s3_full_pred_1_is_br_sharing | ||
output | io_out_s3_full_pred_1_hit | ||
output | io_out_s3_full_pred_2_br_taken_mask_0 | 同上 | |
output | io_out_s3_full_pred_2_br_taken_mask_1 | ||
output | io_out_s3_full_pred_2_slot_valids_0 | ||
output | io_out_s3_full_pred_2_slot_valids_1 | ||
output | [40:0] | io_out_s3_full_pred_2_targets_0 | |
output | [40:0] | io_out_s3_full_pred_2_targets_1 | |
output | [40:0] | io_out_s3_full_pred_2_jalr_target | |
output | [40:0] | io_out_s3_full_pred_2_fallThroughAddr | |
output | io_out_s3_full_pred_2_fallThroughErr | ||
output | io_out_s3_full_pred_2_is_jalr | ||
output | io_out_s3_full_pred_2_is_call | ||
output | io_out_s3_full_pred_2_is_ret | ||
output | io_out_s3_full_pred_2_is_br_sharing | ||
output | io_out_s3_full_pred_2_hit | ||
output | io_out_s3_full_pred_3_br_taken_mask_0 | 同上 | |
output | io_out_s3_full_pred_3_br_taken_mask_1 | ||
output | io_out_s3_full_pred_3_slot_valids_0 | ||
output | io_out_s3_full_pred_3_slot_valids_1 | ||
output | [40:0] | io_out_s3_full_pred_3_targets_0 | |
output | [40:0] | io_out_s3_full_pred_3_targets_1 | |
output | [40:0] | io_out_s3_full_pred_3_jalr_target | |
output | [3:0] | io_out_s3_full_pred_3_offsets_0 | |
output | [3:0] | io_out_s3_full_pred_3_offsets_1 | |
output | [40:0] | io_out_s3_full_pred_3_fallThroughAddr | |
output | io_out_s3_full_pred_3_fallThroughErr | ||
output | io_out_s3_full_pred_3_is_br_sharing | ||
output | io_out_s3_full_pred_3_hit | ||
output | [222:0] | io_out_last_stage_meta | 最后一个阶段输出的 meta 信息 |
output | io_out_last_stage_ftb_entry_valid | 最后一个阶段输出的 FTB 项 | |
output | [3:0] | io_out_last_stage_ftb_entry_brSlots_0_offset | |
output | [11:0] | io_out_last_stage_ftb_entry_brSlots_0_lower | |
output | [1:0] | io_out_last_stage_ftb_entry_brSlots_0_tarStat | |
output | io_out_last_stage_ftb_entry_brSlots_0_sharing | ||
output | io_out_last_stage_ftb_entry_brSlots_0_valid | ||
output | [3:0] | io_out_last_stage_ftb_entry_tailSlot_offset | |
output | [19:0] | io_out_last_stage_ftb_entry_tailSlot_lower | |
output | [1:0] | io_out_last_stage_ftb_entry_tailSlot_tarStat | |
output | io_out_last_stage_ftb_entry_tailSlot_sharing | ||
output | io_out_last_stage_ftb_entry_tailSlot_valid | ||
output | [3:0] | io_out_last_stage_ftb_entry_pftAddr | |
output | io_out_last_stage_ftb_entry_carry | ||
output | io_out_last_stage_ftb_entry_isCall | ||
output | io_out_last_stage_ftb_entry_isRet | ||
output | io_out_last_stage_ftb_entry_isJalr | ||
output | io_out_last_stage_ftb_entry_last_may_be_rvi_call | ||
output | io_out_last_stage_ftb_entry_always_taken_0 | ||
output | io_out_last_stage_ftb_entry_always_taken_1 | ||
input | io_ctrl_btb_enable | 使能信号 | |
input | io_s0_fire_0 | s0 阶段流水线控制信号 | |
input | io_s0_fire_1 | ||
input | io_s0_fire_2 | ||
input | io_s0_fire_3 | ||
output | io_s1_ready | s1 阶段流水线控制信号 | |
input | io_s1_fire_0 | ||
input | io_s1_fire_1 | ||
input | io_s1_fire_2 | ||
input | io_s1_fire_3 | ||
input | io_s2_fire_0 | s2 阶段流水线控制信号 | |
input | io_s2_fire_1 | ||
input | io_s2_fire_2 | ||
input | io_s2_fire_3 | ||
input | io_update_valid | 更新有效性 | |
input | [40:0] | io_update_bits_pc | 传回的预测块pc(用于指示更新的预测块) |
input | io_update_bits_ftb_entry_valid | 是否启用 | |
input | [3:0] | io_update_bits_ftb_entry_brSlots_0_offset | solt 0 中分支指令相对于地址块起始pc的偏移 |
input | [11:0] | io_update_bits_ftb_entry_brSlots_0_lower | 跳转目标地址的低位 |
input | [1:0] | io_update_bits_ftb_entry_brSlots_0_tarStat | 跳转后的 pc 高位是否进退位 |
input | io_update_bits_ftb_entry_brSlots_0_sharing | 无条件跳转指令槽中存储条件分支指令 | |
input | io_update_bits_ftb_entry_brSlots_0_valid | 是否启用 | |
input | [3:0] | io_update_bits_ftb_entry_tailSlot_offset | solt 1 中分支指令相对于地址块起始pc的偏移 |
input | [19:0] | io_update_bits_ftb_entry_tailSlot_lower | 跳转目标地址的低位 |
input | [1:0] | io_update_bits_ftb_entry_tailSlot_tarStat | 跳转后的 pc 高位是否进退位 |
input | io_update_bits_ftb_entry_tailSlot_sharing | 无条件跳转指令槽中存储条件分支指令 | |
input | io_update_bits_ftb_entry_tailSlot_valid | 是否启用 | |
input | [3:0] | io_update_bits_ftb_entry_pftAddr | Partial Fallthrough Addr 如果预测块中没有跳转,那么程序将会顺序执行到达的地址,预测块的结束地址。 |
input | io_update_bits_ftb_entry_carry | pc+pft时是否产生进位 | |
input | io_update_bits_ftb_entry_isCall | 是否是函数调用 | |
input | io_update_bits_ftb_entry_isRet | 是否是函数返回 | |
input | io_update_bits_ftb_entry_isJalr | 是否是 jalr 指令 | |
input | io_update_bits_ftb_entry_last_may_be_rvi_call | 最后一个指令槽存储的可能是 rvi 的 call 指令 | |
input | io_update_bits_ftb_entry_always_taken_0 | 是否预测为总是跳转 | |
input | io_update_bits_ftb_entry_always_taken_1 | 是否预测为总是跳转 | |
input | io_update_bits_old_entry | 是否是旧的 FTB 项 | |
input | [222:0] | io_update_bits_meta | meta 信息 |
对于一般条件分支指令,只需要预测跳转(taken),或者不跳转(no taken),但是对于间接跳转,如call/jump等指令,需要预测跳转到哪里去(Target)。为了让TAGE支持预测跳转地址,ITTAGE(Indirect Target TAGE)应运而生。
ITTAGE 与 TAGE 的主要区别在于,在T0和Tn表中,多了 Target PC 数据。预测时,ITTAGE选择匹配到的、历史最长的表项中的 Target 作为预测结果,并利用 2bit 饱和计数器决定是否输出该结果,或选用替代预测结果。TAGE 预测器说明,请参考 TAGE-SC分支预测器。
由于在昆明湖的BPU设计中,采用多预测器级联的方式进行预测,因此在子预测器实现上,会与原始预测器有所不同,其中多数表现在默认预测结果上。
ITTAGE基本功能类似 TAGE 分支预测器,但存在以下不同点:
ITTAGE 内含三个流水级,第一级计算索引,第二级通过索引读出SRAM表中的结果
第一流水级运行:计算index。通过寄存器输出到s1
第二流水级运行:访存SRAM,读取预测用信息。通过寄存器输出到s2
**第三流水级运行:**处理原始预测信息,决定是否输出预测结果。
在昆明湖的实现中,T0与Tn的表结构如下所示:
预测器 | 作用 | 表项构成 | 项数 |
---|---|---|---|
基准预测器T0 | 用于在其他预测器的预测结果都无效时输出预测结果 | 虚表,不存在。 直接将上级预测器FTB 的预测结果作为表项结果 | 虚表,不存在。 直接将上级预测器FTB结果作为索引到的结果 |
预测表T1-T2 | 对每个预测块的输入,所有Tn表都进行预测,在所有预测有效的结果中,选择历史记录最长的结果作为 原始预测信息。历史记录长度由输入的H决定 | target: 41 bits; valid: 1bit; tag: 9bits; ctr: 2bits; us: 1bit(usefulness计数器) | 256项 |
预测表T3-T5 | 512项 |
检索方法与 TAGE 分支预测器一致,只是各表的配置选项不同。
表名称 | FH长度 | FH1长度 | FH2长度 | 最近历史长度(用到GH中的位数) |
---|---|---|---|---|
T1 | 4比特 | 4比特 | 4比特 | 低4位,即把最新4位历史,折叠成FH、FH1、FH2 |
T2 | 8比特 | 8比特 | 8比特 | 低8位,即把最新8位历史,折叠成FH、FH1、FH2 |
T3 | 9比特 | 9比特 | 8比特 | 低13位,即把最新13位历史,折叠成FH、FH1、FH2 |
T4 | 9比特 | 9比特 | 8比特 | 低16位,即把最新16位历史,折叠成FH、FH1、FH2 |
T5 | 9比特 | 9比特 | 8比特 | 低32位,即把最新32位历史,折叠成FH、FH1、FH2 |
其他过程(计算方式 及 计算公式)类似 TAGE-SC分支预测器
当Tn表给出的预测结果,“预测信心”不足时,需要对预测结果进行跳转,成为“替代预测器”。该过程与 TAGE 类似,具体请先阅读TAGE的对应部分。与 TAGE 不同,ITTAGE 的 ctr 并不给出预测方向,只决定是否输出该结果(预测信心)。当 ctr 为 2b00 的时候,即认定为弱信心。选择替代预测结果:
预测过程与 TAGE 类似,但与 TAGE 不同的是ITTAGE 多了一步根据 ctr 决定是否输出预测结果。具体过程如下:
该过程与 TAGE 类似,具体不同点如下:
接口类型 | 位宽 | 信号名 | 备注 |
---|---|---|---|
input | clock | ||
input | reset | ||
input | [40:0] | io_in_bits_s0_pc_3 | 用于预测的PC |
input | [7:0] | io_in_bits_folded_hist_3_hist_14_folded_hist | T2 折叠历史 |
input | [8:0] | io_in_bits_folded_hist_3_hist_13_folded_hist | T3 折叠历史 |
input | [3:0] | io_in_bits_folded_hist_3_hist_12_folded_hist | T1 折叠历史 |
input | [8:0] | io_in_bits_folded_hist_3_hist_10_folded_hist | T5 折叠历史 |
input | [8:0] | io_in_bits_folded_hist_3_hist_6_folded_hist | T4 折叠历史 |
input | [7:0] | io_in_bits_folded_hist_3_hist_4_folded_hist | T3 折叠历史 |
input | [7:0] | io_in_bits_folded_hist_3_hist_3_folded_hist | T5 折叠历史 |
input | [7:0] | io_in_bits_folded_hist_3_hist_2_folded_hist | T4 折叠历史 |
input | [40:0] | io_in_bits_resp_in_0_s2_full_pred_3_jalr_target | FTB项输入,提供替代预测 |
output | [40:0] | io_out_s3_full_pred_0_jalr_target | |
output | [40:0] | io_out_s3_full_pred_1_jalr_target | |
output | [40:0] | io_out_s3_full_pred_2_jalr_target | |
output | [40:0] | io_out_s3_full_pred_3_jalr_target | |
output | [222:0] | io_out_last_stage_meta | [100:0] 有效,是ITTAGE的Meta信息 |
input | io_s0_fire_3 | s0阶段使能信号 | |
input | io_s1_fire_3 | s1阶段使能信号 | |
input | io_s2_fire_0 | s2阶段使能信号,相同 | |
input | io_s2_fire_1 | ||
input | io_s2_fire_2 | ||
input | io_s2_fire_3 | ||
input | io_update_valid | 是否进行更新 | |
input | [40:0] | io_update_bits_pc | 待更新的预测块pc索引 |
input | [7:0] | io_update_bits_spec_info_folded_hist_hist_14_folded_hist | T2 更新时传入的历史 |
input | [8:0] | io_update_bits_spec_info_folded_hist_hist_13_folded_hist | T3 更新时传入的历史 |
input | [3:0] | io_update_bits_spec_info_folded_hist_hist_12_folded_hist | T1 更新时传入的历史 |
input | [8:0] | io_update_bits_spec_info_folded_hist_hist_10_folded_hist | T5 更新时传入的历史 |
input | [8:0] | io_update_bits_spec_info_folded_hist_hist_6_folded_hist | T4 更新时传入的历史 |
input | [7:0] | io_update_bits_spec_info_folded_hist_hist_4_folded_hist | T3 更新时传入的历史 |
input | [7:0] | io_update_bits_spec_info_folded_hist_hist_3_folded_hist | T5 更新时传入的历史 |
input | [7:0] | io_update_bits_spec_info_folded_hist_hist_2_folded_hist | T4 更新时传入的历史 |
input | [3:0] | io_update_bits_ftb_entry_tailSlot_offset | 待更新的FTB项offset |
input | io_update_bits_ftb_entry_tailSlot_sharing | 待更新的FTB项是否是有条件跳转 | |
input | io_update_bits_ftb_entry_tailSlot_valid | 待更新的tailSlot是否启用 | |
input | io_update_bits_ftb_entry_isRet | tailSlot是否是Ret指令 | |
input | io_update_bits_ftb_entry_isJalr | tailSlot是否是Jalr指令 | |
input | io_update_bits_cfi_idx_valid | 控制流指令在预测块中的索引.valid信号 | |
input | [3:0] | io_update_bits_cfi_idx_bits | 控制流指令在预测块中的索引 |
input | io_update_bits_jmp_taken | 预测块内无条件跳转指令被触发 | |
input | io_update_bits_mispred_mask_2 | 是否预测错误 | |
input | [222:0] | io_update_bits_meta | 预测时传出 meta 信息的[222:25] 即{25h0, _ubtb_io_out_last_stage_meta[5:0] ,_tage_io_out_last_stage_meta[87:0] ,_ftb_io_out_last_stage_meta[2:0], _ittage_io_out_last_stage_meta[100:0]} |
input | [40:0] | io_update_bits_full_target | 预测块的跳转目标(下一个预测块的起始地址) |
接口类型 | 位宽 | 信号名 | 备注 |
---|---|---|---|
input | io_in_bits_resp_in_0_s2_full_pred_0_br_taken_mask_0 | 从FTB输入 完全透传到输出 包括jalr_target | |
input | io_in_bits_resp_in_0_s2_full_pred_0_br_taken_mask_1 | ||
input | io_in_bits_resp_in_0_s2_full_pred_0_slot_valids_0 | ||
input | io_in_bits_resp_in_0_s2_full_pred_0_slot_valids_1 | ||
input | [40:0] | io_in_bits_resp_in_0_s2_full_pred_0_targets_0 | |
input | [40:0] | io_in_bits_resp_in_0_s2_full_pred_0_targets_1 | |
input | [40:0] | io_in_bits_resp_in_0_s2_full_pred_0_jalr_target | |
input | [3:0] | io_in_bits_resp_in_0_s2_full_pred_0_offsets_0 | |
input | [3:0] | io_in_bits_resp_in_0_s2_full_pred_0_offsets_1 | |
input | [40:0] | io_in_bits_resp_in_0_s2_full_pred_0_fallThroughAddr | |
input | io_in_bits_resp_in_0_s2_full_pred_0_is_br_sharing | ||
input | io_in_bits_resp_in_0_s2_full_pred_0_hit | ||
input | io_in_bits_resp_in_0_s2_full_pred_1_br_taken_mask_0 | ||
input | io_in_bits_resp_in_0_s2_full_pred_1_br_taken_mask_1 | ||
input | io_in_bits_resp_in_0_s2_full_pred_1_slot_valids_0 | ||
input | io_in_bits_resp_in_0_s2_full_pred_1_slot_valids_1 | ||
input | [40:0] | io_in_bits_resp_in_0_s2_full_pred_1_targets_0 | |
input | [40:0] | io_in_bits_resp_in_0_s2_full_pred_1_targets_1 | |
input | [40:0] | io_in_bits_resp_in_0_s2_full_pred_1_jalr_target | |
input | [3:0] | io_in_bits_resp_in_0_s2_full_pred_1_offsets_0 | |
input | [3:0] | io_in_bits_resp_in_0_s2_full_pred_1_offsets_1 | |
input | [40:0] | io_in_bits_resp_in_0_s2_full_pred_1_fallThroughAddr | |
input | io_in_bits_resp_in_0_s2_full_pred_1_is_br_sharing | ||
input | io_in_bits_resp_in_0_s2_full_pred_1_hit | ||
input | io_in_bits_resp_in_0_s2_full_pred_2_br_taken_mask_0 | ||
input | io_in_bits_resp_in_0_s2_full_pred_2_br_taken_mask_1 | ||
input | io_in_bits_resp_in_0_s2_full_pred_2_slot_valids_0 | ||
input | io_in_bits_resp_in_0_s2_full_pred_2_slot_valids_1 | ||
input | [40:0] | io_in_bits_resp_in_0_s2_full_pred_2_targets_0 | |
input | [40:0] | io_in_bits_resp_in_0_s2_full_pred_2_targets_1 | |
input | [40:0] | io_in_bits_resp_in_0_s2_full_pred_2_jalr_target | |
input | [3:0] | io_in_bits_resp_in_0_s2_full_pred_2_offsets_0 | |
input | [3:0] | io_in_bits_resp_in_0_s2_full_pred_2_offsets_1 | |
input | [40:0] | io_in_bits_resp_in_0_s2_full_pred_2_fallThroughAddr | |
input | io_in_bits_resp_in_0_s2_full_pred_2_is_jalr | RAS 模块使用的信息,透传 | |
input | io_in_bits_resp_in_0_s2_full_pred_2_is_call | ||
input | io_in_bits_resp_in_0_s2_full_pred_2_is_ret | ||
input | io_in_bits_resp_in_0_s2_full_pred_2_last_may_be_rvi_call | ||
input | io_in_bits_resp_in_0_s2_full_pred_2_is_br_sharing | 从FTB输入 完全透传到输出 包括jalr_target fallThroughErr 表示 FTB项 中记录的 pftAddr 有误 生成方式:比较 pftAddr 代表的预测块结束地址是否大于预测块的起始地址,如果小于,则代表出现错误,此信号置为有效。这种情况可能会发生在 pc 索引到错误的 FTB 项的情况。 FTQ使用这个变量,与ITTAGE无关 | |
input | io_in_bits_resp_in_0_s2_full_pred_2_hit | ||
input | io_in_bits_resp_in_0_s2_full_pred_3_br_taken_mask_0 | ||
input | io_in_bits_resp_in_0_s2_full_pred_3_br_taken_mask_1 | ||
input | io_in_bits_resp_in_0_s2_full_pred_3_slot_valids_0 | ||
input | io_in_bits_resp_in_0_s2_full_pred_3_slot_valids_1 | ||
input | [40:0] | io_in_bits_resp_in_0_s2_full_pred_3_targets_0 | |
input | [40:0] | io_in_bits_resp_in_0_s2_full_pred_3_targets_1 | |
input | [3:0] | io_in_bits_resp_in_0_s2_full_pred_3_offsets_0 | |
input | [3:0] | io_in_bits_resp_in_0_s2_full_pred_3_offsets_1 | |
input | [40:0] | io_in_bits_resp_in_0_s2_full_pred_3_fallThroughAddr | |
input | io_in_bits_resp_in_0_s2_full_pred_3_fallThroughErr | ||
input | io_in_bits_resp_in_0_s2_full_pred_3_is_br_sharing | ||
input | io_in_bits_resp_in_0_s2_full_pred_3_hit | ||
input | io_in_bits_resp_in_0_s3_full_pred_0_br_taken_mask_0 | 除了 jalr_target 可能被修改,其他都是透传 | |
input | io_in_bits_resp_in_0_s3_full_pred_0_br_taken_mask_1 | ||
input | io_in_bits_resp_in_0_s3_full_pred_0_slot_valids_0 | ||
input | io_in_bits_resp_in_0_s3_full_pred_0_slot_valids_1 | ||
input | [40:0] | io_in_bits_resp_in_0_s3_full_pred_0_targets_0 | |
input | [40:0] | io_in_bits_resp_in_0_s3_full_pred_0_targets_1 | |
input | [40:0] | io_in_bits_resp_in_0_s3_full_pred_0_fallThroughAddr | |
input | io_in_bits_resp_in_0_s3_full_pred_0_fallThroughErr | ||
input | io_in_bits_resp_in_0_s3_full_pred_0_is_br_sharing | ||
input | io_in_bits_resp_in_0_s3_full_pred_0_hit | ||
input | [40:0] | io_in_bits_resp_in_0_s3_full_pred_0_jalr_target | |
input | io_in_bits_resp_in_0_s3_full_pred_1_br_taken_mask_0 | 同上 | |
input | io_in_bits_resp_in_0_s3_full_pred_1_br_taken_mask_1 | ||
input | io_in_bits_resp_in_0_s3_full_pred_1_slot_valids_0 | ||
input | io_in_bits_resp_in_0_s3_full_pred_1_slot_valids_1 | ||
input | [40:0] | io_in_bits_resp_in_0_s3_full_pred_1_targets_0 | |
input | [40:0] | io_in_bits_resp_in_0_s3_full_pred_1_targets_1 | |
input | [40:0] | io_in_bits_resp_in_0_s3_full_pred_1_fallThroughAddr | |
input | io_in_bits_resp_in_0_s3_full_pred_1_fallThroughErr | ||
input | io_in_bits_resp_in_0_s3_full_pred_1_is_br_sharing | ||
input | io_in_bits_resp_in_0_s3_full_pred_1_hit | ||
input | [40:0] | io_in_bits_resp_in_0_s3_full_pred_1_jalr_target | |
input | io_in_bits_resp_in_0_s3_full_pred_2_br_taken_mask_0 | 同上 | |
input | io_in_bits_resp_in_0_s3_full_pred_2_br_taken_mask_1 | ||
input | io_in_bits_resp_in_0_s3_full_pred_2_slot_valids_0 | ||
input | io_in_bits_resp_in_0_s3_full_pred_2_slot_valids_1 | ||
input | [40:0] | io_in_bits_resp_in_0_s3_full_pred_2_targets_0 | |
input | [40:0] | io_in_bits_resp_in_0_s3_full_pred_2_targets_1 | |
input | [40:0] | io_in_bits_resp_in_0_s3_full_pred_2_fallThroughAddr | |
input | io_in_bits_resp_in_0_s3_full_pred_2_fallThroughErr | ||
input | io_in_bits_resp_in_0_s3_full_pred_2_is_jalr | ||
input | io_in_bits_resp_in_0_s3_full_pred_2_is_call | ||
input | io_in_bits_resp_in_0_s3_full_pred_2_is_ret | ||
input | io_in_bits_resp_in_0_s3_full_pred_2_is_br_sharing | ||
input | io_in_bits_resp_in_0_s3_full_pred_2_hit | ||
input | [40:0] | io_in_bits_resp_in_0_s3_full_pred_2_jalr_target | |
input | io_in_bits_resp_in_0_s3_full_pred_3_br_taken_mask_0 | 同上 | |
input | io_in_bits_resp_in_0_s3_full_pred_3_br_taken_mask_1 | ||
input | io_in_bits_resp_in_0_s3_full_pred_3_slot_valids_0 | ||
input | io_in_bits_resp_in_0_s3_full_pred_3_slot_valids_1 | ||
input | [40:0] | io_in_bits_resp_in_0_s3_full_pred_3_targets_0 | |
input | [40:0] | io_in_bits_resp_in_0_s3_full_pred_3_targets_1 | |
input | [3:0] | io_in_bits_resp_in_0_s3_full_pred_3_offsets_0 | |
input | [3:0] | io_in_bits_resp_in_0_s3_full_pred_3_offsets_1 | |
input | [40:0] | io_in_bits_resp_in_0_s3_full_pred_3_fallThroughAddr | |
input | io_in_bits_resp_in_0_s3_full_pred_3_fallThroughErr | ||
input | io_in_bits_resp_in_0_s3_full_pred_3_is_br_sharing | ||
input | io_in_bits_resp_in_0_s3_full_pred_3_hit | ||
input | [40:0] | io_in_bits_resp_in_0_s3_full_pred_3_jalr_target | |
input | io_in_bits_resp_in_0_last_stage_ftb_entry_valid | 透传到output,不做修改 来源是FTB | |
input | [3:0] | io_in_bits_resp_in_0_last_stage_ftb_entry_brSlots_0_offset | |
input | [11:0] | io_in_bits_resp_in_0_last_stage_ftb_entry_brSlots_0_lower | |
input | [1:0] | io_in_bits_resp_in_0_last_stage_ftb_entry_brSlots_0_tarStat | |
input | io_in_bits_resp_in_0_last_stage_ftb_entry_brSlots_0_sharing | ||
input | io_in_bits_resp_in_0_last_stage_ftb_entry_brSlots_0_valid | ||
input | [3:0] | io_in_bits_resp_in_0_last_stage_ftb_entry_tailSlot_offset | |
input | [19:0] | io_in_bits_resp_in_0_last_stage_ftb_entry_tailSlot_lower | |
input | [1:0] | io_in_bits_resp_in_0_last_stage_ftb_entry_tailSlot_tarStat | |
input | io_in_bits_resp_in_0_last_stage_ftb_entry_tailSlot_sharing | ||
input | io_in_bits_resp_in_0_last_stage_ftb_entry_tailSlot_valid | ||
input | [3:0] | io_in_bits_resp_in_0_last_stage_ftb_entry_pftAddr | |
input | io_in_bits_resp_in_0_last_stage_ftb_entry_carry | ||
input | io_in_bits_resp_in_0_last_stage_ftb_entry_isCall | ||
input | io_in_bits_resp_in_0_last_stage_ftb_entry_isRet | ||
input | io_in_bits_resp_in_0_last_stage_ftb_entry_isJalr | ||
input | io_in_bits_resp_in_0_last_stage_ftb_entry_last_may_be_rvi_call | ||
input | io_in_bits_resp_in_0_last_stage_ftb_entry_always_taken_0 | ||
input | io_in_bits_resp_in_0_last_stage_ftb_entry_always_taken_1 | ||
output | io_out_s2_full_pred_0_br_taken_mask_0 | 完全透传传入值 prefix: io_in_bits_resp_in_ | |
output | io_out_s2_full_pred_0_br_taken_mask_1 | ||
output | io_out_s2_full_pred_0_slot_valids_0 | ||
output | io_out_s2_full_pred_0_slot_valids_1 | ||
output | [40:0] | io_out_s2_full_pred_0_targets_0 | |
output | [40:0] | io_out_s2_full_pred_0_targets_1 | |
output | [40:0] | io_out_s2_full_pred_0_jalr_target | |
output | [3:0] | io_out_s2_full_pred_0_offsets_0 | |
output | [3:0] | io_out_s2_full_pred_0_offsets_1 | |
output | [40:0] | io_out_s2_full_pred_0_fallThroughAddr | |
output | io_out_s2_full_pred_0_is_br_sharing | ||
output | io_out_s2_full_pred_0_hit | ||
output | io_out_s2_full_pred_1_br_taken_mask_0 | ||
output | io_out_s2_full_pred_1_br_taken_mask_1 | ||
output | io_out_s2_full_pred_1_slot_valids_0 | ||
output | io_out_s2_full_pred_1_slot_valids_1 | ||
output | [40:0] | io_out_s2_full_pred_1_targets_0 | |
output | [40:0] | io_out_s2_full_pred_1_targets_1 | |
output | [40:0] | io_out_s2_full_pred_1_jalr_target | |
output | [3:0] | io_out_s2_full_pred_1_offsets_0 | |
output | [3:0] | io_out_s2_full_pred_1_offsets_1 | |
output | [40:0] | io_out_s2_full_pred_1_fallThroughAddr | |
output | io_out_s2_full_pred_1_is_br_sharing | ||
output | io_out_s2_full_pred_1_hit | ||
output | io_out_s2_full_pred_2_br_taken_mask_0 | ||
output | io_out_s2_full_pred_2_br_taken_mask_1 | ||
output | io_out_s2_full_pred_2_slot_valids_0 | ||
output | io_out_s2_full_pred_2_slot_valids_1 | ||
output | [40:0] | io_out_s2_full_pred_2_targets_0 | |
output | [40:0] | io_out_s2_full_pred_2_targets_1 | |
output | [40:0] | io_out_s2_full_pred_2_jalr_target | |
output | [3:0] | io_out_s2_full_pred_2_offsets_0 | |
output | [3:0] | io_out_s2_full_pred_2_offsets_1 | |
output | [40:0] | io_out_s2_full_pred_2_fallThroughAddr | |
output | io_out_s2_full_pred_2_is_jalr | ||
output | io_out_s2_full_pred_2_is_call | ||
output | io_out_s2_full_pred_2_is_ret | ||
output | io_out_s2_full_pred_2_last_may_be_rvi_call | ||
output | io_out_s2_full_pred_2_is_br_sharing | ||
output | io_out_s2_full_pred_2_hit | ||
output | io_out_s2_full_pred_3_br_taken_mask_0 | ||
output | io_out_s2_full_pred_3_br_taken_mask_1 | ||
output | io_out_s2_full_pred_3_slot_valids_0 | ||
output | io_out_s2_full_pred_3_slot_valids_1 | ||
output | [40:0] | io_out_s2_full_pred_3_targets_0 | |
output | [40:0] | io_out_s2_full_pred_3_targets_1 | |
output | [40:0] | io_out_s2_full_pred_3_jalr_target | |
output | [3:0] | io_out_s2_full_pred_3_offsets_0 | |
output | [3:0] | io_out_s2_full_pred_3_offsets_1 | |
output | [40:0] | io_out_s2_full_pred_3_fallThroughAddr | |
output | io_out_s2_full_pred_3_fallThroughErr | ||
output | io_out_s2_full_pred_3_is_br_sharing | ||
output | io_out_s2_full_pred_3_hit | ||
output | io_out_s3_full_pred_0_br_taken_mask_0 | 见对应prefix的输入 | |
output | io_out_s3_full_pred_0_br_taken_mask_1 | ||
output | io_out_s3_full_pred_0_slot_valids_0 | ||
output | io_out_s3_full_pred_0_slot_valids_1 | ||
output | [40:0] | io_out_s3_full_pred_0_targets_0 | |
output | [40:0] | io_out_s3_full_pred_0_targets_1 | |
output | [40:0] | io_out_s3_full_pred_0_fallThroughAddr | |
output | io_out_s3_full_pred_0_fallThroughErr | ||
output | io_out_s3_full_pred_0_is_br_sharing | ||
output | io_out_s3_full_pred_0_hit | ||
output | io_out_s3_full_pred_1_br_taken_mask_0 | 见对应prefix的输入 | |
output | io_out_s3_full_pred_1_br_taken_mask_1 | ||
output | io_out_s3_full_pred_1_slot_valids_0 | ||
output | io_out_s3_full_pred_1_slot_valids_1 | ||
output | [40:0] | io_out_s3_full_pred_1_targets_0 | |
output | [40:0] | io_out_s3_full_pred_1_targets_1 | |
output | [40:0] | io_out_s3_full_pred_1_fallThroughAddr | |
output | io_out_s3_full_pred_1_fallThroughErr | ||
output | io_out_s3_full_pred_1_is_br_sharing | ||
output | io_out_s3_full_pred_1_hit | ||
output | io_out_s3_full_pred_2_br_taken_mask_0 | 见对应prefix的输入 | |
output | io_out_s3_full_pred_2_br_taken_mask_1 | ||
output | io_out_s3_full_pred_2_slot_valids_0 | ||
output | io_out_s3_full_pred_2_slot_valids_1 | ||
output | [40:0] | io_out_s3_full_pred_2_targets_0 | |
output | [40:0] | io_out_s3_full_pred_2_targets_1 | |
output | [40:0] | io_out_s3_full_pred_2_fallThroughAddr | |
output | io_out_s3_full_pred_2_fallThroughErr | ||
output | io_out_s3_full_pred_2_is_jalr | ||
output | io_out_s3_full_pred_2_is_call | ||
output | io_out_s3_full_pred_2_is_ret | ||
output | io_out_s3_full_pred_2_is_br_sharing | ||
output | io_out_s3_full_pred_2_hit | ||
output | io_out_s3_full_pred_3_br_taken_mask_0 | 见对应prefix的输入 | |
output | io_out_s3_full_pred_3_br_taken_mask_1 | ||
output | io_out_s3_full_pred_3_slot_valids_0 | ||
output | io_out_s3_full_pred_3_slot_valids_1 | ||
output | [40:0] | io_out_s3_full_pred_3_targets_0 | |
output | [40:0] | io_out_s3_full_pred_3_targets_1 | |
output | [3:0] | io_out_s3_full_pred_3_offsets_0 | |
output | [3:0] | io_out_s3_full_pred_3_offsets_1 | |
output | [40:0] | io_out_s3_full_pred_3_fallThroughAddr | |
output | io_out_s3_full_pred_3_fallThroughErr | ||
output | io_out_s3_full_pred_3_is_br_sharing | ||
output | io_out_s3_full_pred_3_hit | ||
output | io_out_last_stage_ftb_entry_valid | 完全透传传入的值 | |
output | [3:0] | io_out_last_stage_ftb_entry_brSlots_0_offset | |
output | [11:0] | io_out_last_stage_ftb_entry_brSlots_0_lower | |
output | [1:0] | io_out_last_stage_ftb_entry_brSlots_0_tarStat | |
output | io_out_last_stage_ftb_entry_brSlots_0_sharing | ||
output | io_out_last_stage_ftb_entry_brSlots_0_valid | ||
output | [3:0] | io_out_last_stage_ftb_entry_tailSlot_offset | |
output | [19:0] | io_out_last_stage_ftb_entry_tailSlot_lower | |
output | [1:0] | io_out_last_stage_ftb_entry_tailSlot_tarStat | |
output | io_out_last_stage_ftb_entry_tailSlot_sharing | ||
output | io_out_last_stage_ftb_entry_tailSlot_valid | ||
output | [3:0] | io_out_last_stage_ftb_entry_pftAddr | |
output | io_out_last_stage_ftb_entry_carry | ||
output | io_out_last_stage_ftb_entry_isCall | ||
output | io_out_last_stage_ftb_entry_isRet | ||
output | io_out_last_stage_ftb_entry_isJalr | ||
output | io_out_last_stage_ftb_entry_last_may_be_rvi_call | ||
output | io_out_last_stage_ftb_entry_always_taken_0 | ||
output | io_out_last_stage_ftb_entry_always_taken_1 |
_ubtb_io_out_last_stage_meta
_tage_io_out_last_stage_meta
_ftb_io_out_last_stage_meta
位宽 | 信号名 | 备注 |
---|---|---|
100 | s3_provided | 是否有结果 |
[99:97] | s3_provider | 提供结果的表项 |
96 | s3_altProvided | 是否有替代预测表项 |
[95:93] | s3_altProvider | 提供结果的替代预测表项 |
92 | resp_meta_altDiffers | 替代预测的结果是否和主预测的结果不同 |
91 | s3_providerU | 主预测的useful bit |
[90:89] | s3_providerCtr | 主预测给出的置信度 |
[88:87] | s3_altProviderCtr | 替代预测给出的置信度 |
86 | resp_meta_allocate_valid_r | 有空余的表项可供申请 |
[85:83] | resp_meta_allocate_bits_r | 申请哪个表中的表项 |
82 | s3_tageTaken_dup_3 | 在不使用FTB的情况下始为true,使用FTB也为true |
[81:41] | s3_providerTarget | 主预测给出的跳转地址 |
[40:0] | s3_altProviderTarget | 替代预测给出的跳转地址 |
RAS 指的是 “Return Address Stack”,即返回地址栈。它通过跟踪程序的返回地址,帮助确定程序中的分支行为。由前所述,在程序中存在很多分支:if/else、 switch/case、while/for loop、iteration、call/return
等。RAS分支预测器则专门针对 call/return类型。
function _add(a, b){
return (a > 0 ? a : 0) + (b > 0? b : 0);
}
function add(a, b){
return a + b;
}
function sub(a, b){
return a - b;
}
function main(){
a = 1;
b = 2;
c = add(a, b);
d = sub(a, b);
}
如上图所示,在main函数中调用了add和sub,add又调用了函数_add。在该过程中,每次调用(call)的跳转地址和返回的地址固定,且在call的时候,就可以同时得到返回地址。函数的调用过程是一个“栈的出入”过程,因此可以通过“栈”结构进行分支预测:每遇到call指令,把当前PC+4(压缩指令和普通指令的偏移不同)进行压栈push操作;遇到return指令,则进行进行pop操作,得到的地址即为目标跳转地址。在基于“预测块”的BPU中,RAS无法知道当前块是否是call或者ret,因此需要依赖其他预测器,利用前级预测器的结果进行RAS操作。
具体的,在香山的RAS预测器中,其s2阶段,需要判断上一级s2的输出是否预测为call或者ret(即输入信号io.s2_full_pred.hit_taken_on_call/ret有效 ),如果是call则push其后续指令地址入栈,是ret则从栈中pop出地址作为预测结果。因为在BPU的预测器中,人为假定s3阶段得到的结果比s2阶段好,所以RAS预测器在s3阶段需要进行检查,如果上一级的s3预测结果与s2不一致,则采信s3的结果,并按需求判断是否需要撤销或者补齐之前s2阶段的栈操作。例如s2阶段预测为call指令,进行了push操作,而s3为普通分支指令,不需要进行任何操作,此时就需要撤销push;如果s2预测为普通分支指令,s3预测为call,则需要进行push操作补齐。
在普通的RAS设计中,通过栈进行预测函数返回地址。在理想情况下,本节假定RAS可进行随时备份,栈顶指针用sp表示,预测地址用paddr表示。RAS的基本操作有以下4种:
由于预测可能会出错,出错时需要回到原始状态,因此在push时需要对当前栈的状态进行备份(在软件领域,通常称为“快照”,本文在后继内容中也用快照进行称呼),标记为s。当遇到call指令时,获取call指令的返回地址 addr = 当前pc + 4(如果是压缩指令则addr = pc+2),然后压栈:sp = addr;sp += 1。
原因同上,对当前栈进行快照,标记为s。当遇到ret指令时,预测的跳转地址 paddr = sp,然后进行出栈, sp = sp - 1。对当前栈进行快照,标记为s。
由于BPU是对程序的分支进行预测,因此就有“预测对”和“预测错”两种情况,当CPU后端发现分支预测错误,就会进行重定向(redirect)操作,告诉BPU哪个地方预测错误,正确结果是多少。重定向时RAS模块会收到正确的分支和当时预测时RAS栈信息。根据正确分支指令类型不同,首先恢复快照s有如下情形:
(1)之前被预测的指令,实际是 call指令,根据redirect中给的addr地址,执行push操作;
(2)之前被预测的指令,实际是 ret指令,执行pop操作;
提交(Commit)操作即后端告诉前端,之前预测的结果正确。理想情况下,RAS预测器此时不需要进行任何操作。
由于处理器在执行过程中可能会进行错误的猜测,导致数据污染,RAS需要通过恢复机制来确保数据的准确性。特别是在昆明湖的实际电路设计中,不可能有无限大的栈,也无法随时备份数据。因此,昆明湖的RAS实现中,使用了基于持久化栈的返回地址预测器。具体来说,将RAS分为提交栈(commit stack)和推测栈(spec queue)两个部分,推测栈利用了香山BPU的预测结果来进行预测,然后根据后端返回的信息将数据压入提交栈中。下面详细介绍昆明湖的RAS设计:
为了实现对RAS栈进行快照的功能,昆明湖的RAS推测栈采用了基于循环数组的 “链式表示“ 。设计具体如下:
在这个设计中,数据管理是通过一个循环数组来实现的。循环数组有一个起始地址(BOS)和一个尾指针(TOSW),两者之间的数据是有效数据,其他部分为空闲数据。使用链式结构表示“Sepc栈”,每个栈元素记录其上一个数据的编号,进行栈操作时可以通过该编号获取上一个元素。RAS栈的栈底指针与BOS共用。
如图所示的初始状态S,RAS栈的元素为0、1、4、5。元素5记录了其上一个元素的位置4,元素4记录了其上一个元素的位置1。在状态S下进行push操作时,RAS栈顶指针TOSR等于TOSW,在新的TOSR位置7存入新元素,并记录其上一个元素的位置5,然后TOSW后移(TOSW = TOSW + 1)。进行pop操作时,RAS栈顶指针TOSR根据栈顶元素保存的索引移动到上一个元素的位置4,但不会修改内存分配指针TOSW,因此Spec栈的元素不一定是连续的。
在栈不溢出的情况下,RAS栈每次通过TOSW在数组上分配新数据,因此在正常的Push/Pop操作中,所有过程状态和中间数据都被保存。当需要恢复到状态S时,只需复位相应的栈指针。因此,在每次RAS预测时,需要将操作前的栈指针(BOS、TOSR、TOSW)保存于预测结果中,以便用于commit栈操作和在重定向时恢复状态。该结构的优点是可以保存完整的过程数据,但频繁的push操作会导致较大的空间资源消耗。
当预测结果被确认(commit)后,表示预测是正确的,不会再进行“栈”回滚。也就是说,RAS预测器在收到预测块“P”的commit消息后,不会再收到P块的重定向消息,因此在对P块进行push操作时的快照将不会被用到。
为了优化,RAS栈中的元素被分类处理:未commit的元素存储在链式结构的推测栈中,已commit的元素存储在基于循环数组的提交栈中。这样,原始的RAS栈被拆分成两部分:未commit的部分用链式结构保存,以便需要时可以恢复快照;已commit的部分用普通栈结构保存,因为它们不需要快速恢复。优化后的RAS结构如下所示:
如上图所示,根据普通预测的call/ret和commit提交的call/ret,把原始的RAS栈可以拆分成两个独立的栈,分别称为“预测栈”( spec_stack,链式结构)和“提交栈”(commit_stack,普通结构),由于RASStack对外表现为当拍内就能完成任务,因此在Stack内需要进行数据旁路,对数据进行缓存,由于栈结构发生了变化,具体的Pop/Push操作如下:
1、遇到普通call和ret:
(1)预测块为call正确,在spec_stack上进行push操作,具体为上述链式栈Push过程,并将产生的快照信息送到FTQ存储;
(2)预测块为ret正确,利用spec_stack的栈顶作为预测值,然后进行pop操作,并将产生的快照信息送到FTQ存储,如果spec_stack为空,则使用commit_stack的栈顶元素作为预测结果(不进行pop操作)。
2、Commit操作:
(1)FTQ执行结果为call正确,通过FTQ传回来的快照信息对commit_stack进行push操作,同时更新spec栈的BOS指针,将此次commit的数据及其之前的数据设置为无效数据。
(2)FTQ执行结果为ret正确,通过FTQ传回来的快照信息对commit_stack上进行普通pop操作,注意到在commit pop之前会先对spec栈进行pop操作,所以commit栈弹出来的地址不需要使用。
3、Redirect操作:
(1)从redirect消息中获取之前预测时的栈指针(BOS、TOSR、TOSW,ssp),覆盖当前值指针值完成预测栈状态回滚,若此时为Call指令或者Ret指令,则再对Spec栈进行对应的操作。
(2)该操作不影响提交栈。
由于昆明湖 BPU 的假定S3的结果比S2好,因此发生不一致时,需要再次对RAS栈进行修复。具体不一致情况和对应的修复操作如下表所示:
S2预测结果 | S3预测结果 | 修复操作 |
---|---|---|
push | keep | pop |
keep | pop | pop |
pop | keep | push |
keep | push | push |
因为SC预测器的特性,S2和S3的操作不存在pop/push或者push/pop情况
在RAS预测器中,核心组件为 RASStack ,其接口说明如下:
接口名称 | 功能描述 | 接口名称 | 功能描述 |
---|---|---|---|
in.spec_push_valid | 预测有Call指令,Spec压栈 | in.s2_fire | s2信号有效 |
in.spec_pop_valid | 预测有Ret指令,Spec出栈 | in.s3_fire | s3信号有效 |
in.spec_push_addr | Ret地址 | in.s3_cancel | s3和s2的预测结果不一样,需要撤销s2的操作 |
out.spec_pop_addr | RAS的栈顶数据 | in.s3_meta | s3需要的s2时的现场信息 |
out.ssp | commit栈顶指针 | in.s3_missed_pop | s3判断需要再次进行pop |
out.sctr | commit栈顶重复元素计数器 | in.s3_missed_push | s3判断需要再次进行push |
out.nsp | commit栈顶,会被ssp覆盖 | in.s3_pushAddr | 需要再次push时的地址 |
out.TOSR | spec栈栈顶指针 | in.redirect_valid | 需要重定向 |
out.TOSW | spec栈数据分配指针 | in.redirect_isCall | 真实执行情况是Call |
out.BOS | spec栈栈底指针 | in.redirect_isRet | 真实执行情况是Return |
in.commit_push_valid | FTQ执行结果为Call正确 | in.redirect_meta_ssp | 之前预测时的现场信息ssp |
in.commit_pop_valid | FTQ执行结果为Ret正确 | in.redirect_meta_sctr | 之前预测时的现场信息sctr |
in.commit_push_addr | 更新信息中的Ret地址 | in.redirect_meta_TOSW | 之前预测时的现场信息TOSW |
in.commit_meta_TOSW | 更新信息中的TOSW | ||
in.redirect_meta_TOSR | 之前预测时的现场信息TOSR | ||
in.commit_meta_TOSR | 更新信息中的TOSR | in.redirect_meta_NOS | 之前预测时的现场信息NOS |
in.commit_meta_ssp | 更新信息中的现场信息SSP | in.redirect_callAddr | 重定向地址 |
in.commit_meta_sctr | 更新信息中的现场信息SCTR | ||
RASStack模块与BasePredictor的接口关系如下:
stack接口 | 转换过程 | 描述 |
---|---|---|
s.spec_push_valid | io.s2_fire(2) && s2_full_pred.hit_taken_on_call && !io.s3_redirect(2) | s2输入有效,且上级预测为call跳转 |
s.spec_pop_valid | io.s2_fire(2) && s2_full_pred.hit_taken_on_ret && !io.s3_redirect(2) | s2输入有效,且上级预测为ret跳转 |
s.spec_push_addr | s2_full_pred.fallThroughAddr + Mux(s2_full_pred.last_may_be_rvi_call, 2.U, 0.U) | 上级预测器s2预测的fallThroughAddr(即PC+2),判断是否压缩指令是否需要 +2 |
s.redirect_isCall | redirect.bits.level === 0.U && recover_cfi.pd.isCall | |
s.redirect_isRet | redirect.bits.level === 0.U && recover_cfi.pd.isRet | |
s.redirect_meta_* | redirect.bits.cfiUpdate.* | |
s.commit_push_valid | io.update.is_call_taken | call指令预测正确 |
s.commit_push_valid | io.update.is_ret_taken | ret指令预测正确 |
s.commit_push_addr | update.ftb_entry.getFallThrough(update.pc) + Mux(update.ftb_entry.last_may_be_rvi_call, 2.U, 0.U) | 根据是否为压缩指令,进行地址+2或者+0 |
s.commit_meta_* | io.update.bits.meta.asTypeOf(new RASMeta) | |
io.out.last_stage_spec_info.* | s3_meta.* | s3_meta = RegEnable(s2_meta, io.s2_fire(2))由s2_meta延迟一怕得到 |
io.out.last_stage_meta | s3_meta | |
io.out.s2.full_pred.*.jalr_target | :=stack.spec_pop_addr | 预测地址(栈顶地址,只预测ret) |
io.out.s3.full_pred.*.jalr_target | :=RegEnable(stack.spec_pop_addr, io.s2_fire(2)) | 由s2延迟一拍得到 |
io.out.s2/3.full_pred.targets.last | :=Mux(s2/3_is_jalr, s2/3_jalr_target, s2/3_last_target_in) | 如果时call执行,更新targets.last的结果 |
在RAS中,只涉及到2拍,S2和S3。
S2中的主要工作:
1、根据上一个预测器的S2预测结果,完成通过Push/PoP过程完成预测,并得到结果sepc_pop_addr。
2、根据commit信号执行更新操作
S3中的主要工作:
1、根据上一个预测器S3中的预测结果,以及S2进行的操作,判断是否需要进行Pop/Push的撤销操作。预测器假定S3的预测结果比S2好,如果发生S2和S3预测不一致时,RAS预测器采信S3结果进行栈操作。
2、S3和S2的预测过程一致,只是数据不同。
3、执行重定向(重定向信息由前一拍获得)操作