xref: /XiangShan/src/main/scala/xiangshan/frontend/BPU.scala (revision 627c0a19d70511e59db9d2cebda4d0c7979c630a)
1package xiangshan.frontend
2
3import chisel3._
4import chisel3.util._
5import xiangshan._
6import xiangshan.utils._
7import xiangshan.backend.ALUOpType
8import utils._
9
10class TableAddr(val idxBits: Int, val banks: Int) extends XSBundle {
11  def tagBits = VAddrBits - idxBits - 2
12
13  val tag = UInt(tagBits.W)
14  val idx = UInt(idxBits.W)
15  val offset = UInt(2.W)
16
17  def fromUInt(x: UInt) = x.asTypeOf(UInt(VAddrBits.W)).asTypeOf(this)
18  def getTag(x: UInt) = fromUInt(x).tag
19  def getIdx(x: UInt) = fromUInt(x).idx
20  def getBank(x: UInt) = getIdx(x)(log2Up(banks) - 1, 0)
21  def getBankIdx(x: UInt) = getIdx(x)(idxBits - 1, log2Up(banks))
22}
23
24class Stage1To2IO extends XSBundle {
25  val pc = Output(UInt(VAddrBits.W))
26  val btb = new Bundle {
27    val hits = Output(UInt(FetchWidth.W))
28    val targets = Output(Vec(FetchWidth, UInt(VAddrBits.W)))
29  }
30  val jbtac = new Bundle {
31    val hitIdx = Output(UInt(FetchWidth.W))
32    val target = Output(UInt(VAddrBits.W))
33  }
34  val tage = new Bundle {
35    val hits = Output(UInt(FetchWidth.W))
36    val takens = Output(Vec(FetchWidth, Bool()))
37  }
38  val hist = Output(Vec(FetchWidth, UInt(HistoryLength.W)))
39  val btbPred = ValidIO(new BranchPrediction)
40}
41
42class BPUStage1 extends XSModule {
43  val io = IO(new Bundle() {
44    val in = new Bundle { val pc = Flipped(Decoupled(UInt(VAddrBits.W))) }
45    // from backend
46    val redirectInfo = Flipped(new RedirectInfo)
47    // from Stage3
48    val flush = Input(Bool())
49    val s3RollBackHist = Input(UInt(HistoryLength.W))
50    // to ifu, quick prediction result
51    val s1OutPred = ValidIO(new BranchPrediction)
52    // to Stage2
53    val out = Decoupled(new Stage1To2IO)
54  })
55
56  // flush Stage1 when io.flush
57  val flushS1 = BoolStopWatch(io.flush, io.in.pc.fire(), startHighPriority = true)
58
59  // global history register
60  val ghr = RegInit(0.U(HistoryLength.W))
61  // modify updateGhr and newGhr when updating ghr
62  val updateGhr = WireInit(false.B)
63  val newGhr = WireInit(0.U(HistoryLength.W))
64  when (updateGhr) { ghr := newGhr }
65  // use hist as global history!!!
66  val hist = Mux(updateGhr, newGhr, ghr)
67
68  // Tage predictor
69  val tage = Module(new Tage)
70  tage.io.req.valid := io.in.pc.fire()
71  tage.io.req.bits.pc := io.in.pc.bits
72  tage.io.req.bits.hist := hist
73  tage.io.redirectInfo <> io.redirectInfo
74  io.out.bits.tage <> tage.io.out
75  io.s1OutPred.bits.tageMeta := tage.io.meta
76
77  // BTB
78  val btbAddr = new TableAddr(log2Up(BtbSets), BtbBanks)
79  val predictWidth = FetchWidth
80  def btbDataEntry() = new Bundle {
81    val valid = Bool()
82    val target = UInt(VAddrBits.W)
83    val pred = UInt(2.W) // 2-bit saturated counter as a quick predictor
84    val _type = UInt(2.W)
85    val offset = UInt(offsetBits().W) // Could be zero
86
87    def offsetBits() = log2Up(FetchWidth / predictWidth)
88  }
89  def btbMetaEntry() = new Bundle {
90    val valid = Bool()
91    // TODO: don't need full length of tag
92    val tag = UInt(btbAddr.tagBits.W)
93  }
94
95  val btbMeta = List.fill(BtbWays)(List.fill(BtbBanks)(
96    Module(new SRAMTemplate(btbMetaEntry(), set = BtbSets / BtbBanks, way = 1, shouldReset = true, holdRead = true))
97  ))
98  val btbData = List.fill(BtbWays)(List.fill(BtbBanks)(
99    Module(new SRAMTemplate(btbDataEntry(), set = BtbSets / BtbBanks, way = predictWidth, shouldReset = true, holdRead = true))
100  ))
101
102  // BTB read requests
103  // read addr comes from pc[6:2]
104  // read 4 ways in parallel
105  (0 until BtbWays).map(
106    w => (0 until BtbBanks).map(
107      b => {
108        btbMeta(w)(b).reset := reset.asBool
109        btbMeta(w)(b).io.r.req.valid := io.in.pc.fire() && b.U === btbAddr.getBank(io.in.pc.bits)
110        btbMeta(w)(b).io.r.req.bits.setIdx := btbAddr.getBankIdx(io.in.pc.bits)
111        btbData(w)(b).reset := reset.asBool
112        btbData(w)(b).io.r.req.valid := io.in.pc.fire() && b.U === btbAddr.getBank(io.in.pc.bits)
113        btbData(w)(b).io.r.req.bits.setIdx := btbAddr.getBankIdx(io.in.pc.bits)
114      }
115    )
116  )
117
118  // latch pc for 1 cycle latency when reading SRAM
119  val pcLatch = RegEnable(io.in.pc.bits, io.in.pc.fire())
120  // Entries read from SRAM
121  val btbMetaRead = Wire(Vec(BtbWays, btbMetaEntry()))
122  val btbDataRead = Wire(Vec(BtbWays, Vec(predictWidth, btbDataEntry())))
123  val btbReadFire = Wire(Vec(BtbWays, Vec(BtbBanks, Bool())))
124  // 1/4 hit
125  val btbWayHits = Wire(Vec(BtbWays, Bool()))
126
127  // #(predictWidth) results
128  val btbTargets = Wire(Vec(predictWidth, UInt(VAddrBits.W)))
129  val btbTypes = Wire(Vec(predictWidth, UInt(2.W)))
130  // val btbPreds = Wire(Vec(FetchWidth, UInt(2.W)))
131  val btbTakens = Wire(Vec(predictWidth, Bool()))
132  val btbValids = Wire(Vec(predictWidth, Bool()))
133
134  val btbHitWay = Wire(UInt(log2Up(BtbWays).W))
135  val btbHitBank = btbAddr.getBank(pcLatch)
136
137  (0 until BtbWays).map(
138    w => {
139      btbMetaRead(w) := btbMeta(w)(btbHitBank).io.r.resp.data(0)
140      (0 until predictWidth).map(i => btbDataRead(w)(i) := btbData(w)(btbHitBank).io.r.resp.data(i))
141    }
142  )
143
144  btbWayHits := 0.U.asTypeOf(Vec(BtbWays, Bool()))
145  btbValids := 0.U.asTypeOf(Vec(predictWidth, Bool()))
146  btbTargets := DontCare
147  btbTakens := DontCare
148  btbTypes := DontCare
149  for (w <- 0 until BtbWays) {
150    for (b <- 0 until BtbBanks) { btbReadFire(w)(b) := btbMeta(w)(b).io.r.req.fire() && btbData(w)(b).io.r.req.fire() }
151    when (btbMetaRead(w).valid && btbMetaRead(w).tag === btbAddr.getTag(pcLatch)) {
152      btbWayHits(w) := !flushS1 && RegNext(btbReadFire(w)(btbHitBank), init = false.B)
153      for (i <- until predictWidth) {
154        btbValids(i) := btbDataRead(w)(i).valid
155        btbTargets(i) := btbDataRead(w)(i).target
156        btbTakens(i) := (btbDataRead(w)(i).pred)(1).asBool
157        btbTypes(i) := btbDataRead(w)(i)._type
158      }
159    }
160  }
161
162  val btbHit = btbWayHits.reduce(_|_)
163  btbHitWay := OHToUInt(HighestBit(btbWayHits.asUInt, BtbWays))
164
165  // Priority mux which corresponds with inst orders
166  // BTB only produce one single prediction
167  val btbJumps = Wire(Vec(predictWidth, Bool()))
168  (0 until predictWidth).map(i => btbJumps(i) := btbValids(i) && (btbTypes(i) === BTBtype.J || btbTypes(i) === BTBtype.B && btbTakens(i)))
169  val btbTakenTarget = MuxCase(0.U, btbJumps zip btbTargets)
170  val btbTakenType   = MuxCase(0.U, btbJumps zip btbTypes)
171  val btbTaken       = btbJumps.reduce(_|_)
172  // Record which inst is predicted taken
173  val btbTakenIdx = MuxCase(0.U, btbJumps zip (0 until predictWidth).map(_.U))
174
175  // JBTAC, divided into 8 banks, makes prediction for indirect jump except ret.
176  val jbtacAddr = new TableAddr(log2Up(JbtacSize), JbtacBanks)
177  def jbtacEntry() = new Bundle {
178    val valid = Bool()
179    // TODO: don't need full length of tag and target
180    val tag = UInt(jbtacAddr.tagBits.W)
181    val target = UInt(VAddrBits.W)
182    val offset = UInt(log2Up(FetchWidth).W)
183  }
184
185  val jbtac = List.fill(JbtacBanks)(Module(new SRAMTemplate(jbtacEntry(), set = JbtacSize / JbtacBanks, shouldReset = true, holdRead = true, singlePort = false)))
186
187  val jbtacRead = Wire(Vec(JbtacBanks, jbtacEntry()))
188
189  val jbtacFire = Reg(Vec(JbtacBanks, Bool()))
190  // Only read one bank
191  val histXORAddr = io.in.pc.bits ^ Cat(hist, 0.U(2.W))(VAddrBits - 1, 0)
192  val histXORAddrLatch = RegEnable(histXORAddr, io.in.pc.valid)
193  jbtacFire := 0.U.asTypeOf(Vec(JbtacBanks, Bool()))
194  (0 until JbtacBanks).map(
195    b => {
196      jbtac(b).reset := reset.asBool
197      jbtac(b).io.r.req.valid := io.in.pc.fire() && b.U === jbtacAddr.getBank(histXORAddr)
198      jbtac(b).io.r.req.bits.setIdx := jbtacAddr.getBankIdx(histXORAddr)
199      jbtacFire(b) := jbtac(b).io.r.req.fire()
200      jbtacRead(b) := jbtac(b).io.r.resp.data(0)
201    }
202  )
203
204  val jbtacBank = jbtacAddr.getBank(histXORAddrLatch)
205  val jbtacHit = jbtacRead(jbtacBank).valid && jbtacRead(jbtacBank).tag === jbtacAddr.getTag(histXORAddrLatch) && !flushS1 && jbtacFire(jbtacBank)
206  val jbtacHitIdx = jbtacRead(jbtacBank).offset
207  val jbtacTarget = jbtacRead(jbtacBank).target
208
209  // redirect based on BTB and JBTAC
210  io.out.valid := RegNext(io.in.pc.fire()) && !flushS1
211
212  io.s1OutPred.valid := io.out.valid
213  io.s1OutPred.bits.redirect := btbHit && btbTaken || jbtacHit
214  io.s1OutPred.bits.instrValid := LowerMask(UIntToOH(btbTakenIdx), FetchWidth) & LowerMask(UIntToOH(jbtacHitIdx), FetchWidth)
215  io.s1OutPred.bits.target := Mux(btbTakenIdx < jbtacHitIdx, btbTakenTarget, jbtacTarget)
216  io.s1OutPred.bits.hist := DontCare  //////////////////////TODO
217  io.s1OutPred.bits.rasSp := DontCare
218  io.s1OutPred.bits.rasTopCtr := DontCare
219
220  io.out.bits.pc := pcLatch
221  io.out.bits.btb.hits := btbValids.asUInt
222  (0 until FetchWidth).map(i => io.out.bits.btb.targets(i) := btbTargets(i))
223  io.out.bits.jbtac.hitIdx := UIntToOH(jbtacHitIdx)
224  io.out.bits.jbtac.target := jbtacTarget
225  io.out.bits.hist := DontCare  //////////////////////TODO
226  io.out.bits.btbPred := io.s1OutPred
227
228  io.in.pc.ready := true.B
229
230}
231
232class Stage2To3IO extends Stage1To2IO {
233}
234
235class BPUStage2 extends XSModule {
236  val io = IO(new Bundle() {
237    // flush from Stage3
238    val flush = Input(Bool())
239    val in = Flipped(Decoupled(new Stage1To2IO))
240    val out = Decoupled(new Stage2To3IO)
241  })
242
243  // flush Stage2 when Stage3 or banckend redirects
244  val flushS2 = BoolStopWatch(io.flush, io.in.fire(), startHighPriority = true)
245  io.out.valid := !flushS2 && RegNext(io.in.fire())
246  io.in.ready := !io.out.valid || io.out.fire()
247
248  // do nothing
249  io.out.bits := RegEnable(io.in.bits, io.in.fire())
250}
251
252class BPUStage3 extends XSModule {
253  val io = IO(new Bundle() {
254    val flush = Input(Bool())
255    val in = Flipped(Decoupled(new Stage2To3IO))
256    val out = ValidIO(new BranchPrediction)
257    // from icache
258    val predecode = Flipped(ValidIO(new Predecode))
259    // from backend
260    val redirectInfo = Flipped(new RedirectInfo)
261    // to Stage1 and Stage2
262    val flushBPU = Output(Bool())
263    // to Stage1, restore ghr in stage1 when flushBPU is valid
264    val s1RollBackHist = Output(UInt(HistoryLength.W))
265  })
266
267  val flushS3 = BoolStopWatch(io.flush, io.in.fire(), startHighPriority = true)
268  val inLatch = RegInit(0.U.asTypeOf(io.in.bits))
269  val validLatch = RegInit(false.B)
270  when (io.in.fire()) { inLatch := io.in.bits }
271  when (io.in.fire()) {
272    validLatch := !io.flush
273  }.elsewhen (io.out.valid) {
274    validLatch := false.B
275  }
276  io.out.valid := validLatch && io.predecode.valid && !flushS3
277  io.in.ready := !validLatch || io.out.valid
278
279  // RAS
280  // TODO: split retAddr and ctr
281  def rasEntry() = new Bundle {
282    val retAddr = UInt(VAddrBits.W)
283    val ctr = UInt(8.W) // layer of nested call functions
284  }
285  val ras = RegInit(VecInit(Seq.fill(RasSize)(0.U.asTypeOf(rasEntry()))))
286  val sp = Counter(RasSize)
287  val rasTop = ras(sp.value)
288  val rasTopAddr = rasTop.retAddr
289
290  // get the first taken branch/jal/call/jalr/ret in a fetch line
291  // brTakenIdx/jalIdx/callIdx/jalrIdx/retIdx/jmpIdx is one-hot encoded.
292  // brNotTakenIdx indicates all the not-taken branches before the first jump instruction.
293  val brIdx = inLatch.btb.hits & Cat(io.predecode.bits.fuTypes.map { t => ALUOpType.isBranch(t) }).asUInt & io.predecode.bits.mask
294  val brTakenIdx = LowestBit(brIdx & inLatch.tage.takens.asUInt, FetchWidth)
295  val jalIdx = LowestBit(inLatch.btb.hits & Cat(io.predecode.bits.fuTypes.map { t => t === ALUOpType.jal }).asUInt & io.predecode.bits.mask, FetchWidth)
296  val callIdx = LowestBit(inLatch.btb.hits & io.predecode.bits.mask & Cat(io.predecode.bits.fuTypes.map { t => t === ALUOpType.call }).asUInt, FetchWidth)
297  val jalrIdx = LowestBit(inLatch.jbtac.hitIdx & io.predecode.bits.mask & Cat(io.predecode.bits.fuTypes.map { t => t === ALUOpType.jalr }).asUInt, FetchWidth)
298  val retIdx = LowestBit(io.predecode.bits.mask & Cat(io.predecode.bits.fuTypes.map { t => t === ALUOpType.ret }).asUInt, FetchWidth)
299
300  val jmpIdx = LowestBit(brTakenIdx | jalIdx | callIdx | jalrIdx | retIdx, FetchWidth)
301  val brNotTakenIdx = brIdx & ~inLatch.tage.takens.asUInt & LowerMask(jmpIdx, FetchWidth)
302
303  io.out.bits.redirect := jmpIdx.orR.asBool
304  io.out.bits.target := Mux(jmpIdx === retIdx, rasTopAddr,
305    Mux(jmpIdx === jalrIdx, inLatch.jbtac.target,
306    Mux(jmpIdx === 0.U, inLatch.pc + 4.U, // TODO: RVC
307    PriorityMux(jmpIdx, inLatch.btb.targets))))
308  io.out.bits.instrValid := LowerMask(jmpIdx, FetchWidth).asTypeOf(Vec(FetchWidth, Bool()))
309  io.out.bits.tageMeta := inLatch.btbPred.bits.tageMeta
310  //io.out.bits._type := Mux(jmpIdx === retIdx, BTBtype.R,
311  //  Mux(jmpIdx === jalrIdx, BTBtype.I,
312  //  Mux(jmpIdx === brTakenIdx, BTBtype.B, BTBtype.J)))
313  val firstHist = inLatch.btbPred.bits.hist(0)
314  // there may be several notTaken branches before the first jump instruction,
315  // so we need to calculate how many zeroes should each instruction shift in its global history.
316  // each history is exclusive of instruction's own jump direction.
317  val histShift = Wire(Vec(FetchWidth, UInt(log2Up(FetchWidth).W)))
318  val shift = Wire(Vec(FetchWidth, Vec(FetchWidth, UInt(1.W))))
319  (0 until FetchWidth).map(i => shift(i) := Mux(!brNotTakenIdx(i), 0.U, ~LowerMask(UIntToOH(i.U), FetchWidth)).asTypeOf(Vec(FetchWidth, UInt(1.W))))
320  for (j <- 0 until FetchWidth) {
321    var tmp = 0.U
322    for (i <- 0 until FetchWidth) {
323      tmp = tmp + shift(i)(j)
324    }
325    histShift(j) := tmp
326  }
327  (0 until FetchWidth).map(i => io.out.bits.hist(i) := firstHist << histShift(i))
328  // save ras checkpoint info
329  io.out.bits.rasSp := sp.value
330  io.out.bits.rasTopCtr := rasTop.ctr
331
332  // flush BPU and redirect when target differs from the target predicted in Stage1
333  io.out.bits.redirect := !inLatch.btbPred.bits.redirect ^ jmpIdx.orR.asBool ||
334    inLatch.btbPred.bits.redirect && jmpIdx.orR.asBool && io.out.bits.target =/= inLatch.btbPred.bits.target
335  io.flushBPU := io.out.bits.redirect && io.out.valid
336
337  // speculative update RAS
338  val rasWrite = WireInit(0.U.asTypeOf(rasEntry()))
339  rasWrite.retAddr := inLatch.pc + OHToUInt(callIdx) << 2.U + 4.U
340  val allocNewEntry = rasWrite.retAddr =/= rasTopAddr
341  rasWrite.ctr := Mux(allocNewEntry, 1.U, rasTop.ctr + 1.U)
342  when (io.out.valid) {
343    when (jmpIdx === callIdx) {
344      ras(Mux(allocNewEntry, sp.value + 1.U, sp.value)) := rasWrite
345      when (allocNewEntry) { sp.value := sp.value + 1.U }
346    }.elsewhen (jmpIdx === retIdx) {
347      when (rasTop.ctr === 1.U) {
348        sp.value := Mux(sp.value === 0.U, 0.U, sp.value - 1.U)
349      }.otherwise {
350        ras(sp.value) := Cat(rasTop.ctr - 1.U, rasTopAddr).asTypeOf(rasEntry())
351      }
352    }
353  }
354  // use checkpoint to recover RAS
355  val recoverSp = io.redirectInfo.redirect.rasSp
356  val recoverCtr = io.redirectInfo.redirect.rasTopCtr
357  when (io.redirectInfo.valid && io.redirectInfo.misPred) {
358    sp.value := recoverSp
359    ras(recoverSp) := Cat(recoverCtr, ras(recoverSp).retAddr).asTypeOf(rasEntry())
360  }
361
362  // roll back global history in S1 if S3 redirects
363  io.s1RollBackHist := PriorityMux(jmpIdx, io.out.bits.hist)
364}
365
366class BPU extends XSModule {
367  val io = IO(new Bundle() {
368    // from backend
369    // flush pipeline if misPred and update bpu based on redirect signals from brq
370    val redirectInfo = Flipped(new RedirectInfo)
371
372    val in = new Bundle { val pc = Flipped(Valid(UInt(VAddrBits.W))) }
373
374    val btbOut = ValidIO(new BranchPrediction)
375    val tageOut = ValidIO(new BranchPrediction)
376
377    // predecode info from icache
378    // TODO: simplify this after implement predecode unit
379    val predecode = Flipped(ValidIO(new Predecode))
380  })
381
382  val s1 = Module(new BPUStage1)
383  val s2 = Module(new BPUStage2)
384  val s3 = Module(new BPUStage3)
385
386  s1.io.redirectInfo <> io.redirectInfo
387  s1.io.flush := s3.io.flushBPU || io.redirectInfo.flush()
388  s1.io.in.pc.valid := io.in.pc.valid
389  s1.io.in.pc.bits <> io.in.pc.bits
390  io.btbOut <> s1.io.s1OutPred
391  s1.io.s3RollBackHist := s3.io.s1RollBackHist
392
393  s1.io.out <> s2.io.in
394  s2.io.flush := s3.io.flushBPU || io.redirectInfo.flush()
395
396  s2.io.out <> s3.io.in
397  s3.io.flush := io.redirectInfo.flush()
398  s3.io.predecode <> io.predecode
399  io.tageOut <> s3.io.out
400  s3.io.redirectInfo <> io.redirectInfo
401}