xref: /XiangShan/src/main/scala/xiangshan/frontend/BPU.scala (revision 2445e0c0d14b74fd77d6510ff2b020097d3dffa3)
1package xiangshan.frontend
2
3import chisel3._
4import chisel3.util._
5import xiangshan._
6import xiangshan.utils._
7import xiangshan.backend.ALUOpType
8import utils._
9import chisel3.util.experimental.BoringUtils
10import xiangshan.backend.decode.XSTrap
11
12class TableAddr(val idxBits: Int, val banks: Int) extends XSBundle {
13  def tagBits = VAddrBits - idxBits - 2
14
15  val tag = UInt(tagBits.W)
16  val idx = UInt(idxBits.W)
17  val offset = UInt(2.W)
18
19  def fromUInt(x: UInt) = x.asTypeOf(UInt(VAddrBits.W)).asTypeOf(this)
20  def getTag(x: UInt) = fromUInt(x).tag
21  def getIdx(x: UInt) = fromUInt(x).idx
22  def getBank(x: UInt) = getIdx(x)(log2Up(banks) - 1, 0)
23  def getBankIdx(x: UInt) = getIdx(x)(idxBits - 1, log2Up(banks))
24}
25
26class Stage1To2IO extends XSBundle {
27  val pc = Output(UInt(VAddrBits.W))
28  val btb = new Bundle {
29    val hits = Output(UInt(FetchWidth.W))
30    val targets = Output(Vec(FetchWidth, UInt(VAddrBits.W)))
31  }
32  val jbtac = new Bundle {
33    val hitIdx = Output(UInt(FetchWidth.W))
34    val target = Output(UInt(VAddrBits.W))
35  }
36  val tage = new Bundle {
37    val hits = Output(UInt(FetchWidth.W))
38    val takens = Output(Vec(FetchWidth, Bool()))
39  }
40  val hist = Output(Vec(FetchWidth, UInt(HistoryLength.W)))
41  val btbPred = ValidIO(new BranchPrediction)
42}
43
44class BPUStage1 extends XSModule {
45  val io = IO(new Bundle() {
46    val in = new Bundle { val pc = Flipped(Decoupled(UInt(VAddrBits.W))) }
47    // from backend
48    val redirectInfo = Input(new RedirectInfo)
49    // from Stage3
50    val flush = Input(Bool())
51    val s3RollBackHist = Input(UInt(HistoryLength.W))
52    val s3Taken = Input(Bool())
53    // to ifu, quick prediction result
54    val s1OutPred = ValidIO(new BranchPrediction)
55    // to Stage2
56    val out = Decoupled(new Stage1To2IO)
57  })
58
59  io.in.pc.ready := true.B
60
61  // flush Stage1 when io.flush
62  val flushS1 = BoolStopWatch(io.flush, io.in.pc.fire(), startHighPriority = true)
63
64  // global history register
65  val ghr = RegInit(0.U(HistoryLength.W))
66  // modify updateGhr and newGhr when updating ghr
67  val updateGhr = WireInit(false.B)
68  val newGhr = WireInit(0.U(HistoryLength.W))
69  when (updateGhr) { ghr := newGhr }
70  // use hist as global history!!!
71  val hist = Mux(updateGhr, newGhr, ghr)
72
73  // Tage predictor
74  // val tage = Module(new FakeTAGE)
75  val tage = if(EnableBPD) Module(new Tage) else Module(new FakeTAGE)
76  tage.io.req.valid := io.in.pc.fire()
77  tage.io.req.bits.pc := io.in.pc.bits
78  tage.io.req.bits.hist := hist
79  tage.io.redirectInfo <> io.redirectInfo
80  io.out.bits.tage <> tage.io.out
81  io.s1OutPred.bits.tageMeta := tage.io.meta
82
83  // latch pc for 1 cycle latency when reading SRAM
84  val pcLatch = RegEnable(io.in.pc.bits, io.in.pc.fire())
85
86  val r = io.redirectInfo.redirect
87  val updateFetchpc = r.pc - r.fetchIdx << 2.U
88  // BTB
89  val btb = Module(new BTB)
90  btb.io.in.pc <> io.in.pc
91  btb.io.in.pcLatch := pcLatch
92  btb.io.redirectValid := io.redirectInfo.valid
93  btb.io.flush := io.flush
94
95  btb.io.update.fetchPC := updateFetchpc
96  btb.io.update.fetchIdx := r.fetchIdx
97  btb.io.update.hit := r.btbHitWay
98  btb.io.update.misPred := io.redirectInfo.misPred
99  btb.io.update.writeWay := r.btbVictimWay
100  btb.io.update.oldCtr := r.btbPredCtr
101  btb.io.update.taken := r.taken
102  btb.io.update.target := r.brTarget
103  btb.io.update._type := r._type
104
105  val btbHit = btb.io.out.hit
106  val btbTaken = btb.io.out.taken
107  val btbTakenIdx = btb.io.out.takenIdx
108  val btbTakenTarget = btb.io.out.target
109  val btbWriteWay = btb.io.out.writeWay
110  val btbNotTakens = btb.io.out.notTakens
111  val btbCtrs = VecInit(btb.io.out.dEntries.map(_.pred))
112  val btbValids = VecInit(btb.io.out.dEntries.map(_.valid))
113  val btbTargets = VecInit(btb.io.out.dEntries.map(_.target))
114  val btbTypes = VecInit(btb.io.out.dEntries.map(_._type))
115
116
117  val jbtac = Module(new JBTAC)
118  jbtac.io.in.pc <> io.in.pc
119  jbtac.io.in.pcLatch := pcLatch
120  jbtac.io.in.hist := hist
121  jbtac.io.redirectValid := io.redirectInfo.valid
122  jbtac.io.flush := io.flush
123
124  jbtac.io.update.fetchPC := updateFetchpc
125  jbtac.io.update.fetchIdx := r.fetchIdx
126  jbtac.io.update.misPred := io.redirectInfo.misPred
127  jbtac.io.update._type := r._type
128  jbtac.io.update.target := r.target
129  jbtac.io.update.hist := r.hist
130
131  val jbtacHit = jbtac.io.out.hit
132  val jbtacTarget = jbtac.io.out.target
133  val jbtacHitIdx = jbtac.io.out.hitIdx
134
135  // calculate global history of each instr
136  val firstHist = RegNext(hist)
137  val histShift = Wire(Vec(FetchWidth, UInt(log2Up(FetchWidth).W)))
138  val shift = Wire(Vec(FetchWidth, Vec(FetchWidth, UInt(1.W))))
139  (0 until FetchWidth).map(i => shift(i) := Mux(!btbNotTakens(i), 0.U, ~LowerMask(UIntToOH(i.U), FetchWidth)).asTypeOf(Vec(FetchWidth, UInt(1.W))))
140  for (j <- 0 until FetchWidth) {
141    var tmp = 0.U
142    for (i <- 0 until FetchWidth) {
143      tmp = tmp + shift(i)(j)
144    }
145    histShift(j) := tmp
146  }
147  (0 until FetchWidth).map(i => io.s1OutPred.bits.hist(i) := firstHist << histShift(i))
148
149  // update ghr
150  updateGhr := io.s1OutPred.bits.redirect || io.flush
151  val brJumpIdx = Mux(!(btbHit && btbTaken), 0.U, UIntToOH(btbTakenIdx))
152  val indirectIdx = Mux(!jbtacHit, 0.U, UIntToOH(jbtacHitIdx))
153  //val newTaken = Mux(io.redirectInfo.flush(), !(r._type === BTBtype.B && !r.taken), )
154  newGhr := Mux(io.redirectInfo.flush(),    (r.hist << 1.U) | !(r._type === BTBtype.B && !r.taken),
155            Mux(io.flush,                   Mux(io.s3Taken, io.s3RollBackHist << 1.U | 1.U, io.s3RollBackHist),
156            Mux(io.s1OutPred.bits.redirect, PriorityMux(brJumpIdx | indirectIdx, io.s1OutPred.bits.hist) << 1.U | 1.U,
157                                            io.s1OutPred.bits.hist(0) << PopCount(btbNotTakens))))
158
159  // redirect based on BTB and JBTAC
160  // io.out.valid := RegNext(io.in.pc.fire()) && !flushS1
161  io.out.valid := RegNext(io.in.pc.fire()) && !io.flush
162
163  io.s1OutPred.valid := io.out.valid
164  io.s1OutPred.bits.redirect := btbHit && btbTaken || jbtacHit
165  // io.s1OutPred.bits.instrValid := LowerMask(UIntToOH(btbTakenIdx), FetchWidth) & LowerMask(UIntToOH(jbtacHitIdx), FetchWidth)
166  io.s1OutPred.bits.instrValid := Mux(io.s1OutPred.bits.redirect, LowerMask(LowestBit(brJumpIdx | indirectIdx, FetchWidth), FetchWidth), Fill(FetchWidth, 1.U(1.W))).asTypeOf(Vec(FetchWidth, Bool()))
167  io.s1OutPred.bits.target := Mux(brJumpIdx === LowestBit(brJumpIdx | indirectIdx, FetchWidth), btbTakenTarget, jbtacTarget)
168  io.s1OutPred.bits.btbVictimWay := btbWriteWay
169  io.s1OutPred.bits.predCtr := btbCtrs
170  io.s1OutPred.bits.btbHitWay := btbHit
171  io.s1OutPred.bits.rasSp := DontCare
172  io.s1OutPred.bits.rasTopCtr := DontCare
173
174  io.out.bits.pc := pcLatch
175  io.out.bits.btb.hits := btbValids.asUInt
176  (0 until FetchWidth).map(i => io.out.bits.btb.targets(i) := btbTargets(i))
177  io.out.bits.jbtac.hitIdx := UIntToOH(jbtacHitIdx)
178  io.out.bits.jbtac.target := jbtacTarget
179  // TODO: we don't need this repeatedly!
180  io.out.bits.hist := io.s1OutPred.bits.hist
181  io.out.bits.btbPred := io.s1OutPred
182
183
184
185  // debug info
186  XSDebug(true.B, "[BPUS1]in:(%d %d)   pc=%x ghr=%b\n", io.in.pc.valid, io.in.pc.ready, io.in.pc.bits, hist)
187  XSDebug(true.B, "[BPUS1]outPred:(%d) redirect=%d instrValid=%b tgt=%x\n",
188    io.s1OutPred.valid, io.s1OutPred.bits.redirect, io.s1OutPred.bits.instrValid.asUInt, io.s1OutPred.bits.target)
189  XSDebug(io.flush && io.redirectInfo.flush(),
190    "[BPUS1]flush from backend: pc=%x tgt=%x brTgt=%x _type=%b taken=%d oldHist=%b fetchIdx=%d isExcpt=%d\n",
191    r.pc, r.target, r.brTarget, r._type, r.taken, r.hist, r.fetchIdx, r.isException)
192  XSDebug(io.flush && !io.redirectInfo.flush(),
193    "[BPUS1]flush from Stage3:  s3Taken=%d s3RollBackHist=%b\n", io.s3Taken, io.s3RollBackHist)
194
195}
196
197class Stage2To3IO extends Stage1To2IO {
198}
199
200class BPUStage2 extends XSModule {
201  val io = IO(new Bundle() {
202    // flush from Stage3
203    val flush = Input(Bool())
204    val in = Flipped(Decoupled(new Stage1To2IO))
205    val out = Decoupled(new Stage2To3IO)
206  })
207
208  // flush Stage2 when Stage3 or banckend redirects
209  val flushS2 = BoolStopWatch(io.flush, io.in.fire(), startHighPriority = true)
210  val inLatch = RegInit(0.U.asTypeOf(io.in.bits))
211  when (io.in.fire()) { inLatch := io.in.bits }
212  val validLatch = RegInit(false.B)
213  when (io.in.fire()) {
214    validLatch := !io.flush
215  }.elsewhen (io.out.fire()) {
216    validLatch := false.B
217  }
218
219  io.out.valid := !io.flush && !flushS2 && validLatch
220  io.in.ready := !validLatch || io.out.fire()
221
222  // do nothing
223  io.out.bits := inLatch
224
225  // debug info
226  XSDebug(true.B, "[BPUS2]in:(%d %d) pc=%x out:(%d %d) pc=%x\n",
227    io.in.valid, io.in.ready, io.in.bits.pc, io.out.valid, io.out.ready, io.out.bits.pc)
228  XSDebug(true.B, "[BPUS2]validLatch=%d pc=%x\n", validLatch, inLatch.pc)
229  XSDebug(io.flush, "[BPUS2]flush!!!\n")
230}
231
232class BPUStage3 extends XSModule {
233  val io = IO(new Bundle() {
234    val flush = Input(Bool())
235    val in = Flipped(Decoupled(new Stage2To3IO))
236    val out = ValidIO(new BranchPrediction)
237    // from icache
238    val predecode = Flipped(ValidIO(new Predecode))
239    // from backend
240    val redirectInfo = Input(new RedirectInfo)
241    // to Stage1 and Stage2
242    val flushBPU = Output(Bool())
243    // to Stage1, restore ghr in stage1 when flushBPU is valid
244    val s1RollBackHist = Output(UInt(HistoryLength.W))
245    val s3Taken = Output(Bool())
246  })
247
248  val flushS3 = BoolStopWatch(io.flush, io.in.fire(), startHighPriority = true)
249  val inLatch = RegInit(0.U.asTypeOf(io.in.bits))
250  val validLatch = RegInit(false.B)
251  when (io.in.fire()) { inLatch := io.in.bits }
252  when (io.in.fire()) {
253    validLatch := !io.flush
254  }.elsewhen (io.out.valid) {
255    validLatch := false.B
256  }
257  io.out.valid := validLatch && io.predecode.valid && !flushS3
258  io.in.ready := !validLatch || io.out.valid
259
260  // RAS
261  // TODO: split retAddr and ctr
262  def rasEntry() = new Bundle {
263    val retAddr = UInt(VAddrBits.W)
264    val ctr = UInt(8.W) // layer of nested call functions
265  }
266  val ras = RegInit(VecInit(Seq.fill(RasSize)(0.U.asTypeOf(rasEntry()))))
267  val sp = Counter(RasSize)
268  val rasTop = ras(sp.value)
269  val rasTopAddr = rasTop.retAddr
270
271  // get the first taken branch/jal/call/jalr/ret in a fetch line
272  // brTakenIdx/jalIdx/callIdx/jalrIdx/retIdx/jmpIdx is one-hot encoded.
273  // brNotTakenIdx indicates all the not-taken branches before the first jump instruction.
274  val brIdx = inLatch.btb.hits & Reverse(Cat(io.predecode.bits.fuOpTypes.map { t => ALUOpType.isBranch(t) }).asUInt) & io.predecode.bits.mask
275  val brTakenIdx = LowestBit(brIdx & inLatch.tage.takens.asUInt, FetchWidth)
276  val jalIdx = LowestBit(inLatch.btb.hits & Reverse(Cat(io.predecode.bits.fuOpTypes.map { t => t === ALUOpType.jal }).asUInt) & io.predecode.bits.mask, FetchWidth)
277  val callIdx = LowestBit(inLatch.btb.hits & io.predecode.bits.mask & Reverse(Cat(io.predecode.bits.fuOpTypes.map { t => t === ALUOpType.call }).asUInt), FetchWidth)
278  val jalrIdx = LowestBit(inLatch.jbtac.hitIdx & io.predecode.bits.mask & Reverse(Cat(io.predecode.bits.fuOpTypes.map { t => t === ALUOpType.jalr }).asUInt), FetchWidth)
279  val retIdx = LowestBit(io.predecode.bits.mask & Reverse(Cat(io.predecode.bits.fuOpTypes.map { t => t === ALUOpType.ret }).asUInt), FetchWidth)
280
281  val jmpIdx = LowestBit(brTakenIdx | jalIdx | callIdx | jalrIdx | retIdx, FetchWidth)
282  val brNotTakenIdx = brIdx & ~inLatch.tage.takens.asUInt & LowerMask(jmpIdx, FetchWidth) & io.predecode.bits.mask
283
284  io.out.bits.redirect := jmpIdx.orR.asBool
285  io.out.bits.target := Mux(jmpIdx === retIdx, rasTopAddr,
286    Mux(jmpIdx === jalrIdx, inLatch.jbtac.target,
287    Mux(jmpIdx === 0.U, inLatch.pc + 32.U, // TODO: RVC
288    PriorityMux(jmpIdx, inLatch.btb.targets))))
289  io.out.bits.instrValid := Mux(jmpIdx.orR, LowerMask(jmpIdx, FetchWidth), Fill(FetchWidth, 1.U(1.W))).asTypeOf(Vec(FetchWidth, Bool()))
290  io.out.bits.btbVictimWay := inLatch.btbPred.bits.btbVictimWay
291  io.out.bits.predCtr := inLatch.btbPred.bits.predCtr
292  io.out.bits.btbHitWay := inLatch.btbPred.bits.btbHitWay
293  io.out.bits.tageMeta := inLatch.btbPred.bits.tageMeta
294  //io.out.bits._type := Mux(jmpIdx === retIdx, BTBtype.R,
295  //  Mux(jmpIdx === jalrIdx, BTBtype.I,
296  //  Mux(jmpIdx === brTakenIdx, BTBtype.B, BTBtype.J)))
297  val firstHist = inLatch.btbPred.bits.hist(0)
298  // there may be several notTaken branches before the first jump instruction,
299  // so we need to calculate how many zeroes should each instruction shift in its global history.
300  // each history is exclusive of instruction's own jump direction.
301  val histShift = Wire(Vec(FetchWidth, UInt(log2Up(FetchWidth).W)))
302  val shift = Wire(Vec(FetchWidth, Vec(FetchWidth, UInt(1.W))))
303  (0 until FetchWidth).map(i => shift(i) := Mux(!brNotTakenIdx(i), 0.U, ~LowerMask(UIntToOH(i.U), FetchWidth)).asTypeOf(Vec(FetchWidth, UInt(1.W))))
304  for (j <- 0 until FetchWidth) {
305    var tmp = 0.U
306    for (i <- 0 until FetchWidth) {
307      tmp = tmp + shift(i)(j)
308    }
309    histShift(j) := tmp
310  }
311  (0 until FetchWidth).map(i => io.out.bits.hist(i) := firstHist << histShift(i))
312  // save ras checkpoint info
313  io.out.bits.rasSp := sp.value
314  io.out.bits.rasTopCtr := rasTop.ctr
315
316  // flush BPU and redirect when target differs from the target predicted in Stage1
317  io.out.bits.redirect := inLatch.btbPred.bits.redirect ^ jmpIdx.orR.asBool ||
318    inLatch.btbPred.bits.redirect && jmpIdx.orR.asBool && io.out.bits.target =/= inLatch.btbPred.bits.target
319  io.flushBPU := io.out.bits.redirect && io.out.valid
320
321  // speculative update RAS
322  val rasWrite = WireInit(0.U.asTypeOf(rasEntry()))
323  rasWrite.retAddr := inLatch.pc + OHToUInt(callIdx) << 2.U + 4.U
324  val allocNewEntry = rasWrite.retAddr =/= rasTopAddr
325  rasWrite.ctr := Mux(allocNewEntry, 1.U, rasTop.ctr + 1.U)
326  when (io.out.valid) {
327    when (jmpIdx === callIdx) {
328      ras(Mux(allocNewEntry, sp.value + 1.U, sp.value)) := rasWrite
329      when (allocNewEntry) { sp.value := sp.value + 1.U }
330    }.elsewhen (jmpIdx === retIdx) {
331      when (rasTop.ctr === 1.U) {
332        sp.value := Mux(sp.value === 0.U, 0.U, sp.value - 1.U)
333      }.otherwise {
334        ras(sp.value) := Cat(rasTop.ctr - 1.U, rasTopAddr).asTypeOf(rasEntry())
335      }
336    }
337  }
338  // use checkpoint to recover RAS
339  val recoverSp = io.redirectInfo.redirect.rasSp
340  val recoverCtr = io.redirectInfo.redirect.rasTopCtr
341  when (io.redirectInfo.valid && io.redirectInfo.misPred) {
342    sp.value := recoverSp
343    ras(recoverSp) := Cat(recoverCtr, ras(recoverSp).retAddr).asTypeOf(rasEntry())
344  }
345
346  // roll back global history in S1 if S3 redirects
347  io.s1RollBackHist := Mux(io.s3Taken, PriorityMux(jmpIdx, io.out.bits.hist), io.out.bits.hist(0) << PopCount(brIdx & ~inLatch.tage.takens.asUInt))
348  // whether Stage3 has a taken jump
349  io.s3Taken := jmpIdx.orR.asBool
350
351  // debug info
352  XSDebug(io.in.fire(), "[BPUS3]in:(%d %d) pc=%x\n", io.in.valid, io.in.ready, io.in.bits.pc)
353  XSDebug(io.out.valid, "[BPUS3]out:%d pc=%x redirect=%d predcdMask=%b instrValid=%b tgt=%x\n",
354    io.out.valid, inLatch.pc, io.out.bits.redirect, io.predecode.bits.mask, io.out.bits.instrValid.asUInt, io.out.bits.target)
355  XSDebug(true.B, "[BPUS3]flushS3=%d\n", flushS3)
356  XSDebug(true.B, "[BPUS3]validLatch=%d predecode.valid=%d\n", validLatch, io.predecode.valid)
357  XSDebug(true.B, "[BPUS3]brIdx=%b brTakenIdx=%b brNTakenIdx=%b jalIdx=%b jalrIdx=%b callIdx=%b retIdx=%b\n",
358    brIdx, brTakenIdx, brNotTakenIdx, jalIdx, jalrIdx, callIdx, retIdx)
359
360  // BPU's TEMP Perf Cnt
361  BoringUtils.addSource(io.out.valid, "MbpS3Cnt")
362  BoringUtils.addSource(io.out.valid && io.out.bits.redirect, "MbpS3TageRed")
363  BoringUtils.addSource(io.out.valid && (inLatch.btbPred.bits.redirect ^ jmpIdx.orR.asBool), "MbpS3TageRedDir")
364  BoringUtils.addSource(io.out.valid && (inLatch.btbPred.bits.redirect
365              && jmpIdx.orR.asBool && (io.out.bits.target =/= inLatch.btbPred.bits.target)), "MbpS3TageRedTar")
366}
367
368class BPU extends XSModule {
369  val io = IO(new Bundle() {
370    // from backend
371    // flush pipeline if misPred and update bpu based on redirect signals from brq
372    val redirectInfo = Input(new RedirectInfo)
373
374    val in = new Bundle { val pc = Flipped(Valid(UInt(VAddrBits.W))) }
375
376    val btbOut = ValidIO(new BranchPrediction)
377    val tageOut = ValidIO(new BranchPrediction)
378
379    // predecode info from icache
380    // TODO: simplify this after implement predecode unit
381    val predecode = Flipped(ValidIO(new Predecode))
382  })
383
384  val s1 = Module(new BPUStage1)
385  val s2 = Module(new BPUStage2)
386  val s3 = Module(new BPUStage3)
387
388  s1.io.redirectInfo <> io.redirectInfo
389  s1.io.flush := s3.io.flushBPU || io.redirectInfo.flush()
390  s1.io.in.pc.valid := io.in.pc.valid
391  s1.io.in.pc.bits <> io.in.pc.bits
392  io.btbOut <> s1.io.s1OutPred
393  s1.io.s3RollBackHist := s3.io.s1RollBackHist
394  s1.io.s3Taken := s3.io.s3Taken
395
396  s1.io.out <> s2.io.in
397  s2.io.flush := s3.io.flushBPU || io.redirectInfo.flush()
398
399  s2.io.out <> s3.io.in
400  s3.io.flush := io.redirectInfo.flush()
401  s3.io.predecode <> io.predecode
402  io.tageOut <> s3.io.out
403  s3.io.redirectInfo <> io.redirectInfo
404
405  // TODO: temp and ugly code, when perf counters is added( may after adding CSR), please mv the below counter
406  val bpuPerfCntList = List(
407    ("MbpInstr","         "),
408    ("MbpRight","         "),
409    ("MbpWrong","         "),
410    ("MbpBRight","        "),
411    ("MbpBWrong","        "),
412    ("MbpJRight","        "),
413    ("MbpJWrong","        "),
414    ("MbpIRight","        "),
415    ("MbpIWrong","        "),
416    ("MbpRRight","        "),
417    ("MbpRWrong","        "),
418    ("MbpS3Cnt","         "),
419    ("MbpS3TageRed","     "),
420    ("MbpS3TageRedDir","  "),
421    ("MbpS3TageRedTar","  ")
422  )
423
424  val bpuPerfCnts = List.fill(bpuPerfCntList.length)(RegInit(0.U(XLEN.W)))
425  val bpuPerfCntConds = List.fill(bpuPerfCntList.length)(WireInit(false.B))
426  (bpuPerfCnts zip bpuPerfCntConds) map { case (cnt, cond) => { when (cond) { cnt := cnt + 1.U }}}
427
428  for(i <- bpuPerfCntList.indices) {
429    BoringUtils.addSink(bpuPerfCntConds(i), bpuPerfCntList(i)._1)
430  }
431
432  val xsTrap = WireInit(false.B)
433  BoringUtils.addSink(xsTrap, "XSTRAP_BPU")
434
435  // if (!p.FPGAPlatform) {
436    when (xsTrap) {
437      printf("=================BPU's PerfCnt================\n")
438      for(i <- bpuPerfCntList.indices) {
439        printf(bpuPerfCntList(i)._1 + bpuPerfCntList(i)._2 + " <- " + "%d\n", bpuPerfCnts(i))
440      }
441    }
442  // }
443}