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