1
0

Improve frontend branch prediction

- Put correctness responsibility on Frontend, not IBuf, for improved
  separation of concerns.  Frontend must detect case that the BTB
  predicts a taken branch in the middle of an instruction.

- Pass BTB information down pipeline unconditionally, fixing case that
  screws up the branch history when the BTB misses and the instruction
  is misaligned.

- Remove jumpInFrontend option; it's now unconditional.

- Default to one-bit counters in the BHT.  For tiny BHTs like these, it's
  more resource efficient to have a larger index space than to have
  hysteresis.
This commit is contained in:
Andrew Waterman
2017-11-08 17:23:25 -08:00
parent 989eeb78f9
commit d0c6cbba6b
5 changed files with 177 additions and 157 deletions

View File

@ -30,7 +30,6 @@ case class RocketCoreParams(
mtvecWritable: Boolean = true,
fastLoadWord: Boolean = true,
fastLoadByte: Boolean = false,
jumpInFrontend: Boolean = true,
tileControlAddr: Option[BigInt] = None,
mulDiv: Option[MulDivParams] = Some(MulDivParams()),
fpu: Option[FPUParams] = Some(FPUParams())
@ -123,7 +122,6 @@ class Rocket(implicit p: Parameters) extends CoreModule()(p)
val ex_reg_xcpt_interrupt = Reg(Bool())
val ex_reg_valid = Reg(Bool())
val ex_reg_rvc = Reg(Bool())
val ex_reg_btb_hit = Reg(Bool())
val ex_reg_btb_resp = Reg(new BTBResp)
val ex_reg_xcpt = Reg(Bool())
val ex_reg_flush_pipe = Reg(Bool())
@ -137,7 +135,6 @@ class Rocket(implicit p: Parameters) extends CoreModule()(p)
val mem_reg_xcpt_interrupt = Reg(Bool())
val mem_reg_valid = Reg(Bool())
val mem_reg_rvc = Reg(Bool())
val mem_reg_btb_hit = Reg(Bool())
val mem_reg_btb_resp = Reg(new BTBResp)
val mem_reg_xcpt = Reg(Bool())
val mem_reg_replay = Reg(Bool())
@ -292,7 +289,6 @@ class Rocket(implicit p: Parameters) extends CoreModule()(p)
ex_reg_replay := !take_pc && ibuf.io.inst(0).valid && ibuf.io.inst(0).bits.replay
ex_reg_xcpt := !ctrl_killd && id_xcpt
ex_reg_xcpt_interrupt := !take_pc && ibuf.io.inst(0).valid && csr.io.interrupt
ex_reg_btb_hit := ibuf.io.inst(0).bits.btb_hit
when (!ctrl_killd) {
ex_ctrl := id_ctrl
@ -374,7 +370,7 @@ class Rocket(implicit p: Parameters) extends CoreModule()(p)
val mem_int_wdata = Mux(!mem_reg_xcpt && (mem_ctrl.jalr ^ mem_npc_misaligned), mem_br_target, mem_reg_wdata.asSInt).asUInt
val mem_cfi = mem_ctrl.branch || mem_ctrl.jalr || mem_ctrl.jal
val mem_cfi_taken = (mem_ctrl.branch && mem_br_taken) || mem_ctrl.jalr || mem_ctrl.jal
val mem_direction_misprediction = (Bool(coreParams.jumpInFrontend) || mem_reg_btb_hit) && mem_ctrl.branch && mem_br_taken =/= mem_reg_btb_resp.taken
val mem_direction_misprediction = mem_ctrl.branch && mem_br_taken =/= (usingBTB && mem_reg_btb_resp.taken)
val mem_misprediction = if (usingBTB) mem_wrong_npc else mem_cfi_taken
take_pc_mem := mem_reg_valid && (mem_misprediction || mem_reg_sfence)
@ -393,7 +389,6 @@ class Rocket(implicit p: Parameters) extends CoreModule()(p)
mem_reg_load := ex_ctrl.mem && isRead(ex_ctrl.mem_cmd)
mem_reg_store := ex_ctrl.mem && isWrite(ex_ctrl.mem_cmd)
mem_reg_sfence := ex_sfence
mem_reg_btb_hit := ex_reg_btb_hit
mem_reg_btb_resp := ex_reg_btb_resp
mem_reg_flush_pipe := ex_reg_flush_pipe
mem_reg_slow_bypass := ex_slow_bypass
@ -608,8 +603,8 @@ class Rocket(implicit p: Parameters) extends CoreModule()(p)
ibuf.io.inst(0).ready := !ctrl_stalld
io.imem.btb_update.valid := (mem_reg_replay && mem_reg_btb_hit) || (mem_reg_valid && !take_pc_wb && mem_wrong_npc && (!mem_cfi || mem_cfi_taken))
io.imem.btb_update.bits.isValid := !mem_reg_replay && mem_cfi
io.imem.btb_update.valid := mem_reg_valid && !take_pc_wb && mem_wrong_npc && (!mem_cfi || mem_cfi_taken)
io.imem.btb_update.bits.isValid := mem_cfi
io.imem.btb_update.bits.cfiType :=
Mux((mem_ctrl.jal || mem_ctrl.jalr) && mem_waddr(0), CFIType.call,
Mux(mem_ctrl.jalr && mem_reg_inst(19,15) === BitPat("b00?01"), CFIType.ret,
@ -618,14 +613,14 @@ class Rocket(implicit p: Parameters) extends CoreModule()(p)
io.imem.btb_update.bits.target := io.imem.req.bits.pc
io.imem.btb_update.bits.br_pc := (if (usingCompressed) mem_reg_pc + Mux(mem_reg_rvc, UInt(0), UInt(2)) else mem_reg_pc)
io.imem.btb_update.bits.pc := ~(~io.imem.btb_update.bits.br_pc | (coreInstBytes*fetchWidth-1))
io.imem.btb_update.bits.prediction.valid := mem_reg_btb_hit
io.imem.btb_update.bits.prediction.bits := mem_reg_btb_resp
io.imem.btb_update.bits.prediction := mem_reg_btb_resp
io.imem.bht_update.valid := mem_reg_valid && !take_pc_wb && mem_ctrl.branch
io.imem.bht_update.valid := mem_reg_valid && !take_pc_wb
io.imem.bht_update.bits.pc := io.imem.btb_update.bits.pc
io.imem.bht_update.bits.taken := mem_br_taken
io.imem.bht_update.bits.mispredict := mem_wrong_npc
io.imem.bht_update.bits.prediction := io.imem.btb_update.bits.prediction
io.imem.bht_update.bits.branch := mem_ctrl.branch
io.imem.bht_update.bits.prediction := mem_reg_btb_resp.bht
io.fpu.valid := !ctrl_killd && id_ctrl.fp
io.fpu.killx := ctrl_killx