xref: /XiangShan/src/main/scala/xiangshan/frontend/IBuffer.scala (revision a38d1eab87777ed93b417106a7dfd58a062cee18)
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 org.chipsalliance.cde.config.Parameters
20import chisel3._
21import chisel3.util._
22import xiangshan._
23import utils._
24import utility._
25import xiangshan.ExceptionNO._
26
27class IBufPtr(implicit p: Parameters) extends CircularQueuePtr[IBufPtr](
28  p => p(XSCoreParamsKey).IBufSize
29) {
30}
31
32class IBufInBankPtr(implicit p: Parameters) extends CircularQueuePtr[IBufInBankPtr](
33  p => p(XSCoreParamsKey).IBufSize / p(XSCoreParamsKey).IBufNBank
34) {
35}
36
37class IBufBankPtr(implicit p: Parameters) extends CircularQueuePtr[IBufBankPtr](
38  p => p(XSCoreParamsKey).IBufNBank
39) {
40}
41
42class IBufferIO(implicit p: Parameters) extends XSBundle {
43  val flush = Input(Bool())
44  val ControlRedirect = Input(Bool())
45  val ControlBTBMissBubble = Input(Bool())
46  val TAGEMissBubble = Input(Bool())
47  val SCMissBubble = Input(Bool())
48  val ITTAGEMissBubble = Input(Bool())
49  val RASMissBubble = Input(Bool())
50  val MemVioRedirect = Input(Bool())
51  val in = Flipped(DecoupledIO(new FetchToIBuffer))
52  val out = Vec(DecodeWidth, DecoupledIO(new CtrlFlow))
53  val full = Output(Bool())
54  val decodeCanAccept = Input(Bool())
55  val stallReason = new StallReasonIO(DecodeWidth)
56}
57
58class IBufEntry(implicit p: Parameters) extends XSBundle {
59  val inst = UInt(32.W)
60  val pc = UInt(VAddrBits.W)
61  val foldpc = UInt(MemPredPCWidth.W)
62  val pd = new PreDecodeInfo
63  val pred_taken = Bool()
64  val ftqPtr = new FtqPtr
65  val ftqOffset = UInt(log2Ceil(PredictWidth).W)
66  val exceptionType = IBufferExceptionType()
67  val exceptionFromBackend = Bool()
68  val triggered = TriggerAction()
69  val isLastInFtqEntry = Bool()
70
71  def fromFetch(fetch: FetchToIBuffer, i: Int): IBufEntry = {
72    inst   := fetch.instrs(i)
73    pc     := fetch.pc(i)
74    foldpc := fetch.foldpc(i)
75    pd     := fetch.pd(i)
76    pred_taken := fetch.ftqOffset(i).valid
77    ftqPtr := fetch.ftqPtr
78    ftqOffset := fetch.ftqOffset(i).bits
79    exceptionType := IBufferExceptionType.cvtFromFetchExcpAndCrossPageAndRVCII(
80      fetch.exceptionType(i),
81      fetch.crossPageIPFFix(i),
82      fetch.illegalInstr(i),
83    )
84    exceptionFromBackend := fetch.exceptionFromBackend(i)
85    triggered := fetch.triggered(i)
86    isLastInFtqEntry := fetch.isLastInFtqEntry(i)
87    this
88  }
89
90  def toCtrlFlow: CtrlFlow = {
91    val cf = Wire(new CtrlFlow)
92    cf.instr := inst
93    cf.pc := pc
94    cf.foldpc := foldpc
95    cf.exceptionVec := 0.U.asTypeOf(ExceptionVec())
96    cf.exceptionVec(instrPageFault)      := IBufferExceptionType.isPF (this.exceptionType)
97    cf.exceptionVec(instrGuestPageFault) := IBufferExceptionType.isGPF(this.exceptionType)
98    cf.exceptionVec(instrAccessFault)    := IBufferExceptionType.isAF (this.exceptionType)
99    cf.exceptionVec(EX_II)               := IBufferExceptionType.isRVCII(this.exceptionType)
100    cf.exceptionFromBackend := exceptionFromBackend
101    cf.trigger := triggered
102    cf.pd := pd
103    cf.pred_taken := pred_taken
104    cf.crossPageIPFFix := IBufferExceptionType.isCrossPage(this.exceptionType)
105    cf.storeSetHit := DontCare
106    cf.waitForRobIdx := DontCare
107    cf.loadWaitBit := DontCare
108    cf.loadWaitStrict := DontCare
109    cf.ssid := DontCare
110    cf.ftqPtr := ftqPtr
111    cf.ftqOffset := ftqOffset
112    cf.isLastInFtqEntry := isLastInFtqEntry
113    cf
114  }
115
116  object IBufferExceptionType extends NamedUInt(3) {
117    def None         = "b000".U
118    def NonCrossPF   = "b001".U
119    def NonCrossGPF  = "b010".U
120    def NonCrossAF   = "b011".U
121    // illegal instruction
122    def rvcII        = "b100".U
123    def CrossPF      = "b101".U
124    def CrossGPF     = "b110".U
125    def CrossAF      = "b111".U
126
127    def cvtFromFetchExcpAndCrossPageAndRVCII(fetchExcp: UInt, crossPage: Bool, rvcIll: Bool): UInt = {
128      require(
129        fetchExcp.getWidth == ExceptionType.width,
130        s"The width(${fetchExcp.getWidth}) of fetchExcp should be equal to " +
131        s"the width(${ExceptionType.width}) of frontend.ExceptionType."
132      )
133      MuxCase(0.U, Seq(
134        crossPage     -> Cat(1.U(1.W), fetchExcp),
135        fetchExcp.orR -> fetchExcp,
136        rvcIll        -> this.rvcII,
137      ))
138    }
139
140    def isRVCII(uint: UInt): Bool = {
141      this.checkInputWidth(uint)
142      uint(2) && uint(1, 0) === 0.U
143    }
144
145    def isCrossPage(uint: UInt): Bool = {
146      this.checkInputWidth(uint)
147      uint(2) && uint(1, 0) =/= 0.U
148    }
149
150    def isPF (uint: UInt): Bool = uint(1, 0) === this.NonCrossPF (1, 0)
151    def isGPF(uint: UInt): Bool = uint(1, 0) === this.NonCrossGPF(1, 0)
152    def isAF (uint: UInt): Bool = uint(1, 0) === this.NonCrossAF (1, 0)
153  }
154}
155
156class IBuffer(implicit p: Parameters) extends XSModule with HasCircularQueuePtrHelper with HasPerfEvents {
157  val io = IO(new IBufferIO)
158
159  // io alias
160  private val decodeCanAccept = io.decodeCanAccept
161
162  // Parameter Check
163  private val bankSize = IBufSize / IBufNBank
164  require(IBufSize % IBufNBank == 0, s"IBufNBank should divide IBufSize, IBufNBank: $IBufNBank, IBufSize: $IBufSize")
165  require(IBufNBank >= DecodeWidth,
166    s"IBufNBank should be equal or larger than DecodeWidth, IBufNBank: $IBufNBank, DecodeWidth: $DecodeWidth")
167
168  // IBuffer is organized as raw registers
169  // This is due to IBuffer is a huge queue, read & write port logic should be precisely controlled
170  //                             . + + E E E - .
171  //                             . + + E E E - .
172  //                             . . + E E E - .
173  //                             . . + E E E E -
174  // As shown above, + means enqueue, - means dequeue, E is current content
175  // When dequeue, read port is organized like a banked FIFO
176  // Dequeue reads no more than 1 entry from each bank sequentially, this can be exploit to reduce area
177  // Enqueue writes cannot benefit from this characteristic unless use a SRAM
178  // For detail see Enqueue and Dequeue below
179  private val ibuf: Vec[IBufEntry] = RegInit(VecInit.fill(IBufSize)(0.U.asTypeOf(new IBufEntry)))
180  private val bankedIBufView: Vec[Vec[IBufEntry]] = VecInit.tabulate(IBufNBank)(
181    bankID => VecInit.tabulate(bankSize)(
182      inBankOffset => ibuf(bankID + inBankOffset * IBufNBank)
183    )
184  )
185
186
187  // Bypass wire
188  private val bypassEntries = WireDefault(VecInit.fill(DecodeWidth)(0.U.asTypeOf(Valid(new IBufEntry))))
189  // Normal read wire
190  private val deqEntries = WireDefault(VecInit.fill(DecodeWidth)(0.U.asTypeOf(Valid(new IBufEntry))))
191  // Output register
192  private val outputEntries = RegInit(VecInit.fill(DecodeWidth)(0.U.asTypeOf(Valid(new IBufEntry))))
193  private val outputEntriesValidNum = PriorityMuxDefault(outputEntries.map(_.valid).zip(Seq.range(1, DecodeWidth).map(_.U)).reverse.toSeq, 0.U)
194
195  // Between Bank
196  private val deqBankPtrVec: Vec[IBufBankPtr] = RegInit(VecInit.tabulate(DecodeWidth)(_.U.asTypeOf(new IBufBankPtr)))
197  private val deqBankPtr: IBufBankPtr = deqBankPtrVec(0)
198  private val deqBankPtrVecNext = Wire(deqBankPtrVec.cloneType)
199  // Inside Bank
200  private val deqInBankPtr: Vec[IBufInBankPtr] = RegInit(VecInit.fill(IBufNBank)(0.U.asTypeOf(new IBufInBankPtr)))
201  private val deqInBankPtrNext = Wire(deqInBankPtr.cloneType)
202
203  val deqPtr = RegInit(0.U.asTypeOf(new IBufPtr))
204  val deqPtrNext = Wire(deqPtr.cloneType)
205
206  val enqPtrVec = RegInit(VecInit.tabulate(PredictWidth)(_.U.asTypeOf(new IBufPtr)))
207  val enqPtr = enqPtrVec(0)
208
209  val numTryEnq = WireDefault(0.U)
210  val numEnq = Mux(io.in.fire, numTryEnq, 0.U)
211
212  // empty and decode can accept insts
213  val useBypass = enqPtr === deqPtr && decodeCanAccept
214
215  // The number of decode accepted insts.
216  // Since decode promises accepting insts in order, use priority encoder to simplify the accumulation.
217  private val numOut = Wire(UInt(log2Ceil(DecodeWidth).W))
218  private val numDeq = numOut
219
220  // counter current number of valid
221  val numValid = distanceBetween(enqPtr, deqPtr)
222  val numValidAfterDeq = numValid - numDeq
223  // counter next number of valid
224  val numValidNext = numValid + numEnq - numDeq
225  val allowEnq = RegInit(true.B)
226  val numFromFetch = Mux(io.in.valid, PopCount(io.in.bits.enqEnable), 0.U)
227
228  allowEnq := (IBufSize - PredictWidth).U >= numValidNext // Disable when almost full
229
230  val enqOffset = VecInit.tabulate(PredictWidth)(i => PopCount(io.in.bits.valid.asBools.take(i)))
231  val enqData = VecInit.tabulate(PredictWidth)(i => Wire(new IBufEntry).fromFetch(io.in.bits, i))
232
233  val outputEntriesIsNotFull = !outputEntries(DecodeWidth-1).valid
234  when(decodeCanAccept) {
235    numOut := Mux(numValid >= DecodeWidth.U, DecodeWidth.U, numValid)
236  }.elsewhen(outputEntriesIsNotFull) {
237    numOut := Mux(numValid >= DecodeWidth.U - outputEntriesValidNum, DecodeWidth.U - outputEntriesValidNum, numValid)
238  }.otherwise {
239    numOut := 0.U
240  }
241  val numBypass = Wire(UInt(log2Ceil(DecodeWidth).W))
242  // when using bypass, bypassed entries do not enqueue
243  when(useBypass) {
244    when(numFromFetch >= DecodeWidth.U) {
245      numTryEnq := numFromFetch - DecodeWidth.U
246      numBypass := DecodeWidth.U
247    } .otherwise {
248      numTryEnq := 0.U
249      numBypass := numFromFetch
250    }
251  } .otherwise {
252    numTryEnq := numFromFetch
253    numBypass := 0.U
254  }
255
256  /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
257  // Bypass
258  /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
259  bypassEntries.zipWithIndex.foreach {
260    case (entry, idx) =>
261      // Select
262      val validOH = Range(0, PredictWidth).map {
263        i =>
264          io.in.bits.valid(i) &&
265            io.in.bits.enqEnable(i) &&
266            enqOffset(i) === idx.asUInt
267      } // Should be OneHot
268      entry.valid := validOH.reduce(_ || _) && io.in.fire && !io.flush
269      entry.bits := Mux1H(validOH, enqData)
270
271      // Debug Assertion
272      XSError(io.in.valid && PopCount(validOH) > 1.asUInt, "validOH is not OneHot")
273  }
274
275  // => Decode Output
276  // clean register output
277  io.out zip outputEntries foreach {
278    case (io, reg) =>
279      io.valid := reg.valid
280      io.bits := reg.bits.toCtrlFlow
281  }
282  (outputEntries zip bypassEntries).zipWithIndex.foreach {
283    case ((out, bypass), i) =>
284      when(decodeCanAccept) {
285        when(useBypass && io.in.valid) {
286          out := bypass
287        }.otherwise {
288          out := deqEntries(i)
289        }
290      }.elsewhen(outputEntriesIsNotFull){
291        out.valid := deqEntries(i).valid
292        out.bits := Mux(i.U < outputEntriesValidNum, out.bits, VecInit(deqEntries.take(i + 1).map(_.bits))(i.U - outputEntriesValidNum))
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] = VecInit.tabulate(IBufNBank)(
347    bankID => Mux1H(UIntToOH(deqInBankPtr(bankID).value), bankedIBufView(bankID))
348  )
349  for (i <- 0 until DecodeWidth) {
350    deqEntries(i).valid := validVec(i)
351    deqEntries(i).bits := Mux1H(UIntToOH(deqBankPtrVec(i).value), readStage1)
352  }
353  // Pointer maintenance
354  deqBankPtrVecNext := VecInit(deqBankPtrVec.map(_ + numDeq))
355  deqPtrNext := deqPtr + numDeq
356  deqInBankPtrNext.zip(deqInBankPtr).zipWithIndex.foreach {
357    case ((ptrNext, ptr), idx) => {
358      // validVec[k] == bankValid[deqBankPtr + k]
359      // So bankValid[n] == validVec[n - deqBankPtr]
360      val validIdx = Mux(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
410  val matchBubble = Wire(UInt(log2Up(TopDownCounters.NumStallReasons.id).W))
411  val deqValidCount = PopCount(validVec.asBools)
412  val deqWasteCount = DecodeWidth.U - deqValidCount
413  matchBubble := (TopDownCounters.NumStallReasons.id - 1).U - PriorityEncoder(topdown_stage.reasons.reverse)
414
415  io.stallReason.reason.map(_ := 0.U)
416  for (i <- 0 until DecodeWidth) {
417    when(i.U < deqWasteCount) {
418      io.stallReason.reason(DecodeWidth - i - 1) := matchBubble
419    }
420  }
421
422  when(!(deqWasteCount === DecodeWidth.U || topdown_stage.reasons.asUInt.orR)) {
423    // should set reason for FetchFragmentationStall
424    // topdown_stage.reasons(TopDownCounters.FetchFragmentationStall.id) := true.B
425    for (i <- 0 until DecodeWidth) {
426      when(i.U < deqWasteCount) {
427        io.stallReason.reason(DecodeWidth - i - 1) := TopDownCounters.FetchFragBubble.id.U
428      }
429    }
430  }
431
432  when(io.stallReason.backReason.valid) {
433    io.stallReason.reason.map(_ := io.stallReason.backReason.bits)
434  }
435
436  // Debug info
437  XSError(
438    deqPtr.value =/= deqBankPtr.value + deqInBankPtr(deqBankPtr.value).value * IBufNBank.asUInt,
439    "Dequeue PTR mismatch"
440  )
441  XSError(isBefore(enqPtr, deqPtr) && !isFull(enqPtr, deqPtr), "\ndeqPtr is older than enqPtr!\n")
442
443  XSDebug(io.flush, "IBuffer Flushed\n")
444
445  when(io.in.fire) {
446    XSDebug("Enque:\n")
447    XSDebug(p"MASK=${Binary(io.in.bits.valid)}\n")
448    for(i <- 0 until PredictWidth){
449      XSDebug(p"PC=${Hexadecimal(io.in.bits.pc(i))} ${Hexadecimal(io.in.bits.instrs(i))}\n")
450    }
451  }
452
453  for (i <- 0 until DecodeWidth) {
454    XSDebug(io.out(i).fire,
455      p"deq: ${Hexadecimal(io.out(i).bits.instr)} PC=${Hexadecimal(io.out(i).bits.pc)}" +
456      p"v=${io.out(i).valid} r=${io.out(i).ready} " +
457      p"excpVec=${Binary(io.out(i).bits.exceptionVec.asUInt)} crossPageIPF=${io.out(i).bits.crossPageIPFFix}\n")
458  }
459
460  XSDebug(p"numValid: ${numValid}\n")
461  XSDebug(p"EnqNum: ${numEnq}\n")
462  XSDebug(p"DeqNum: ${numDeq}\n")
463
464  val afterInit = RegInit(false.B)
465  val headBubble = RegInit(false.B)
466  when (io.in.fire) { afterInit := true.B }
467  when (io.flush) {
468    headBubble := true.B
469  } .elsewhen(numValid =/= 0.U) {
470    headBubble := false.B
471  }
472  val instrHungry = afterInit && (numValid === 0.U) && !headBubble
473
474  QueuePerf(IBufSize, numValid, !allowEnq)
475  XSPerfAccumulate("flush", io.flush)
476  XSPerfAccumulate("hungry", instrHungry)
477
478  val ibuffer_IDWidth_hvButNotFull = afterInit && (numValid =/= 0.U) && (numValid < DecodeWidth.U) && !headBubble
479  XSPerfAccumulate("ibuffer_IDWidth_hvButNotFull", ibuffer_IDWidth_hvButNotFull)
480
481  val FrontBubble = Mux(decodeCanAccept, DecodeWidth.U - numOut, 0.U)
482
483  val perfEvents = Seq(
484    ("IBuffer_Flushed  ", io.flush),
485    ("IBuffer_hungry   ", instrHungry),
486    ("IBuffer_1_4_valid", (numValid > (0 * (IBufSize / 4)).U) & (numValid < (1 * (IBufSize / 4)).U)),
487    ("IBuffer_2_4_valid", (numValid >= (1 * (IBufSize / 4)).U) & (numValid < (2 * (IBufSize / 4)).U)),
488    ("IBuffer_3_4_valid", (numValid >= (2 * (IBufSize / 4)).U) & (numValid < (3 * (IBufSize / 4)).U)),
489    ("IBuffer_4_4_valid", (numValid >= (3 * (IBufSize / 4)).U) & (numValid < (4 * (IBufSize / 4)).U)),
490    ("IBuffer_full     ", numValid.andR),
491    ("Front_Bubble     ", FrontBubble)
492  )
493  generatePerfEvent()
494}
495