xref: /XiangShan/src/main/scala/xiangshan/frontend/BPU.scala (revision 5de128a3bbb2758419e54bf9f783b60c4c322b0f)
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 BPU extends XSModule {
23  val io = IO(new Bundle() {
24    val flush = Input(Bool())
25    val in = new Bundle { val pc = Flipped(Valid(UInt(VAddrBits.W))) }
26    val out = new Bundle { val redirect = Valid(UInt(VAddrBits.W)) }
27  })
28
29  val flush = BoolStopWatch(io.flush, io.in.pc.valid, startHighPriority = true)
30
31  // BTB makes a quick prediction for branch and direct jump, which is
32  // 4-way set-associative, and each way is divided into 4 banks.
33  val btbAddr = new TableAddr(log2Up(BtbSets), BtbBanks)
34  def btbEntry() = new Bundle {
35    val valid = Bool()
36    // TODO: don't need full length of tag and target
37    val tag = UInt(btbAddr.tagBits.W)
38    val _type = UInt(2.W)
39    val target = UInt(VAddrBits.W)
40    val pred = UInt(2.W) // 2-bit saturated counter as a quick predictor
41  }
42
43  val btb = List.fill(BtbBanks)(List.fill(BtbWays)(
44    Module(new SRAMTemplate(btbEntry(), set = BtbSets / BtbBanks, shouldReset = true, holdRead = true, singlePort = true))))
45
46  // val fetchPkgAligned = btbAddr.getBank(io.in.pc.bits) === 0.U
47  val HeadBank = btbAddr.getBank(io.in.pc.bits)
48  val TailBank = btbAddr.getBank(io.in.pc.bits + FetchWidth.U << 2.U - 4.U)
49  for (b <- 0 until BtbBanks) {
50    for (w <- 0 until BtbWays) {
51      btb(b)(w).reset := reset.asBool
52      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)
53      btb(b)(w).io.r.req.bits.setIdx := btbAddr.getBankIdx(io.in.pc.bits)
54    }
55  }
56  // latch pc for 1 cycle latency when reading SRAM
57  val pcLatch = RegEnable(io.in.pc.bits, io.in.pc.valid)
58  val btbRead = Wire(Vec(BtbBanks, Vec(BtbWays, btbEntry())))
59  val btbHits = Wire(Vec(FetchWidth, Bool()))
60  val btbTargets = Wire(Vec(FetchWidth, UInt(VAddrBits.W)))
61  val btbTypes = Wire(Vec(FetchWidth, UInt(2.W)))
62  // val btbPreds = Wire(Vec(FetchWidth, UInt(2.W)))
63  val btbTakens = Wire(Vec(FetchWidth, Bool()))
64  for (b <- 0 until BtbBanks) {
65    for (w <- 0 until BtbWays) {
66      btbRead(b)(w) := btb(b)(w).io.r.resp.data(0)
67    }
68  }
69  for (i <- 0 until FetchWidth) {
70    btbHits(i) := false.B
71    for (b <- 0 until BtbBanks) {
72      when (b.U === btbAddr.getBank(pcLatch)) {
73        for (w <- 0 until BtbWays) {
74          when (btbRead(b)(w).valid && btbRead(b)(w).tag === btbAddr.getTag(Cat(pcLatch(VAddrBits - 1, 2), 0.U(2.W)) + i.U << 2)) {
75            btbHits(i) := !flush && RegNext(btb(b)(w).io.r.req.fire(), init = false.B)
76            btbTargets(i) := btbRead(b)(w).target
77            btbTypes(i) := btbRead(b)(w)._type
78            // btbPreds(i) := btbRead(b)(w).pred
79            btbTakens(i) := (btbRead(b)(w).pred)(1).asBool
80          }
81        }
82      }
83    }
84  }
85
86  // JBTAC, divided into 8 banks, makes prediction for indirect jump except ret.
87  val jbtacAddr = new TableAddr(log2Up(JbtacSize), JbtacBanks)
88  def jbtacEntry() = new Bundle {
89    val valid = Bool()
90    // TODO: don't need full length of tag and target
91    val tag = UInt(jbtacAddr.tagBits.W)
92    val target = UInt(VAddrBits.W)
93  }
94
95  val jbtac = List.fill(JbtacBanks)(Module(new SRAMTemplate(jbtacEntry(), set = JbtacSize / JbtacBanks, shouldReset = true, holdRead = true, singlePort = true)))
96
97  (0 until JbtacBanks).map(i => jbtac(i).reset := reset.asBool)
98  (0 until JbtacBanks).map(i => jbtac(i).io.r.req.valid := io.in.pc.valid)
99  (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))
100
101  val jbtacRead = Wire(Vec(JbtacBanks, jbtacEntry()))
102  (0 until JbtacBanks).map(i => jbtacRead(i) := jbtac(i).io.r.resp.data(0))
103  val jbtacHits = Wire(Vec(FetchWidth, Bool()))
104  val jbtacTargets = Wire(Vec(FetchWidth, UInt(VAddrBits.W)))
105  val jbtacHeadBank = jbtacAddr.getBank(Cat(pcLatch(VAddrBits - 1, 2), 0.U(2.W)))
106  for (i <- 0 until FetchWidth) {
107    jbtacHits(i) := false.B
108    for (b <- 0 until JbtacBanks) {
109      when (jbtacHeadBank + i.U === b.U) {
110        jbtacHits(i) := jbtacRead(b).valid && jbtacRead(b).tag === jbtacAddr.getTag(Cat(pcLatch(VAddrBits - 1, 2), 0.U(2.W)) + i.U << 2) &&
111          !flush && RegNext(jbtac(b).io.r.req.fire(), init = false.B)
112        jbtacTargets(i) := jbtacRead(b).target
113      }
114    }
115  }
116
117  // redirect based on BTB and JBTAC
118  val redirectMask = Wire(Vec(FetchWidth, Bool()))
119  val redirectTarget = Wire(Vec(FetchWidth, UInt(VAddrBits.W)))
120  (0 until FetchWidth).map(i => redirectMask(i) := btbHits(i) && Mux(btbTypes(i) === BTBtype.B, btbTakens(i), true.B) || jbtacHits(i))
121  (0 until FetchWidth).map(i => redirectTarget(i) := Mux(btbHits(i) && !(btbTypes(i) === BTBtype.B && !btbTakens(i)), btbTargets(i), jbtacTargets(i)))
122  io.out.redirect.valid := redirectMask.asUInt.orR
123  io.out.redirect.bits := PriorityMux(redirectMask, redirectTarget)
124
125}
126