Files
ciciec2026_loongson/rtl/ip/open-la500/doc/分支预测.md
2026-04-12 22:20:18 +08:00

11 KiB
Raw Permalink Blame History

分支预测

分支预测即根据分支历史提前预测指令跳转方向及跳转目标。分支预测器种类繁多例如BTBBranch Target Buffer、BHTBranch History Table、RASReturn Address Stack但适用于五级流水的其实并不多。如设计概述中所介绍的五级流水中pfs级发出取指请求fs级取回指令ds级开始译码并明确跳转指令的所有信息。也就表示分支预测只有一拍的空间可以利用且无预译码可依据的信息只有pc跳转方向及跳转目标都只能靠猜。适用的实际上仅有BTB。不过对于较为特殊的jirl指令即寄存器跳转若直接采用BTB的机制会使得命中率极低。因此在BTB的基础上进行改造由CAM表记录jirl指令的pc并由RAS机制预测其跳转目标。


接下来介绍分支预测器的具体实现,分为两点:分支预测器的内部实现;分支预测器同流水线的交互。

分支预测器设计

分支预测器的逻辑可以分为两个部分由PC进行预测跳转目标及跳转方向的预测部分根据指令译码结果与分支预测器预测结果的比较对分支预测器历史信息进行修正的修正部分。

预测部分

首先来看模块的接口。

  • fetch_pc取指pc
  • fetch_en取指使能
  • ret_en预测结果使能
  • ret_pc预测跳转目标
  • taken预测跳转方向
  • ret_index指示当前预测结果的对应项用于修正

btb表有32项包含四个内容

  • btb_pc30位用于和取指pc匹配
  • btb_target30位存放跳转目标
  • btb_counter2位由最高位指示跳转方向
  • btb_valid1位当前项是否有效

对于jirl指令的预测逻辑由两部分组成16项的CAM表以及8项的RAS。
CAM表包含两个内容

  • ras_pc30位用于和取指pc匹配
  • ras_valid1位当前项是否有效

RAS简单来说就是一个栈。存储30位的跳转目标由ras_ptr指示栈顶。

明确表项内容后来看预测逻辑。fetch_en以及fetch_pc进入分支预测器时首先缓存一拍。单看分支预测器缓存插在什么位置其实都可以只要有一拍的延迟即可但考虑到赋值nextpc即fetch_pc的路径非常长因此将缓存插在靠前的位置。缓存后的信号为fetch_en_r及fetch_pc_r将其与两个CAM的PC部分btb_pc和ras_pc进行匹配。该项有效且pc匹配则表示命中。命中项记录在btb_match_rd和ras_match_rd中。

两个表中有一项命中则置起ret_en。若命中在btb CAM表中ret_pc取对应项的btb_target即可若命中在ras CAM表中则取ras栈顶的跳转目标。理论上btb_match_rd和ras_match_rd为one-hot且只会命中在一个表中因此不必使用优先级逻辑。ret_index同理而taken在btb_match时需要看对应项btb_counter最高位而ras_match时直接置为1。预测阶段不会对分支预测器的历史信息进行任何修正。

修正部分

在ds译码级汇总分支预测信息以及译码得到的正确分支跳转信息。
预测信息同上:

  • ds_btb_target
  • ds_btb_index
  • ds_btb_taken
  • ds_btb_en

正确的分支跳转信息如下:

  • br_to_btb表示当前指令是否为分支预测器所覆盖的跳转指令当前结构的分支预测器覆盖所有跳转指令。
  • br_taken跳转方向
  • br_target跳转目标

译码级根据以上信息,稍加分析并传递至分支预测器,传递信息包括:

  • operate_en分支预测器修正操作使能。注意该信号需要在ds_ready_go、es_allowin和ds_valid为1时置起且无例外。只有ds当拍允许流动至下一级时才能发出修正操作。对于ds_ready_go因为流水线中指令寄存器的依赖有可能导致ds阻塞阻塞时指令所得到的寄存器值是错误的即译码得到的分支跳转信息也是错误的。而es_allowin的介入使其操作使能仅维持一拍避免重复操作。
  • operate_pc修正操作针对的分支指令的pc
  • operate_index修正操作针对的CAM表项
  • pop_ras为jirl指令时置起
  • push_ras为bl指令时置起
  • add_entry当前指令为分支指令但预测器未进行预测时置起。这里与上了一个额外条件br_taken因为当pc在分支预测器中未命中时顺序指令也算是预测为untaken。br_taken不为1时并不需要在预测器中建项
  • delete_entry当前指令不为分支指令但预测器进行预测时置起。这种情况出现的概率极低只有在自修改代码中可以不用考虑
  • pre_error当前指令为分支指令且预测器进行预测但跳转目标不一致时置起
  • pre_right当前指令为分支指令且预测器进行预测且跳转目标一致时置起
  • target_error当前指令为分支指令且预测器进行预测且跳转目标一致但跳转目标不一致时置起
  • right_orien正确的跳转方向
  • right_target正确的跳转目标

分支预测器会根据以上信息进行修正首先由pop_ras即是否为jirl指令区分当前操作的是BTB部分还是RAS部分针对BTB部分操作类型包括

  • 建项add_entry置起时。使能指定项并填入operate_pc和right_target以及初始化btb_counter为2'b10。对于填入项即index的选择若BTB CAM表中还留有未使能项则选择其中一项。若所有项都有效则选择btb_counter为2'b00的一项即大概率不跳转的预测项因为未在btb中命中的pc即预测为不跳转两者效果是相同的只不过该pc若下一次为跳转则会重新建项相当于将其btb_counter由2'b00直接转为2'b10很有可能导致下一次的预测出错。但是这种情况出现的概率肯定比替换掉其他btb_counter的值导致下一次预测错的概率低。若所有项都有效且btb_counter都不为2'b00则随机选择一项。
  • 修正跳转目标target_error置起时。重新填入right_target并初始化btb_counter为2'b10。
  • 修正跳转方向pre_error或者pre_right置起时。根据right_orien调整btb_counter若正确的分支方向为taken则累加若为untaken则递减。

