xref: /XiangShan/src/main/scala/xiangshan/frontend/IBuffer.scala (revision 1592abd11eecf7bec0f1453ffe4a7617167f8ba9)
1/***************************************************************************************
2* Copyright (c) 2020-2021 Institute of Computing Technology, Chinese Academy of Sciences
3* Copyright (c) 2020-2021 Peng Cheng Laboratory
4*
5* XiangShan is licensed under Mulan PSL v2.
6* You can use this software according to the terms and conditions of the Mulan PSL v2.
7* You may obtain a copy of Mulan PSL v2 at:
8*          http://license.coscl.org.cn/MulanPSL2
9*
10* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
11* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
12* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
13*
14* See the Mulan PSL v2 for more details.
15***************************************************************************************/
16
17package xiangshan.frontend
18
19import chisel3._
20import chisel3.util._
21import org.chipsalliance.cde.config.Parameters
22import utility._
23import utils._
24import xiangshan._
25import xiangshan.ExceptionNO._
26
27class IBufPtr(implicit p: Parameters) extends CircularQueuePtr[IBufPtr](p => p(XSCoreParamsKey).IBufSize) {}
28
29class IBufInBankPtr(implicit p: Parameters) extends CircularQueuePtr[IBufInBankPtr](p =>
30      p(XSCoreParamsKey).IBufSize / p(XSCoreParamsKey).IBufNBank
31    ) {}
32
33class IBufBankPtr(implicit p: Parameters) extends CircularQueuePtr[IBufBankPtr](p => p(XSCoreParamsKey).IBufNBank) {}
34
35class IBufferIO(implicit p: Parameters) extends XSBundle {
36  val flush                = Input(Bool())
37  val ControlRedirect      = Input(Bool())
38  val ControlBTBMissBubble = Input(Bool())
39  val TAGEMissBubble       = Input(Bool())
40  val SCMissBubble         = Input(Bool())
41  val ITTAGEMissBubble     = Input(Bool())
42  val RASMissBubble        = Input(Bool())
43  val MemVioRedirect       = Input(Bool())
44  val in                   = Flipped(DecoupledIO(new FetchToIBuffer))
45  val out                  = Vec(DecodeWidth, DecoupledIO(new CtrlFlow))
46  val full                 = Output(Bool())
47  val decodeCanAccept      = Input(Bool())
48  val stallReason          = new StallReasonIO(DecodeWidth)
49}
50
51class IBufEntry(implicit p: Parameters) extends XSBundle {
52  val inst             = UInt(32.W)
53  val pc               = UInt(VAddrBits.W)
54  val foldpc           = UInt(MemPredPCWidth.W)
55  val pd               = new PreDecodeInfo
56  val pred_taken       = Bool()
57  val ftqPtr           = new FtqPtr
58  val ftqOffset        = UInt(log2Ceil(PredictWidth).W)
59  val exceptionType    = IBufferExceptionType()
60  val backendException = Bool()
61  val triggered        = TriggerAction()
62  val isLastInFtqEntry = Bool()
63  val debug_seqNum     = InstSeqNum()
64
65  def fromFetch(fetch: FetchToIBuffer, i: Int): IBufEntry = {
66    inst       := fetch.instrs(i)
67    pc         := fetch.pc(i)
68    foldpc     := fetch.foldpc(i)
69    pd         := fetch.pd(i)
70    pred_taken := fetch.ftqOffset(i).valid
71    ftqPtr     := fetch.ftqPtr
72    ftqOffset  := fetch.ftqOffset(i).bits
73    exceptionType := IBufferExceptionType.cvtFromFetchExcpAndCrossPageAndRVCII(
74      fetch.exceptionType(i),
75      fetch.crossPageIPFFix(i),
76      fetch.illegalInstr(i)
77    )
78    backendException := fetch.backendException(i)
79    triggered        := fetch.triggered(i)
80    isLastInFtqEntry := fetch.isLastInFtqEntry(i)
81    debug_seqNum     := fetch.debug_seqNum(i)
82    this
83  }
84
85  def toCtrlFlow: CtrlFlow = {
86    val cf = Wire(new CtrlFlow)
87    cf.instr                             := inst
88    cf.pc                                := pc
89    cf.foldpc                            := foldpc
90    cf.exceptionVec                      := 0.U.asTypeOf(ExceptionVec())
91    cf.exceptionVec(instrPageFault)      := IBufferExceptionType.isPF(this.exceptionType)
92    cf.exceptionVec(instrGuestPageFault) := IBufferExceptionType.isGPF(this.exceptionType)
93    cf.exceptionVec(instrAccessFault)    := IBufferExceptionType.isAF(this.exceptionType)
94    cf.exceptionVec(EX_II)               := IBufferExceptionType.isRVCII(this.exceptionType)
95    cf.backendException                  := backendException
96    cf.trigger                           := triggered
97    cf.pd                                := pd
98    cf.pred_taken                        := pred_taken
99    cf.crossPageIPFFix                   := IBufferExceptionType.isCrossPage(this.exceptionType)
100    cf.storeSetHit                       := DontCare
101    cf.waitForRobIdx                     := DontCare
102    cf.loadWaitBit                       := DontCare
103    cf.loadWaitStrict                    := DontCare
104    cf.ssid                              := DontCare
105    cf.ftqPtr                            := ftqPtr
106    cf.ftqOffset                         := ftqOffset
107    cf.isLastInFtqEntry                  := isLastInFtqEntry
108    cf.debug_seqNum                      := debug_seqNum
109    cf
110  }
111
112  object IBufferExceptionType extends NamedUInt(3) {
113    def None        = "b000".U
114    def NonCrossPF  = "b001".U
115    def NonCrossGPF = "b010".U
116    def NonCrossAF  = "b011".U
117    // illegal instruction
118    def rvcII    = "b100".U
119    def CrossPF  = "b101".U
120    def CrossGPF = "b110".U
121    def CrossAF  = "b111".U
122
123    def cvtFromFetchExcpAndCrossPageAndRVCII(fetchExcp: UInt, crossPage: Bool, rvcIll: Bool): UInt = {
124      require(
125        fetchExcp.getWidth == ExceptionType.width,
126        s"The width(${fetchExcp.getWidth}) of fetchExcp should be equal to " +
127          s"the width(${ExceptionType.width}) of frontend.ExceptionType."
128      )
129      MuxCase(
130        0.U,
131        Seq(
132          crossPage     -> Cat(1.U(1.W), fetchExcp),
133          fetchExcp.orR -> fetchExcp,
134          rvcIll        -> this.rvcII
135        )
136      )
137    }
138
139    def isRVCII(uint: UInt): Bool = {
140      this.checkInputWidth(uint)
141      uint(2) && uint(1, 0) === 0.U
142    }
143
144    def isCrossPage(uint: UInt): Bool = {
145      this.checkInputWidth(uint)
146      uint(2) && uint(1, 0) =/= 0.U
147    }
148
149    def isPF(uint:  UInt): Bool = uint(1, 0) === this.NonCrossPF(1, 0)
150    def isGPF(uint: UInt): Bool = uint(1, 0) === this.NonCrossGPF(1, 0)
151    def isAF(uint:  UInt): Bool = uint(1, 0) === this.NonCrossAF(1, 0)
152  }
153}
154
155class IBuffer(implicit p: Parameters) extends XSModule with HasCircularQueuePtrHelper with HasPerfEvents {
156  val io = IO(new IBufferIO)
157
158  // io alias
159  private val decodeCanAccept = io.decodeCanAccept
160
161  // Parameter Check
162  private val bankSize = IBufSize / IBufNBank
163  require(IBufSize % IBufNBank == 0, s"IBufNBank should divide IBufSize, IBufNBank: $IBufNBank, IBufSize: $IBufSize")
164  require(
165    IBufNBank >= DecodeWidth,
166    s"IBufNBank should be equal or larger than DecodeWidth, IBufNBank: $IBufNBank, DecodeWidth: $DecodeWidth"
167  )
168
169  // IBuffer is organized as raw registers
170  // This is due to IBuffer is a huge queue, read & write port logic should be precisely controlled
171  //                             . + + E E E - .
172  //                             . + + E E E - .
173  //                             . . + E E E - .
174  //                             . . + E E E E -
175  // As shown above, + means enqueue, - means dequeue, E is current content
176  // When dequeue, read port is organized like a banked FIFO
177  // Dequeue reads no more than 1 entry from each bank sequentially, this can be exploit to reduce area
178  // Enqueue writes cannot benefit from this characteristic unless use a SRAM
179  // For detail see Enqueue and Dequeue below
180  private val ibuf: Vec[IBufEntry] = RegInit(VecInit.fill(IBufSize)(0.U.asTypeOf(new IBufEntry)))
181  private val bankedIBufView: Vec[Vec[IBufEntry]] = VecInit.tabulate(IBufNBank)(bankID =>
182    VecInit.tabulate(bankSize)(inBankOffset => ibuf(bankID + inBankOffset * IBufNBank))
183  )
184
185  // Bypass wire
186  private val bypassEntries = WireDefault(VecInit.fill(DecodeWidth)(0.U.asTypeOf(Valid(new IBufEntry))))
187  // Normal read wire
188  private val deqEntries = WireDefault(VecInit.fill(DecodeWidth)(0.U.asTypeOf(Valid(new IBufEntry))))
189  // Output register
190  private val outputEntries = RegInit(VecInit.fill(DecodeWidth)(0.U.asTypeOf(Valid(new IBufEntry))))
191  private val outputEntriesValidNum =
192    PriorityMuxDefault(outputEntries.map(_.valid).zip(Seq.range(1, DecodeWidth).map(_.U)).reverse.toSeq, 0.U)
193
194  // Between Bank
195  private val deqBankPtrVec: Vec[IBufBankPtr] = RegInit(VecInit.tabulate(DecodeWidth)(_.U.asTypeOf(new IBufBankPtr)))
196  private val deqBankPtr:    IBufBankPtr      = deqBankPtrVec(0)
197  private val deqBankPtrVecNext = Wire(deqBankPtrVec.cloneType)
198  // Inside Bank
199  private val deqInBankPtr: Vec[IBufInBankPtr] = RegInit(VecInit.fill(IBufNBank)(0.U.asTypeOf(new IBufInBankPtr)))
200  private val deqInBankPtrNext = Wire(deqInBankPtr.cloneType)
201
202  val deqPtr     = RegInit(0.U.asTypeOf(new IBufPtr))
203  val deqPtrNext = Wire(deqPtr.cloneType)
204
205  val enqPtrVec = RegInit(VecInit.tabulate(PredictWidth)(_.U.asTypeOf(new IBufPtr)))
206  val enqPtr    = enqPtrVec(0)
207
208  val numTryEnq = WireDefault(0.U)
209  val numEnq    = Mux(io.in.fire, numTryEnq, 0.U)
210
211  // empty and decode can accept insts
212  val useBypass = enqPtr === deqPtr && decodeCanAccept
213
214  // The number of decode accepted insts.
215  // Since decode promises accepting insts in order, use priority encoder to simplify the accumulation.
216  private val numOut = Wire(UInt(log2Ceil(DecodeWidth).W))
217  private val numDeq = numOut
218
219  // counter current number of valid
220  val numValid         = distanceBetween(enqPtr, deqPtr)
221  val numValidAfterDeq = numValid - numDeq
222  // counter next number of valid
223  val numValidNext = numValid + numEnq - numDeq
224  val allowEnq     = RegInit(true.B)
225  val numFromFetch = Mux(io.in.valid, PopCount(io.in.bits.enqEnable), 0.U)
226
227  allowEnq := (IBufSize - PredictWidth).U >= numValidNext // Disable when almost full
228
229  val enqOffset = VecInit.tabulate(PredictWidth)(i => PopCount(io.in.bits.valid.asBools.take(i)))
230  val enqData   = VecInit.tabulate(PredictWidth)(i => Wire(new IBufEntry).fromFetch(io.in.bits, i))
231
232  val outputEntriesIsNotFull = !outputEntries(DecodeWidth - 1).valid
233  when(decodeCanAccept) {
234    numOut := Mux(numValid >= DecodeWidth.U, DecodeWidth.U, numValid)
235  }.elsewhen(outputEntriesIsNotFull) {
236    numOut := Mux(numValid >= DecodeWidth.U - outputEntriesValidNum, DecodeWidth.U - outputEntriesValidNum, numValid)
237  }.otherwise {
238    numOut := 0.U
239  }
240  val numBypass = Wire(UInt(log2Ceil(DecodeWidth).W))
241  // when using bypass, bypassed entries do not enqueue
242  when(useBypass) {
243    when(numFromFetch >= DecodeWidth.U) {
244      numTryEnq := numFromFetch - DecodeWidth.U
245      numBypass := DecodeWidth.U
246    }.otherwise {
247      numTryEnq := 0.U
248      numBypass := numFromFetch
249    }
250  }.otherwise {
251    numTryEnq := numFromFetch
252    numBypass := 0.U
253  }
254
255  /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
256  // Bypass
257  /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
258  bypassEntries.zipWithIndex.foreach {
259    case (entry, idx) =>
260      // Select
261      val validOH = Range(0, PredictWidth).map {
262        i =>
263          io.in.bits.valid(i) &&
264          io.in.bits.enqEnable(i) &&
265          enqOffset(i) === idx.asUInt
266      } // Should be OneHot
267      entry.valid := validOH.reduce(_ || _) && io.in.fire && !io.flush
268      entry.bits  := Mux1H(validOH, enqData)
269
270      // Debug Assertion
271      XSError(io.in.valid && PopCount(validOH) > 1.asUInt, "validOH is not OneHot")
272  }
273
274  // => Decode Output
275  // clean register output
276  io.out zip outputEntries foreach {
277    case (io, reg) =>
278      io.valid := reg.valid
279      io.bits  := reg.bits.toCtrlFlow
280  }
281  (outputEntries zip bypassEntries).zipWithIndex.foreach {
282    case ((out, bypass), i) =>
283      when(decodeCanAccept) {
284        when(useBypass && io.in.valid) {
285          out := bypass
286        }.otherwise {
287          out := deqEntries(i)
288        }
289      }.elsewhen(outputEntriesIsNotFull) {
290        out.valid := deqEntries(i).valid
291        out.bits := Mux(
292          i.U < outputEntriesValidNum,
293          out.bits,
294          VecInit(deqEntries.take(i + 1).map(_.bits))(i.U - outputEntriesValidNum)
295        )
296      }
297  }
298
299  /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
300  // Enqueue
301  /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
302  io.in.ready := allowEnq
303  // Data
304  ibuf.zipWithIndex.foreach {
305    case (entry, idx) => {
306      // Select
307      val validOH = Range(0, PredictWidth).map {
308        i =>
309          val useBypassMatch = enqOffset(i) >= DecodeWidth.U &&
310            enqPtrVec(enqOffset(i) - DecodeWidth.U).value === idx.asUInt
311          val normalMatch = enqPtrVec(enqOffset(i)).value === idx.asUInt
312          val m = Mux(useBypass, useBypassMatch, normalMatch) // when using bypass, bypassed entries do not enqueue
313
314          io.in.bits.valid(i) && io.in.bits.enqEnable(i) && m
315      } // Should be OneHot
316      val wen = validOH.reduce(_ || _) && io.in.fire && !io.flush
317
318      // Write port
319      // Each IBuffer entry has a PredictWidth -> 1 Mux
320      val writeEntry = Mux1H(validOH, enqData)
321      entry := Mux(wen, writeEntry, entry)
322
323      // Debug Assertion
324      XSError(io.in.valid && PopCount(validOH) > 1.asUInt, "validOH is not OneHot")
325    }
326  }
327  // Pointer maintenance
328  when(io.in.fire && !io.flush) {
329    enqPtrVec := VecInit(enqPtrVec.map(_ + numTryEnq))
330  }
331
332  /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
333  // Dequeue
334  /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
335  val outputEntriesValidNumNext = Wire(UInt(log2Ceil(DecodeWidth).W))
336  XSError(outputEntriesValidNumNext > DecodeWidth.U, "Ibuffer: outputEntriesValidNumNext > DecodeWidth.U")
337  val validVec = UIntToMask(outputEntriesValidNumNext(log2Ceil(DecodeWidth) - 1, 0), DecodeWidth)
338  when(decodeCanAccept) {
339    outputEntriesValidNumNext := Mux(useBypass, numBypass, numDeq)
340  }.elsewhen(outputEntriesIsNotFull) {
341    outputEntriesValidNumNext := outputEntriesValidNum + numDeq
342  }.otherwise {
343    outputEntriesValidNumNext := outputEntriesValidNum
344  }
345  // Data
346  // Read port
347  // 2-stage, IBufNBank * (bankSize -> 1) + IBufNBank -> 1
348  // Should be better than IBufSize -> 1 in area, with no significant latency increase
349  private val readStage1: Vec[IBufEntry] =
350    VecInit.tabulate(IBufNBank)(bankID => Mux1H(UIntToOH(deqInBankPtr(bankID).value), bankedIBufView(bankID)))
351  for (i <- 0 until DecodeWidth) {
352    deqEntries(i).valid := validVec(i)
353    deqEntries(i).bits  := Mux1H(UIntToOH(deqBankPtrVec(i).value), readStage1)
354  }
355  // Pointer maintenance
356  deqBankPtrVecNext := VecInit(deqBankPtrVec.map(_ + numDeq))
357  deqPtrNext        := deqPtr + numDeq
358  deqInBankPtrNext.zip(deqInBankPtr).zipWithIndex.foreach {
359    case ((ptrNext, ptr), idx) => {
360      // validVec[k] == bankValid[deqBankPtr + k]
361      // So bankValid[n] == validVec[n - deqBankPtr]
362      val validIdx = Mux(
363        idx.asUInt >= deqBankPtr.value,
364        idx.asUInt - deqBankPtr.value,
365        ((idx + IBufNBank).asUInt - deqBankPtr.value)(log2Ceil(IBufNBank) - 1, 0)
366      )(log2Ceil(DecodeWidth) - 1, 0)
367      val bankAdvance = numOut > validIdx
368      ptrNext := Mux(bankAdvance, ptr + 1.U, ptr)
369    }
370  }
371
372  // Flush
373  when(io.flush) {
374    allowEnq      := true.B
375    enqPtrVec     := enqPtrVec.indices.map(_.U.asTypeOf(new IBufPtr))
376    deqBankPtrVec := deqBankPtrVec.indices.map(_.U.asTypeOf(new IBufBankPtr))
377    deqInBankPtr  := VecInit.fill(IBufNBank)(0.U.asTypeOf(new IBufInBankPtr))
378    deqPtr        := 0.U.asTypeOf(new IBufPtr())
379    outputEntries.foreach(_.valid := false.B)
380  }.otherwise {
381    deqPtr        := deqPtrNext
382    deqInBankPtr  := deqInBankPtrNext
383    deqBankPtrVec := deqBankPtrVecNext
384  }
385  io.full := !allowEnq
386
387  /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
388  // TopDown
389  /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
390  val topdown_stage = RegInit(0.U.asTypeOf(new FrontendTopDownBundle))
391  topdown_stage := io.in.bits.topdown_info
392  when(io.flush) {
393    when(io.ControlRedirect) {
394      when(io.ControlBTBMissBubble) {
395        topdown_stage.reasons(TopDownCounters.BTBMissBubble.id) := true.B
396      }.elsewhen(io.TAGEMissBubble) {
397        topdown_stage.reasons(TopDownCounters.TAGEMissBubble.id) := true.B
398      }.elsewhen(io.SCMissBubble) {
399        topdown_stage.reasons(TopDownCounters.SCMissBubble.id) := true.B
400      }.elsewhen(io.ITTAGEMissBubble) {
401        topdown_stage.reasons(TopDownCounters.ITTAGEMissBubble.id) := true.B
402      }.elsewhen(io.RASMissBubble) {
403        topdown_stage.reasons(TopDownCounters.RASMissBubble.id) := true.B
404      }
405    }.elsewhen(io.MemVioRedirect) {
406      topdown_stage.reasons(TopDownCounters.MemVioRedirectBubble.id) := true.B
407    }.otherwise {
408      topdown_stage.reasons(TopDownCounters.OtherRedirectBubble.id) := true.B
409    }
410  }
411
412  val matchBubble   = Wire(UInt(log2Up(TopDownCounters.NumStallReasons.id).W))
413  val deqValidCount = PopCount(validVec.asBools)
414  val deqWasteCount = DecodeWidth.U - deqValidCount
415  matchBubble := (TopDownCounters.NumStallReasons.id - 1).U - PriorityEncoder(topdown_stage.reasons.reverse)
416
417  io.stallReason.reason.map(_ := 0.U)
418  for (i <- 0 until DecodeWidth) {
419    when(i.U < deqWasteCount) {
420      io.stallReason.reason(DecodeWidth - i - 1) := matchBubble
421    }
422  }
423
424  when(!(deqWasteCount === DecodeWidth.U || topdown_stage.reasons.asUInt.orR)) {
425    // should set reason for FetchFragmentationStall
426    // topdown_stage.reasons(TopDownCounters.FetchFragmentationStall.id) := true.B
427    for (i <- 0 until DecodeWidth) {
428      when(i.U < deqWasteCount) {
429        io.stallReason.reason(DecodeWidth - i - 1) := TopDownCounters.FetchFragBubble.id.U
430      }
431    }
432  }
433
434  when(io.stallReason.backReason.valid) {
435    io.stallReason.reason.map(_ := io.stallReason.backReason.bits)
436  }
437
438  // Debug info
439  XSError(
440    deqPtr.value =/= deqBankPtr.value + deqInBankPtr(deqBankPtr.value).value * IBufNBank.asUInt,
441    "Dequeue PTR mismatch"
442  )
443  XSError(isBefore(enqPtr, deqPtr) && !isFull(enqPtr, deqPtr), "\ndeqPtr is older than enqPtr!\n")
444
445  XSDebug(io.flush, "IBuffer Flushed\n")
446
447  XSDebug(io.in.fire, "Enque:\n")
448  XSDebug(io.in.fire, p"MASK=${Binary(io.in.bits.valid)}\n")
449  for (i <- 0 until PredictWidth) {
450    XSDebug(io.in.fire, p"PC=${Hexadecimal(io.in.bits.pc(i))} ${Hexadecimal(io.in.bits.instrs(i))}\n")
451  }
452
453  for (i <- 0 until DecodeWidth) {
454    XSDebug(
455      io.out(i).fire,
456      p"deq: ${Hexadecimal(io.out(i).bits.instr)} PC=${Hexadecimal(io.out(i).bits.pc)}" +
457        p"v=${io.out(i).valid} r=${io.out(i).ready} " +
458        p"excpVec=${Binary(io.out(i).bits.exceptionVec.asUInt)} crossPageIPF=${io.out(i).bits.crossPageIPFFix}\n"
459    )
460  }
461
462  XSDebug(p"numValid: ${numValid}\n")
463  XSDebug(p"EnqNum: ${numEnq}\n")
464  XSDebug(p"DeqNum: ${numDeq}\n")
465
466  val afterInit  = RegInit(false.B)
467  val headBubble = RegInit(false.B)
468  when(io.in.fire)(afterInit := true.B)
469  when(io.flush) {
470    headBubble := true.B
471  }.elsewhen(numValid =/= 0.U) {
472    headBubble := false.B
473  }
474  val instrHungry = afterInit && (numValid === 0.U) && !headBubble
475
476  QueuePerf(IBufSize, numValid, !allowEnq)
477  XSPerfAccumulate("flush", io.flush)
478  XSPerfAccumulate("hungry", instrHungry)
479
480  val ibuffer_IDWidth_hvButNotFull = afterInit && (numValid =/= 0.U) && (numValid < DecodeWidth.U) && !headBubble
481  XSPerfAccumulate("ibuffer_IDWidth_hvButNotFull", ibuffer_IDWidth_hvButNotFull)
482
483  val FrontBubble = Mux(decodeCanAccept && !headBubble, DecodeWidth.U - numOut, 0.U)
484
485  val fetchLatency = decodeCanAccept && !headBubble && numOut === 0.U
486
487  XSPerfAccumulate("if_fetch_bubble", FrontBubble)
488  XSPerfAccumulate("if_fetch_bubble_eq_max", fetchLatency)
489
490  val perfEvents = Seq(
491    ("IBuffer_Flushed  ", io.flush),
492    ("IBuffer_hungry   ", instrHungry),
493    ("IBuffer_1_4_valid", (numValid > (0 * (IBufSize / 4)).U) & (numValid < (1 * (IBufSize / 4)).U)),
494    ("IBuffer_2_4_valid", (numValid >= (1 * (IBufSize / 4)).U) & (numValid < (2 * (IBufSize / 4)).U)),
495    ("IBuffer_3_4_valid", (numValid >= (2 * (IBufSize / 4)).U) & (numValid < (3 * (IBufSize / 4)).U)),
496    ("IBuffer_4_4_valid", (numValid >= (3 * (IBufSize / 4)).U) & (numValid < (4 * (IBufSize / 4)).U)),
497    ("IBuffer_full     ", numValid.andR),
498    ("Front_Bubble     ", FrontBubble),
499    ("Fetch_Latency_Bound", fetchLatency)
500  )
501  generatePerfEvent()
502}
503