xref: /XiangShan/src/main/scala/xiangshan/frontend/BPU.scala (revision a1803a8415082983a9a912ab42115c40b02121e7)
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    // update bpu based on redirect signals from brq
26    val redirect = Flipped(ValidIO(new Redirect))
27    val in = new Bundle { val pc = Flipped(Valid(UInt(VAddrBits.W))) }
28    val predMask = Output(Vec(FetchWidth, Bool()))
29    val predTargets = Output(Vec(FetchWidth, UInt(VAddrBits.W)))
30  })
31
32  val flush = BoolStopWatch(io.redirect.valid, io.in.pc.valid, startHighPriority = true)
33
34  // BTB makes a quick prediction for branch and direct jump, which is
35  // 4-way set-associative, and each way is divided into 4 banks.
36  val btbAddr = new TableAddr(log2Up(BtbSets), BtbBanks)
37  def btbEntry() = new Bundle {
38    val valid = Bool()
39    // TODO: don't need full length of tag and target
40    val tag = UInt(btbAddr.tagBits.W)
41    val _type = UInt(2.W)
42    val target = UInt(VAddrBits.W)
43    val pred = UInt(2.W) // 2-bit saturated counter as a quick predictor
44  }
45
46  val btb = List.fill(BtbBanks)(List.fill(BtbWays)(
47    Module(new SRAMTemplate(btbEntry(), set = BtbSets / BtbBanks, shouldReset = true, holdRead = true, singlePort = true))))
48
49  // val fetchPkgAligned = btbAddr.getBank(io.in.pc.bits) === 0.U
50  val HeadBank = btbAddr.getBank(io.in.pc.bits)
51  val TailBank = btbAddr.getBank(io.in.pc.bits + FetchWidth.U << 2.U - 4.U)
52  for (b <- 0 until BtbBanks) {
53    for (w <- 0 until BtbWays) {
54      btb(b)(w).reset := reset.asBool
55      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)
56      btb(b)(w).io.r.req.bits.setIdx := btbAddr.getBankIdx(io.in.pc.bits)
57    }
58  }
59  // latch pc for 1 cycle latency when reading SRAM
60  val pcLatch = RegEnable(io.in.pc.bits, io.in.pc.valid)
61  val btbRead = Wire(Vec(BtbBanks, Vec(BtbWays, btbEntry())))
62  val btbHits = Wire(Vec(FetchWidth, Bool()))
63  val btbTargets = Wire(Vec(FetchWidth, UInt(VAddrBits.W)))
64  val btbTypes = Wire(Vec(FetchWidth, UInt(2.W)))
65  // val btbPreds = Wire(Vec(FetchWidth, UInt(2.W)))
66  val btbTakens = Wire(Vec(FetchWidth, Bool()))
67  for (b <- 0 until BtbBanks) {
68    for (w <- 0 until BtbWays) {
69      btbRead(b)(w) := btb(b)(w).io.r.resp.data(0)
70    }
71  }
72  for (i <- 0 until FetchWidth) {
73    btbHits(i) := false.B
74    for (b <- 0 until BtbBanks) {
75      for (w <- 0 until BtbWays) {
76        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)) {
77          btbHits(i) := !flush && RegNext(btb(b)(w).io.r.req.fire(), init = false.B)
78          btbTargets(i) := btbRead(b)(w).target
79          btbTypes(i) := btbRead(b)(w)._type
80          // btbPreds(i) := btbRead(b)(w).pred
81          btbTakens(i) := (btbRead(b)(w).pred)(1).asBool
82        }.otherwise {
83          btbHits(i) := false.B
84          btbTargets(i) := DontCare
85          btbTypes(i) := DontCare
86          btbTakens(i) := DontCare
87        }
88      }
89    }
90  }
91
92  // JBTAC, divided into 8 banks, makes prediction for indirect jump except ret.
93  val jbtacAddr = new TableAddr(log2Up(JbtacSize), JbtacBanks)
94  def jbtacEntry() = new Bundle {
95    val valid = Bool()
96    // TODO: don't need full length of tag and target
97    val tag = UInt(jbtacAddr.tagBits.W)
98    val target = UInt(VAddrBits.W)
99  }
100
101  val jbtac = List.fill(JbtacBanks)(Module(new SRAMTemplate(jbtacEntry(), set = JbtacSize / JbtacBanks, shouldReset = true, holdRead = true, singlePort = true)))
102
103  (0 until JbtacBanks).map(i => jbtac(i).reset := reset.asBool)
104  (0 until JbtacBanks).map(i => jbtac(i).io.r.req.valid := io.in.pc.valid)
105  (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))
106
107  val jbtacRead = Wire(Vec(JbtacBanks, jbtacEntry()))
108  (0 until JbtacBanks).map(i => jbtacRead(i) := jbtac(i).io.r.resp.data(0))
109  val jbtacHits = Wire(Vec(FetchWidth, Bool()))
110  val jbtacTargets = Wire(Vec(FetchWidth, UInt(VAddrBits.W)))
111  val jbtacHeadBank = jbtacAddr.getBank(Cat(pcLatch(VAddrBits - 1, 2), 0.U(2.W)))
112  for (i <- 0 until FetchWidth) {
113    jbtacHits(i) := false.B
114    for (b <- 0 until JbtacBanks) {
115      when (jbtacHeadBank + i.U === b.U) {
116        jbtacHits(i) := jbtacRead(b).valid && jbtacRead(b).tag === jbtacAddr.getTag(Cat(pcLatch(VAddrBits - 1, 2), 0.U(2.W)) + i.U << 2) &&
117          !flush && RegNext(jbtac(b).io.r.req.fire(), init = false.B)
118        jbtacTargets(i) := jbtacRead(b).target
119      }.otherwise {
120        jbtacHits(i) := false.B
121        jbtacTargets(i) := DontCare
122      }
123    }
124  }
125
126  // redirect based on BTB and JBTAC
127  (0 until FetchWidth).map(i => io.predMask(i) := btbHits(i) && Mux(btbTypes(i) === BTBtype.B, btbTakens(i), true.B) || jbtacHits(i))
128  (0 until FetchWidth).map(i => io.predTargets(i) := Mux(btbHits(i) && !(btbTypes(i) === BTBtype.B && !btbTakens(i)), btbTargets(i), jbtacTargets(i)))
129
130
131  // update bpu, including BTB, JBTAC...
132  // 1. update BTB
133  // 1.1 read the selected bank
134  for (b <- 0 until BtbBanks) {
135    for (w <- 0 until BtbWays) {
136      btb(b)(w).io.r.req.valid := io.redirect.valid && btbAddr.getBank(io.redirect.bits.pc) === b.U
137      btb(b)(w).io.r.req.bits.setIdx := btbAddr.getBankIdx(io.redirect.bits.pc)
138    }
139  }
140
141  // 1.2 match redirect pc tag with the 4 tags in a btb line, find a way to write
142  // val redirectLatch = RegEnable(io.redirect.bits, io.redirect.valid)
143  val redirectLatch = RegNext(io.redirect.bits, init = 0.U.asTypeOf(new Redirect))
144  val bankLatch = btbAddr.getBank(redirectLatch.pc)
145  val btbUpdateRead = Wire(Vec(BtbWays, btbEntry()))
146  val btbValids = Wire(Vec(BtbWays, Bool()))
147  val btbUpdateTagHits = Wire(Vec(BtbWays, Bool()))
148  for (b <- 0 until BtbBanks) {
149    for (w <- 0 until BtbWays) {
150      when (b.U === bankLatch) {
151        btbUpdateRead(w) := btb(b)(w).io.r.resp.data(0)
152        btbValids(w) := btbUpdateRead(w).valid && RegNext(btb(b)(w).io.r.req.fire(), init = false.B)
153      }.otherwise {
154        btbUpdateRead(w) := 0.U.asTypeOf(btbEntry())
155        btbValids(w) := false.B
156      }
157    }
158  }
159  (0 until BtbWays).map(w => btbUpdateTagHits(w) := btbValids(w) && btbUpdateRead(w).tag === btbAddr.getTag(redirectLatch.pc))
160  // val btbWriteWay = Wire(Vec(BtbWays, Bool()))
161  val btbWriteWay = Wire(UInt(BtbWays.W))
162  val btbInvalids = ~ btbValids.asUInt
163  when (btbUpdateTagHits.asUInt.orR) {
164    // tag hits
165    btbWriteWay := btbUpdateTagHits.asUInt
166  }.elsewhen (!btbValids.asUInt.andR) {
167    // no tag hits but there are free entries
168    btbWriteWay := Mux(btbInvalids >= 8.U, "b1000".U,
169      Mux(btbInvalids >= 4.U, "b0100".U,
170      Mux(btbInvalids >= 2.U, "b0010".U, "b0001".U)))
171  }.otherwise {
172    // no tag hits and no free entry, select a victim way
173    btbWriteWay := UIntToOH(LFSR64()(log2Up(BtbWays) - 1, 0))
174  }
175
176  // 1.3 calculate new 2-bit counter value
177  val btbWrite = WireInit(0.U.asTypeOf(btbEntry()))
178  btbWrite.valid := true.B
179  btbWrite.tag := btbAddr.getTag(redirectLatch.pc)
180  btbWrite._type := redirectLatch._type
181  btbWrite.target := redirectLatch.brTarget
182  val oldPred = WireInit("b01".U)
183  oldPred := PriorityMux(btbWriteWay.asTypeOf(Vec(BtbWays, Bool())), btbUpdateRead.map{ e => e.pred })
184  val newPred = Mux(redirectLatch.taken, Mux(oldPred === "b11".U, "b11".U, oldPred + 1.U),
185    Mux(oldPred === "b00".U, "b00".U, oldPred - 1.U))
186  btbWrite.pred := Mux(btbUpdateTagHits.asUInt.orR && redirectLatch._type === BTBtype.B, newPred, "b01".U)
187
188  // 1.4 write BTB
189  for (b <- 0 until BtbBanks) {
190    for (w <- 0 until BtbWays) {
191      when (b.U === bankLatch) {
192        btb(b)(w).io.w.req.valid := OHToUInt(btbWriteWay) === w.U &&
193          RegNext(io.redirect.valid, init = false.B) &&
194          (redirectLatch._type === BTBtype.B || redirectLatch._type === BTBtype.J)
195        btb(b)(w).io.w.req.bits.setIdx := btbAddr.getBankIdx(redirectLatch.pc)
196        btb(b)(w).io.w.req.bits.data := btbWrite
197      }.otherwise {
198        btb(b)(w).io.w.req.valid := false.B
199        btb(b)(w).io.w.req.bits.setIdx := DontCare
200        btb(b)(w).io.w.req.bits.data := DontCare
201      }
202    }
203  }
204
205  // 2. update JBTAC
206  val jbtacWrite = WireInit(0.U.asTypeOf(jbtacEntry()))
207  jbtacWrite.valid := true.B
208  jbtacWrite.tag := jbtacAddr.getTag(io.redirect.bits.pc)
209  jbtacWrite.target := io.redirect.bits.target
210  (0 until JbtacBanks).map(b =>
211    jbtac(b).io.w.req.valid := io.redirect.valid &&
212      b.U === jbtacAddr.getBank(io.redirect.bits.pc) &&
213      io.redirect.bits._type === BTBtype.I)
214  (0 until JbtacBanks).map(b => jbtac(b).io.w.req.bits.setIdx := jbtacAddr.getBankIdx(io.redirect.bits.pc))
215  (0 until JbtacBanks).map(b => jbtac(b).io.w.req.bits.data := jbtacWrite)
216}
217