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