这是本节的多页打印视图。
点击此处打印.
返回本页常规视图.
FTQ概述
下文(包括所有的FTQ文档)中会提到一些关于BPU和IFU的相关知识,详情需要去查看对应的文档:
hint:建议先从BPU基础设计中着重理解以下概念:
- 什么是分支预测?
- 什么是分支预测块?一个有帮助的链接:预测块
- (可选)什么是重定向,什么是预测结果重定向?
- (可选)分支预测的流水级
简介
FTQ 是分支预测和取指单元之间的缓冲队列,它的主要职能是暂存 BPU 预测的取指目标,并根据这些取指目标给 IFU 发送取指请求。它的另一重要职能是暂存 BPU 各个预测器的预测信息,在指令提交后把这些信息送回 BPU 用作预测器的训练,因此它需要维护指令从预测到提交的完整的生命周期。另外,后端将存储来自FTQ的取指目标PC,便于自身读取。
![[Pasted image 20250222103931.png]]
模块之间的中转站
从上图,FTQ很大程度上相当于一个中转站,中间人的角色,一方面,它承担着BPU和IFU之间的交互,这通常是因为BPU预测的速度快于IFU取值执行,所以使用FTQ作为缓冲。另一方面,它承担着后端与前端的交互,比如把前端将要执行的pc交给后端去执行。
显然,FTQ的 中转远不止这么多,下面更具体地讨论一下FTQ怎么中转各个前端或后端模块的信息的。
BPU和FTQ
BPUtoFTQ:BPU会将分支预测结果和meta数据发给FTQ。
- 从分支预测结果中,我们可以提取出分支预测块对应的取值目标,比如,一个不跨缓存行且所有指令均为RVC指令的分支预测块对应的取值目标,是从分支预测块起始地址开始的以2B为间隔的连续16条指令。
- meta信息则存储了各个预测器相关的预测信息,由于BPU预测有三个流水级,每个流水级都有相应的预测器,所以只有到s3阶段才有可能收集到所有预测器的预测信息,直到此时FTQ才接受到完整的meta,这些信息会在该分支预测块的全部指令被后端提交时交给BPU进行训练
- FTBEntry:严格来说,它其实也是meta的一部分,但是因为更新的时候ftb_entry需要在原来的基础上继续修改,为了不重新读一遍ftb,另外给它存储一个副本。
FTQtoBPU:FTQ会将带元数据的训练信息和重定向信息发回给BPU
FTQ和IFU
FTQtoIFU:FTQ会将存储的取值目标发往IFU进行取值译码和把后端的重定向信息也移交给IFU
- 取值目标同时也发给:
- toICache:同样的取值目标会被发给指令缓存单元,看对应的指令是否在缓存单元内存在,如果有会被直接发送给IFU加速取值效率
- toPrefetch: prefetch是ICache的一个组件,负责预取功能
- 转发后端重定向:
- 后端重定向不仅需要转发给BPU帮助其回到正确状态,也同时需要转发给IFU帮助其回到正确状态
IFUtoFTQ:IFU将预译码信息和重定向信息写回FTQ
- 预译码信息:包含分支预测块对应的预测宽度内所有指令的预译码信息
预测宽度:一个指令块预测块覆盖的指令范围,香山中是16条rvc指令
- 重定向信息其实也是根据预译码信息得到的:当预译码信息中指出预测块内某一条指令预测出错时,写回IFU重定向信息
后端和FTQ
FTQ到后端:FTQ会将存储的取值目标发往后端,后端存储 PC后,在本地即可进行读取取指目标。
- 除了IFU,预测块的取值目标也会发给后端,但这里有一点区别:IFU空闲时才能从FTQ中获取取值目标,但是后端会一直取得最新的预测块的取指目标
后端到FTQ:后端重定向和指令commit
- 后端重定向与更新:后端是实际执行指令的单元,通过后端的执行结果,才能确认一条指令是否执行错误,产生重定向,同时,在发生重定向时,根据后端实际执行结果生成更新信息。
- 指令commit:当一个分支预测块内的所有指令都被执行,在后端提交,这标志着FTQ队列中这个分支预测块对应的FTQ项已经结束了它的生命周期,可以从队列中移除了,这时候,我们就可以把它的更新信息发给FTQ了。
FTQ指针
FTQ的全名叫取值目标队列,队列中的一个项叫做FTQ项,BPU写入预测结果时是写入队列中哪个位置,IFU又是从哪个队列取FTQ项?这时候,我们需要一个FTQ指针去索引FTQ项,而由于和不同模块的交互需要索引不同的FTQ项,因此,有以下类型的FTQ指针,下面,由指令生命周期为例,大致介绍这些指针:
指令在 FTQ 中的生存周期
指令以预测块为单位,从 BPU 预测后便送进 FTQ,直到指令所在的预测块中的所有指令全部在后端提交完成,FTQ 才会在存储结构中完全释放该预测块所对应的项。这个过程中发生的事如下:
- 预测块从 BPU 发出,进入 FTQ,
bpuPtr
指针加一,初始化对应 FTQ 项的各种状态,把各种预测信息写入存储结构;如果预测块来自 BPU 覆盖预测逻辑,则恢复 bpuPtr
和 ifuPtr
- FTQ 向 IFU 发出取指请求,
ifuPtr
指针加一,等待预译码信息写回
- IFU 写回预译码信息,
ifuWbPtr
指针加一,如果预译码检测出了预测错误,则给 BPU 发送相应的重定向请求,恢复 bpuPtr
和 ifuPtr
- 指令进入后端执行,如果后端检测出了误预测,则通知 FTQ,给 IFU 和 BPU 发送重定向请求,恢复
bpuPtr
、ifuPtr
和 ifuWbPtr
- 指令在后端提交,通知 FTQ,等 FTQ 项中所有的有效指令都已提交,
commPtr
指针加一,从存储结构中读出相应的信息,送给 BPU 进行训练
预测块 n
内指令的生存周期会涉及到 FTQ 中的 bpuPtr
、ifuPtr
、ifuWbPtr
和 commPtr
四个指针,当 bpuPtr
开始指向 n+1
时,预测块内的指令进入生存周期,当 commPtr
指向 n+1
后,预测块内的指令完成生存周期。
循环队列
FTQ队列实际上是一个循环队列,所有类型的FTQ指针都是同一类型,ftqPtr的value字段用来表示索引,flag字段则用来表示循环轮数,flag只有一位,进入新的循环时flag位翻转。
这样,我们就可以在一个有限的队列空间内不断更新新的项,以及正确进行比较,判断哪个项在队列中更靠前或者更靠后。
1 - FTQ顶层
简述
在FTQ概述中,我们已经知道了,FTQ的作用就是多个模块交互的中转站,大致了解了它接受其他模块的哪些信息,它如何接受并存储这些信息在FTQ中,并如何把这些存储信息传递给需要的模块。
下面我们来具体了解一下FTQ与其他模块的交互接口,我们会对这种交互有一个更具体的认识。
IO一览
模块间IO
- fromBpu:接受BPU预测结果的接口(BpuToFtqIO)
- fromIfu:接受IFU预译码写回的接口(IfuToFtqIO)
- fromBackend:接受后端执行结果和commit信号的接口(CtrlToFtqIO)
- toBpu:向BPU发送训练信息和重定向信息的接口(FtqToBpuIO)
- toIfu:向IFU发送取值目标和重定向信息的接口(FtqToIfuIO)
- toICache:向ICache发送取值目标的接口(FtqToICacheIO)
- toBackend:向后端发送取值目标的接口(FtqToCtrlIO)
- toPrefetch:向Prefetch发送取值目标的接口(FtqToPrefetchIO)
- mmio
其他
上述是主要的IO接口,此外,还有一些用于性能统计的IO接口,比如对BPU预测正确和错误结果次数进行统计,并进行转发的IO, 还有转发BPU各预测器预测信息的IO。
IfuToFtqIO
我们知道从IFU,我们会得到预译码信息和重定向信息,而后者其实也是从预译码信息中生成。所以从IFU到FTQ的接口主要就是用来传递预译码信息的
- pdWb:IFU向FTQ写回某个FTQ项的预译码信息
- 接口类型:PredecodeWritebackBundle
- 信号列表:
- pc:一个分支预测块覆盖的预测范围内的所有pc
- 接口类型:Vec(PredictWidth, UInt(VAddrBits.W))
- pd:预测范围内所有指令的预译码信息
- 接口类型:Vec(PredictWidth, new PreDecodeInfo)
- PreDecodeInfo:每条指令的预译码信息
- 接口类型:PreDecodeInfo
- 信号列表:
- valid:预译码有效信号
- isRVC:是RVC指令
- brType:跳转指令类型
- 接口类型:UInt(2.W)
- 说明:根据brType的值判断跳转指令类型
- b01:对应分支指令
- b10:对应jal
- b11:对应jalr
- b00:对应非控制流指令
- isCall:是Call指令
- isRet:是Ret指令
- ftqIdx:FTQ项的索引,标记写回到哪个FTQ项
- ftqOffset:由BPU预测结果得到的,在该指令块中指令控制流指令的位置(指令控制流指令就是实际发生跳转的指令)
- 接口类型:UInt(log2Ceil(PredictWidth).W)
- misOffset:预译码发现发生预测错误的指令在指令块中的位置
- 接口类型:ValidUndirectioned(UInt(log2Ceil(PredictWidth).W))
- 说明:它的valid信号拉高表示该信号有效,也就说明存在预测错误,会引发重定向
- cfiOffset:由预译码结果得到的,在该指令块中指令控制流指令的位置(指令控制流指令就是实际发生跳转的指令)
- 接口类型:ValidUndirectioned(UInt(log2Ceil(PredictWidth).W))
- target:该指令块的目标地址
- 接口类型:UInt(VAddrBits.W)
- 说明:所谓目标地址,即在指令块中有控制流指令时,控制流指令的地址,在没有控制流指令时,指令块顺序执行,该指令块最后一条指令的下一条指令
- jalTarget:jal指令的跳转地址
- instrRange:有效指令范围
- 接口类型:Vec(PredictWidth, Bool())
- 说明:表示该条指令是不是在这个预测块的有效指令范围内(第一条有效跳转指令之前的指令)
CtrlToFtqIO
后端控制块向FTQ发送指令提交信息,后端执行结果的接口。
- rob_commits:一个提交宽度内的RobCommitInfo信息。
- 接口类型:Vec(CommitWidth, Valid(new RobCommitInfo))
- 详情链接:RobCommitInfo
- redirect:后端提供重定向信息的接口。
- 接口类型:Valid(new Redirect)
- 详情链接:Redirect
- ftqIdxAhead:提前重定向的FTQ指针,将要重定向的FTQ项的指针提前发送
- 接口类型: Vec(BackendRedirectNum, Valid(new FtqPtr))
- 说明:虽然有三个接口,但实际上只用到了第一个接口,后面两个弃用了
- ftqIdxSelOH:独热码,本来是依靠该信号从提前重定向ftqIdxAhead中选择一个,但现在只有一个接口了,独热码也只有一位了。
- 接口类型:Valid(UInt((BackendRedirectNum).W))
- 说明:为了实现提前一拍读出在ftq中存储的重定向数据,减少redirect损失,后端会向ftq提前一拍(相对正式的后端redirect信号)传送ftqIdxAhead信号和ftqIdxSelOH信号。
FtqToICacheIO
FTQ向IFU发送取值目标,ICache是指令缓存,如果取值目标在ICache中命中,由ICache将指令发给IFU
- req:FTQ向ICache发送取值目标的请求
- 接口类型:Decoupled(new FtqToICacheRequestBundle)
- 信号列表:
- pcMemRead:FTQ针对ICache发送的取值目标,ICache通过5个端口同时读取取指目标
- 接口类型:Vec(5, new FtqICacheInfo)
- FtqICacheInfo: FTQ针对ICache发送的取值目标
- 信号列表:
- ftqIdx:指令块在FTQ中的位置索引
- startAddr:预测块起始地址
- nextlineStart:起始地址所在cacheline的下一个cacheline的开始地址
- 说明:通过startAddr(blockOffBits - 1)这一位(也就是块内偏移地址的最高位)可以判断该预读取pc地址是位于cacheline的前半块还是后半块,若是前半块,由于取值块大小为cacheline大小的一半,不会发生跨cacheline行
- readValid: 对应5个pcMemRead是否有效
- backendException:是否有后端异常
FtqToCtrlIO
FTQ向后端控制模块转发PC,后端将这些pc存储在本地,之后直接在本地读取这些pc
写入后端pc mem
- pc_mem_wen:FTQ向后端pc存储单元pc_mem写使能信号
- pc_mem_waddr:写入地址
- 接口类型:Output(UInt(log2Ceil(FtqSize).W))
- pc_mem_wdata:写入数据,是一个指令块的取值目标
- 接口类型:Output(new Ftq_RF_Components),详见FTQ子队列相关介绍
写入最新目标
- newest_entry_en:是否启用
- newest_entry_target:最新指令块的跳转目标
- 接口类型:Output(UInt(VAddrBits.W))
- newest_entry_ptr:最新指令块的索引值
FtqToPrefetchIO
- req:FTQ向Prefetch发送取值目标的请求
- flushFromBPU: 来自BPU的冲刷信息
- 接口类型:BpuFlushInfo
- 信号列表:
- s2 :BPU预测结果重定向(注意这种重定向是BPU自己产生的,与其他类型要做区分)发生在s2阶段时,此阶段的分支预测块的索引
- 接口类型:Valid(new FtqPtr)
- 说明:valid信号有效时,说明此时s2流水级分支预测结果与其s1阶段预测结果不一致,产生s2阶段重定向
- s3:BPU预测结果重定向(注意这种重定向是BPU自己产生的,与其他类型要做区分)发生在s3阶段时,此阶段的分支预测块的索引
- 接口类型:Valid(new FtqPtr)
- 说明:与s2类似
- 说明:发生预测结果重定向的时候,预取单元和IFU都可能会被冲刷,比如,如果发生s2阶段重定向,FTQ会比较发给IFU req接口中的ftqIdx和s2阶段预测结果的ftqIdx,如果s2阶段的ftqIdx不在req的ftqIdx之后,这意味着,s2阶段产生的预测结果重定向之前的错误预测结果s1阶段预测结果被发给IFU进行取指了,为了消除这种错误,需要向IFU发送s2阶段flush信号。
- backendException:后端执行发生的异常
- 接口类型:UInt(ExceptionType.width.W)
- 说明:表示后端执行时发生异常的类型,有这样几种类型的异常:
def none: UInt = "b00".U(width.W)
def pf: UInt = "b01".U(width.W) // instruction page fault
def gpf: UInt = "b10".U(width.W) // instruction guest page fault
def af: UInt = "b11".U(width.W) // instruction access fault
2 - FTQ子队列
文档概述
请注意:从本篇开始,就涉及待验证的功能点和测试点了
在之前的介绍中,我们采用FTQ项这个术语描述描述FTQ队列中的每一个元素,实际上,这只是一种便于抽象的说法。
实际上的FTQ队列,是由好多个子队列共同构成的,一些子队列维护一类信息,另一些子队列维护另一类信息,相同ftqIdx索引的子队列信息共同构成一个完整的FTQ项。
为什么要把它们分开成多个子队列呢?因为某些模块只需要FTQ项中的某一些信息,比如IFU想要取值目标,它只需要专门存储取值目标的子队列提供的信息就行了。另外,在我们更改FTQ项的内容时,也只需要写入需要更新的子队列,比如IFU预译码写回时,只需要写回专门存储预译码信息的队列了。
下面来介绍一些FTQ的主要子队列,以及它们内部存储的数据结构。此外,FTQ还有一些存储中间状态的更小的队列
术语说明
名称 |
定义 |
FTB项 |
分支预测结果的基本组成项,包含对预测块中分支指令和跳转指令的预测 |
取指目标 |
一个预测块内包含的所有指令PC,当然,它不是直接发送所有PC,而是发送部分信号,接收方可由该信号推出所有PC |
子模块列表
子模块 |
描述 |
ftq_redirect_mem
|
重定向存储子队列,存储来自分支预测结果的重定向信息 |
ftq_pd_mem |
预译码存储子队列,存储来自IFU的对指令块的预译码信息 |
ftb_entry_mem |
FTB项存储子队列,存储自分支预测结果中的ftb项 |
ftq_pc_mem |
取指目标子队列,存储来自分支预测结果的取指目标 |
模块功能说明
1. ftq_redirect_mem存储重定向信息
ftq_redirect_mem是香山ftq的一个子队列。它记录了重定向需要的一些信息,帮助重定向回正确状态,这些信息来自于BPU分支预测中的RAS预测器,以及顶层的分支历史指针,如果想要了解,可以参考BPU的RAS子文档了解如何通过这些信息回溯到之前的状态。
它是一个寄存器堆,由64(FtqSize)个表项(Ftq_Redirect_SRAMEntry)构成。支持同步读写操作。有3个读端口和1个写端口,每个读端口负责与不同的模块交互。
1.1 ftq_redirect_mem读操作
- 读操作:
- 输入:
- 需要使能ren,这是一个向量,可指定任意读端口可读
- 从任意读端口中输入要读取的元素在ftq_redirect_mem中的地址,这是一个从0到ftqsize-1的索引
- 输出:
- 从发起输入的读端口对应的读出端口中读出Ftq_Redirect_SRAMEntry。
1.2 ftq_redirect_mem写操作
- 写操作
- 输入:
- 需要使能wen,可指定写端口可写
- 向写端口中输入要写入的元素在ftq_redirect_mem中的地址,这是一个从0到ftqsize-1的索引
- 向wdata中写入Ftq_Redirect_SRAMEntry
- 多端口读:可以从多个读端口读取结果
每个子队列的读写基本都是类似的,后面不再赘述
Ftq_Redirect_SRAMEntry
ftq_redirect_mem存储的表项。继承自SpeculativeInfo,存储RAS预测器相关重定向信息,根据这些信息回溯到之前的状态
- sc_disagree:统计分支指令在sc预测器中预测是否发生错误
- 接口类型:Some(Vec(numBr, Bool()))
- 说明:Option 类型,表明这个值可能不存在,在非FPGA平台才有,否则为none
- 信号列表:
- SpeculativeInfo:推测信息,帮助BPU在发生重定向的时候回归正常的状态
- 接口列表:
- histPtr:重定向请求需要恢复的全局历史指针,可参见BPU顶层文档了解详情
- 说明:以下都属于RAS重定向信息,可参见BPU文档了解如何利用这些信息进行重定向
- ssp:重定向请求指令对应的 RAS 推测栈栈顶在提交栈位置的指针
- 接口类型:UInt(log2Up(RasSize).W)
- sctr:重定向请求指令对应的 RAS 推测栈栈顶递归计数 Counter
- TOSW:重定向请求指令对应的 RAS 推测栈(队列)写指针
- TOSR:重定向请求指令对应的 RAS 推测栈(队列)读指针
- NOS:重定向请求指令对应的 RAS 推测栈(队列)读指针
- topAddr:
序号 |
功能名称 |
测试点名称 |
描述 |
1.1 |
FTQ_REDIRECT_MEM |
WRITE |
向单端口输入wen,waddr决定是否写入以及写入地址,写入wdata |
1.2 |
FTQ_REDIRECT_MEM |
READ |
向多端口中输入ren,raddr决定是否读以及读取地址,从rdata读取 |
2. ftq_pd_mem存储预译码信息
由64(FtqSize)个表项(Ftq_pd_Entry)构成。支持同步读写操作。有2个读端口和1个写端口。具有读写使能信号。
存储来自IFU预译码的写回信息,它是一个寄存器堆,由64(FtqSize)个表项(Ftq_pd_Entry)构成。有2个读端口和1个写端口。
ftq_pd_mem直接接收来自IfuToFtqIO的信号,从中获取Ftq_pd_Entry,表示一个指令块对应的预译码信息表项。读取时获取预测块内某条指令的预测信息
Ftq_pd_Entry
- brMask:一个指令预测宽度内(16条rvc指令)的指令块中,哪些指令是分支指令
- 接口类型:Vec(PredictWidth, Bool())
- jmpInfo:jump信息,其值对应不同的jmp指令类型,表示指令块内jmp指令类型
- 接口类型:ValidUndirectioned(Vec(3, Bool()))
- 说明: jumpinfo有效的时候,第0位是0,表示jal指令,第0位是1,表示jalr指令,第1位是1,表示call指令,第二位是1,表示ret指令。
- jmpOffset:jmp指令在指令预测块中的偏移地址
- 接口类型: UInt(log2Ceil(PredictWidth).W)
- rvcMask:一个预测块内的指令(16条rvc指令)哪些是rvc指令
- 接口类型:Vec(PredictWidth, Bool())
2.1 ftq_pd_mem写操作
PredecodeWritebackBundle(IfuToFtqIO)如何写入ftq_pd_mem的一条Ftq_pd_Entry
Ftq_pd_Entry项的写入是通过PredecodeWritebackBundle这个接口进行写入的(其实也就是IfuToFtqIO)
从fromPdWb接口中接收信号生成表项:
- brmask:PredecodeWritebackBundle有一个预测块内的所有指令的预译码信息,当一条指令的预译码信息有效(valid)且是分支指令(is_br)时, bool序列对应位置的指令被判定为分支指令
- jumpInfo:
- valid:预测块内存在一条指令,其预译码信息有效(valid),且是jmp指令(isJal或者isJalr)时,jumpInfo有效
- bits:预测块内的第一条有效跳转指令的info,它是一个三位序列,从低到高(拉高)对应该指令被预译码为是isJalr,isCall,isRet
- jmpOffset:预测块内第一条有效jmp跳转指令的偏移
- rvcMask:原封不动接受同名信号
- jalTarget:原封不动接收同名信号
2.2 ftq_pd_mem写操作
ftq_pd_mem的一条Ftq_pd_Entry如何以PreDecodeInfo(to pd)的形式输出
PreDecodeInfo是一个Ftq_pd_Entry中的一条指令的预译码,需要输入offset,指定该预译码指令在预测块内的偏移
-
valid:直接set为1
-
isRVC:设置为rvcMask bool序列中对应偏移的值
-
isBr:设置为brMask bool序列中对应偏移的值
-
isJalr:输入的偏移量等于jumpOffset,且jumpInfo有效并指明该指令type是isJalr(jmpInfo.valid && jmpInfo.bits(0))
序号 |
功能名称 |
测试点名称 |
描述 |
2.1 |
FTQ_PD_MEM |
WRITE |
向单端口输入wen,waddr决定是否写入以及写入地址,写入wdata |
2.2 |
FTQ_PD_MEM |
READ |
向多端口中输入ren,raddr决定是否读以及读取地址,从rdata读取 |
3. ftb_entry_mem存储FTB项
有两个读端口,一个写端口,FtqSize个表项,存储的数据项为FTBEntry_FtqMem,FTBEntry_FtqMem与FTBEntry基本上是一致的。
FTBEntry_FtqMem
- brSlots:分支指令槽
- 接口类型:Vec(numBrSlot, new FtbSlot_FtqMem)
- FtbSlot_FtqMem:
- 信号列表:
- offset:给分支指令在相对于指令块起始地址的偏移
- 接口类型:UInt(log2Ceil(PredictWidth).W)
- sharing:对于tailSlot来说,启用sharing表示把这个slot让给分支指令来被预测
- valid:预测槽有效
- 接口类型:Bool
- 说明:当slot有效时,我们才能说这条指令是br指令还是jmp指令
- tailSlot:跳转指令槽
- FTBEntry_part:FTBEntry_FtqMem的父类,存储部分FTB信息,记录跳转指令的类型
- 信号列表:
- isCall:接口类型:Bool
- isRet:接口类型:Bool
- isJalr:接口类型:Bool
3.1 ftb_entry_mem读操作
除了读出FTB项之外,顶层还可以从FTBEntry_FtqMem获取以下有效信息,在这里我们不需要验证以下内容,但是在验证顶层的时候我们会用到以下内容,在此处提一下,此外,以下内容并不会生成具体的信号接口,而是产生相应的判断逻辑:
- jmpValid:预测块中jmp指令有效
- 说明:当tailslot有效且不分享给分支指令时,jmp有效
- getBrRecordedVec:三维向量,对于三个slot
- 说明:接收一个offset偏移,如果命中有效分支slot(或者sharing拉高的tailslot),对应slot的向量元素拉高。
- brIsSaved:给定offset的指令是否是分支指令
- 说明:采用slot预测结果来说明是不是分支指令,前提需要信号有效
- getBrMaskByOffset:
- 说明:在给定offset范围内的三个slot中的指令是否是有效分支指令,用一个三位maks表示
- newBrCanNotInsert:能否插入新的brSlot
- 说明:给定offset超过有效tailSlot对应的offset时,不能插入新的brSlot
序号 |
功能名称 |
测试点名称 |
描述 |
3.1 |
FTQ_ENTRY_MEM |
WRITE |
向单端口输入wen,waddr决定是否写入以及写入地址,写入wdata |
3.2 |
FTQ_ENTRY_MEM |
READ |
向多端口中输入ren,raddr决定是否读以及读取地址,从rdata读取 |
4. ftq_pc_mem存储取指目标
pc存储子队列。存储项为Ftq_RF_Components,用来读取取指信息,取值信息交给IFU进行取指。
Ftq_RF_Components
信号含义
- startAddr: 预测块的起始地址
- nexLineAddr: 预测块下一个缓存行的起始地址
- startAddr加上64个字节,一个缓存行的大小是64字节
- isNextMask: 一个预测宽度内的16条指令各自是否属于下一个预测块(在最新版本rtl中已被编译优化掉)
- 通过计算某条指令相对于预测块起始地址的偏移量(每条指令两个字节)得到偏移地址,该偏移地址的第4位(从0开始)为1,表示该指令属于下一个预测块。
- 进一步说,其实也就可以根据它判断该指令是否在预测块跨缓存行的时候判断该指令是否属于下一个cacheline了
- fallThruError :预测出的下一个顺序取指地址是否存在错误
4.1 ftq_pc_mem写操作
信息获取:上述信息都可以从一个单流水级分支预测结果 (BranchPredictionBundle)中获取。
获取方式:startAddr直接获取BranchPredictonBundle中的pc,fallThruError直接获取BranchPredictionBundle中的fallThruError。
4.2 ftq_pc_mem读操作
多端口读:ftq_pc_mem的每个读端口的读地址被直接连到各个FTQ指针的写入信号,这样做的目的,是可以及时的读取,从pc存储子队列读出的项一定是此时FTQ指针指向的项
读写时机
写入时机:BPU流水级的S1阶段,创建新的预测entry时写入
读出时机: 读数据每个时钟周期都会存进Reg。如果IFU不需要从bypass中读取数据,Reg数据直连给Icache和IFU,如果IFU不需要从bypass中读取数据,Reg数据直连给Icache和IFU
序号 |
功能名称 |
测试点名称 |
描述 |
4.1 |
FTQ_PC_MEM |
WRITE |
向单端口输入wen,waddr决定是否写入以及写入地址,写入wdata |
4.2 |
FTQ_PC_MEM |
READ |
向多端口中输入ren,raddr决定是否读以及读取地址,从rdata读取 |
存储的数据为Ftq_1R_SRAMEntry,同样有FtqSize项
Ftq_1R_SRAMEntry接口列表
- meta:分支预测的meta数据
- ftb_entry:分支预测的FTB项
写入时机:在 BPU的s3阶段接收信息,因为对于一个指令预测块,只有在其s3阶段才能获取完整的mata信息,同样被接收的还有最后阶段ftqentry信息
序号 |
功能名称 |
测试点名称 |
描述 |
5.1 |
FTQ_META_1R_SRAM |
WRITE |
向单端口输入wen,waddr决定是否写入以及写入地址,写入wdata |
5.2 |
FTQ_META_1R_SRAM |
READ |
向多端口中输入ren,raddr决定是否读以及读取地址,从rdata读取 |
接口说明
Ftq_Redirect_SRAMEntry
ftq_redirect_mem存储的表项。继承自SpeculativeInfo,存储RAS预测器相关重定向信息,根据这些信息回溯到之前的状态
- sc_disagree:统计分支指令在sc预测器中预测是否发生错误
- 接口类型:Some(Vec(numBr, Bool()))
- 说明:Option 类型,表明这个值可能不存在,在非FPGA平台才有,否则为none
- 信号列表:
- SpeculativeInfo:推测信息,帮助BPU在发生重定向的时候回归正常的状态
- 接口列表:
- histPtr:重定向请求需要恢复的全局历史指针,可参见BPU顶层文档了解详情
- 说明:以下都属于RAS重定向信息,可参见BPU文档了解如何利用这些信息进行重定向
- ssp:重定向请求指令对应的 RAS 推测栈栈顶在提交栈位置的指针
- 接口类型:UInt(log2Up(RasSize).W)
- sctr:重定向请求指令对应的 RAS 推测栈栈顶递归计数 Counter
- TOSW:重定向请求指令对应的 RAS 推测栈(队列)写指针
- TOSR:重定向请求指令对应的 RAS 推测栈(队列)读指针
- NOS:重定向请求指令对应的 RAS 推测栈(队列)读指针
- topAddr:
Ftq_pd_Entry
- brMask:一个指令预测宽度内(16条rvc指令)的指令块中,哪些指令是分支指令
- 接口类型:Vec(PredictWidth, Bool())
- jmpInfo:jump信息,其值对应不同的jmp指令类型,表示指令块内jmp指令类型
- 接口类型:ValidUndirectioned(Vec(3, Bool()))
- 说明: jumpinfo有效的时候,第0位是0,表示jal指令,第0位是1,表示jalr指令,第1位是1,表示call指令,第二位是1,表示ret指令。
- jmpOffset:jmp指令在指令预测块中的偏移地址
- 接口类型: UInt(log2Ceil(PredictWidth).W)
- rvcMask:一个预测块内的指令(16条rvc指令)哪些是rvc指令
- 接口类型:Vec(PredictWidth, Bool())
测试点总表
序号 |
功能名称 |
测试点名称 |
描述 |
1.1 |
FTQ_REDIRECT_MEM |
WRITE |
向单端口输入wen,waddr决定是否写入以及写入地址,写入wdata |
1.2 |
FTQ_REDIRECT_MEM |
READ |
向多端口中输入ren,raddr决定是否读以及读取地址,从rdata读取 |
2.1 |
FTQ_PD_MEM |
WRITE |
向单端口输入wen,waddr决定是否写入以及写入地址,写入wdata |
2.2 |
FTQ_PD_MEM |
READ |
向多端口中输入ren,raddr决定是否读以及读取地址,从rdata读取 |
3.1 |
FTQ_ENTRY_MEM |
WRITE |
向单端口输入wen,waddr决定是否写入以及写入地址,写入wdata |
3.2 |
FTQ_ENTRY_MEM |
READ |
向多端口中输入ren,raddr决定是否读以及读取地址,从rdata读取 |
4.1 |
FTQ_PC_MEM |
WRITE |
向单端口输入wen,waddr决定是否写入以及写入地址,写入wdata |
4.2 |
FTQ_PC_MEM |
READ |
向多端口中输入ren,raddr决定是否读以及读取地址,从rdata读取 |
5.1 |
FTQ_META_1R_SRAM |
WRITE |
向单端口输入wen,waddr决定是否写入以及写入地址,写入wdata |
5.2 |
FTQ_META_1R_SRAM |
READ |
向多端口中输入ren,raddr决定是否读以及读取地址,从rdata读取 |
附录
虽然列在附录,但实际上这段内容依然十分重要,当你需要的时候请一定要查看。
其余状态子队列
上述存储结构是FTQ中比较核心的存储结构,实际上,还有一些子队列用来存储一些状态信息,也同样都是存储ftqsize个(64)元素。主要有以下:
update_target:记录每个FTQ项的跳转目标,跳转目标有两种,一种是当该FTQ项对应的分支预测结果中指明的该分支预测块中执行跳转的分支指令将要跳转到的地址,另一种则是分支预测块中不发生跳转,跳转目标为分支预测块中指令顺序执行的下一条指令地址。
- 此外,与之配套的还有newest_entry_target,newest_entry_ptr用来指示新写入的跳转目标地址,和它对应的指令预测块或者说FTQ项的在FTQ中的位置,同时,有辅助信号newest_entry_target_modified和newest_entry_ptr_modified用来标识该地址的FTQ项跳转地址是否被修改。
写入时机:上一个周期的bpu_in_fire有效的时候,或者说相对于bpu_in_fire有效时延迟一个周期写入。
newest_entry_ptr,newest_entry_target:这几个内部信号,表明我们当前最新的有效FTQ项。BPU新的写入,重定向等等都会对最新FTQ项进行新的安排,在相应的文档中,对其生成方式做具体的描述。
cfiIndex_vec:记录每个FTQ项的发生跳转的指令cfi(control flow instruction)指令在其分支预测块中的位置
写入时机:相对于bpu_in_fire有效时延迟一个周期写入。
mispredict_vec:记录每个FTQ项的分支预测结果是否有误,初始化为false
pred_stage:记录每个FTQ项的分支预测结果是来自于哪个阶段
写入时机:相对于bpu_in_fire有效时延迟一个周期写入。
pred_s1_cycle:记录每个FTQ项的分支预测结果对应的s1阶段的分支预测结果生成的时间(cycle数)
写入时机:相对于bpu_in_fire有效时延迟两个周期写入。
commitStateQueueReg:记录每个FTQ项中对应的分支预测块中每条指令(一般是16条rvc指令,对应一个预测宽度)的提交状态,提交状态有c_empty ,c_toCommit ,c_committed ,c_flushed,依次用从0开始的从小到大的枚举量表示,初始化为c_empty状态
写入时机:相对于bpu_in_fire有效时延迟一个周期写入。
entry_fetch_status:记录每个FTQ项的分支预测结果是否被送到ifu中,该状态由两个枚举量f_to_send , f_sent来表示, 初始化为f_sent状态。
写入时机:上一个周期的bpu_in_fire有效的时候,相对于bpu_in_fire有效时延迟一个周期写入。
写入数据:写入f_to_send
entry_hit_status:记录每个FTQ项拿到的分支预测结果是否是ftb entry hit的,即生成该分支预测结果的时候是否是从FTB ( 预测结果生成:hit)(非必须了解)中,读取到了对应的记录表项。初始化为not_hit状态。
写入时机:当来自BPU的全局分支预测信息中s2阶段的分支预测结果有效时,写入s2阶段分支预测结果中指名的hit状态,因为FTB预测器是分支预测s2阶段开始生效的,在此时判断预测项是否在FTB缓存中命中
newest_entry_ptr,newest_entry_target这几个内部信号,它们不是队列,但是它们很重要,表明我们当前应该关注的最新的FTQ项及对应的跳转目标。BPU新的写入,重定向等等都会对最新FTQ项进行新的安排,在涉及到修改该信号的相应的文档中,对其生成方式做具体的描述。
3 - FTQ接收BPU分支预测结果
文档概述
BPU会将分支预测结果和meta数据发给FTQ。
- 从分支预测结果中,我们可以提取出分支预测块对应的取值目标,比如,一个不跨缓存行且所有指令均为RVC指令的分支预测块对应的取值目标,是从分支预测块起始地址开始的以2B为间隔的连续16条指令。
- meta信息则存储了各个预测器相关的预测信息,由于BPU预测有三个流水级,每个流水级都有相应的预测器,所以只有到s3阶段才有可能收集到所有预测器的预测信息,直到此时FTQ才接受到完整的meta,这些信息会在该分支预测块的全部指令被后端提交时交给BPU进行训练
- FTBEntry:严格来说,它其实也是meta的一部分,但是因为更新的时候ftb_entry需要在原来的基础上继续修改,为了不重新读一遍ftb,另外给它存储一个副本。
术语说明
名称 |
定义 |
BPU (Branch Prediction Unit) |
分支预测单元 |
FTQ (Fetch Target Queue) |
采集目标队列 |
IFU (Instruction Fetch Unit) |
指令采集单元 |
RAS (Return Address Stack) |
返回地址堆 |
FTQ Entry |
FTQ队列中的单个表项 |
模块功能说明
1. 新的预测块进队条件
1.1 成功接收数据
1.1.1 FTQ准备好接收信号
- FTQ准备好接收信号:
当FTQ队列中元素小于FtqSize或者可以提交指令块(canCommit拉高,说明可以提交指令块,在后面的文档: FTQ向BPU发送更新信息中介绍怎么判断是否可以提交指令块)的时候,来自BPU的新的指令预测块可以进入FTQ队列,队列准备好接收新的预测块,fromBpu的resp接口ready信号拉高。
1.1.2 BPU准备好要发送的信号
- BPU准备好要发送的信号:
当BPU发往FTQ的接口vaid信号拉高,表示发送信号准备好
满足以上两个条件时,fromBpu的resp接口fire,表示接口数据被成功发送到FTQ中。
1.2 允许BPU入队allowBpuIn
- 重定向发生时,会回滚到之前的状态,新发送的BPU预测信息自然就不需要了。允许BPU入队时不能发生重定向
1.2.1 后端重定向发生
- 后端重定向发生:
- 标志:接收后端写回信息的接口fromBackend的重定向接口redirect有效,则该周期不允许入队,如果没有发生真实提前重定向realAhdValid(参见FTQ接收后端重定向一文),则下一个周期也不允许入队。
1.2.2 IFU重定向发生
- IFU重定向发生:
- 标志:IFU重定向信息生成的两个周期,均不许入队(参见FTQ接收IFU重定向一文了解IFU重定向信息的生成)
只要避免上述两种重定向出现的情况,就可以允许BPU入队,即可以把发送到FTQ的数据,写入FTQ项
1.3 以BPU预测结果重定向的方式入队
上述的BPU入队方式是一个全新的预测块进队,即BPU分支预测的s1阶段结果入队,此时未发生预测结果重定向。
当BPU发生预测结果重定向时,只要允许BPU入队allowBpuIn,也可以看作预测结果入队,不过这种入队是覆写队列中已有的FTQ项,没有写入新的指令块。
- BPU预测结果发生重定向的具体标志:fromBpu的resp接口的s2(s2阶段的预测信息)有效,且s2的hasRedirect拉高,表示在s2阶段发生了重定向,s3阶段重定向是一样的。
综合两种形式的BPU入队,这里称之为广义BPU入队方便区分,记为bpu_in_fire,该信号拉高,表明发生广义BPU入队。
2. 写入FTQ项
之前已经说明过了,FTQ项只是一个抽象的概念,FTQ有很多个子队列组成,它们的项共同构成一个FTQ项,所以,向FTQ中写入FTQ项,实际上就是就是把BPU的预测信息写到对应的FTQ子队列中。
FTQ主要获取以下信息作为bpu_in_resp
- bpu_in_resp:BPU交给FTQ的resp详见BPU文档,resp中含有s1,s2,s3三个阶段的指令预测信息,bpu_in_resp将获取其中某一阶段预测信息selectedResp作为其值。未发生重定向时,使用s1作为预测结果,s2或者s3发生重定向信息时,优先s3的预测信息作为selectedResp。某阶段发生重定向的标志与上文讲述的一样一样。
从selectedResp(bpu_in_resp)中,我们还可以获取以下目标信息帮助我们写入子队列:ftq_idx,帮助我们索引写入子队列的地址
2.1 写入FTQ子队列:
2.1.1 写入ftq_pc_mem
- ftq_pc_mem: 来自BPU的selectedResp预测信息被写入ftq_pc_mem, 该存储结构有ftqsize个表项,对应队列中的所有ftq表项,每个存储元素可以推出对应的ftq表项中每条指令的pc地址
接收信号列表:
- wen:接收bpu_in_fire作为写使能信号
- waddr:接收selectedResp的ftq_idx
- wdata:selectedResp的相应信号
2.1.2 写入ftq_redirect_mem
- ftq_redirect_mem: 在BPU的s3(也就是最终阶段)接收信息,因为重定向信息只有在s3阶段才能得到。里面存储了RAS重定向相关的信息帮助BPU进行重定向。
接收信号列表:
- wen:从BPU(fromBpu)回应(resp)的lastStage有效信号
- waddr:从BPU回应的lastStage的ftq_idx.value
- wdata:从BPU回应的last_stage_spec_info
- ftq_meta_1r_sram:在 BPU的s3阶段接收信息,同样是因为对于一个指令预测块,只有在其s3阶段才能获取完整的mata信息,同样被接收的还有最后阶段ftqentry信息
接收信号列表:
- wen:从BPU(fromBpu)回应(resp)的lastStage有效信号
- waddr:从BPU回应的lastStage的ftq_idx的value
- wdata:
- meta:从BPU回应的last_stage_meta
- ftb_entry:从BPU回应的last_stage_ftb_entry
2.1.4 写入ftb_entry_mem
- ftb_entry_mem:虽然ftq_meta_1r_sram中存储有最后阶段ftbentry,但此处出于更高效率读取专门把它存在ftb_entry_mem中。
接收信号列表:
- wen:从BPU(fromBpu)回应(resp)的lastStage有效信号
- waddr:从BPU回应的lastStage的ftq_idx的value字段
- wdata:从BPU回应的last_stage_ftb_entry
从中可以看到,FTQ虽然名字上听起来是一个队列,实际上内部却是由数个队列组成,他们共同构成了FTQ这个大队列
2.2 写入状态队列
上述存储结构是FTQ中比较核心的存储结构,实际上,还有一些子队列用来存储一些状态信息,也同样都是存储ftqsize个(64)元素,需要被写入,写入时机是在发生bpu_in_fire的下一个周期,或者再下一个周期 。主要有以下:
2.2.1 写入update_target
update_target:记录每个FTQ项的跳转目标,跳转目标有两种,一种是当该FTQ项对应的分支预测结果中指明的该分支预测块中执行跳转的分支指令将要跳转到的地址,另一种则是分支预测块中不发生跳转,跳转目标为分支预测块中指令顺序执行的下一条指令地址。
- 此外,与之配套的还有newest_entry_target,newest_entry_ptr用来指示bpu_in_resp推出的跳转目标地址,表示下一次预测时开始的目标地址,和它对应的bpu_in_resp指令预测块在FTQ中的位置。
- 同时,有辅助信号newest_entry_target_modified和newest_entry_ptr_modified用来标识该这两个字段是否被修改。
- 写入时机:相对于bpu_in_fire有效时延迟一个周期写入。
- 写入地址:bpu_in_resp记录的要写入FTQ的地址
- 写入数据:bpu_in_resp.getTarget
2.2.2 写入cfiIndex_vec
cfiIndex_vec:记录每个FTQ项的发生跳转的指令cfi(control flow instruction)指令在其分支预测块中的位置
- 写入时机:相对于bpu_in_fire有效时延迟一个周期写入。
- 写入地址:bpu_in_resp记录的要写入FTQ的地址
- 写入数据:bpu_in_resp推断出的跳转目标
2.2.3 写入mispredict_vec
mispredict_vec:记录每个FTQ项的所有指令的预测结果是否有误,初始化为false
- 写入时机:相对于bpu_in_fire有效时延迟两个周期写入。
- 写入地址:bpu_in_resp记录的要写入FTQ的地址
- 写入数据:将该指令块的所有预测结果对应的值设置为false
2.2.4 写入pred_stage
pred_stage:记录每个FTQ项的分支预测结果是来自于哪个阶段
- 写入时机:相对于bpu_in_fire有效时延迟一个周期写入。
- 写入地址:bpu_in_resp记录的要写入FTQ的地址
写入pred_s1_cycle(不需要测试)
pred_s1_cycle:记录每个FTQ项的分支预测结果对应的s1阶段的分支预测结果生成的时间(cycle数)
- 写入时机:相对于bpu_in_fire有效时延迟两个周期写入。
- 写入地址:bpu_in_resp记录的要写入FTQ的地址
2.2.5 写入commitStateQueueReg
commitStateQueueReg:记录每个FTQ项中对应的分支预测块中每条指令(一般是16条rvc指令,对应一个预测宽度)的提交状态,提交状态有c_empty ,c_toCommit ,c_committed ,c_flushed,依次用从小到大的枚举量表示,初始化为c_empty状态
- 写入时机:相对于bpu_in_fire有效时延迟一个周期写入。
- 写入数据:写入c_empty
- 写入地址:bpu_in_resp记录的要写入FTQ的地址
2.2.6 写入entry_fetch_status
entry_fetch_status:记录每个FTQ项的分支预测结果是否被送到ifu中,该状态由两个枚举量f_to_send , f_sent来表示, 初始化为f_sent状态。
- 写入时机:相对于bpu_in_fire有效时延迟一个周期写入。
- 写入数据:写入f_to_send
- 写入地址:bpu_in_resp记录的要写入FTQ的地址
2.2.7 写入entry_hit_status
entry_hit_status:记录每个FTQ项拿到的分支预测结果是否是ftb entry hit的,即生成该分支预测结果的时候是否是从ftb中,读取到了对应的记录表项。初始化为not_hit状态。
- 写入时机:当来自BPU的全局分支预测信息中s2阶段的分支预测结果有效时,写入s2阶段分支预测结果中指名的hit状态
- 写入地址:bpu_in_resp记录的要写入FTQ的地址
- 写入数据:f_to_send
注:之所以延迟时钟周期写入,是为了缩短关键路径,以及帮助减少扇出
3 转发分支预测重定向:
3.1 转发给IFU
- s2以及s3阶段的预测重定向信息通过FTQ与Ifu的接口toIfu的flushFromBpu发送给IFU,当完整分支预测结果中的s2阶段分支预测结果发生预测结果重定向时,flushFromBpu.s2.valid拉高,flushFromBpu.s2.bits接收s2阶段分支预测结果中指明的该分支预测结果在FTQ中的位置ftq_idx。
3.2 转发给预取
- 该重定向信号同样会通过toPrefetch.flushFromBpu接口以相同的方式传递给Prefetch
s3阶段向IFU以及Prefetch的重定向传递与s2阶段的重定向信号传递一样。该阶段的重定向信号传递会覆盖可能的s2阶段重定向信号传递结果
4 修正FTQ指针
此外,分支预测结果重定向也会影响ifuPtr与pfPtr两个指针信号的写入信号。
4.1 正常修改
- 正常情况下,allowToIfu(条件和allowToBpu一样),同时BPU向Ifu发送FTQ项的io接口toIfu.req发生fire的时候,ifuPtr寄存器中写入ifuPtr+1。同样发生修改的还有pfPtr,当allowToIfu,同时BPU向Prefetch发送FTQ项的io接口totoPrefetch.req发生fire的时候。
4.2 发生重定向时修改
- 而如果是发生重定向的时候,比如s2阶段预测结果发生重定向,此时,若ifuPtr不在s2阶段预测结果中指明的ftq_idx之前,ifuPtr写入该ftq_idx,pfPtr_write同样如此
bpuptr:
由FTQ交给BPU用于指示新的指令预测块应该放到FTQ队列中的位置,上述存储结构,ftq_pc_mem,ftq_redirect_mem,ftq_meta_1r_sram,ftb_entry_mem基本上也是通过与该指针相关的信号得知信息应该存储的addr(bpuptr交给BPU,BPU基于此获知每个阶段预测结果的ftq_idx)。
bpuptr寄存器的输出值直接连到FTQ发往BPU的接口toBpu的enq_ptr字段中,当然,再次之前,bpuptr的值会根据实际情况修改。
在enq from bpu的过程中,正常情况下,发生enq的时候,也就是新的预测块进队时,bpuptr+1,BPU将要向FTQ中写入的位置前进一位
但是,如果发生重定向的时候,比如,如果s2阶段预测结果发生重定向,bpuptr被更新为s2阶段分支预测结果的ftq_idx+1,表示BPU将要向FTQ中写入的位置为s2阶段预测结果在FTQ中位置的后一位,因为此时新的全局预测结果会基于s2的预测结果展开下一轮预测(即以s2分支预测块的下一块展开预测,自然会被写入),该结果会覆盖enq_fire发生时的结果,此外s3阶段的分支预测重定向时,会覆盖可能的s2阶段重定向修改的bpuptr
其他的ftq指针也是类似的,用于指示写入FTQ的地址
接口说明
FTQ接收BPU分支预测结果工程中涉及到的IO接口如下,在FTQ顶层IO一文中有详细说明
接口 |
作用 |
fromBackend |
根据是否有重定向确认是否允许BPU预测结果入队 |
fromBPU |
接收BPU预测结果 |
toIfu |
发送更新的IFU指针,转发BPU预测结果重定向 |
toPrefetch |
发送更新的Prefetch指针,转发BPU预测结果重定向 |
toBpu |
发送更新的BPU指针 |
测试点总表
序号 |
功能名称 |
测试点名称 |
描述 |
1.1.1 |
BPU_IN_RECEIVE |
FTQ_READY |
当FTQ队列中元素小于FtqSize或者可以提交指令块的时候,队列准备好接收新的预测块 |
1.1.2 |
BPU_IN_RECEIVE |
BPU_VALID |
BPU准备好要发送的信号 |
1.2.1 |
BPU_IN_ALLOW |
BACKEND |
接收后端写回信息的接口fromBackend的重定向接口redirect有效,则该周期不允许入队,如果没有发生真实提前重定向,则下一个周期也不允许入队 |
1.2.2 |
BPU_IN_ALLOW |
IFU |
IFU重定向信息生成的两个周期,均不许入队 |
1.3.1 |
BPU_IN_BY_REDIRECT |
REDIRECT |
当BPU发生预测结果重定向时,只要允许BPU入队allowBpuIn,也可以看作预测结果入队 |
2.1.1 |
WRITE_FTQ_SUBQUEUE |
FTQ_PC |
根据BPU预测结果写入ftq_pc_mem |
2.1.2 |
WRITE_FTQ_SUBQUEUE |
FTQ_REDIRECT |
根据BPU预测结果写入ftq_redirect_mem |
2.1.3 |
WRITE_FTQ_SUBQUEUE |
FTQ_MATA |
根据BPU预测结果写入ftq_meta_1r_sram |
2.1.4 |
WRITE_FTQ_SUBQUEUE |
FTQ_ENTRY |
根据BPU预测结果写入ftb_entry_mem |
2.2.1 |
WRITE_FTQ_STATEQUEUE |
UPDATED_TARGET |
根据BPU预测结果写入update_target |
2.2.2 |
WRITE_FTQ_STATEQUEUE |
CFIINDEX |
根据BPU预测结果写入cfiIndex_vec |
2.2.3 |
WRITE_FTQ_STATEQUEUE |
MISPREDICT |
根据BPU预测结果写入mispredict_vec |
2.2.4 |
WRITE_FTQ_STATEQUEUE |
PRED_STAGE |
根据BPU预测结果写入pred_stage |
2.2.5 |
WRITE_FTQ_STATEQUEUE |
COMMITSTATE |
根据BPU预测结果写入commitStateQueueReg |
2.2.6 |
WRITE_FTQ_STATEQUEUE |
ENTRY_FETCH_STATU |
根据BPU预测结果写入entry_fetch_status |
2.2.7 |
WRITE_FTQ_STATEQUEUE |
ENTRY_HIT_STATU |
根据BPU预测结果写入entry_hit_status |
3.1 |
TRANSFER_BPU_REDIRECT |
IFU |
转发分支预测重定向给IFU |
3.2 |
TRANSFER_BPU_REDIRECT |
PREFETCH |
转发分支预测重定向给PREFETCH |
4.1 |
UPDATE_FTQ_PTR |
NORMAL |
正常情况下修改FTQ指针 |
4.2 |
UPDATE_FTQ_PTR |
REDIRECT |
发生重定向时修改FTQ指针 |
4 - FTQ向IFU发送取指目标
文档概述
IFU需要取FTQ中的项进行取指令操作,同时也会简单地对指令进行解析,并写回错误的指令
FTQ发送给IFU的信号同时也需发送给ICache一份,ICache是指令缓存,帮助快速读取指令。
术语说明
- ifuPtr:该寄存器信号指示了当前FTQ中需要读取的项的指针。直接发送给io.toIfu.req接口的ftqIdx。
- entry_is_to_send:entry_fetch_status存储每个FTQ项的发送状态,初始化并默认为当前ifuptr指向的项对应的发送状态,后续可能因为旁路逻辑等改变
- entry_ftq_offset: 从cfiIndex_vec中初始化并默认为当前ifuptr指向项的跳转指令在预测块中的偏移,后续可能因为旁路逻辑等改变
- entry_next:本次取指结束后下一次取值的开始地址
- pc_mem_ifu_ptr_rdata:获取ifuptr指向FTQ项的取指信息(从ftq_pc_mem的读取接口ifuPtr_rdata中获取)
- pc_mem_ifu_plus1_rdata:获取ifuptr+1指向FTQ项的pc相关信息(从ftq_pc_mem的读取接口ifuPtrPlus1_rdata中)
- copied_ifu_plus1_to_send:多个相同的复制信号,entry_fetch_status中指向ifuPtrPlus1的项是f_to_send状态或者上一周期bpu_in_fire,同时旁路bpu指针bpu_in_bypass_ptr等于ifuptr+1时,信号copied_ifu_plus1_to_send在一周期后拉高
- copied_ifu_ptr_to_send:同理,只是把ifuptr+1改成了ifuptr
模块功能说明
1. 获取取指目标信息
获取取指目标有两个来源,一个是BPU写入信息时,直接将取指目标旁路出来,一种则是从存储取指目标的队列ftq_pc_mem中读取。使用前一种方式的前提,是刚好ifuPtr指向的读取项刚好就是旁路指针信号bpu_in_resp_ptr(BPU入队时写入项的ftqIdx)
- 旁路逻辑:pc信号在被写入存储子队列时就被旁路一份,写入信号ftq_pc_mem.io.wdata在bpu_in_fire信号拉高时被旁路到旁路信号寄存器bpu_in_bypass_buf中。同时被旁路的还有指针信号bpu_in_resp_ptr,在同样的条件下被旁路到寄存器bpu_in_bypass_ptr中
- 读取ftq_pc_mem: 存储pc相关的取指目标,该存储队列有多个读接口,对所有ftqptr的写入信号(比如ifuPtr_write, ifuPtrPlus1_write等)被直接连接到存储队列的读取接口,这样,在ftqPtr寄存器正式被更新时,就可以同时直接从对应的读取接口中返回对应指针的读取结果,比如ftq_pc_mem.io.ifuPtr_rdata
1.1 准备发往ICache的取指目标
有以下三种情况,分别对应测试点1.1.1,1.1.2,1.1.3
- 旁路生效,即旁路bpu指针等于ifuptr,且上一周期bpu输入有效结果(last_cycle_bpu_in表示上一周期bpu_in_fire)有效(也就相当于该旁路指针是有效的),此时,直接向toICache接口输入旁路pc信息bpu_in_bypass_buf
- 不满足情况1,但是上一周期发生ifu_fire(即FTQ发往IFU的接口发生fire),成功传输信号,此toICache中被写入pc存储子队列ftq_pc_mem中ifuptr+1对应项的结果,这是因为此时发生了ifu_fire,新的ifuptr还未来得及更新(即加1),所以直接从后一项中获取新的发送数据
- 前两种情况都不满足,此时toICache接口中被写入pc存储队列中ifuptr对应项的结果
1.2 提前一周期准备发往Prefetch的取指目标
有以下三种情况,分别对应测试点1.2.1,1.2.2,1.2.3
同样有三种情况:
- bpu有信号写入(bpu_in_fire),同时bpu_in_resp_ptr等于pfptr的写入信号pfptr_write, (此时pfptr_write还没有正式被写入pfptr中),读取bpu向pc存储队列的写入信号wdata,下一周期写入ToPrefetch
xxxptr_write:是相应FTQptr寄存器的write信号,连接到寄存器的写端口,寄存器在时钟上升沿成功写入write信号
- 不满足情况1,且由bpu到prefetch的接口发生fire,即bpu向预取单元成功发送信号,pc存储单元的pfPtrPlus1_rdata下一周期写入ToPrefetch接口,选择指针加1对应项的原因与toICache类似。
- 不满足以上两种情况:pc存储单元的pfPtr_rdata在下一周期被写入ToPrefetch接口
1.3 设置下一个发送的指令块的起始地址
有以下三种情况,分别对应测试点1.3.1,1.3.2,1.3.3
target(entry_next_addr)旁路逻辑:
有三种情况:
- 上一周期bpu写入信号,且旁路指针等于ifuptr:
- toIfu:写入旁路pc信息bpu_in_bypass_buf
- entry_is_to_send :拉高
- entry_next_addr :bpu预测结果中跳转地址last_cycle_bpu_target
- entry_ftq_offset :bpu预测结果中跳转指令在预测块中的偏移last_cycle_cfiIndex
- 不满足情况1,bpu到ifu的接口发生fire,信号成功写入
- toIfu:写入pc存储队列的读出信号ifuPtrPlus1_rdata,这同样是因为ifuptr还没来得及更改,所以直接使用ifuptr+1对应项的rdata
- entry_is_to_send :发送状态队列中ifuPtrPlus1对应项为f_to_send或者在上一周期bpu有写入时旁路bpu指针等于ifuptr加1,entry_is_to_send拉高。
- entry_next_addr :
- 如果上一周期bpu有写入且bpu旁路指针等于ifuptr+1,写入bpu旁路pc信号的startAddr字段,而这个项的pc信息还没有写入,正在pc旁路信号中,这是因为ifuptr+1对应下一个指令预测块,它的起始地址实际上就是ifuptr对应指令的预测块的跳转目标。
- 如果不满足该条件,
- ifuptr等于newest_entry_ptr: 使用newest_entry_target作为entry_next_addr,newest_entry_ptr,newest_entry_target这几个内部信号,表明我们当前队列中最新的有效的FTQ项。如之前所说,BPU新的写入,重定向等等都会对最新FTQ项进行新的安排,在相应的文档中,对其生成方式做具体的描述。
- 不满足条件1:使用pc存储队列的ifuPtrPlus2_rdata.startAddr
- 不满足情况1,2:
- toIfu:写入pc存储队列的读出信号ifuPtr_rdata
- entry_is_to_send :发送状态队列中ifuPtr对应项为f_to_send或者在上一周期bpu有写入时旁路bpu指针等于ifuptr
- entry_next_addr :
- 如果上一周期bpu有写入且bpu旁路指针等于ifuptr+1,写入bpu旁路pc信号的startAddr字段。
- 如果不满足该条件,
1. ifuptr等于newest_entry_ptr: 使用newest_entry_target作为entry_next_addr。
2. 不满足上面的条件1:使用pc存储队列的ifuPtrPlus1_rdata.startAddr,为什么条件2和条件3,一个使用ifuPtrPlus2_rdata.startAddr作为entry_next_addr ,一个使用ifuPtrPlus1_rdata.startAddr作为,这也是出于时序的考虑:
因为要获得实际上的ifuptr+1对应项的start值作为结果,而因为第一处那里因为ifuptr还没来得及更新(加1)同步到当前实际的ifuptr,所以要加2来达到实际上的ifuptr+1对应的值,而第二处的ifuptr已经更新了,所以只用加1就行了。
2. 发送取指信息
2.1 发送取指目标
2.1.1 发送给IFU
toIfu接口的req接口:
FTQ通过该接口向IFU发送取指信号:
- valid:要发送的FTQ项处于将发送状态entry_is_to_send且ifuptr不等于bpuptr
- nextStartAddr:递交最终的entry_next_addr
- ftqOffset:递交最终的entry_ftq_offset
- toIfu:递交pc信息
2.1.2 发送给ICache
toICache的req接口:
FTQ通过该接口向ICache发送取指信号:
- valid:FTQ项处于将发送状态entry_is_to_send且ifuptr不等于bpuptr
- readValid:ICache的有多个read接口,readVlid是一个向量,表示这几个read接口是否有效,readVlid中的每个元素的写入值与valid一样
- pcMemRead:同样是一个向量,对应readVlid向量的ICache的多个pc信号read接口,从toIfu接口中将pc信息结果写入向量中各接口,接口的ftqIdx字段被写入ifuPtr
- backendException:后端出现异常,同时后端pc错误指针等于ifuPtr
2.1.3 发送给Prefetch
toPrefetch的req接口:
- valid:传给预取模块的项的状体toPrefetchEntryToSend为1,(toPrefetchEntryToSend会玩一个周期存储nextCycleToPrefetchEntryToSend的值),且pfptr不等于bpuptr,
- toPrefetch:递交pc
- ftqIdx字段被设置为pfptr寄存器的值
- backendException:在后端pc错误指针等于pfptr的时候,传入后端异常信号,否则传入无异常信号
2.2 错误命中
错误命中falsehit:
当发往Ifu的pc接口toIfu中发生fallThruError(预测块的fall through地址小于预测的起始地址时),且hit状态队列entry_hit_status中ifuPtr对应项显示命中的话,进行如下判断:
当发往ifu的接口toIfu的req接口发生fire,且bpu的预测结果不发生满足以下条件的重定向: s2或者s3的重定向的预测块对应的FTQ项索引号ftq_idx等于ifuptr, 此时,hit状态队列中ifuptr对应项被设置为false_hit。
2.3 BPU冲刷
bpu向ifu的req请求的flush:
发往ifu的flushfrombpu(来自bpu的冲刷)接口中,记录有s2,s3阶段的指针,如果其中一条指针不大于发往ifu的req接口的ftqIdx的时候,表示应该被冲刷掉req信号,即冲刷掉新的发送给FTQ的预测信息。
2.4 更新发送状态
成功发送:
发往ifu的req接口发生fire,且req不被来自bpu的flush给冲刷掉时:
entry_fetch_status状态队列中ifuptr对应项的发送状态置为f_sent。表示该ftq项被成功发送 了
接口说明
顶层IO |
子接口 |
作用 |
toIFU |
req |
发送取指目标 |
toIFU |
flushfrombpu |
冲刷掉发送给IFU的取指目标 |
toICache |
req |
发送取指目标 |
toPrefetch |
req |
发送取指目标 |
测试点总表
序号 |
功能名称 |
测试点名称 |
描述 |
1.1.1 |
GET_PC_FOR_ICACHE |
COND1 |
旁路生效,即旁路bpu指针等于ifuptr,且上一周期bpu输入有效结果有效,直接向toICache接口输入旁路pc信息bpu_in_bypass_buf |
1.1.2 |
GET_PC_FOR_ICACHE |
COND2 |
不满足情况1,但是上一周期发生ifu_fire,成功传输信号,此时toICache中被写入pc存储子队列ftq_pc_mem中ifuptr+1对应项的结果 |
1.1.3 |
GET_PC_FOR_ICACHE |
COND3 |
前两种情况都不满足,此时toICache中被写入pc存储队列中ifuptr对应项的结果 |
1.2.1 |
GET_PC_FOR_PREFETCH |
COND1 |
bpu有信号写入,同时bpu_in_resp_ptr等于pfptr的写入信号pfptr_write, 读取bpu向pc存储队列的写入信号wdata,下一周期写入ToPrefetch |
1.2.2 |
GET_PC_FOR_PREFETCH |
COND2 |
不满足情况1,且由bpu到prefetch的接口发生fire,即bpu向预取单元成功发送信号,pc存储单元的pfPtrPlus1_rdata下一周期写入ToPrefetch接口 |
1.2.3 |
GET_PC_FOR_PREFETCH |
COND3 |
不满足以上两种情况:pc存储单元的pfPtr_rdata在下一周期被写入ToPrefetch接口 |
1.3.1 |
SET_NEXT_ADDR |
COND1 |
上一周期bpu写入信号,且旁路指针等于ifuptr时设置下一个发送的指令块的起始地址 |
1.3.2 |
SET_NEXT_ADDR |
COND2 |
不满足情况1,bpu到ifu的接口发生fire时设置下一个发送的指令块的起始地址 |
1.3.3 |
SET_NEXT_ADDR |
COND3 |
不满足情况1,2时设置下一个发送的指令块的起始地址 |
2.1.1 |
SEND_PC |
IFU |
向IFU发送取指目标 |
2.1.2 |
SEND_PC |
ICACHE |
向ICache发送取指目标 |
2.1.3 |
SEND_PC |
PREFETCH |
向Prefetch发送取指目标 |
2.2 |
FALSE_HIT |
FALSE_HIT |
当发往Ifu的pc接口toIfu中发生fallThruError,且FTB项命中时判断是否是错误命中 |
2.3 |
FLUSH_FROM_BPU |
FLUSH_FROM_BPU |
发往ifu的flushfrombpu(来自bpu的冲刷)接口中的s2,s3阶段的指针其中一条指针不大于发往ifu的req接口的ftqIdx的时候,应该冲刷掉新的发送给FTQ的预测信息 |
2.4 |
UPDATE_SEND_STATU |
UPDATE_SEND_STATU |
发往ifu的req接口发生fire,且req不被来自bpu的flush给冲刷掉时: entry_fetch_status状态队列中ifuptr对应项的发送状态置为f_sent |
5 - IFU向FTQ写回预译码信息
文档概述
IFU获取来自BPU的预测信息之后,会执行预译码,并将FTQ项写回FTQ中去。我们会比对FTQ中原BPU预测项和预译码的结果,判断是否有预测错误
基本流程
预译码写回ftq_pd_mem:
- FTQ从pdWb接口中获取IFU的写回信息,FTQ首先将预译码写回信息写回到ftq_pd_mem,
更新提交状态队列commitStateQueue:
- 然后根据写回信息中指令的有效情况更新提交状态队列commitStateQueue。
比对错误:
- 同时,从ftb_entry_mem读出ifu_Wb_idx所指的FTB项,将该FTB项的预测结果与预译码写回结果进行对比,看两者对分支的预测结果是否有所不同。
综合错误:
- 之后就综合根据预译码信息可能得到的错误:有前面说的比对BPU的预测结果和预译码结果得到的错误,也有直接根据预译码得到的错误预测信息。根据错误预测结果更新命中状态队列。
更新写回指针
- 最后,如果IFU成功写回,ifu_Wb_idx更新加1。
术语说明
名称 |
定义 |
预译码 |
IFU会对取指目标进预译码,之后写回FTQ |
ifuWbPtr |
IFU写回指针,知识IFU预译码要写入FTQ的位置 |
模块功能说明
1. 预译码写回ftq_pd_mem
写回有效:预译码信息pdWb有效时,写有效
写回地址:pdWb的ftqIdx的value
写回值:解析整个pdWb的结果
2. 更新提交状态队列
当预译码信息pdWb有效时,相当于写回有效,此时,根据预译码信息中每条指令的有效情况和该指令是否在有效范围内,判断指令的提交状态是否可以修改,若可以修改,则将提交状态队列,写回项中的指令状态修改
详细信号表示
pdWb有效时,ifu_wb_valid拉高。
此时,对于预译码信息中每一条指令的预译码结果pd做判断:
如果预译码结果valid,且指令在有效范围内(根据insrtRange的bool数组指示),则提交状态队列commitStateQueue中,写回项中的指令状态修改为c_toCommit,表示可以提交,这是因为只有在FTQ项被预译码写回后,才能根据后端提交信息提交该FTQ项,之后会把预译码信息一并发往更新通道。
3. 比对预测结果与预译码结果
从ftb存储队列ftb_entry_mem中的读取ifu写回指针ifuwbptr的对应项:
- pdWb有效的时候,读有效,读取地址为预译码信息中指示的ftqIdx。
当命中状态队列指示待比对项ftb命中,且回写有效时,读取出FTB存储队列中对应的项,与预译码信息进行比对,当BPU预测的FTB项指示指令是有效分支指令,而预译码信息中则指示不是有效分支指令时,发生分支预测错误,当BPU预测的FTB项指示指令是有效jmp指令,而预译码信息中则指示不是有效jmp指令时,发生跳转预测错误
详细信号表示:
ifu_wb_valid回写有效时,ftb_entry_mem回写指针对应读使能端口ren有效,读取地址为ifu_wb_idx预测译码信息中指示的ftqIdx的value值。
回写项命中且回写有效,hit_pd_valid信号有效,此时,读取ftb存储队列中的FTB项,读出brSlots与tailSlot,并进行比对:
3.1 判断是否有分支预测错误br_false_hit
测试点3.1.1和3.1.2对应以下两种条件导致的br_false_hit
- 判断是否有分支预测错误br_false_hit:
- brSlots的任意一项有效,同时在预译码信息中不满足这一项对应的pd有效且isBr字段拉高表明是分支指令,
- taiSlot有效且sharing字段拉高表明该slot为分支slot,同时在预译码信息中不满足这一项对应的pd有效且isBr字段拉高表明是分支指令
满足任意条件可判断发生分支预测错误br_false_hit,该信号拉高
3.2 判断是否发生jmp预测错误jal_false_hit
- 判断是否发生jmp预测错误jal_false_hit:
- 预测结果中必须指明指令预测有效,且其中isJal拉高表面是jal指令或者指明是isjalr指令
4. 预译码错误
直接从预测结果中获取错误预测相关信息,如果回写项ftb命中且missoffset字段有效表明有错误预测的指令,hit_pd_mispred信号拉高,表示预译码结果中直接指明有预测错误的指令。
5. 综合错误
综合比对预测结果与预译码结果得到的错误信息,与预译码错误直接获得的预测错误,任意一种发生时has_false_hit拉高表示有预测错误,此时,命中状态队列entry_hit_status中写回项的状态置为h_false_hit
6. 更新写回指针
ifu_wb_valid拉高,表示写回有效,将ifuWbPtr更新为原值加1。
接口说明
测试点总表
序号 |
功能名称 |
测试点名称 |
描述 |
1 |
WB_PD |
WB_PD |
向ftq_pd_mem中写回预译码信息 |
2 |
UPDATE_COMMITSTATE |
UPDATE_COMMITSTATE |
当预译码信息pdWb有效时,根据预译码信息中每条指令的有效情况和该指令是否在有效范围内,判断指令的提交状态是否可以修改,若可以修改,则将提交状态队列,写回项中的指令状态修改 |
3.1.1 |
BR_FALSE_HIT |
COND1 |
brSlots的任意一项有效,同时在预译码信息中不满足这一项对应的pd有效且isBr字段拉高 |
3.1.2 |
BR_FALSE_HIT |
COND2 |
taiSlot有效且sharing字段拉高表明该slot为分支slot,同时在预译码信息中不满足这一项对应的pd有效且isBr字段拉高 |
3.2 |
JAL_FALSE_HIT |
JAL_FALSE_HIT |
指令预测有效,且其中isJal拉高或者指明是isjalr指令 |
4 |
PD_MISS |
PD_MISS |
如果回写项ftb命中且missoffset字段有效表明有错误预测的指令,hit_pd_mispred信号拉高 |
5 |
FALSE_HIT |
FALSE_HIT |
综合比对预测结果与预译码结果得到的错误信息,与预译码错误直接获得的预测错误,任意一种发生时has_false_hit拉高表示有预测错误,此时,命中状态队列entry_hit_status中写回项的状态置为h_false_hit |
6 |
UPDATE_IFU_WB_PTR |
UPDATE_IFU_WB_PTR |
ifu_wb_valid拉高,将ifuWbPtr更新为原值加1 |
6 - FTQ接收后端重定向
文档概述
FTQ重定向信息有两个来源,分别是IFU 和 后端。两者的 重定向接口大致相似,但重定向的过程有一定区别。
对于重定向,后端有提前重定向机制,为了实现提前一拍读出在ftq中存储的重定向数据,减少redirect损失,后端会向ftq提前一拍(相对正式的后端redirect信号)传送ftqIdxAhead信号和ftqIdxSelOH信号。ftqIdxSelOH信号出现的原因,是早期版本要读多个ftqIdxAhead信号,以独热码的形式选其中一路作为最终确认的提前索引值,但现在只需要从一个端口获取ftqIdx信号了,ftqIdxAhead只能确认这一个端口了。
术语说明
名称 |
定义 |
sc_disagree |
统计SC预测错误用的性能计数器中需要用到的值,SC预测器是BPU子预测器TAGE-SC预测器的一个部分 |
模块功能说明
1. 接收后端重定向信号
时序
1.1 提前重定向
第一个周期:
- 后端重定向写回时,首先会从后端到FTQ的IO接口(CtrltoFtqIO)中,看ftqIdx是不是有效信号,且此时后端正式重定向信号redirect无效(因为提前重定向会比正式重定向提前一拍,所以此时正式重定向无效),这时,提前重定向信号aheadValid有效, 将使用提前获取的重定向ftqIdx,
1.2 真实提前重定向
第二个周期:
- 如果此时后端正式重定向信号有效了,且ftqIdxSelOH拉高,说明在正式重定向阶段成功对ftqIdxAhead信号进行选中,同时上一周期重定向信号aheadValid是有效的,则真实提前重定向信号realAhdValid拉高,在此时读取
1.3 存储后端重定向信号
第三个周期:
- 该周期会把来自后端的重定向信息的存储一份在寄存器backendRedirectReg中,具体的来说,当上一个周期后端重定向有效时,将后端重定向bits字段(存储实际内容)被写入寄存器的bits字段。
- 而实际决定信号是否有效的valid字段(决定该信号是否有效)则在上一周期真实提前重定向信号有效(表示确实使用了提前重定向的ftqIdx进行重定向)的情况下,被写入false,因为提前重定向发生时,我们直接使用当前的后端重定向信号交给FTQ就可以了。而不需要多保存一个周期。
- 真实提前重定向信号无效时,则由上一周期后端正式重定向的有效值决定,只有信号有效时,我们才需要把它存下来,之后交给FTQ。
2. 选择重定向信号
信号抉择:
是提前获取后端重定向信息还是延迟一个周期从寄存器内读取?
真实重定向有效时,直接将后端重定向信息传递给FTQ,否则,取重定向寄存器内的信号作为重定向信息传递给FTQ,相当于晚一个周期发送重定向信息。最后被选择的重定向信息作为后端重定向结果fromBackendRedirect发送给FTQ
接下来讲讲后端重定向在这三个周期到底通过ftqIdx到底读了哪些FTQ子队列中的信息,以及怎么使用它们。
3. 整合子队列信号
3.1 读取子队列
接下来讲讲后端重定向在这三个周期到底通过ftqIdx到底读了哪些FTQ子队列中的信息,以及怎么使用它们。
后端重定向读取的子队列:
- ftq_redirect_mem:FTQ会根据后端重定向提供的ftqIdx读出ftq_Redirect_SRAMEntry,借助它提供的信息重定向到之前的状态。
- ftq_entry_mem:读出重定向指令块对应的FTB项
- ftq_pd_mem:读出重定向指令块的预译码信息
3.1.1 发生提前重定向时,读取子队列需要两个周期
3.1.2 未发生提前重定向时,读取子队列需要三个周期
读子队列时序:
第一个周期:
- 提前重定向信号有效时,将子队列的读端口,读有效信号拉高,输入ftqIdxAhead的value字段作为读地址,发起读取请求。
第二个周期:
- case1. 如果第一周期的提前重定向无效,而现在正式重定向有效,则在此时才拉高读有效信号,使用正式重定向接口的ftqIdx作为读取地址,发起读取请求。
- case2. 真实提前重定向有效了,此时因为前一个周期已经发起读取请求,此时可以直接从子队列的读端口读出了
第三个周期
- 真实提前重定向无效,但至少前一个周期正式重定向发起的读取请求能保证在当前周期从子队列中读出。
3.2 将子队列信息整合到后端重定向信号
处理读取信息
FTQ会将从子队列中读出的信息整合到fromBackendRedirect中。
具体来说:
- 重定向redirect接口的CfiUpdateInfo接口直接接收ftq_Redirect_SRAMEntry中的同名信号。
- 利用fromBackendRedirect中指示的ftqOffset读取指令块预译码信息中实际跳转指令的预译码信息,该ftqOffset为后端执行过后确定的控制流指令在指令块内的偏移。
- 得到的预译码信息被直接连接到CfiUpdateInfo接口的pd接口中
- 对于读出的指令块对应的FTB项,我们可以从中得知实际执行时得到的跳转指令,是否在FTB项被预测为跳转指令,或者是被预测为jmp指令,如果是,则cfiUpdateInfo的br_hit接口或者jr_hit接口被拉高,表示对应的分支预测结果正确了。
- 具体来说:通过发送ftqOffset,ftb项以brIsSaved的方式判断是否br_hit,判断是否jr_hit的方式也是类似的(r_ftb_entry.isJalr && r_ftb_entry.tailSlot.offset === r_ftqOffset)。
- 在CfiUpdateInfo接口设置为br_hit的时候,还会根据这条发生跳转的分支指令是哪个槽从ftq_Redirect_SRAMEntry重定向接口的sc_disagree统计SC预测错误用的性能计数器中,获取对应值,最后整合到后端重定向接口中(如果没有br_hit,对应计数器的两个值都为0)。
接口说明
顶层IO |
功能 |
fromBackend |
接收后端重定向信息 |
测试点总表
序号 |
功能名称 |
测试点名称 |
描述 |
1.1 |
RECERIVE_BACKEND_REDIRECT |
REDIRECT_AHEAD |
后端重定向写回时,首先会从后端到FTQ的IO接口(CtrltoFtqIO)中,看ftqIdx是不是有效信号,且此时后端正式重定向信号redirect无效,这时,提前重定向信号aheadValid有效 |
1.2 |
RECERIVE_BACKEND_REDIRECT |
REAL_REDIRECT_AHEAD |
如果此时后端正式重定向信号有效了,且ftqIdxSelOH拉高,同时上一周期重定向信号aheadValid是有效的,则真实提前重定向信号realAhdValid拉高 |
1.3 |
RECERIVE_BACKEND_REDIRECT |
STORE_REDIRECT |
后端真实重定向无效时写入寄存器 |
2 |
CHOOSE_AHEAD |
CHOOSE_AHEAD |
真实重定向有效时,直接将后端重定向信息传递给FTQ,否则,取重定向寄存器内的信号作为重定向信息传递给FTQ |
3.1.1 |
READ_FTQ_SUBQUEUE |
READ_AHEAD |
发生提前重定向时,读取子队列需要两个周期 |
3.1.2 |
READ_FTQ_SUBQUEUE |
READ_NO_AHEAD |
未发生提前重定向时,读取子队列需要三个周期 |
3.2 |
ADD_SUBQUEUE_INFO |
ADD_SUBQUEUE_INFO |
将子队列信息整合到后端重定向信号 |
7 - FTQ接收IFU重定向
文档概述
除了后端,IFU也会发送重定向相关消息,和后端不同,IFU的重定向信息来自于预译码写回信息。相同的是,它们都是通过BranchPredictionRedirect的接口传递重定向信息。
术语说明
名称 |
定义 |
RedirectLevel |
重定向等级,重定向请求是否包括本位置,低表示在本位置后重定向,高表示在本位置重定向。它在之后决定了由重定向导致的冲刷信号是否会影响到发生重定向的指令 |
模块功能说明
1. IFU重定向信号生成
流程
IFU重定向是通过这个BranchPredictionRedirect接口传递的,下面来讲述IFU重定向怎么生成IFU的BranchPredictionRedirect内相应信号的,这个过程需要两个周期
信号列表:
第一个周期
1.1 IFU 重定向触发条件
- valid:当预译码写回pdWb有效,且pdWb的missOffset字段有效表明存在预测错误的指令,同时后端冲刷信号backendFlush无效时,valid信号有效。
1.2 IFU生成重定向信号
- ftqIdx:接收pdWb指定的ftqIdx
- ftqOffset:接收pdWb的missOffset的bits字段
- level:RedirectLevel.flushAfter,将重定向等级设置为flushAfter
- BTBMissBubble:true
- debugIsMemVio:false
- debugIsCtrl:false
- cfiUpdate:
信号列表:
- pc:pdWb中记录的指令块中所有指令pc中,missOffset对应的pc
- pd:pdWb中记录的指令块中所有指令的pd中,missOffset对应的pd
- predTaken:从cfiIndex_vec子队列中读取pdWb中ftqIdx索引的项是否valid,有效说明指令块内被预测为有控制流指令。
- target:pdWb中的target
- taken:pdWb中cfiOffset的valid字段,有效时表明预译码认为指令块中存在指令控制流指令
- isMisPred:pdWb中missOffset的valid字段,有效时表明预译码认为指令块中存在预测错误的指令
第二个周期:
该周期进行的信号生成是在第一周期valid字段有效的情况下才继续的
- cifUpdate:
信号列表:
- 重定向RAS相关信号:通过ftqIdx索引从 ftq_redirect_mem读出ftq_Redirect_SRAMEntry,把其中的所有信号直接传递给cfiUpdate的同名信号中。
- target:已在第一周期写入cfiUpdate的pd有效,且isRet字段拉高,指明发生预测错误的指令本是一条Ret指令,此时,将target设置为cfiUpdate的topAddr,帮助回到发生错误之前的状态。
2. 重定向结果生效
两个周期生成完整的重定向信息后,IFU重定向信息才有效,有可能被FTQ采取,完整的IFU重定向结果记为ifuRedirectToBpu
3. IFU 冲刷信号 (ifuFlush
)
指令流控制信号:
ifuFlush:来自IFU的冲刷信号,主要是由IFU重定向造成的,生成IFU重定向信息的两个周期内,该信号都拉高
- 标志:IFU重定向信息产生接口BranchPredictionRedirect中valid有效,表示开始生成重定向信息,该周期以及下一个周期,ifuFlush拉高
接口说明
顶层IO |
作用 |
fromIFU |
接收来自IFU的预译码信息 |
接口时序
测试点总表
序号 |
功能名称 |
测试点名称 |
描述 |
1.1 |
IFU_REDIRECT |
IFU_REDIRECT_GRN_VALID |
当预译码写回pdWb有效,且pdWb的missOffset字段有效表明存在预测错误的指令,同时后端冲刷信号backendFlush无效时,valid信号有效 |
1.2 |
IFU_REDIRECT |
IFU_REDIRECT_GEN |
允许生成IFU重定向时,在两周期内生成具体信号 |
2 |
IFU_REDIRECT_TO_BPU |
IFU_REDIRECT_TO_BPU |
IFU重定向生成后,IFU重定向结果生效 |
3 |
IFU_FLUSH |
IFU_FLUSH |
生成IFU重定向信息的两个周期内,ifuFlush信号都拉高 |
8 - FTQ向后端发送取指目标
文档概述
pc取值目标会发给后端pc mem让他自己进行存储,之后从自己的pc mem取指,此外,最新的FTQ项和对应的跳转目标也会发给后端。
怎样算是一个最新的FTQ项,BPU最新发送的预测块可以是最新的FTQ项,其次,重定向发生时,需要回滚到发生错误预测之前的状态,从指定的FTQ项开始重新开始预测,预译码等等,这也可以是被更新的最新的FTQ项。
术语说明
模块功能说明
流程
1.发送取值目标到pc mem
- 发送时机:bpu_in_fire,即BPU向前端发送有效预测信息,或者重定向信息的时候。以此为基础之后的第二个周期,进行发送,通过将toBackend接口的pc_mem_wen设置为true的方式指明开始发送
- 接口信号列表:
- pc_mem_wen:设置为true
- pc_mem_waddr:接收bpu_in_fire那个周期BPU发送的ftqIdx
- pc_mem_wdata:接收bpu_in_fire那个周期,FTQ读取的ftq_pc_mem中的取指目标
2.更新最新的FTQ项
- 发送时机:
- 最新的FTQ项可能是由BPU写入最新预测信息造成的,发送取值目标到pc mem也是因为BPU写入最新预测信息才写入的,如果是这种情况造成的,更新FTQ项和写入pc mem的时机是一致的。
- 此外发生重定向时,也会进行状态回滚更新FTQ项,标志是后端接口fromBackend的重定向redirect信号有效,或者写入BPU的接口toBPU的redirctFromIFU拉高说明当前有来自IFU的重定向
- (注释(可忽略)IFU重定向信号生成有两个周期,可以认为第一个周期预译码信息中missoffset有效说明IFU重定向发生,也可以认为第二个周期redirctFromIFU拉高说明重定向发生,此处取后者)。
- 同样是向toBackend中写入
- 接口信号列表:
- newest_entry_en:前面说的发送时机到来时,再延迟一个周期达到真正的写入时机,这时才拉高信号
- newest_entry_ptr:发送时机到来时的newest_entry_ptr,在真正的写入时机写入
- newest_entry_target:发送时机到来时的newest_entry_target
newest_entry_ptr,newest_entry_target这几个都是同名的内部信号,如之前所说,BPU新的写入,重定向等等都会对最新FTQ项进行新的安排,在相应的文档中,对其生成方式做具体的描述。
接口说明
顶层IO |
作用 |
toBackend |
发送取指令目标,让后端进行储存 |
测试点总表
序号 |
功能名称 |
测试点名称 |
描述 |
1 |
SEND_PC_TO_BACKEND |
SEND_PC |
发送取值目标到pc mem |
2 |
SEND_PC_TO_BACKEND |
UPDATE_NEWEST |
更新最新的FTQ项 |
9 - 执行单元修改FTQ状态队列
文档概述
后端的写回信息,包括重定向信息和更新信息,实际上都是执行之后,由实际执行单元根据结果发回的
术语说明
名称 |
定义 |
cfiIndex_vec |
控制流指令索引队列,记录每个指令块中控制流指令的索引 |
update_target |
更新目标队列,记录每个指令块的跳转目标 |
FTQ最新项 |
BPU新的写入,重定向等等都会对最新FTQ项进行新的安排,表明我们当前关注的最新FTQ项。 |
模块功能说明
1. 由后端的写回信号修改FTQ状态
1.1 修改FTQ状态队列
从后端写回FTQ接口fromBackend中的redirect接口中,我们可以读出valid,ftqPtr,ftqOffset(后端实际执行时确认的控制流指令的偏移),taken,mispred字段,依靠它们来判断,如何修改FTQ的状态队列和相关的变量
后端执行单元写回时被修改的队列:
1.1.1 修改cfiIndex_vec
- cfiIndex_vec:
修改方式:执行写回修改队列中ftqPtr那一项
- valid:fromBackend中的redirect接口中,valid有效,taken有效,且ftqOffset小于或者等于cfiIndex_vec中ftqPtr那一项指定的偏移:这说明重定向发生,实际执行结果判断ftqPtr索引的指令块确实会发生跳转,且实际执行跳转的指令在被预测为发生跳转的指令之前或等于它。所以这时指令块是会发生跳转的,控制流索引队列的ftqPtr项valid
- bits:fromBackend中的redirect接口中,valid有效,taken有效,且ftqOffset小于cfiIndex_vec中ftqPtr那一项指定的偏移,偏移量被更新为更小值ftqOffset。
1.1.2 修改update_target
- update_target:
- ftqPtr索引项的跳转目标修改为fromBackend的redirect接口中的cifUpdate中指定的target
1.1.3 修改mispredict_vec
- mispredict_vec:
- 如果该重定向指令是来自后端的重定向指令, ftqPtr索引项的ftqOffset偏移指令被设置为fromBackend的redirect接口中的cifUpdate中指定的isMisPred
1.2 修改FTQ最新项
- newest_entry_target:
- 被修改为重定向接口中cfiUpdate指定的target
- 辅助信号newest_entry_target_modified被指定为true
- newest_entry_ptr:
- 修改为重定向接口指定的ftqIdx
- 辅助信号newest_entry_ptr_modified被指定为true
2. 由IFU的写回信号修改FTQ状态
IFU既然也能和后端一样生成重定向信息,那么他也能在产生重定向信息的时候修改这些状态队列和FTQ最新项,区别:
- 但是,由于IFU没有真的执行,所以它的预译码结果并不能作为决定指令块是不是真的被错误预测了,所以它不能修改mispredict_vec的状态
- 其次,后端重定向优先级永远高于IFU重定向,两者同时发生时只采用后端重定向。
所以这个部分也有以下测试点:
2.1.1 修改cfiIndex_vec
2.1.2 修改update_target
2.2 修改FTQ最新项
常量说明
常量名 |
常量值 |
解释 |
常量1 |
64 |
常量1解释 |
常量2 |
8 |
常量2解释 |
常量3 |
16 |
常量3解释 |
接口说明
顶层IO |
子接口 |
|
fromBackend |
redirect |
|
测试点总表
实际使用下面的表格时,请用有意义的英文大写的功能名称和测试点名称替换下面表格中的名称
序号 |
功能名称 |
测试点名称 |
描述 |
1.1.1 |
BACKEDN_REDIRECT_UPDATE_STATE |
UPDATE_CFIINDEXVEC |
后端重定向修改cfiinedex状态队列 |
1.1.2 |
BACKEDN_REDIRECT_UPDATE_STATE |
UPDATE_UPDATE_TARGET |
后端重定向修改update_target状态队列 |
1.1.3 |
BACKEDN_REDIRECT_UPDATE_STATE |
UPDATE_MISPREDICTVEC |
后端重定向修改mispredict状态队列 |
1.2 |
BACKEDN_REDIRECT_UPDATE_NEWEST |
BACKEDN_REDIRECT_UPDATE_NEWEST |
后端重定向修改FTQ最新项 |
2.1.1 |
IFU_REDIRECT_UPDATE_STATE |
UPDATE_CFIINDEXVEC |
IFU重定向修改cfiinedex状态队列 |
2.1.2 |
IFU_REDIRECT_UPDATE_STATE |
UPDATE_UPDATE_TARGET |
IFU重定向修改update_target状态队列 |
2.2 |
IFU_REDIRECT_UPDATE_NEWEST |
IFU_REDIRECT_UPDATE_NEWEST |
IFU重定向修改FTQ最新项 |
10 - 冲刷指针和状态队列
文档概述
之前讲了,后端和IFU重定向写回会修改一些状态队列。此外,FtqPtr也是一种比较重要的维护信息。由后端或者IFU引起的重定向,需要恢复各种类型用来索引FTQ项的FtqPtr。而当重定向是由后端发起的时候,还要修改提交状态队列,说明指令已经被执行。
术语说明
名称 |
定义 |
FTQ指针 |
用来索引FTQ项,有不同类型的FTQ指针,比如bpuPtr,ifuPtr |
flush |
冲刷,发生时需要重置FTQ指针,以及重置其他状态 |
融合指令 |
一条指令可以和其他指令融合,形成融合指令 |
模块功能说明
1. 冲刷FTQ指针及提交状态队列
流程
后端和IFU的重定向信号都会冲刷指针,更具体的来说:
1.1 冲刷条件
- 后端写回接口fromBackend有效,或者IFU重定向有效:(当预译码写回pdWb有效,且pdWb的missOffset字段有效表明存在预测错误的指令,同时后端冲刷信号backendFlush无效)。(参考:从IFU重定向的第一个周期,重定向valid值有效条件)
1.2 冲刷指针
第一个周期:
- 冲刷指针:确认后端和IFU的重定向信号可能冲刷指针时,从两个重定向来源的redirect接口读出重定向信息,包括ftqIdx,ftqOffset,重定向等级RedirectLevel。有两个来源时,优先后端的重定向信息。
冲刷指针列表:
- bpuPtr:ftqIdx+1
- ifuPtr:ftqIdx+1
- ifuWbPtr:ftqIdx+1
- pfPtr:ftqIdx+1
注:只是在当前周期向指针寄存器写入更新信息,实际生效是在下一个周期。
这样一来,所有类型指针当前指向的都是发生重定向的指令块的下一项了,我们从这一项开始重新进行分支预测,预译码,等等。
1.3 冲刷提交状态队列
第二个周期:
如果上一个周期的重定向来源是后端,FTQ会进一步更改提交状态队列
- 提交状态队列中,对于重定向的指令块(通过ftqIdx索引),位于ftqOffset后面的指令的状态被设置为c_empty
- 对于正好处于ftqOffset的指令,判断RedirectLevel,低表示在本位置后flush,高表示在本位置flush,所以level为高时,对于的指令提交状态被设置为flush。
2 转发到顶层IO
实际上,在发生重定向的时候,还涉及一些将重定向信息通过FTQ顶层IO接口转发给其他模块的操作,比如ICache需要flush信号取进行冲刷,IFU也需要后端的重定向信号对它进行重定向,具体来说:
在流程的第一个周期:
2.1 flush转发到icacheFlush
- flush信号顶层IO转发(icacheFlush):
- 确认后端和IFU的重定向信号可能冲刷指针时,拉高FTQ顶层IO接口中的icacheFlush信号,把重定向产生的flush信号转发给ICache
2.2 重定向信号转发到IFU
- 重定向信号顶层IO转发(toIFU):
- redirect:
- bits:接收来自后端的重定向信号
- valid:后端的重定向信号有效时有效,保持有效,直到下个周期依然有效
3 重排序缓冲区提交
其实,除了后端重定向会更新提交状态队列,最直接的更新提交状态队列的方式是通过FTQ顶层IO中frombackend里提供的提交信息,rob_commits告知我们哪些指令需要被提交。
rob_commits的valid字段有效,可以根据其中信息对指令进行提交,修改状态队列。对于被执行的指令,是如何提交的,如何对应地修改提交状态队列,有两种情况:
3.1 提交普通指令
- 对于普通指令,根据rob_commits的ftqIdx和ftqOffset索引提交状态队列中的某条指令,将对应的提交状态设置为c_commited
3.2 提交融合指令
- 对于融合指令,根据提交类型commitType对被索引的指令和另一与之融合的指令进行提交,将对应的提交状态设置为c_commited
- commitType = 4:同时把被索引指令的下一条指令设为c_commited
- commitType = 5:同时把被索引指令的之后的第二条指令设为c_commited
- commitType = 6:同时把被指令块的下一个指令块的第0条指令设为c_commited
- commitType = 7:同时把被指令块的下一个指令块的第1条指令设为c_commited
接口说明
顶层IO |
作用 |
fromBackend |
接收后端重定向和指令提交 |
fromIfu |
接收IFU重定向 |
icacheFlush |
将flush信号转发到icache |
toIFU |
将后端重定向转发到IFU |
测试点总表
序号 |
功能名称 |
测试点名称 |
描述 |
1.1 |
FLUSH_FTQPTR_AND_COMMITSTATE |
FLUSH_COND |
后端写回接口fromBackend有效,或者IFU重定向有效时,进行冲刷 |
1.2 |
FLUSH_FTQPTR_AND_COMMITSTATE |
FLUSH_FTQ_PTR |
优先采用后端重定向信息冲刷FTQ指针 |
1.3 |
FLUSH_FTQPTR_AND_COMMITSTATE |
FLUSH_COMMIT_STATE |
发生后端重定向时,进一步修改提交状态队列 |
2.1 |
TRANSFER_TO_TOP |
FLUSH |
后端和IFU的重定向信号可能冲刷指针,拉高FTQ顶层IO接口中的icacheFlush信号 |
2.2 |
TRANSFER_TO_TOP |
IFU |
将重定向信号转发到IFU |
3.1 |
COMMIT_BY_ROB |
NORMAL |
对于普通指令,根据rob_commits的ftqIdx和ftqOffset索引提交状态队列中的某条指令,将对应的提交状态设置为c_commited |
3.2 |
COMMIT_BY_ROB |
FUSION |
对于融合指令,根据提交类型commitType对被索引的指令和另一与之融合的指令进行提交,将对应的提交状态设置为c_commited |
11 - FTQ向BPU发送更新与重定向信息
文档概述
FTQ将已提交指令的更新信息发往BPU进行训练,同时转发重定向信息。
术语说明
模块功能说明
1. 转发重定向
向toBPU接口进行转发:
1.1 IFU重定向结果有效
- redirctFromIFU:IFU重定向结果有效时,拉高该信号(注意:IFU重定向有效的时机有两种说法,因为IFU重定向结果生成需要两个周期,此处取后者,即,IFU重定向生成过程的第二个周期有效,也是IFU生成完整重定向结果的周期)
1.2 选择后端重定向或者IFU重定向
- redirect:如果后端重定向结果fromBackendRedirect有效,选用fromBackendRedirect,否则选用IFU重定向结果ifuRedirectToBpu
2 BPU更新暂停
BPU的更新需要两个周期,故需要三种状态去表明我们当前的更新状态:更新的第一个周期,第二个周期,更新完成。
当发生更新的时候,会暂停FTQ对指令块的提交以及发送更新信息。
3 提交指令块
FTQ需要对当前comPtr指向的当前提交指令块,进行判断是否能够提交。
这个过程比较复杂。
由于 香山V2版本 的后端会在 ROB 中重新压缩 FTQ entry,因此并不能保证提交一个 entry 中的每条指令,甚至不能保证每一个 entry 都有指令提交。
判断一个 entry 是否被提交有如下几种可能:
- robCommPtr 在 commPtr 之后(ptr更大)。也就是说,后端已经开始提交之后 entry 的指令,在 robCommPtr 指向的 entry 之前的 entry 一定都已经提交完成
- commitStateQueue 中的某个指令块内最后一条有效范围内指令被提交。FTQ项中该指令被提交意味着这FTQ项内的指令已经全部被提交
在此以外,还必须要考虑到,后端存在 flush itself 的 redirect 请求,这意味着这条指令自身也需要重新执行,这包括异常、load replay 等情况。在这种情况下,这一FTQ项不应当被提交以更新 BPU,否则会导致 BPU 准确率显著下降。
3.1 canCommit
具体来看,判断commPtr指向的指令块能否提交,如果可以提交记为canCommit。
canCommit的设置条件如下:
3.1.1 COND1
- 当commPtr不等于ifuWbPtr,且没有因为BPU更新而暂停,同时robCommPtr在commPtr之后。之所以要求commPtr不等于ifuWbPtr是因为,前面说过了必须先预译码写回FTQ项才能提交
3.1.2 COND2
- commitStateQueue 中commPtr对应指令块有指令处于c_toCommit 或c_committed状态。且指令块中最后一条处于c_toCommit 或c_committed状态的指令是c_committed的。
这两种情况下,canCommit拉高,说明可以提交该指令块
3.2 canMoveCommPtr
3.2.1 提交指令块更新提交指针
在commPtr指向的指令块如果能提交,那么我们自然可以移动CommPtr指向下一个FTQ项了。
3.2.2 指令冲刷更新提交指针
但除此之外,commitStateQueue 中commPtr对应指令块的第一条指令被后端重定向冲刷掉了时,这表明该指令需要重新执行,这一FTQ项不应被提交,但是却可以更新CommPtr指针,因为该指令块内已经没有可以提交的指令了。
- CanMoveCommPtr时,commPtr指针更新加1(一周期后成功写入)。
3.3 robCommPtr更新
有几种情况
3.3.1 COND1
- 当来自后端接口fromBackend的rob_commits信息中,有信息有效时,取最后一条有效交割信息的ftqIdx作为robCommPtr
3.3.2 COND2
- 不满足情况1,选取commPtr, robCommPtr中较大的那个
3.4 mmio提交
发往mmioCommitRead接口
3.4.1 COND1
- 当commPtr比来自mmioCommitRead接口的mmioFtqPtr大时,
3.4.2 COND2
- 或者两者正好相等,且commPtr指向的指令块中有c_toCommit 或c_committed状态的指令,最后一条处于c_toCommit 或c_committed状态的指令是c_committed的
在这两种情况下,mmioLastCommit信号在下一个周期被拉高
4 发送BPU更新信息
FTQ需要从FTQ子队列中,读取提交项的预测信息,重定向信息,meta信息,用这些信息来对BPU发送更新信息。
当canCommit时,可以提交commPtr指向的指令块时,从ftq_pd_mem,ftq_redirect_mem,ftq_meta_1r_sram_mem这些子队列,以及一些小的状态队列中读出对应指令块的相应信息,这些信息需要专门花一个周期才能读取到。具体来说:
- 从预译码信息子队列ftq_pd_mem中读取提交提交指令块(commptr所指)的预译码信息
- 从取指目标子队列ftq_pc_mem中读取取指信息
- 从分支预测重定向信息子队列ftq_redirect_mem中读取提交指令块的重定向信息。
- 从预测阶段状态队列中读取提交块来自BPU的哪个预测阶段
- 从meta信息子队列ftq_meta_1r_sram中读取提交指令块的meta,和相应的ftb_entry。
- 从提交状态队列commitStateQueueReg中读取提交状态,并确认指令块中哪些指令为c_committed,用bool数组表示
- 从控制流索引状态队列cfiIndex_vec中读取指令控制流指令在块中索引
- 结合错误预测状态队列mispredict_vec,和提交状态队列信息确认指令块中的提交错误指令。(即提交状态指示为c_commited 同时错误预测指示为预测错误)
- 从表项命中状态队列entry_hit_status中读取提交指令块是否命中
根据相关信息进行判断:
- 获取提交块的目标,如果commPtr等于newest_entry_ptr,则取newest_entry_target_modified拉高时记录下的newest_entry_target,否则取ftq_pc_mem.io.commPtrPlus1_rdata.startAddr,获取到的提交块目标将会被用来辅助新FTB项的生成
4.1 将子队列读取信息发向更新通道
整合完上述信息后,FTQ会向toBpu的update接口发送更新请求,具体如下:
- valid:canCommit 且 指令块满足命中或者存在cfi指令,valid接口有效,表明可以发送更新请求
- bits:
- false_hit:提交块命中状态指示为h_false_hit时,该信号拉高
- pc:提交块的取指信息中的startAddr
- meta:提交块的meta
- cfi_idx:提交块中cfi指令的index
- full_target:提交块的目标
- from_stage:提交块来自哪个预测阶段
- spec_info:提交块的meta
- pred_hit:提交块的命中状态为hit或者false_hit
另外,被更新的FTB表项也会同时被转发到更新接口,但是新的FTB表项生成方式相对复杂,下一节专门展开叙述
4.2 修正FTB项
更新结果会基于旧的FTB项进行更新,然后直接转发给更新接口。你可能需要先阅读FTB项相关文档了解FTB项的结构和相关信号生成方式
commit表项的相关信息会被发送给一个名为FTBEntryGen的接口,经过一系列组合电路处理,输出更新后的FTB表项信息。
为了更新FTB项,提交项如下信息会被读取:
- 取值目标中的起始地址 startAddr
- meta中旧FTB项 old_entry
- 包含FTQ项内32Byte内所有分支指令的预译码信息 pd
- 此FTQ项内有效指令的真实跳转结果 cfiIndex,包括是否跳转,以及跳转指令相对startAddr的偏移
- 此FTQ项内分支指令(如跳转)的跳转地址(执行结果)
- 预测时FTB是否真正命中(旧FTB项是否有效)
- 对应FTQ项内所有可能指令的误预测 mask
接下来介绍如何通过这些信息更新FTB。
FTB项生成逻辑:
4.2.1 情况1:FTB未命中,则创建一个新的FTB项
我们会根据预译码信息进行判断,预译码会告诉我们,指令块中cfi指令是否是br指令,jmp指令信息(以及是哪种类型的jmp指令)
- 无条件跳转指令处理:
- 不论是否被执行,都一定会被写入新FTB项的tailSlot
- 如果最终FTQ项内跳转的指令是条件分支指令,写入新FTB项的第一个brSlot(目前也只有这一个),对应的strongbias被设置为1作为初始化
- pftAddr设置:
- 存在无条件跳转指令时:以无条件跳转指令的结束地址设置
- 无无条件跳转指令时:以startAddr+取指宽度(32B)设置
- 特殊情况:当4Byte宽度的无条件跳转指令起始地址位于startAddr+30时,虽然结束地址超出取指宽度范围,仍按startAddr+32设置
- carry位根据pftAddr的条件同时设置
- 设置分支类型标志:
- isJalr、isCall、isRet按照无条件跳转指令的类型设置
- 特殊标志:当且仅当4Byte宽度的无条件跳转指令起始地址位于startAddr+30时,置last_may_be_rvi_call位
详细信号说明:
-
cfiIndex有效(说明指令块存在跳转指令),且pd的brmask指明该指令是br指令。则判断控制流指令是br指令
-
pd的jmpinfo有效,且cifIndx有效。则进一步根据jmpinfo判断是那种类型的jmp指令
- 第零位为0:jal
- 第零位为1:jalr
- 第一位为1:call
- 第二位为1:ret
-
判断最后一条指令是否是rvi(4byte)的jmp指令:jmpinfo有效,pd中jmpOffset等于15,且pd的rvcMask指明最后一条指令不是rvc指令
-
判断cfi指令是否是jal指令:cfiindx = jmpOffset,且根据之前的判断确认jmp指令是jal指令
-
判断cfi指令是jalr指令也是同理的。
-
FTB生成:valid被初始化为true
- brslot:在判断控制流指令是br指令时,进行填充
- valid:初始化为true
- offset:cfiindx
- lower和stat:根据startaddr和提交块指定的target计算
- 对应的strongbias:被初始化为true
- tailslot:pd的jmpinfo有效时,进行填充
- valid:根据之前的判断确认jmp指令是jal指令或者是jalr指令时,valid有效
- offset:pd的jmpoffset
- lower和stat:根据startaddr和target计算,如果cfi指令是jalr指令,使用提交块指定的target,否则用pd预测的jalTarget
- 对应的strongbias:根据之前的判断确认jmp指令是jalr指令时,拉高。strongbias是针对于BPU的ittage预测器的,该预测器基于一些统计信息工作,strongbias用来指向指令跳转偏好的强弱,其中jal指令不需要记录strongbias。
- pftAddr:上方介绍已经够详细了
- carry:上方介绍已经足够
- isJalr/isCall/isRet
- last_may_be_rvi_call
4.2.2 情况2:FTB命中,修改旧的FTB项
4.2.2.1 插入brslot的FTB项
在原来的基础上改动即可,比如插入新的slot,注意,只针对新的brslot
- 修改条件:首先根据oldftbentry判断在旧entry中,cfi指令是否被记录为br指令,如果不是,则说明这是一个新的br指令。
- 接着从旧FTB中判断哪些slot可以被插入slot:
- brslot:如果旧FTB的brslot无效,表示该slot空闲,此时可以在此位置插入新的brslot,此外,如果新slot在旧slot之前(新的br指令在旧slotbr指令之前执行,或者说在指令块之前的位置),即使不空也能插入
- tailslot:当不能在brslot插入时,才考虑tailslot,同样,在该slot空闲或者新slot在旧slot之前,可以插入此位置
- 插入slot:
- brslot:能插入时则在这里插入,不能的时候,把对应的strongbias拉低,因为这说明新slot一定在旧slot之后(如果不想要详细了解ittage的原理可以不用理解原因)。
- tailslot:能插入时则在这里插入,不能的时候,如果新slot在旧slot之后,把对应的strongbias拉低,如果不在之后,当原brslot有效(即不空闲),则用插入前的brslot代替该tailslot。对应的strongbias维持不变。
注:tailslot不能插入且新slot在其之前,其实就已经说明brslot一定是可以插入的,所以才有后面的替代
pftaddr
出现新的br指令,同时旧的FTB项内没有空闲的slot,这说明确实发生了在FTB项内确实发生了FTB项的替换,pftaddr也需要做相应的调整。
- 如果没有能插入的位置,使用新的br指令的偏移作为pftaddr对应的偏移,因为此时,新br指令一定在两个slot之后。否则,使用旧FTB项的最后一个slot的offset。将ptfoffset结合startAddr得到最后的pftAddr,carry也进行相应的设置。
- last_may_be_rvi_call,isCall,isRet ,isJalr全部置false。
4.2.2.2 修改jmp target的FTB项
修改条件:当cfi指令是一个jalr指令,且旧的tailslot对应的是一个jump的指令,但tailslot指示的target与提交项指示的target不同时,说明需要对跳转目标进行修改。
- 根据正确的跳转目标对lower和stat进行修改
- 两位strongbias设置成0
4.2.2.3 修改bias的FTB项
当cfi指令就是原FTB项的条件跳转指令,只需要根据跳转情况设置跳转的强弱
- brslot:旧的brslot有发生跳转时,bias在原bias拉高,发生跳转的cfiindex等于该slot的offset,brslot有效时,保持拉高,其余情况拉低。
- tailslot:旧的brslot没有跳转,而tailslot有分支指令且发生跳转,把brslot的bias置为false,tailslot保持bias的方式与上面的brslot一致。
修改条件:当旧的bias拉高且对应的旧的FTB项中的slot中有分支指令,同时修改后的bias拉低。任何一个slot出现这种情况都需要进行修改。
最后,需要抉择出一个修改的FTB项
- 如果cfi是一个新的分支指令,我们采用插入新的slot的FTB项。
- 如果是cfi是一个jalr指令,且跳转目标发生修改,我们采用修改jmp跳转目标的FTB项
- 如cfi指令就是原FTB项的条件跳转指令,采用修改bias的FTB项
4.3 发送新FTB项及相关信号
此时,根据是否hit,我们已经得到更新后的FTB项了,在这个基础上我们继续更新一些相关信号以发送到FTQ更新接口。
- new_br_insert_pos:使用之前我们判断的FTB项中可插入位置的bool数组
- taken_mask:根据cfi指令在更新后FTB项的位置判断,只有分支指令才做此计算,若是jmp指令置为0。
- jump_taken: cfi指令在更新后FTB项的taislot,且jmpValid。
- mispred_mask的最后一项:更新后的FTB项jumpValid,且预译码推断的jmp指令在提交项的错误预测信息中指示错误。
- mispred_mask 预测块内预测错误的掩码。第一、二位分别代表两个条件分支指令是否预测错误,第三位指示无条件跳转指令是否预测错误。
- 接口类型:
Vec(numBr+1, Bool())
- old_entry:如果hit,且FTB项不做任何修改,即不满足上述三种修改FTB项的条件,拉高该信号,说明更新后的FTB项是旧的FTB项。
发送处理后的更新信息
此时,我们就可以向BPU发送处理好的更新信息了,下面是update的接口接收的信号
- ftb_entry:更新后的FTB项
- new_br_insert_pos:上一小节已述
- mispred_mask:上一小节已述
- old_entry:上一小节已述
- br_taken_mask: 上一小节已述
- br_committed:根据提交项的提交状态信息判断新FTB项中的有效分支指令是否已经提交
- jmp_taken:上一小节已述
接口说明
顶层IO |
|
作用 |
toBpu |
|
向BPU发送重定向信息与更新信息 |
fromBackend |
|
获取指令交割信息,判断指令块是否被提交 |
mmioCommiRead |
|
发送mmio指令的提交信息 |
测试点总表 (【必填项】针对细分的测试点,列出表格)
实际使用下面的表格时,请用有意义的英文大写的功能名称和测试点名称替换下面表格中的名称
序号 |
功能名称 |
测试点名称 |
描述 |
1.1 |
TRANSFER_REDIRECT |
REDIRECT_FROM_FLUSH |
IFU重定向结果有效时,拉高该信号 |
1.2 |
TRANSFER_REDIRECT |
CHOOSE_REDIRECT |
如果后端重定向结果fromBackendRedirect有效,选用fromBackendRedirect,否则选用IFU重定向结果ifuRedirectToBpu |
2 |
UPDATE_STALL |
UPDATE_STALL |
当发生BPU的更新时候,会暂停FTQ对指令块的提交以及发送更新信息 |
3.1.1 |
CAN_COMMIT_ENTRY |
COND1 |
当commPtr不等于ifuWbPtr,且没有因为BPU更新而暂停,同时robCommPtr在commPtr之后,canCommit拉高 |
3.1.2 |
CAN_COMMIT_ENTRY |
COND2 |
commitStateQueue 中commPtr对应指令块有指令处于c_toCommit 或c_committed状态。且指令块中最后一条处于c_toCommit 或c_committed状态的指令是c_committed的,canCommit拉高 |
3.2.1 |
MOVECOMMPTR |
BY_ROB_COMMIT |
在commPtr指向的指令块如果能提交,可以移动CommPtr |
3.2.2 |
MOVECOMMPTR |
BY_FLUSH |
commitStateQueue 中commPtr对应指令块的第一条指令被后端重定向冲刷掉,可以移动CommPtr |
3.3.1 |
UPDATE_ROB_COMM_PTR |
COND1 |
当来自后端接口fromBackend的rob_commits信息中,有信息有效时,取最后一条有效交割信息的ftqIdx作为robCommPtr |
3.3.2 |
UPDATE_ROB_COMM_PTR |
COND2 |
不满足情况1,选取commPtr, robCommPtr中较大的那个 |
3.4.1 |
MMIO_LAST_COMMIT |
COND1 |
当commPtr比来自mmioCommitRead接口的mmioFtqPtr大时,mmioLastCommit信号在下一个周期被拉高 |
3.4.2 |
MMIO_LAST_COMMIT |
COND2 |
两者正好相等,且commPtr指向的指令块中有c_toCommit 或c_committed状态的指令,最后一条处于c_toCommit 或c_committed状态的指令是c_committed的,mmioLastCommit信号在下一个周期被拉高 |
4.1 |
SEND_UPDATE_TO_BPU |
SEND_SUBQUEUE_INFO_TO_UPDATE |
将提交项的子队列读取信息发向更新通道 |
4.2.1 |
UPDATE_FTB_ENTRY |
CREATE_NEW |
FTB未命中,创建一个新的FTB项 |
4.2.2.1 |
CREATE_NEW_FTB_ENTRY |
INSERT |
FTB未命中,创建一个新的FTB项,在原来的基础上改动即可,插入新的slot |
4.2.2.2 |
CREATE_NEW_FTB_ENTRY |
jmp target |
FTB未命中,创建一个新的FTB项,在原来的基础上改动即可,当cfi指令是一个jalr指令,且旧的tailslot对应的是一个jump的指令,但tailslot指示的target与提交项指示的target不同时,说明需要对跳转目标进行修改 |
4.2.2.3 |
CREATE_NEW_FTB_ENTRY |
bias |
FTB未命中,创建一个新的FTB项,在原来的基础上改动即可,当cfi指令就是原FTB项的条件跳转指令,只需要根据跳转情况设置跳转的强弱 |
4.3 |
SEND_UPDATE_TO_BPU |
SEND_NEW_FTB_RELATED |
根据是否hit,我们已经得到更新后的FTB项了,在这个基础上我们继续更新一些相关信号以发送到FTQ更新接口。 |