BPU.scala 9.4 KB
Newer Older
1 2 3 4 5 6 7 8
package noop

import chisel3._
import chisel3.util._
import chisel3.util.experimental.BoringUtils

import utils._

9
class TableAddr(val idxBits: Int) extends NOOPBundle {
10
  def tagBits = VAddrBits - 2 - idxBits
11

12
  //val res = UInt((AddrBits - VAddrBits).W)
13 14
  val tag = UInt(tagBits.W)
  val idx = UInt(idxBits.W)
15
  val pad = UInt(2.W)//TODO
16

17
  def fromUInt(x: UInt) = x.asTypeOf(UInt(VAddrBits.W)).asTypeOf(this)
18 19 20 21
  def getTag(x: UInt) = fromUInt(x).tag
  def getIdx(x: UInt) = fromUInt(x).idx
}

22 23 24 25 26 27 28 29 30
object BTBtype {
  def B = "b00".U  // branch
  def J = "b01".U  // jump
  def I = "b10".U  // indirect
  def R = "b11".U  // return

  def apply() = UInt(2.W)
}

Z
Zihao Yu 已提交
31
class BPUUpdateReq extends NOOPBundle {
32
  val valid = Output(Bool())
33
  val pc = Output(UInt(VAddrBits.W))
34
  val isMissPredict = Output(Bool())
35
  val actualTarget = Output(UInt(VAddrBits.W))
36
  val actualTaken = Output(Bool())  // for branch
Z
Zihao Yu 已提交
37
  val fuOpType = Output(FuOpType())
38
  val btbType = Output(BTBtype())
39
  val isRVC = Output(Bool()) // for ras, save PC+2 to stack if is RVC
40 41
}

Z
Zihao Yu 已提交
42
class BPU1 extends NOOPModule {
43
  val io = IO(new Bundle {
44
    val in = new Bundle { val pc = Flipped(Valid((UInt(VAddrBits.W)))) }
Z
Zihao Yu 已提交
45
    val out = new RedirectIO
46
    val flush = Input(Bool())
W
William Wang 已提交
47
    val brIdx = Output(UInt(3.W))
48
    val lateJump = Output(Bool())
49 50
  })

51 52
  val flush = BoolStopWatch(io.flush, io.in.pc.valid, startHighPriority = true)

53 54 55
  // BTB
  val NRbtb = 512
  val btbAddr = new TableAddr(log2Up(NRbtb))
56
  def btbEntry() = new Bundle {
57 58
    val tag = UInt(btbAddr.tagBits.W)
    val _type = UInt(2.W)
59
    val target = UInt(VAddrBits.W)
60
    val brIdx = UInt(3.W)
61
    val valid = Bool()
62 63
  }

Z
Zihao Yu 已提交
64
  val btb = Module(new SRAMTemplate(btbEntry(), set = NRbtb, shouldReset = true, holdRead = true, singlePort = true))
65 66
  // flush BTB when executing fence.i
  val flushBTB = WireInit(false.B)
67
  val flushTLB = WireInit(false.B)
68
  BoringUtils.addSink(flushBTB, "MOUFlushICache")
69 70
  BoringUtils.addSink(flushTLB, "MOUFlushTLB")
  btb.reset := reset.asBool || (flushBTB || flushTLB)
71

72
  Debug(false) {
73 74 75 76 77
    when (reset.asBool || (flushBTB || flushTLB)) {
      printf("[BPU-RESET] %d bpu-reset flushBTB:%d flushTLB:%d\n", GTimer(), flushBTB, flushTLB)
    }
  }

Z
Zihao Yu 已提交
78
  btb.io.r.req.valid := io.in.pc.valid
79
  btb.io.r.req.bits.setIdx := btbAddr.getIdx(io.in.pc.bits)
80

81

82 83
  val btbRead = Wire(btbEntry())
  btbRead := btb.io.r.resp.data(0)
Z
Zihao Yu 已提交
84 85 86
  // since there is one cycle latency to read SyncReadMem,
  // we should latch the input pc for one cycle
  val pcLatch = RegEnable(io.in.pc.bits, io.in.pc.valid)
87
  val btbHit = btbRead.tag === btbAddr.getTag(pcLatch) && !flush && RegNext(btb.io.r.req.fire(), init = false.B) && !(pcLatch(1) && btbRead.brIdx(0)) && btbRead.valid
88
  // btbHit will ignore pc(1,0). pc(1,0) is used to build brIdx
89 90 91 92 93
  // !(pcLatch(1) && btbRead.brIdx(0)) is used to deal with the following case:
  // -------------------------------------------------
  // 0 jump rvc // marked as "take branch" in BTB
  // 2 xxx  rvc <-- jump to here
  // -------------------------------------------------
94 95 96 97
  val lateJump = btbRead.brIdx(2) && btbHit
  io.lateJump := lateJump
  // val lateJumpLatch = RegNext(lateJump)
  // val lateJumpTarget = RegEnable(btbRead.target, lateJump)
98
  Debug(false){
99
    //printf("[BTBHT] lateJump %x lateJumpLatch %x lateJumpTarget %x\n", lateJump, lateJumpLatch, lateJumpTarget)
100
    when(btbHit){
101 102 103
      printf("[BTBHT1] %d pc=%x tag=%x,%x index=%x bridx=%x tgt=%x,%x flush %x type:%x\n", GTimer(), pcLatch, btbRead.tag, btbAddr.getTag(pcLatch), btbAddr.getIdx(pcLatch), btbRead.brIdx, btbRead.target, io.out.target, flush,btbRead._type)
      printf("[BTBHT2] btbRead.brIdx %x mask %x\n", btbRead.brIdx, Cat(lateJump, Fill(2, io.out.valid)))
      printf("[BTBHT5] btbReqValid:%d btbReqSetIdx:%x\n",btb.io.r.req.valid, btb.io.r.req.bits.setIdx)
104 105
    }
  }
106
  
107
  // PHT
108 109
  val pht = Mem(NRbtb, UInt(2.W))
  val phtTaken = RegEnable(pht.read(btbAddr.getIdx(io.in.pc.bits))(1), io.in.pc.valid)
110 111 112 113

