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