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