  // RAS

  val NRras = 16
114
  val ras = Mem(NRras, UInt(VAddrBits.W))
115
  // val raBrIdxs = Mem(NRras, UInt(2.W))
116
  val sp = Counter(NRras)
Z
Zihao Yu 已提交
117
  val rasTarget = RegEnable(ras.read(sp.value), io.in.pc.valid)
118
  // val rasBrIdx = RegEnable(raBrIdxs.read(sp.value), io.in.pc.valid)
119 120

  // update
121
  val req = WireInit(0.U.asTypeOf(new BPUUpdateReq))
122
  val btbWrite = WireInit(0.U.asTypeOf(btbEntry()))
123
  BoringUtils.addSink(req, "bpuUpdateReq")
124

125
  Debug(false){
126 127 128
    when(req.valid){
        printf("[BTBUP] pc=%x tag=%x index=%x bridx=%x tgt=%x type=%x\n", req.pc, btbAddr.getTag(req.pc), btbAddr.getIdx(req.pc), Cat(req.pc(1), ~req.pc(1)), req.actualTarget, req.btbType)
      }
129 130
  }

131 132 133 134 135 136 137 138 139
    //val fflag = req.btbType===3.U && btb.io.w.req.valid && btb.io.w.req.bits.setIdx==="hc9".U
    //when(fflag && GTimer()>2888000.U) {
    //  printf("%d\n", GTimer())
    //  printf("[BTBHT6] btbWrite.type is BTBtype.R/RET!!! Inpc:%x btbWrite.brIdx:%x setIdx:%x\n", io.in.pc.bits, btbWrite.brIdx, btb.io.w.req.bits.setIdx)
    //  printf("[BTBHT6] tag:%x target:%x _type:%x bridx:%x\n", btbWrite.tag,btbWrite.target,btbWrite._type,btbWrite.brIdx)
    //  printf(p"[BTBHT6] req:${req} \n")
    //} 
    //printf("[BTBHT5] tag: target:%x type:%d brIdx:%d\n", req.actualTarget, req.btbType, Cat(req.pc(2,0)==="h6".U && !req.isRVC, req.pc(1), ~req.pc(1)))

140 141 142
  btbWrite.tag := btbAddr.getTag(req.pc)
  btbWrite.target := req.actualTarget
  btbWrite._type := req.btbType
143
  btbWrite.brIdx := Cat(req.pc(2,0)==="h6".U && !req.isRVC, req.pc(1), ~req.pc(1))
144
  btbWrite.valid := true.B 
145 146 147 148 149 150 151
  // NOTE: We only update BTB at a miss prediction.
  // If a miss prediction is found, the pipeline will be flushed
  // in the next cycle. Therefore it is safe to use single-port
  // SRAM to implement BTB, since write requests have higher priority
  // than read request. Again, since the pipeline will be flushed
  // in the next cycle, the read request will be useless.
  btb.io.w.req.valid := req.isMissPredict && req.valid
152
  btb.io.w.req.bits.setIdx := btbAddr.getIdx(req.pc)
153
  btb.io.w.req.bits.data := btbWrite
154

155
  //Debug(true) {
156 157 158
    //when (btb.io.w.req.valid && btbWrite.tag === btbAddr.getTag("hffffffff803541a4".U)) {
    //  printf("[BTBWrite] %d setIdx:%x req.valid:%d pc:%x target:%x bridx:%x\n", GTimer(), btbAddr.getIdx(req.pc), req.valid, req.pc, req.actualTarget, btbWrite.brIdx)
    //}
159 160
  //}

161 162 163
  //when (GTimer() > 77437484.U && btb.io.w.req.valid) {
  //  printf("[BTBWrite-ALL] %d setIdx:%x req.valid:%d pc:%x target:%x bridx:%x\n", GTimer(), btbAddr.getIdx(req.pc), req.valid, req.pc, req.actualTarget, btbWrite.brIdx)
  //}
164

165 166
  val cnt = RegNext(pht.read(btbAddr.getIdx(req.pc)))
  val reqLatch = RegNext(req)
Z
Zihao Yu 已提交
167
  when (reqLatch.valid && ALUOpType.isBranch(reqLatch.fuOpType)) {
168 169 170 171 172
    val taken = reqLatch.actualTaken
    val newCnt = Mux(taken, cnt + 1.U, cnt - 1.U)
    val wen = (taken && (cnt =/= "b11".U)) || (!taken && (cnt =/= "b00".U))
    when (wen) {
      pht.write(btbAddr.getIdx(reqLatch.pc), newCnt)
173 174 175
      //Debug(){
        //printf("BPUPDATE: pc %x cnt %x\n", reqLatch.pc, newCnt)
      //}
176
    }
177
  }
178
  when (req.valid) {
179 180
    when (req.fuOpType === ALUOpType.call)  {
      ras.write(sp.value + 1.U, Mux(req.isRVC, req.pc + 2.U, req.pc + 4.U))
181
      // raBrIdxs.write(sp.value + 1.U, Mux(req.pc(1), 2.U, 1.U))
182 183
      sp.value := sp.value + 1.U
    }
Z
Zihao Yu 已提交
184
    .elsewhen (req.fuOpType === ALUOpType.ret) {
185
      when(sp.value === 0.U) {
186
        //printf("ATTTTT: sp.value is 0.U\n") //TODO: sp.value may equal to 0.U
187
      }
188
      sp.value := Mux(sp.value===0.U, 0.U, sp.value - 1.U) //TODO: sp.value may less than 0.U
189 190 191
    }
  }

192 193
  io.out.target := Mux(btbRead._type === BTBtype.R, rasTarget, btbRead.target)
  // io.out.target := Mux(lateJumpLatch && !flush, lateJumpTarget, Mux(btbRead._type === BTBtype.R, rasTarget, btbRead.target))
194
  // io.out.brIdx  := btbRead.brIdx & Fill(3, io.out.valid)
195
  io.brIdx  := btbRead.brIdx & Cat(true.B, lateJump, Fill(2, io.out.valid))
196
  io.out.valid := btbHit && Mux(btbRead._type === BTBtype.B, phtTaken, true.B && rasTarget=/=0.U) //TODO: add rasTarget=/=0.U, need fix
197 198 199 200 201 202 203
  // io.out.valid := btbHit && Mux(btbRead._type === BTBtype.B, phtTaken, true.B) && !lateJump || lateJumpLatch && !flush && !lateJump
  // Note: 
  // btbHit && Mux(btbRead._type === BTBtype.B, phtTaken, true.B) && !lateJump : normal branch predict
  // lateJumpLatch && !flush && !lateJump : cross line branch predict, bpu will require imem to fetch the next 16bit of current inst in next instline
  // `&& !lateJump` is used to make sure this logic will run correctly when imem stalls (pcUpdate === false)
  // by using `instline`, we mean a 64 bit instfetch result from imem
  // ROCKET uses a 32 bit instline, and its IDU logic is more simple than this implentation.
204 205
}