针对RAS部分操作类型包括

  • 建项add_entry置起时。使能指定项填入operate_pc即可。相当于预测pc的分支类型。index的选择同BTB部分。
  • 进栈当push_ras置起且当前RAS非满时存入当前pc的下一条指令并将指针上移。即当前为bl函数跳转指令其返回地址为bl的下一条指令。RAS指针指向的是空项。
  • 出栈当pop_ras置起且当前RAS非空时指针直接下移一项即可。

分支预测器与流水线交互逻辑

首先简单介绍取指逻辑该部分内容会在后续逐步展开。来看几个关键信号inst_valid指示当前取指pc是否有效该信号直接送往icache等待接收。inst_valid主要看fs_allowin信号即pfs指令允许向后流动时其取指pc即为有效能够避免相同pc取指请求的重复发出。nextpc即为取指pc当前仅关注其最为常规的pc来源seq_pc顺序取指pc。当inst_valid为1时并不代表取指请求即刻便能发出还需要等待icache空闲由inst_addr_ok表示取指请求的其他去向tlb、btb不需要等待。当inst_valid && inst_addr_ok为1取指请求发出pfs_ready_go置起pfs向fs流动。
fetch_en和fetch_pc发送至分支预测器后下一拍得到预测结果btb_en/btb_ret_pc/btb_taken。当btb_en && btb_taken为1时fetch_btb_target置起分支跳转将nextpc纠正至btb_ret_pc。这段逻辑中有一点需要注意分支预测器返回的结果仅维持一拍但pfs并不一定能够向后流动因此需要缓存分支预测器的返回结果避免信息丢失。分支预测器的返回结果缓存在btb_lock_buffer中由btb_lock_en指示缓存是否有效。缓存条件即为分支返回结果且pfs无法向后流动btb_en && !pfs_ready_go缓存释放条件为下一次取指重新发起。而nextpc的维护以及fs向ds传递分支预测结果时都将采用btb_ret_pc_t、btb_ret_pc_t、btb_ret_pc_t、btb_ret_pc_t信号由分支预测器返回信息和缓存的信息相或得到两者同一时刻仅有一个有效。
译码级根据译码和分支预测的结果能够知道下一条指令取指的方向是否正确。取指级有两个方面的工作需要完成修正分支预测的历史信息若预测错误则需纠正取指方向。此处主要讲述第二方面的内容。若译码级检测到分支预测错误则会置起btb_pre_error_flush信号由该信号修正取指方向该工作可继续细分为两个内容

  • 取消掉下一条进入译码级的指令,及错误预测导致错误取指的指令
    两种情况需要考虑:

    • 当该分支指令向后流动的那一拍btb_pre_error_flush && es_allowin为1时若下一拍下一条指令紧接着进入译码级则可直接取消。不过可以用更粗犷的方法即下一拍的ds_valid直接为0无需识别是否有指令进入。
    • 同样的条件若下一拍无指令进入即当拍fs_to_ds_valid为0则置起branch_slot_cancel触发器。该触发器为1时会等待下一条指令进入当检测fs_to_ds_valid为1时将其取消并恢复branch_slot_cancel至0。
  • 纠正nextpc。当译码级发现分支预测错误会将正确的跳转目标btb_pre_error_flush_target和使能信号btb_pre_error_flush通过br_bus传递至取指阶段修正nextpc。 需要考虑两个问题:

    • 当nextpc修正信息向前传递时pfs并不一定处于能够取指的状态例如inst_addr_ok不为1。此时若译码级不阻塞且pfs不将修正信息存入buffer会导致信息丢失。
    • 译码级会记录分支预测错误并取消掉错误预测的分支指令的下一条指令。若pfs在取完预测错误的分支指令后始终处于阻塞状态之后直接被修正至正确的跳转方向则会导致正确的指令被取消掉。

    针对上述两个问题可在pfs中构建一个状态机解决。br_target_inst_req_state表示状态。当发现预测错误后根据流水线状态进入不同的状态。

    • !fs_valid && !inst_addr_ok预测错误分支指令取出后未进行任何取指因为fs级为空inst_addr_ok从未置起。且当前也不处于可取指的状态。进入br_target_inst_req_wait_slot状态。
    • !inst_addr_ok && fs_valid已有一条错误取指的指令在fs中但pfs并不处于能够取指的状态。进入br_target_inst_req_wait_br_target状态。
    • inst_addr_ok && !fs_valid预测错误分支指令取出后未进行任何取指当前处于可取指的状态但当前的指令会在译码级取消。进入br_target_inst_req_wait_br_target状态。

    br_target_inst_req_wait_slot状态会待取指请求发出后切换至br_target_inst_req_wait_br_target状态该状态便会将nextpc修正至存储btb_pre_error_flush_target的pc bufferbr_target_inst_req_buffer。当取指请求再次发出后状态切换至空状态br_target_inst_req_empty。

    构建状态机后还需要注意一个问题。也许pfs处于即刻能够发出取指请求的状态。可粗略的判断fs级是否有指令便改变next_pc同时置起inst_valid即使当前无法取指(inst_addr_ok为0)也没有关系。