IFU简介
IFU(Instruction Fetch Unit),取指令单元,负责从内存或ICache取出指令,经过预译码、扩展RVC和预检之后,将指令交给后续译码器进行进一步的译码。
IFU的子模块包括PreDecode,F3PreDecoder,RVCExpander,PredChecker和FrontendTrigger。
以下是IFU的架构简图:
IFU功能介绍
XS_IFU流水级划分
香山的IFU一共分为5个阶段。
F0阶段:接收FTQ请求,同时告诉FTQ自己已经ready了
F1阶段:从FTQ请求中先计算出每个指令的pc,half_pc、cut_ptr(这是后续将icache返回的指令码进行切分的依据)
F2阶段:从icache获取响应数据(缓存行)并校验,提取出异常信息(包括页错误、访问错误、mmio信息);生成预测到的指令范围(但这并不是一个数字,而是一个用多位表示的bool数组,该位为1表示这一指令在预测块范围内);从缓存行中,利用上一阶段求出的cut_ptr切分出17×2的初步指令码,最后进行预译码和指令扩展。
F3阶段:这一阶段主要是对译码阶段的结果进行预检查,以及MMIO状态下的处理逻辑。
WB(写回)阶段:将预检查的结果写回FTQ,并向IBuffer写指令码和前端信息。
接收FTQ取指令请求(F0流水级)
在F0流水级,IFU接收来自FTQ以预测块为单位的取指令请求。请求内容包括预测块起始地址、起始地址所在cache line的下一个cache line开始地址、下一个预测块的起始地址、该预测块在FTQ里的队列指针、该预测块有无taken的CFI指令(控制流指令)和该taken的CFI指令在预测块里的位置以及请求控制信号(请求是否有效和IFU是否ready)。每个预测块最多包含32字节指令码,最多为16条指令。IFU需要置位ready驱动FTQ向ICache发送请求。
指令切分产生初始指令码(F1、F2流水级)
F0流水级时,FTQ同时会向ICache发送取缓存行的指令。这是ICache在其S2流水级需要返回的,所以IFU在F2流水级才会得到ICache返回的缓存行。在此之前,IFU会在F1流水线先进行PC的计算,以及计算切分缓存行的指针。
进入F2流水级,IFU将会针对每个指令,取出对应的异常信息、物理地址、客户物理地址等。同时,根据FTQ的taken信息,IFU将会计算该预测块在无跳转和跳转发生情况下的有效指令范围。无跳转情况下的指令有效范围ftr_range即当前预测块从起始地址到下一个预测块的起始地址的差值。有跳转情况下的指令有效范围jump_range即当前预测块的起始地址到预测块中第一个跳转指令地址的差值。
最后,IFU需要从缓存行和上一流水级计算的指针,完成对17x2字节初始指令码的拼接。这里的拼接代码可能存在一些迷惑性
val f2_cache_response_data = fromICache.map(_.bits.data)
val f2_data_2_cacheline = Cat(f2_cache_response_data(0), f2_cache_response_data(0))
在调用cut之前,我们先是从ICache获取了缓存行(ICache返回的缓存行种类已经在ICache中进行了分类讨论,IFU中直接使用即可),然后将第0个缓存数据进行了拼接, 这一操作的原因来自于ICache中对数据的细粒度拆分:
fetch block可能跨缓存行,但是由于fetch block最大只有34B,如果将两个缓存行(2x64B)都传送给IFU则显得浪费,因此,fetch block的选择由ICache完成。
ICache返回给IFU的并不是直接的预测块,而是带有跨缓存行信息的64字节。
我们将每个缓存行分为8份,如下所示:
cacheline 0: |0-7|0-6|0-5|0-4|0-3|0-2|0-1|0-0|
cacheline 1: |1-7|1-6|1-5|1-4|1-3|1-2|1-1|1-0|
如果fetch block的起始位置为0-1,则必定不跨缓存行。
如果fetch block的起始位置为0-6,那么fetch block的位置为0-6~1-2,此时传送的缓存行结构如下:
cacheline 0: |0-7|0-6|xx|xx|xx|1-2|1-1|1-0|
由此,只要将该缓存行复制一遍,即可获得拼接后的fetch block。
综上所述,对这两种情况,我们都只需要把返回的cacheline复制一份拼接在一起,从中间截取就可以拿到数据。
预译码(F2流水级,主要由PreDecode模块完成)
在F2流水级,我们需要将上一步完成切分的指令码交给PreDecode子模块,他的作用主要有二:
其一是生成预译码信息,包括该指令是否是有效指令的开始、是否是RVC指令、是否是CFI指令、CFI指令类型(branch/jal/jalr/call/ret)、CFI指令的目标地址计算偏移等。输出的预译码信息中brType域的编码如下:
CFI指令类型 | brType类型编码 |
---|---|
非CFI | 00 |
branch指令 | 01 |
jal指令 | 10 |
jalr指令 | 11 |
其二是将初始指令码两两组合之后,得到16x4字节的指令码(从起始地址开始,2字节做地址递增,地址开始的4字节作为一条32位初始指令码)。
此外,预译码阶段还需要分类讨论,得出两种指令有效向量(起始指令是不是RVI指令的后半部分),并交给IFU进行判断,可以参阅后面的跨预测块32位指令处理部分
其他功能和详细内容参见PreDecode子模块的描述。
指令扩展(F3流水级)
这一部分将从PreDecode返回的16条指令码分别送交指令扩展器进行32位指令扩展(RVI保持不变, RVC指令根据手册的规定进行扩充)。
但是,如果指令非法,需要向IBuffer写入原始指令码。
预测错误预检查(F3流水级,主要由PreChecker子模块完成)
这一功能是为了将一些不依赖于执行结果的预测错误在早期就发现出来。这一阶段检查五类错误:
jal类型错误:预测块的范围内有jal指令,但是预测器没有对这条指令预测跳转;
ret类型错误:预测块的范围内有ret指令,但是预测器没有对这条指令预测跳转;
无效指令预测错误:预测器对一条无效的指令(不在预测块范围/是一条32位指令中间)进行了预测;
非CFI指令预测错误:预测器对一条有效但是不是CFI的指令进行了预测;
转移目标地址错误:预测器给出的转移目标地址不正确。
在预检查的最后将会修正之前预测的各个指令的跳转情况。同时,如果存在jal或者ret类型预测错误,还将修正fixedRange——这是指令有效范围向量,可以看作一个bool数组,其中某一位为1也就是对应的指令在这一范围内。
前端重定向(WB阶段)
如果在预测错误预检查的部分发现了上述的五类错误,那么需要在写回阶段产生一个前端重定向将F3以外的流水级进行冲刷, 从而让BPU能够从正确路径重新开始预测。
还有一种情况下需要冲刷流水线。在下一节中,如果误判了当前预测块的最后2B为RVI指令的上半部分,则也需要冲刷当前预测块F3之前的流水级。
跨预测块32位指令处理
因为预测块的长度有限制,因此存在一条RVI指令前后两字节分别在两个预测块的情况。IFU首先在第一个预测块里检查最后2字节是不是一条RVI指令的开始,如果是并且该预测块没有跳转,那么就设置一个标识寄存器f3_lastHalf_valid,告诉接下来的预测块含有后半条指令。在F2预译码时,会产生两种不同的指令有效向量:
预测块起始地址开始即为一条指令的开始,以这种方式根据后续指令是RVC还是RVI产生指令有效向量
预测块起始地址是一条RVI指令的中间,以起始地址 + 2位一条指令的开始产生有效向量
在F3,根据是否有跨预测块RVI标识来决定选用哪种作为最终的指令有效向量,如果f3_lastHalf_valid为高则选择后一种(即这个预测块第一个2字节不是指令的开始)。IFU所做的处理只是把这条指令算在第一个预测块里,而把第二个预测块的起始地址位置的2字节通过改变指令有效向量来无效掉。
将指令码和前端信息送入IBuffer(F3流水级)
F3流水级最终得到经过扩展的32位指令码(或者对于非法指令直接传递原始指令码),以及16条指令中每条指令的例外信息、 预译码信息、FTQ队列中的指针位置、其他后端需要的信息(比如经过折叠的PC)等。IFU除了常规的valid-ready控制信号外, 还会给IBuffer两个特殊的信号:一个是16位的io_toIbuffer_bits_valid(因为我们最后组合出来的指令也是16条, 所以这里每一位刚好也对应一个指令的状态,为1说明是一条指令的开始,为0则是说明是一条指令的中间),标识预测块里有效的指令。 另一个是16位的io_toIbuffer_bits_enqEnable,这个在io_toIbuffer_bits_valid的基础上与上了被修正过的预测块的指令范围fixedRange。 enqEnable为1表示这个2字节指令码是一条指令的开始且在预测块表示的指令范围内。
除此之外,异常信息也需要写给IBuffer。
注意一个特例:当且仅当发生guest page fault时,后端需要gpaddr信息,为了节省面积,gpaddr不走正常通路进入ibuffer, 而是随ftqPtr被发送到gpaMem,后端需要时从gpaMem读出。IFU需要保证gpf发生时通向gpaMem的valid拉高、gpaddr正确。
分支预测overriding冲刷流水线
当FTQ内未缓存足够预测块时,IFU可能直接使用简单分支预测器提供的预测地址进行取指,这种情况下,当精确预测器发现简单预测器错误时,需要通知IFU取消正在进行的取指请求。具体而言,当BPU的S2流水级发现错误时,需要冲刷IFU的F0流水级;当BPU的S3流水级发现错误时,需要冲刷IFU的F0/F1流水级(BPU的简单预测器在S1给出结果,最晚在S3进行overriding,因此IFU的F2/F3流水级一定是最好的预测,不需要冲刷;类似地,不存在BPU S2到IFU F1的冲刷)。
IFU在收到BPU发送的冲刷请求时,会将F0/F1流水级上取指请求的指针与BPU发送的冲刷请求的指针进行比较,若冲刷的指针在取指的指针之前,说明当前取指请求在错误的执行路径上,需要进行流水线冲刷;反之,IFU可以忽略BPU发送的这一冲刷请求。此外,比较的时候还需要注意flag的情况,flag是一个指示队列循环的指针,flag不同即在不同的“圈”上,此时反而是idx的值更小,ftqIdx才会更大。
指令信息和误预测信息写回FTQ(WB阶段)
在F3的下一级WB级,IFU将指令PC、预译码信息、错误预测指令的位置、正确的跳转地址以及预测块的正确指令范围等信息写回FTQ,同时传递该预测块的FTQ指针用以区分不同请求。
同时,正如前面提到的,IFU检测到预测错误时会进行前端冲刷,同样地,FTQ也需要据此进行冲刷,因此,这也是IFU写回错误信息的意义——可以辅助FTQ判断是否冲刷流水线。
MMIO处理逻辑
在处理器上电复位时,内存还没有准备好,此时需要从Flash中取指令执行。 这种情况下需要IFU向MMIO总线发送宽度为64位的请求从flash地址空间取指令执行。同时IFU禁止对MMIO总线的推测执行,即IFU需要等到每一条指令执行完成得到准确的下一条指令地址之后才继续向总线发送请求。
这之后,根据FTQ中的指令地址,决定是否MMIO取指令。
以上是MMIO状态机的简图。在一开始,处于m_idle状态,如果处于mmio请求的场景,则转换到 m_waitLastCmt,之后只要之前所有的指令都已完成提交——或者这一指令就是第一条指令, 则进入m_sendReq状态将请求发送到InstrUncache模块,发送完成后进入m_waitResp状态。 接收请求后,由于MMIO总线的带宽限制为64位, 因此存在一条指令一次MMIO请求不能取得完整指令码的情况(这是由于MMIO并不支持非对齐访问,具体地说,如果RVI指令的起始地址的[2, 1]两位为b11,则64位总线无法一次传递所有指令),所以需要增加地址进行重发,进入m_sendTLB状态, 再次查询ITLB,如果tlb的pbmt状态和上一条的存在差别,则为访问异常,综合TLB自身的异常结果和根据pbmt判定的访问异常,如果存在异常,则直接把指令和异常信息发到Ibuffer,进入等待,否则进入m_sendPMP状态,向PMP发送请求, 这里需要查看pmp_recheck的结果,如果该请求的mmio状态和上一条的mmio状态不一致,那么说明可能存在访问错误,置为访问异常,否则根据PMP的回复结果决定是否存在PMP异常。 如果存在异常,则将报错信息发送给Ibuffer,直接进入等待。 一切正常的话,进入m_resendReq状态,重新发送请求到InstrUncache模块。 无论是否重发,最后获得完整数据之后,根据地址从64位数据中截取指令码。并以每个预测块一条指令的形式(相当于只有起始地址的指令)发送到IBuffer。 接下来进入m_waitCommit状态等待,直到ROB返回指令已提交的信号即进入m_commited状态,CFI指令由后端发送给FTQ进行冲刷,而顺序指令则由IFU复用前端重定向通路刷新流水线, 同时复用FTQ写回机制,把它当作一条错误预测的指令进行冲刷,重定向到该指令地址 + 2或者+4(根据这条指令是RVI还是RVC选择)
对于跨缓存行预测块,他们的mmio和pbmt状态应当等同。不匹配的错误应当只在后一个缓存行报告。
此外,如果当前pbmt为nc,则会跳过waitLastCmt和waitCommit状态。因为这些内存空间是幂等的,所以可以进行推测性取指。
Trigger实现对于PC的硬件断点功能
该工作主要由FrontEndTrigger子模块完成。
IFU接口说明
为方便测试开展,需要对IFU的接口进行进一步的说明,以明确各个接口的含义。
FTQ交互接口
编译后可用的接口包括:
req FTQ取指请求
req是FTQ向IFU的取指令请求,编译后包含以下成员:
接口名 | 解释 |
---|---|
ftqIdx | 指示当前预测块在FTQ中的位置。 |
ftqOffset | 指示预测块的大小 |
startAddr | 当前预测块的起始地址。 |
nextlineStart | 起始地址所在cacheline的下一个cacheline的开始地址。 |
nextStartAddr | 下一个预测块的起始地址 |
redirect FTQ重定向请求
FTQ会向IFU发送重定向请求,这通过fromFtq.redirect完成,从而指示IFU应该冲刷的内容。
编译后,redirect包含以下接口成员:
接口名 | 解释 |
---|---|
ftqIdx | 需要冲刷的ftq预测块序号,包含flag和value两个量。 |
level | 重定向等级 |
ftq_offset | ftq预测块中跳转指令的位置 |
此外,还有valid变量指示是否需要重定向。
fromBPUFlush
来自IFU的冲刷请求,这是预测错误引起的,包括s3和s2两个同构成员,指示是否在BPU的s3和s2流水级发现了问题,s3的详细结构如下
接口名 | 解释 |
---|---|
valid | 是否存在s3流水级冲刷要求 |
ftqIdx | s3流水级请求冲刷的预测块的指针 |
toFtq_pdWb 写回
接口名 | 解释 |
---|---|
cfioffset | 经由PredChecker修复的跳转指令的预测位置。但经过编译后,cfioffset的数值已经被优化了,只剩下了cfioffset_valid表示是否存在编译优化。 |
ftqIdx | 表明预测块在FTQ中的位置,这条信息主要是对FTQ有用,要和FTQ传入的请求保持一致。 |
instrRange | 可以看作是一个bool数组,表示该条指令是不是在这个预测块的有效指令范围内(第一条有效跳转指令之后的指令)。 |
jalTarget | 表明该预测块跳转指令的跳转目标。 |
misOffset | 表明错误预测的指令在预测块中的位置。 |
pc | 预测块中所有指令的PC指针。 |
pd | 每条指令的预测信息,包括CFI指令的类型、isCall、isRet和isRVC。 |
target | 该预测块最后一条指令的下一条指令的pc。 |
ICache交互接口
控制信号
接口名 | 解释 |
---|---|
icache_ready | ICache通知IFU自己已经准备好了,可以发送缓存行了。 |
icache_stop | IFU在F3流水级之前出现了问题,通知ICache停下。 |
ICacheInter.resp ICache传送给IFU的信息
接口名 | 解释 |
---|---|
data | ICache传送的缓存行。 |
doubleLine | 指示ICache传来的预测块是否跨缓存行。 |
exception | ICache向IFU报告每个缓存行上的异常情况,方便ICache生成每个指令的异常向量。 |
backendException | ICache向IFU报告后端是否存在异常 |
gpaddr | 客户页地址 |
isForVSnonLeafPTE | 是否为非叶的PTE,这个数据最终会流向写回gpaddrMem的信号 |
itlb_pbmt | ITLB基于客户页的内存类型,对MMIO状态有用 |
paddr | 指令块的起始物理地址 |
vaddr | 指令块起始虚拟地址、指令块起始物理地址 |
pmp_mmio | 指示当前指令块是否在MMIO空间 |
性能相关接口
ICachePerf和perf,可以先不关注。
ITLBInter
该接口仅在MMIO状态下,IFU重发请求时活跃。
req IFU向ITLB发送的请求
这是IFU向ITLB发送的查询请求,只有一个量:bits_vaddr,传递需要让ITLB查询的虚拟地址。
resp ITLB返回给IFU的查询结果
这是ITLB返回给IFU的查询结果,包含如下接口:
接口名 | 解释 |
---|---|
excp | 指令的异常信息,包含三个量:访问异常指令af_instr、客户页错误指令gpf_instr、页错误指令pf_instr |
gpaddr | 客户页地址 |
isForVSnonLeafPTE | 指示传入的是否是非叶PTE |
paddr | 指令物理地址 |
pbmt | 指令的基于页的内存类型 |
UncacheInter
该接口在MMIO状态下活跃,负责接收IFU并返回指令码。
toUncache
这是IFU向Uncache发送的请求,除了ready和valid以外,还传送了一个48位数据,即需要获取的指令的物理地址。
fromUncache
这是Uncache给IFU的回复,除了valid以外,还传送一个32位数据,即指令码(可为RVC或RVI指令)
toIbuffer
IFU通过这个接口向Ibuffer写入取指结果。包含以下成员:
接口名 | 解释 |
---|---|
backendException | 是否存在后端异常 |
crossPageIPFFix | 表示跨页异常向量 |
valid | 和一般意义上的valid相区别,表示每条指令是否是合法指令的开始(RVI指令的上半条或者RVC指令) |
enqable | 对每条指令,其为valid并且在预测块的范围内 |
exceptionType | 每个指令的异常类型 |
foldpc | 压缩过后的pc |
ftqOffset | 指令是否在预测块范围中 |
ftqPtr | ftq预测块在FTQ的位置 |
illegalInstr | 这条指令是否为非法指令 |
instrs | 拼接后的指令码 |
isLastInFtqEntry | 判断该指令是否为这个预测块中最后一条有效指令的开始 |
pd | 指令控制信息,包括CFI指令的类型和RVC指令的判定 |
triggered | 指令是否触发前端的trigger |
toBackend_gpaddrMem
这组接口在gpfault发生时使用,由IFU向gpaddrMem传递预测块指针和页错误地址。
接口名 | 解释 |
---|---|
waddr | 传递ftq指针 |
wdata.gpaddr | 传递出错的客户页地址 |
wdata.isForVSnonLeafPTE | 指示是否为非叶PTE |
wen | 类似valid,指示gpaddrMem存在gpfault需要处理 |
io_csr_fsIsOff
指示是否使能了fs.CSR,对非法指令的判断很关键。
rob_commits 来自ROB的提交信息
共分为8个相同结构的rob_commit,包含以下成员
接口名 | 解释 |
---|---|
ftqIdx | 预测块指针 |
ftqOffset | 预测块的大小 |
pmp
和物理内存保护相关,在mmio状态下重发请求时使用。
req
IFU向pmp发起的请求,传递前一步从ITLB查询得到的物理地址。
resp
PMP给IFU的回复结果,包含以下成员
接口名 | 解释 |
---|---|
mmio | 在MMIO空间 |
instr | 对指令的判断结果,当指令不可执行时,该值为true |
mmio_commits
mmioFtqPtr
IFU传递给FTQ的idx,用于查询上一个预测块的MMIO状态
mmioLastCommit
上一个请求是MMIO请求
frontendTrigger
用于设置前端断点
包含以下成员:
debugMode
debug的模式
triggerCanRaiseBpExp
trigger是否可以引起断点异常
tEnableVec
信号数组,表示是否使能对应的trigger
tupdate
表示更新的断点信息,其中包含tdata和addr,addr是请求设置的断点idx。
tdata包括下列成员:
接口名 | 解释 |
---|---|
matchType | 断点匹配类型,等于、大于、小于 |
action | 触发执行的动作 |
tdata2 | 触发端点的基准数值 |
select | 是否选择 |
chain | 是否传导 |
IFU功能点和测试点
功能点1 接收FTQ预测块请求
功能点1.1 F0流水级接收请求
向FTQ报告自己已ready。
所以,我们只需要在发送请求后检查和ftq相关的的ready情况即可。
序号 | 名称 | 描述 |
---|---|---|
1 | ready置位 | IFU接收FTQ请求后,设置ready |
功能点2 指令切分产生初始指令码
功能点2.1 F1流水级计算信息和切分指针
F1流水级也会计算PC。
同时还需要生成17位的切分指针(也就是从拼接后的缓存行切出初始指令码的idx数组,在昆明湖架构中,计算方式为拼接00和startAddr[5:1], 然后分别与0~16相加) 用于后续从缓存行提取初始指令码。
所以,首先我们需要检查F1流水级生成的PC的正确与否。如果可能,也需要检查一下切分指针的生成。
所以,可以总结出以下的测试点:
序号 | 名称 | 描述 |
---|---|---|
2.1.1 | PC生成 | IFU接收FTQ请求后,在F1流水级生成PC |
2.1.2 | 切取指针生成 | IFU接收FTQ请求后,在F1流水级生成后续切取缓存行的指针 |
功能点2.2 F2流水级获取指令信息
包括获取异常信息、物理地址、客户物理地址、是否在MMIO空间等。
获取异常信息之后,还需要计算异常向量。ICache会为每个缓存行返回一个异常类型,只需要计算每个指令pc属于哪个缓存行, 然后将对应缓存行的异常类型赋给该位置即可。
所以,只需要分别检查几种指令信息即可。
序号 | 名称 | 描述 |
---|---|---|
2.2.1 | 异常向量生成 | IFU接收ICache内容后,会根据ICache的结果生成属于每个指令的异常向量 |
2.2.2 | 物理地址提取 | IFU接收ICache内容后,会根据ICache的结果生成属于每个端口的物理地址。 |
2.2.3 | 客户物理地址提取 | IFU接收ICache内容后,会根据ICache的结果生成0号端口的客户物理地址。 |
2.2.4 | MMIO空间信息提取 | IFU接收ICache内容后,会根据ICache的结果判断当前取指请求是否属于MMIO空间。 |
功能点2.3 F2流水级计算预测块有效指令范围
指令有效范围包括两种,无跳转和有跳转的
无跳转的指令有效范围为当前预测块从起始地址到下一个预测块的起始地址的所有指令。
有跳转的指令有效范围jump_range为当前预测块的起始地址到预测块中第一个跳转指令地址(包含第一个跳转指令地址)之间的所有指令。
最终的指令有效范围是两者相与的结果。
序号 | 名称 | 描述 |
---|---|---|
2.3.1 | 无跳转指令有效范围生成 | IFU根据FTQ请求,计算无跳转指令有效范围 |
2.3.2 | 有跳转指令有效范围生成 | IFU根据FTQ请求,计算跳转指令有效范围 |
功能点2.4 提取初始指令码
IFU需要将ICache返回的缓存行复制一份并拼接。然后利用上一流水级计算的idx数组,从缓存行提取17x2字节的初始指令码。
序号 | 名称 | 描述 |
---|---|---|
2.4 | 切取初始指令码 | IFU根据上一流水级的切取指针,从缓存行提取初始指令码。 |
功能点3 预译码
多数的功能都由preDecoder子模块完成,因此这里只罗列由IFU本身需要完成的功能。
功能点3.1 F3流水级选取指令有效向量
由于存在跨缓存行的32位指令,IFU需要做的是,根据上一个预测块最后两字节是否为一条RVI指令的开始,从两种指令有效开始向量中,选择一种。
序号 | 名称 | 描述 |
---|---|---|
3.1.1 | 上一预测块结尾为RVC或RVI下半部分 | 上一预测块的最后2字节恰为RVC指令或RVI指令的后半部分,选择第一位为True的有效开始向量 |
3.1.2 | 上一预测块结尾为RVI上半部分 | 上一预测块的最后2字节为RVI,选择第一位为False的有效开始向量 |
功能点4 指令扩展
将PreDecode返回的16条指令码分别送交指令扩展其进行32位指令扩展,RVI保持不变。RVC指令根据手册规定进行扩展。 如果指令非法,则需要将原始指令填写到CSR(控制状态寄存器)中。IFU自身仅仅控制最后填写的是哪种指令。
所以,我们对IFU模块,只关注最后写的指令是何种指令。注意,这里可以修改fsIsOff的入参,测试c.fp指令是否返回原始指令码
序号 | 名称 | 描述 |
---|---|---|
4.1 | 合法RVC指令写扩展指令码 | 对合法RVC指令,写扩展后的指令码 |
4.2 | 非法RVC指令写原始指令码 | 对非法RVC指令,写原始指令码 |
4.3 | RVI指令不扩展 | RVI指令直接写入原始指令即可 |
功能点5 预测错误预检
主要由PredChecker子模块完成。测试点和PredChecker子模块的测试点类似,IFU没有额外的测试点。预检可以和重定向一起测试。
功能点6 前端重定向和流水线冲刷
功能点6.1 预测错误重定向
如果发现了预检阶段检出的错误,则需要产生前端重定向,将F3以外的流水级冲刷
只需要构造有预测错误的预测请求,检查冲刷情况即可。
序号 | 名称 | 描述 |
---|---|---|
6.1.1 | JAL预测错误冲刷 | 预测请求中存在JAL预测错误,需要冲刷流水线 |
6.1.2 | RET预测错误冲刷 | 预测请求中存在RET预测错误,需要冲刷流水线 |
6.1.3 | 非CFI预测错误冲刷 | 预测请求中存在非CFI预测错误,需要冲刷流水线 |
6.1.4 | 无效指令预测错误冲刷 | 预测请求中存在无效指令预测错误,需要冲刷流水线 |
6.1.5 | 跳转目标错误冲刷 | 预测请求中存在跳转目标错误,需要冲刷流水线 |
功能点6.2 跨预测块32位指令处理
如果发现当前预测块的最后两个字节是一条RVI指令的开始,则设置一个标识f3_lastHalf_valid,告诉接下来的预测块含有后半条指令。
我们没有办法直接观察到这个标识,但是可以通过下一预测块来判定:
序号 | 名称 | 描述 |
---|---|---|
6.2.1 | 跨预测块32位指令处理 | 连续传入两个预测块,其中有一条32位指令跨两个预测块,后一个预测块的指令开始向量的首位应该为False |
但是,如果这一判断出现问题(比如当前预测块存在跳转),则需要进行流水线冲刷。
这一功能需要PredChecker子模块“配合”(仅仅通过外部IO的修改很难触发这个防御机制),实现起来比较麻烦,但是还是列举一个测试点:
序号 | 名称 | 描述 |
---|---|---|
6.2.2 | 跨预测块指令误判 | 当IFU根据PredChecker修复的指令有效范围错判了跨预测块指令时,需要将F3以外的流水级全部冲刷 |
功能点7 将指令码和前端信息输出给IBuffer
功能点7.1 传送指令码和前端信息
传送给IBuffer的信息包括:经过扩展的32位指令码、16条指令中每条指令的例外信息、预译码信息、FTQ队列中的指针位置、其他后端需要的信息(经过折叠的PC)、 io_toIbuffer_bits_valid(表示指令是否是一条指令的开始)、io_toIbuffer_bits_enqEnable(前者与上被修正过的预测块指令范围, 从而还能表示指令是否在预测块表示的指令范围内)。
这里要做的只是确认这些信息是否正确传递
序号 | 名称 | 描述 |
---|---|---|
7.1.1 | 指令码传送 | IFU向IBuffer传送扩展后的指令码 |
7.1.2 | 异常信息传送 | IFU向IBuffer传送每个指令的异常信息 |
7.1.3 | 预译码信息传送 | IFU向IBuffer传递每个指令的预译码信息 |
7.1.4 | FTQ指针传送 | IFU向IBuffer传送FTQ预测块的指针 |
7.1.5 | 折叠PC传送 | IFU向IBuffer传送折叠的PC |
7.1.6 | 有效开始向量 | IFU向IBuffer传送表示指令有效和指令是否为指令开始的向量 |
功能点7.2 客户页错误传送gpaddr信息
当且仅当发生guest page fault时,后端需要gpaddr信息,为了节省面积,gpaddr不走正常通路进入ibuffer, 而是随ftqPtr被发送到gpaMem,后端需要时从gpaMem读出。IFU需要保证gpf发生时通向gpaMem的valid拉高、gpaddr正确,同时还要传递预测块的ftqIdx(通过waddr传入)。
这里我们只需要确保在客户页错误发生时通向gpaMem的valid为高,且gpaddr正确填入。
序号 | 名称 | 描述 |
---|---|---|
7.2 | 客户页错误 | 客户页错误发生时,IFU应将gpaMem的valid拉高且填入gpaddr |
功能点8 分支预测冲刷流水线
当精确预测器发现简单预测器错误时,通知IFU取消正在进行的取指请求。
功能点8.1 核验指针
IFU收到BPU冲刷请求后,会将F0/F1流水级上取指令请求的指针比较,冲刷的指针在取指之前,即当前取指令请求在错误的执行路径上,才需要 冲刷IFU。
我们仍然需要从两个方向校验这个功能,即当冲刷指针在取指令的指针之前时,IFU能够对流水线进行冲刷。 然而,当冲刷指令在取指令的指针之后时,则不能对流水线进行冲刷。
序号 | 名称 | 描述 |
---|---|---|
8.1.1 | 错误执行路径 | 当冲刷指针在取指令的指针之前时,IFU能够对流水线进行冲刷。 |
8.1.2 | 执行路径无误 | 当冲刷指令在取指令的指针之后时,IFU不能对流水线进行冲刷。 |
功能点8.2 BPU S2流水级发现错误
BPU的S2流水级发现错误时,需冲刷IFU的F0流水级
序号 | 名称 | 描述 |
---|---|---|
测试点8.2 | BPU S2流水级发现错误 | 当BPU的S2流水级出现错误,并且当前取指指针在错误执行路径上时,需要对IFU的F0流水级进行冲刷 |
功能点8.3 BPU S3流水级发现错误
BPU的S3流水级发现错误时,需要冲刷IFU的F0和F1流水级
序号 | 名称 | 描述 |
---|---|---|
8.3 | BPU S3流水级发现错误 | 当BPU的S3流水级出现错误,并且当前取指指针在错误执行路径上时,需要对IFU的F0和F1流水级进行冲刷 |
功能点9 指令信息和误预测信息写回FTQ
功能点9.1 写回指令信息和误预测信息
将指令PC、预译码信息、错误预测指令的位置、正确的跳转地址以及预测块的正确指令范围等信息写回FTQ,并传递该预测块的FTQ指针。
序号 | 名称 | 描述 |
---|---|---|
9.1.1 | 写回指令PC | IFU的WB流水级,需要向FTQ写回指令PC |
9.1.2 | 写回预译码信息 | IFU的WB流水级,需要向FTQ写回每个指令的预译码信息 |
9.1.3 | 写回误预测指令位置 | IFU的WB流水级,需要向FTQ写回BPU错误预测的指令位置 |
9.1.4 | 写回正确跳转地址 | IFU的WB流水级,需要向FTQ写回该预测块的正确跳转地址 |
9.1.5 | 写回正确指令范围 | IFU的WB流水级,需要向FTQ写回预测块的正确指令范围 |
9.1.6 | 传递预测块FTQ指针 | IFU的WB流水级,需要向FTQ传递预测块的FTQ指针 |
功能点10 MMIO处理
功能点10.1 上电复位处理
处理器上电复位时,IFU需向MMIO总线发送宽度为64位的请求从flash地址空间取指令,并禁止对MMIO总线的推测执行。
上电的情况和正常情况其实没有任何区别,但是,上电时的MMIO请求没有任何差别,只是,第一条请求一定是MMIO,并且不需要等待。
序号 | 名称 | 描述 |
---|---|---|
10.1 | 第一条MMIO指令 | IFU收到的第一条MMIO请求可以直接查询Instr Uncache |
功能点10.2 向InstrUncache发送请求
在正常的处理逻辑下,如果请求地址处于MMIO地址空间,则IFU会向FTQ查询指令提交状态,IFU需要等待当前请求之前的所有请求(包括MMIO和非MMIO)提交完成, 才能向InstrUncache模块发送请求。
这里需要和FTQ交互,可以让FTQ模拟请求提交情况,从而测试等待情况。 如果MMIO请求之前的请求都已经提交,则也不需要等待。反之,则需要一直等待直到查询结果表明前面的指令均已提交。故设计测试点如下:
序号 | 名称 | 描述 |
---|---|---|
10.2.1 | 阻塞等待提交 | IFU收到MMIO请求后,查询FTQ,如果前面还有尚未提交的指令,持续等待 |
10.2.2 | 无阻塞发送请求 | 如果查到FTQ不再有未提交的指令,则IFU将指令发送给Instr Uncache |
功能点10.3 跨总线请求处理
由于MMIO不支持非对齐访问,因此当检测到的RVI指令地址[2,1]两位为b11时,64位总线无法一次传递所有指令,所以需要增加地址进行重发,再次查询ITLB。
序号 | 名称 | 描述 |
---|---|---|
10.3.1 | 重发查询ITLB | 遇到一次无法查询完毕的RVI指令时,需要向ITLB查询获得新增指令的物理地址 |
如果存在异常,则直接将指令和异常信息发送到IBuffer并等待,否则向PMP发送请求。
序号 | 名称 | 描述 |
---|---|---|
10.3.2.1 | ITLB异常 | IFU查询ITLB出现异常时,应当将异常信息发送到IBuffer,然后等待ROB提交完成 |
10.3.2.2 | ITLB返回物理地址 | IFU查询ITLB正常返回物理地址时,IFU继续向PMP请求检查 |
根据pmp_recheck的结果,如果和上一次请求状态不一致,则说明存在访问错误, 为访问异常,不然则根据PMP的回复结果决定是否存在异常。如存在异常(访问异常和其他异常),则将报错信息发送给IBuffer并等待。如无异常,重新向InstrUncache模块 发送请求。
序号 | 名称 | 描述 |
---|---|---|
10.3.3.1 | 请求状态不一致 | IFU检查PMP之后如果发现重发请求状态和上一条请求状态不一致,是访问异常,需要将异常直接发送到IBuffer |
10.3.3.2 | PMP检查异常 | PMP检查出现异常的情况下,也需要将异常直接发送到IBuffer并等待ROB提交。 |
10.3.3.3 | Instr Cache请求重发 | PMP检查若无异常,则向Instr Uncache发送请求获取指令码的后半部分。 |
功能点10.4 向IBuffer发送指令
IFU获得完整数据之后,根据地址从64位数据中截取指令码,并以每个预测块一条指令的形式发送到Ibuffer。等待ROB返回指令已提交的信号。
序号 | 名称 | 描述 |
---|---|---|
10.4 | 向IBuffer发送指令 | IFU在获得完整数据后,截取获得指令码,以每个预测块一条指令的形式发送给IBuffer |
功能点10.5 指令冲刷
CFI指令的冲刷由后端发送给FTQ完成。所以不需要在这里设置测试点。
顺序指令由IFU复用前端重定向通路刷新流水线,并复用FTQ写回机制,将该指令当作误预测指令冲刷,重定向到+2或+4的位置。
+2和+4是由RVC和RVI指令决定的,所以设置测试点如下:
序号 | 名称 | 描述 |
---|---|---|
10.5.1 | RVI指令重定向 | 如果是RVI指令,传递给FTQ的冲刷请求应该重定向到PC+4 |
10.5.2 | RVC指令重定向 | 如果是RVC指令,传递给FTQ的冲刷请求应该重定向到PC+2 |
功能点11 硬件断点
该功能主要由FrontEndTrigger子模块完成。不需要为这一功能额外设置测试点(参照FrontendTrigger的测试点即可)
测试点汇总
序号 | 功能 | 名称 | 描述 |
---|---|---|---|
1 | 接收FTQ预测块请求 | ready置位 | IFU接收FTQ请求后,设置ready |
2.1.1 | F1流水级计算信息和切分指针 | PC生成 | IFU接收FTQ请求后,在F1流水级生成PC |
2.1.2 | F1流水级计算信息和切分指针 | 切取指针生成 | IFU接收FTQ请求后,在F1流水级生成后续切取缓存行的指针 |
2.2.1 | F2流水级获取指令信息 | 异常向量生成 | IFU接收ICache内容后,会根据ICache的结果生成属于每个指令的异常向量 |
2.2.2 | F2流水级获取指令信息 | 物理地址提取 | IFU接收ICache内容后,会根据ICache的结果生成属于每个端口的物理地址。 |
2.2.3 | F2流水级获取指令信息 | 客户物理地址提取 | IFU接收ICache内容后,会根据ICache的结果生成0号端口的客户物理地址。 |
2.2.4 | F2流水级获取指令信息 | MMIO空间信息提取 | IFU接收ICache内容后,会根据ICache的结果判断当前取指请求是否属于MMIO空间。 |
2.3.1 | F2流水级计算预测块有效指令范围 | 无跳转指令有效范围生成 | IFU根据FTQ请求,计算无跳转指令有效范围 |
2.3.2 | F2流水级计算预测块有效指令范围 | 有跳转指令有效范围生成 | IFU根据FTQ请求,计算跳转指令有效范围 |
2.4 | 提取初始指令码 | 切取初始指令码 | IFU根据上一流水级的切取指针,从缓存行提取初始指令码。 |
3.1.1 | F3流水级选取指令有效向量 | 上一预测块结尾为RVC或RVI下半部分 | 上一预测块的最后2字节恰为RVC指令或RVI指令的后半部分,选择第一位为True的有效开始向量 |
3.1.2 | F3流水级选取指令有效向量 | 上一预测块结尾为RVI上半部分 | 上一预测块的最后2字节为RVI,选择第一位为False的有效开始向量 |
4.1 | 指令扩展 | 合法RVC指令写扩展指令码 | 对合法RVC指令,写扩展后的指令码 |
4.2 | 指令扩展 | 非法RVC指令写原始指令码 | 对非法RVC指令,写原始指令码 |
4.3 | 指令扩展 | RVI指令不扩展 | RVI指令直接写入原始指令即可 |
6.1.1 | 预测错误重定向 | JAL预测错误冲刷 | 预测请求中存在JAL预测错误,需要冲刷流水线 |
6.1.2 | 预测错误重定向 | RET预测错误冲刷 | 预测请求中存在RET预测错误,需要冲刷流水线 |
6.1.3 | 预测错误重定向 | 非CFI预测错误冲刷 | 预测请求中存在非CFI预测错误,需要冲刷流水线 |
6.1.4 | 预测错误重定向 | 无效指令预测错误冲刷 | 预测请求中存在无效指令预测错误,需要冲刷流水线 |
6.1.5 | 预测错误重定向 | 跳转目标错误冲刷 | 预测请求中存在跳转目标错误,需要冲刷流水线 |
6.2.1 | 跨预测块32位指令处理 | 跨预测块32位指令处理 | 连续传入两个预测块,其中有一条32位指令跨两个预测块,后一个预测块的指令开始向量的首位应该为False |
6.2.2 | 跨预测块32位指令处理 | 跨预测块指令误判 | 当IFU根据PredChecker修复的指令有效范围错判了跨预测块指令时,需要将F3以外的流水级全部冲刷 |
7.1.1 | 传送指令码和前端信息 | 指令码传送 | IFU向IBuffer传送扩展后的指令码 |
7.1.2 | 传送指令码和前端信息 | 异常信息传送 | IFU向IBuffer传送每个指令的异常信息 |
7.1.3 | 传送指令码和前端信息 | 预译码信息传送 | IFU向IBuffer传递每个指令的预译码信息 |
7.1.4 | 传送指令码和前端信息 | FTQ指针传送 | IFU向IBuffer传送FTQ预测块的指针 |
7.1.5 | 传送指令码和前端信息 | 折叠PC传送 | IFU向IBuffer传送折叠的PC |
7.1.6 | 传送指令码和前端信息 | 有效开始向量 | IFU向IBuffer传送表示指令有效和指令是否为指令开始的向量 |
7.2 | 客户页错误传送gpaddr信息 | 客户页错误 | 客户页错误发生时,IFU应将gpaMem的valid拉高且填入gpaddr |
8.1.1 | 核验指针 | 错误执行路径 | 当冲刷指针在取指令的指针之前时,IFU能够对流水线进行冲刷。 |
8.1.2 | 核验指针 | 执行路径无误 | 当冲刷指令在取指令的指针之后时,IFU不能对流水线进行冲刷。 |
8.2 | BPU S2流水级发现错误 | BPU S2流水级发现错误 | 当BPU的S2流水级出现错误,并且当前取指指针在错误执行路径上时,需要对IFU的F0流水级进行冲刷 |
8.3 | BPU S2流水级发现错误 | BPU S3流水级发现错误 | 当BPU的S3流水级出现错误,并且当前取指指针在错误执行路径上时,需要对IFU的F0和F1流水级进行冲刷 |
9.1.1 | 写回指令信息和误预测信息 | 写回指令PC | IFU的WB流水级,需要向FTQ写回指令PC |
9.1.2 | 写回指令信息和误预测信息 | 写回预译码信息 | IFU的WB流水级,需要向FTQ写回每个指令的预译码信息 |
9.1.3 | 写回指令信息和误预测信息 | 写回误预测指令位置 | IFU的WB流水级,需要向FTQ写回BPU错误预测的指令位置 |
9.1.4 | 写回指令信息和误预测信息 | 写回正确跳转地址 | IFU的WB流水级,需要向FTQ写回该预测块的正确跳转地址 |
9.1.5 | 写回指令信息和误预测信息 | 写回指令范围 | IFU的WB流水级,需要向FTQ写回预测块的正确指令范围 |
9.1.6 | 写回指令信息和误预测信息 | 传递预测块FTQ指针 | IFU的WB流水级,需要向FTQ传递预测块的FTQ指针 |
10.1 | 上电复位处理 | 第一条MMIO指令 | IFU收到的第一条MMIO请求可以直接查询Instr Uncache |
10.2.1 | 向InstrUncache发送请求 | 阻塞等待提交 | IFU收到MMIO请求后,查询FTQ,如果前面还有尚未提交的指令,持续等待 |
10.2.2 | 向InstrUncache发送请求 | 无阻塞发送请求 | 如果查到FTQ不再有未提交的指令,则IFU将指令发送给Instr Uncache |
10.3.1 | 跨总线请求处理 | 重发查询ITLB | 遇到一次无法查询完毕的RVI指令时,需要向ITLB查询获得新增指令的物理地址 |
10.3.2.1 | 跨总线请求处理 | ITLB异常 | IFU查询ITLB出现异常时,应当将异常信息发送到IBuffer,然后等待ROB提交完成 |
10.3.2.2 | 跨总线请求处理 | ITLB返回物理地址 | IFU查询ITLB正常返回物理地址时,IFU继续向PMP请求检查 |
10.3.3.1 | 跨总线请求处理 | 请求状态不一致 | IFU检查PMP之后如果发现重发请求状态和上一条请求状态不一致,是访问异常,需要将异常直接发送到IBuffer |
10.3.3.2 | 跨总线请求处理 | PMP检查异常 | PMP检查出现异常的情况下,也需要将异常直接发送到IBuffer并等待ROB提交。 |
10.3.3.3 | 跨总线请求处理 | Instr Cache请求重发 | PMP检查若无异常,则向Instr Uncache发送请求获取指令码的后半部分。 |
10.4 | 向IBuffer发送指令 | 向IBuffer发送指令 | IFU在获得完整数据后,截取获得指令码,以每个预测块一条指令的形式发送给IBuffer |
10.5.1 | 指令冲刷 | RVI指令重定向 | 如果是RVI指令,传递给FTQ的冲刷请求应该重定向到PC+4 |
10.5.2 | 指令冲刷 | RVC指令重定向 | 如果是RVC指令,传递给FTQ的冲刷请求应该重定向到PC+2 |
11.1.1 | 断点设置和检查 | select1判定 | 给定tdata1的select位为1,随机构造其它输入,检查断点是否没有触发 |
11.1.2.1 | 断点设置和检查 | select0关系匹配判定 | 给定tdata1的select位为0,构造PC与tdata2数据的关系同tdata2的match位匹配的输入,检查断点是否触发 |
11.1.2.2 | 断点设置和检查 | select0关系不匹配判定 | 给定tdata1的select位为0,构造PC与tdata2数据的关系同tdata2的match位不匹配的输入,检查断点是否触发 |
11.2.1 | 链式断点 | chain位测试 | 对每个trigger,在满足PC断点触发条件的情况下,设置chain位,检查断点是否一定不触发 |
11.2.2 | 链式断点 | timing测试 | 对两个trigger,仅设置前一个trigger的chain位,且两trigger的timing位不同,随机设置PC等,测试后一个trigger是否一定不触发 |
11.2.3.1 | 链式断点 | 未命中测试 | 对两个trigger,仅设置前一个trigger的chain位,且两trigger的timing位相同,设置后一个trigger命中而前一个未命中,检查后一个trigger是否一定不触发 |
11.2.3.2 | 链式断点 | 命中测试 | 对两个trigger,仅设置前一个trigger的chain位,且两trigger的timing位相同且均命中,检查后一个trigger是否触发 |