Z
Zihao Yu 已提交
206
class BPU2 extends NOOPModule {
207
  val io = IO(new Bundle {
Z
Zihao Yu 已提交
208 209
    val in = Flipped(Valid(new CtrlFlowIO))
    val out = new RedirectIO
210 211 212
  })

  val instr = io.in.bits.instr
Z
Zihao Yu 已提交
213 214
  val immJ = SignExt(Cat(instr(31), instr(19, 12), instr(20), instr(30, 21), 0.U(1.W)), XLEN)
  val immB = SignExt(Cat(instr(31), instr(7), instr(30, 25), instr(11, 8), 0.U(1.W)), XLEN)
215
  val table = Array(
Z
Zihao Yu 已提交
216 217 218 219 220 221 222
    RV32I_BRUInstr.JAL  -> List(immJ, true.B),
    RV32I_BRUInstr.BNE  -> List(immB, instr(31)),
    RV32I_BRUInstr.BEQ  -> List(immB, instr(31)),
    RV32I_BRUInstr.BLT  -> List(immB, instr(31)),
    RV32I_BRUInstr.BGE  -> List(immB, instr(31)),
    RV32I_BRUInstr.BLTU -> List(immB, instr(31)),
    RV32I_BRUInstr.BGEU -> List(immB, instr(31))
223 224 225 226 227
  )
  val default = List(immB, false.B)
  val offset :: predict :: Nil = ListLookup(instr, default, table)

  io.out.target := io.in.bits.pc + offset
Z
Zihao Yu 已提交
228
  io.out.valid := io.in.valid && predict(0)
229
}