xref: /XiangShan/src/main/scala/xiangshan/frontend/NewFtq.scala (revision 700e90ab8c5cd4eef6c56ac062fc0f0d6675c418)
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 chipsalliance.rocketchip.config.Parameters
20import chisel3._
21import chisel3.util._
22import utils._
23import xiangshan._
24import xiangshan.frontend.icache._
25import xiangshan.backend.CtrlToFtqIO
26import xiangshan.backend.decode.ImmUnion
27
28class FtqPtr(implicit p: Parameters) extends CircularQueuePtr[FtqPtr](
29  p => p(XSCoreParamsKey).FtqSize
30){
31}
32
33object FtqPtr {
34  def apply(f: Bool, v: UInt)(implicit p: Parameters): FtqPtr = {
35    val ptr = Wire(new FtqPtr)
36    ptr.flag := f
37    ptr.value := v
38    ptr
39  }
40  def inverse(ptr: FtqPtr)(implicit p: Parameters): FtqPtr = {
41    apply(!ptr.flag, ptr.value)
42  }
43}
44
45class FtqNRSRAM[T <: Data](gen: T, numRead: Int)(implicit p: Parameters) extends XSModule {
46
47  val io = IO(new Bundle() {
48    val raddr = Input(Vec(numRead, UInt(log2Up(FtqSize).W)))
49    val ren = Input(Vec(numRead, Bool()))
50    val rdata = Output(Vec(numRead, gen))
51    val waddr = Input(UInt(log2Up(FtqSize).W))
52    val wen = Input(Bool())
53    val wdata = Input(gen)
54  })
55
56  for(i <- 0 until numRead){
57    val sram = Module(new SRAMTemplate(gen, FtqSize))
58    sram.io.r.req.valid := io.ren(i)
59    sram.io.r.req.bits.setIdx := io.raddr(i)
60    io.rdata(i) := sram.io.r.resp.data(0)
61    sram.io.w.req.valid := io.wen
62    sram.io.w.req.bits.setIdx := io.waddr
63    sram.io.w.req.bits.data := VecInit(io.wdata)
64  }
65
66}
67
68class Ftq_RF_Components(implicit p: Parameters) extends XSBundle with BPUUtils {
69  val startAddr = UInt(VAddrBits.W)
70  val nextLineAddr = UInt(VAddrBits.W)
71  val isNextMask = Vec(PredictWidth, Bool())
72  val fallThruError = Bool()
73  // val carry = Bool()
74  def getPc(offset: UInt) = {
75    def getHigher(pc: UInt) = pc(VAddrBits-1, log2Ceil(PredictWidth)+instOffsetBits+1)
76    def getOffset(pc: UInt) = pc(log2Ceil(PredictWidth)+instOffsetBits, instOffsetBits)
77    Cat(getHigher(Mux(isNextMask(offset) && startAddr(log2Ceil(PredictWidth)+instOffsetBits), nextLineAddr, startAddr)),
78        getOffset(startAddr)+offset, 0.U(instOffsetBits.W))
79  }
80  def fromBranchPrediction(resp: BranchPredictionBundle) = {
81    def carryPos(addr: UInt) = addr(instOffsetBits+log2Ceil(PredictWidth)+1)
82    this.startAddr := resp.pc
83    this.nextLineAddr := resp.pc + (FetchWidth * 4 * 2).U // may be broken on other configs
84    this.isNextMask := VecInit((0 until PredictWidth).map(i =>
85      (resp.pc(log2Ceil(PredictWidth), 1) +& i.U)(log2Ceil(PredictWidth)).asBool()
86    ))
87    this.fallThruError := resp.fallThruError
88    this
89  }
90  override def toPrintable: Printable = {
91    p"startAddr:${Hexadecimal(startAddr)}"
92  }
93}
94
95class Ftq_pd_Entry(implicit p: Parameters) extends XSBundle {
96  val brMask = Vec(PredictWidth, Bool())
97  val jmpInfo = ValidUndirectioned(Vec(3, Bool()))
98  val jmpOffset = UInt(log2Ceil(PredictWidth).W)
99  val jalTarget = UInt(VAddrBits.W)
100  val rvcMask = Vec(PredictWidth, Bool())
101  def hasJal  = jmpInfo.valid && !jmpInfo.bits(0)
102  def hasJalr = jmpInfo.valid && jmpInfo.bits(0)
103  def hasCall = jmpInfo.valid && jmpInfo.bits(1)
104  def hasRet  = jmpInfo.valid && jmpInfo.bits(2)
105
106  def fromPdWb(pdWb: PredecodeWritebackBundle) = {
107    val pds = pdWb.pd
108    this.brMask := VecInit(pds.map(pd => pd.isBr && pd.valid))
109    this.jmpInfo.valid := VecInit(pds.map(pd => (pd.isJal || pd.isJalr) && pd.valid)).asUInt.orR
110    this.jmpInfo.bits := ParallelPriorityMux(pds.map(pd => (pd.isJal || pd.isJalr) && pd.valid),
111                                             pds.map(pd => VecInit(pd.isJalr, pd.isCall, pd.isRet)))
112    this.jmpOffset := ParallelPriorityEncoder(pds.map(pd => (pd.isJal || pd.isJalr) && pd.valid))
113    this.rvcMask := VecInit(pds.map(pd => pd.isRVC))
114    this.jalTarget := pdWb.jalTarget
115  }
116
117  def toPd(offset: UInt) = {
118    require(offset.getWidth == log2Ceil(PredictWidth))
119    val pd = Wire(new PreDecodeInfo)
120    pd.valid := true.B
121    pd.isRVC := rvcMask(offset)
122    val isBr = brMask(offset)
123    val isJalr = offset === jmpOffset && jmpInfo.valid && jmpInfo.bits(0)
124    pd.brType := Cat(offset === jmpOffset && jmpInfo.valid, isJalr || isBr)
125    pd.isCall := offset === jmpOffset && jmpInfo.valid && jmpInfo.bits(1)
126    pd.isRet  := offset === jmpOffset && jmpInfo.valid && jmpInfo.bits(2)
127    pd
128  }
129}
130
131
132
133class Ftq_Redirect_SRAMEntry(implicit p: Parameters) extends XSBundle with HasBPUConst {
134  val rasSp = UInt(log2Ceil(RasSize).W)
135  val rasEntry = new RASEntry
136  // val specCnt = Vec(numBr, UInt(10.W))
137  // val ghist = new ShiftingGlobalHistory
138  val folded_hist = new AllFoldedHistories(foldedGHistInfos)
139  val afhob = new AllAheadFoldedHistoryOldestBits(foldedGHistInfos)
140  val lastBrNumOH = UInt((numBr+1).W)
141
142  val histPtr = new CGHPtr
143
144  def fromBranchPrediction(resp: BranchPredictionBundle) = {
145    assert(!resp.is_minimal)
146    this.rasSp := resp.rasSp
147    this.rasEntry := resp.rasTop
148    this.folded_hist := resp.folded_hist
149    this.afhob := resp.afhob
150    this.lastBrNumOH := resp.lastBrNumOH
151    this.histPtr := resp.histPtr
152    this
153  }
154}
155
156class Ftq_1R_SRAMEntry(implicit p: Parameters) extends XSBundle with HasBPUConst {
157  val meta = UInt(MaxMetaLength.W)
158}
159
160class Ftq_Pred_Info(implicit p: Parameters) extends XSBundle {
161  val target = UInt(VAddrBits.W)
162  val cfiIndex = ValidUndirectioned(UInt(log2Ceil(PredictWidth).W))
163}
164
165
166class FtqRead[T <: Data](private val gen: T)(implicit p: Parameters) extends XSBundle {
167  val ptr = Output(new FtqPtr)
168  val offset = Output(UInt(log2Ceil(PredictWidth).W))
169  val data = Input(gen)
170  def apply(ptr: FtqPtr, offset: UInt) = {
171    this.ptr := ptr
172    this.offset := offset
173    this.data
174  }
175}
176
177
178class FtqToBpuIO(implicit p: Parameters) extends XSBundle {
179  val redirect = Valid(new BranchPredictionRedirect)
180  val update = Valid(new BranchPredictionUpdate)
181  val enq_ptr = Output(new FtqPtr)
182}
183
184class FtqToIfuIO(implicit p: Parameters) extends XSBundle with HasCircularQueuePtrHelper {
185  val req = Decoupled(new FetchRequestBundle)
186  val redirect = Valid(new Redirect)
187  val flushFromBpu = new Bundle {
188    // when ifu pipeline is not stalled,
189    // a packet from bpu s3 can reach f1 at most
190    val s2 = Valid(new FtqPtr)
191    val s3 = Valid(new FtqPtr)
192    def shouldFlushBy(src: Valid[FtqPtr], idx_to_flush: FtqPtr) = {
193      src.valid && !isAfter(src.bits, idx_to_flush)
194    }
195    def shouldFlushByStage2(idx: FtqPtr) = shouldFlushBy(s2, idx)
196    def shouldFlushByStage3(idx: FtqPtr) = shouldFlushBy(s3, idx)
197  }
198}
199
200trait HasBackendRedirectInfo extends HasXSParameter {
201  def numRedirectPcRead = exuParameters.JmpCnt + exuParameters.AluCnt + 1
202  def isLoadReplay(r: Valid[Redirect]) = r.bits.flushItself()
203}
204
205class FtqToCtrlIO(implicit p: Parameters) extends XSBundle with HasBackendRedirectInfo {
206  // write to backend pc mem
207  val pc_mem_wen = Output(Bool())
208  val pc_mem_waddr = Output(UInt(log2Ceil(FtqSize).W))
209  val pc_mem_wdata = Output(new Ftq_RF_Components)
210  val target = Output(UInt(VAddrBits.W))
211  // predecode correct target
212  val pd_redirect_waddr = Valid(UInt(log2Ceil(FtqSize).W))
213  val pd_redirect_target = Output(UInt(VAddrBits.W))
214}
215
216
217class FTBEntryGen(implicit p: Parameters) extends XSModule with HasBackendRedirectInfo with HasBPUParameter {
218  val io = IO(new Bundle {
219    val start_addr = Input(UInt(VAddrBits.W))
220    val old_entry = Input(new FTBEntry)
221    val pd = Input(new Ftq_pd_Entry)
222    val cfiIndex = Flipped(Valid(UInt(log2Ceil(PredictWidth).W)))
223    val target = Input(UInt(VAddrBits.W))
224    val hit = Input(Bool())
225    val mispredict_vec = Input(Vec(PredictWidth, Bool()))
226
227    val new_entry = Output(new FTBEntry)
228    val new_br_insert_pos = Output(Vec(numBr, Bool()))
229    val taken_mask = Output(Vec(numBr, Bool()))
230    val mispred_mask = Output(Vec(numBr+1, Bool()))
231
232    // for perf counters
233    val is_init_entry = Output(Bool())
234    val is_old_entry = Output(Bool())
235    val is_new_br = Output(Bool())
236    val is_jalr_target_modified = Output(Bool())
237    val is_always_taken_modified = Output(Bool())
238    val is_br_full = Output(Bool())
239  })
240
241  // no mispredictions detected at predecode
242  val hit = io.hit
243  val pd = io.pd
244
245  val init_entry = WireInit(0.U.asTypeOf(new FTBEntry))
246
247
248  val cfi_is_br = pd.brMask(io.cfiIndex.bits) && io.cfiIndex.valid
249  val entry_has_jmp = pd.jmpInfo.valid
250  val new_jmp_is_jal  = entry_has_jmp && !pd.jmpInfo.bits(0) && io.cfiIndex.valid
251  val new_jmp_is_jalr = entry_has_jmp &&  pd.jmpInfo.bits(0) && io.cfiIndex.valid
252  val new_jmp_is_call = entry_has_jmp &&  pd.jmpInfo.bits(1) && io.cfiIndex.valid
253  val new_jmp_is_ret  = entry_has_jmp &&  pd.jmpInfo.bits(2) && io.cfiIndex.valid
254  val last_jmp_rvi = entry_has_jmp && pd.jmpOffset === (PredictWidth-1).U && !pd.rvcMask.last
255  // val last_br_rvi = cfi_is_br && io.cfiIndex.bits === (PredictWidth-1).U && !pd.rvcMask.last
256
257  val cfi_is_jal = io.cfiIndex.bits === pd.jmpOffset && new_jmp_is_jal
258  val cfi_is_jalr = io.cfiIndex.bits === pd.jmpOffset && new_jmp_is_jalr
259
260  def carryPos = log2Ceil(PredictWidth)+instOffsetBits
261  def getLower(pc: UInt) = pc(carryPos-1, instOffsetBits)
262  // if not hit, establish a new entry
263  init_entry.valid := true.B
264  // tag is left for ftb to assign
265
266  // case br
267  val init_br_slot = init_entry.getSlotForBr(0)
268  when (cfi_is_br) {
269    init_br_slot.valid := true.B
270    init_br_slot.offset := io.cfiIndex.bits
271    init_br_slot.setLowerStatByTarget(io.start_addr, io.target, numBr == 1)
272    init_entry.always_taken(0) := true.B // set to always taken on init
273  }
274
275  // case jmp
276  when (entry_has_jmp) {
277    init_entry.tailSlot.offset := pd.jmpOffset
278    init_entry.tailSlot.valid := new_jmp_is_jal || new_jmp_is_jalr
279    init_entry.tailSlot.setLowerStatByTarget(io.start_addr, Mux(cfi_is_jalr, io.target, pd.jalTarget), isShare=false)
280  }
281
282  val jmpPft = getLower(io.start_addr) +& pd.jmpOffset +& Mux(pd.rvcMask(pd.jmpOffset), 1.U, 2.U)
283  init_entry.pftAddr := Mux(entry_has_jmp && !last_jmp_rvi, jmpPft, getLower(io.start_addr))
284  init_entry.carry   := Mux(entry_has_jmp && !last_jmp_rvi, jmpPft(carryPos-instOffsetBits), true.B)
285  init_entry.isJalr := new_jmp_is_jalr
286  init_entry.isCall := new_jmp_is_call
287  init_entry.isRet  := new_jmp_is_ret
288  // that means fall thru points to the middle of an inst
289  init_entry.last_may_be_rvi_call := pd.jmpOffset === (PredictWidth-1).U && !pd.rvcMask(pd.jmpOffset)
290
291  // if hit, check whether a new cfi(only br is possible) is detected
292  val oe = io.old_entry
293  val br_recorded_vec = oe.getBrRecordedVec(io.cfiIndex.bits)
294  val br_recorded = br_recorded_vec.asUInt.orR
295  val is_new_br = cfi_is_br && !br_recorded
296  val new_br_offset = io.cfiIndex.bits
297  // vec(i) means new br will be inserted BEFORE old br(i)
298  val allBrSlotsVec = oe.allSlotsForBr
299  val new_br_insert_onehot = VecInit((0 until numBr).map{
300    i => i match {
301      case 0 =>
302        !allBrSlotsVec(0).valid || new_br_offset < allBrSlotsVec(0).offset
303      case idx =>
304        allBrSlotsVec(idx-1).valid && new_br_offset > allBrSlotsVec(idx-1).offset &&
305        (!allBrSlotsVec(idx).valid || new_br_offset < allBrSlotsVec(idx).offset)
306    }
307  })
308
309  val old_entry_modified = WireInit(io.old_entry)
310  for (i <- 0 until numBr) {
311    val slot = old_entry_modified.allSlotsForBr(i)
312    when (new_br_insert_onehot(i)) {
313      slot.valid := true.B
314      slot.offset := new_br_offset
315      slot.setLowerStatByTarget(io.start_addr, io.target, i == numBr-1)
316      old_entry_modified.always_taken(i) := true.B
317    }.elsewhen (new_br_offset > oe.allSlotsForBr(i).offset) {
318      old_entry_modified.always_taken(i) := false.B
319      // all other fields remain unchanged
320    }.otherwise {
321      // case i == 0, remain unchanged
322      if (i != 0) {
323        val noNeedToMoveFromFormerSlot = (i == numBr-1).B && !oe.brSlots.last.valid
324        when (!noNeedToMoveFromFormerSlot) {
325          slot.fromAnotherSlot(oe.allSlotsForBr(i-1))
326          old_entry_modified.always_taken(i) := oe.always_taken(i)
327        }
328      }
329    }
330  }
331
332  // two circumstances:
333  // 1. oe: | br | j  |, new br should be in front of j, thus addr of j should be new pft
334  // 2. oe: | br | br |, new br could be anywhere between, thus new pft is the addr of either
335  //        the previous last br or the new br
336  val may_have_to_replace = oe.noEmptySlotForNewBr
337  val pft_need_to_change = is_new_br && may_have_to_replace
338  // it should either be the given last br or the new br
339  when (pft_need_to_change) {
340    val new_pft_offset =
341      Mux(!new_br_insert_onehot.asUInt.orR,
342        new_br_offset, oe.allSlotsForBr.last.offset)
343
344    // set jmp to invalid
345    old_entry_modified.pftAddr := getLower(io.start_addr) + new_pft_offset
346    old_entry_modified.carry := (getLower(io.start_addr) +& new_pft_offset).head(1).asBool
347    old_entry_modified.last_may_be_rvi_call := false.B
348    old_entry_modified.isCall := false.B
349    old_entry_modified.isRet := false.B
350    old_entry_modified.isJalr := false.B
351  }
352
353  val old_entry_jmp_target_modified = WireInit(oe)
354  val old_target = oe.tailSlot.getTarget(io.start_addr) // may be wrong because we store only 20 lowest bits
355  val old_tail_is_jmp = !oe.tailSlot.sharing
356  val jalr_target_modified = cfi_is_jalr && (old_target =/= io.target) && old_tail_is_jmp // TODO: pass full jalr target
357  when (jalr_target_modified) {
358    old_entry_jmp_target_modified.setByJmpTarget(io.start_addr, io.target)
359    old_entry_jmp_target_modified.always_taken := 0.U.asTypeOf(Vec(numBr, Bool()))
360  }
361
362  val old_entry_always_taken = WireInit(oe)
363  val always_taken_modified_vec = Wire(Vec(numBr, Bool())) // whether modified or not
364  for (i <- 0 until numBr) {
365    old_entry_always_taken.always_taken(i) :=
366      oe.always_taken(i) && io.cfiIndex.valid && oe.brValids(i) && io.cfiIndex.bits === oe.brOffset(i)
367    always_taken_modified_vec(i) := oe.always_taken(i) && !old_entry_always_taken.always_taken(i)
368  }
369  val always_taken_modified = always_taken_modified_vec.reduce(_||_)
370
371
372
373  val derived_from_old_entry =
374    Mux(is_new_br, old_entry_modified,
375      Mux(jalr_target_modified, old_entry_jmp_target_modified, old_entry_always_taken))
376
377
378  io.new_entry := Mux(!hit, init_entry, derived_from_old_entry)
379
380  io.new_br_insert_pos := new_br_insert_onehot
381  io.taken_mask := VecInit((io.new_entry.brOffset zip io.new_entry.brValids).map{
382    case (off, v) => io.cfiIndex.bits === off && io.cfiIndex.valid && v
383  })
384  for (i <- 0 until numBr) {
385    io.mispred_mask(i) := io.new_entry.brValids(i) && io.mispredict_vec(io.new_entry.brOffset(i))
386  }
387  io.mispred_mask.last := io.new_entry.jmpValid && io.mispredict_vec(pd.jmpOffset)
388
389  // for perf counters
390  io.is_init_entry := !hit
391  io.is_old_entry := hit && !is_new_br && !jalr_target_modified && !always_taken_modified
392  io.is_new_br := hit && is_new_br
393  io.is_jalr_target_modified := hit && jalr_target_modified
394  io.is_always_taken_modified := hit && always_taken_modified
395  io.is_br_full := hit && is_new_br && may_have_to_replace
396}
397
398class Ftq(implicit p: Parameters) extends XSModule with HasCircularQueuePtrHelper
399  with HasBackendRedirectInfo with BPUUtils with HasBPUConst with HasPerfEvents
400  with HasICacheParameters{
401  val io = IO(new Bundle {
402    val fromBpu = Flipped(new BpuToFtqIO)
403    val fromIfu = Flipped(new IfuToFtqIO)
404    val fromBackend = Flipped(new CtrlToFtqIO)
405
406    val toBpu = new FtqToBpuIO
407    val toIfu = new FtqToIfuIO
408    val toBackend = new FtqToCtrlIO
409
410    val toPrefetch = new FtqPrefechBundle
411
412    val bpuInfo = new Bundle {
413      val bpRight = Output(UInt(XLEN.W))
414      val bpWrong = Output(UInt(XLEN.W))
415    }
416  })
417  io.bpuInfo := DontCare
418
419  val backendRedirect = Wire(Valid(new Redirect))
420  val backendRedirectReg = RegNext(backendRedirect)
421
422  val stage2Flush = backendRedirect.valid
423  val backendFlush = stage2Flush || RegNext(stage2Flush)
424  val ifuFlush = Wire(Bool())
425
426  val flush = stage2Flush || RegNext(stage2Flush)
427
428  val allowBpuIn, allowToIfu = WireInit(false.B)
429  val flushToIfu = !allowToIfu
430  allowBpuIn := !ifuFlush && !backendRedirect.valid && !backendRedirectReg.valid
431  allowToIfu := !ifuFlush && !backendRedirect.valid && !backendRedirectReg.valid
432
433  val bpuPtr, ifuPtr, ifuWbPtr, commPtr = RegInit(FtqPtr(false.B, 0.U))
434  val ifuPtrPlus1 = RegInit(FtqPtr(false.B, 1.U))
435  val validEntries = distanceBetween(bpuPtr, commPtr)
436
437  // **********************************************************************
438  // **************************** enq from bpu ****************************
439  // **********************************************************************
440  val new_entry_ready = validEntries < FtqSize.U
441  io.fromBpu.resp.ready := new_entry_ready
442
443  val bpu_s2_resp = io.fromBpu.resp.bits.s2
444  val bpu_s3_resp = io.fromBpu.resp.bits.s3
445  val bpu_s2_redirect = bpu_s2_resp.valid && bpu_s2_resp.hasRedirect
446  val bpu_s3_redirect = bpu_s3_resp.valid && bpu_s3_resp.hasRedirect
447
448  io.toBpu.enq_ptr := bpuPtr
449  val enq_fire = io.fromBpu.resp.fire() && allowBpuIn // from bpu s1
450  val bpu_in_fire = (io.fromBpu.resp.fire() || bpu_s2_redirect || bpu_s3_redirect) && allowBpuIn
451
452  val bpu_in_resp = io.fromBpu.resp.bits.selectedResp
453  val bpu_in_stage = io.fromBpu.resp.bits.selectedRespIdx
454  val bpu_in_resp_ptr = Mux(bpu_in_stage === BP_S1, bpuPtr, bpu_in_resp.ftq_idx)
455  val bpu_in_resp_idx = bpu_in_resp_ptr.value
456
457  // read ports:                                                ifuReq1 + ifuReq2 + commitUpdate
458  val ftq_pc_mem = Module(new SyncDataModuleTemplate(new Ftq_RF_Components, FtqSize, 3, 1, "FtqPC",
459    concatData=false, Some(Seq.tabulate(3)(i => false))))
460  // resp from uBTB
461  ftq_pc_mem.io.wen(0) := bpu_in_fire
462  ftq_pc_mem.io.waddr(0) := bpu_in_resp_idx
463  ftq_pc_mem.io.wdata(0).fromBranchPrediction(bpu_in_resp)
464
465  //                                                            ifuRedirect + backendRedirect + commit
466  val ftq_redirect_sram = Module(new FtqNRSRAM(new Ftq_Redirect_SRAMEntry, 1+1+1))
467  // these info is intended to enq at the last stage of bpu
468  ftq_redirect_sram.io.wen := io.fromBpu.resp.bits.lastStage.valid
469  ftq_redirect_sram.io.waddr := io.fromBpu.resp.bits.lastStage.ftq_idx.value
470  ftq_redirect_sram.io.wdata.fromBranchPrediction(io.fromBpu.resp.bits.lastStage)
471  println(f"ftq redirect SRAM: entry ${ftq_redirect_sram.io.wdata.getWidth} * ${FtqSize} * 3")
472  println(f"ftq redirect SRAM: ahead fh ${ftq_redirect_sram.io.wdata.afhob.getWidth} * ${FtqSize} * 3")
473
474  val ftq_meta_1r_sram = Module(new FtqNRSRAM(new Ftq_1R_SRAMEntry, 1))
475  // these info is intended to enq at the last stage of bpu
476  ftq_meta_1r_sram.io.wen := io.fromBpu.resp.bits.lastStage.valid
477  ftq_meta_1r_sram.io.waddr := io.fromBpu.resp.bits.lastStage.ftq_idx.value
478  ftq_meta_1r_sram.io.wdata.meta := io.fromBpu.resp.bits.meta
479  //                                                            ifuRedirect + backendRedirect + commit
480  val ftb_entry_mem = Module(new SyncDataModuleTemplate(new FTBEntry, FtqSize, 1+1+1, 1))
481  ftb_entry_mem.io.wen(0) := io.fromBpu.resp.bits.lastStage.valid
482  ftb_entry_mem.io.waddr(0) := io.fromBpu.resp.bits.lastStage.ftq_idx.value
483  ftb_entry_mem.io.wdata(0) := io.fromBpu.resp.bits.lastStage.ftb_entry
484
485
486  // multi-write
487  val update_target = Reg(Vec(FtqSize, UInt(VAddrBits.W))) // could be taken target or fallThrough
488  val cfiIndex_vec = Reg(Vec(FtqSize, ValidUndirectioned(UInt(log2Ceil(PredictWidth).W))))
489  val mispredict_vec = Reg(Vec(FtqSize, Vec(PredictWidth, Bool())))
490  val pred_stage = Reg(Vec(FtqSize, UInt(2.W)))
491
492  val c_invalid :: c_valid :: c_commited :: Nil = Enum(3)
493  val commitStateQueue = RegInit(VecInit(Seq.fill(FtqSize) {
494    VecInit(Seq.fill(PredictWidth)(c_invalid))
495  }))
496
497  val f_to_send :: f_sent :: Nil = Enum(2)
498  val entry_fetch_status = RegInit(VecInit(Seq.fill(FtqSize)(f_sent)))
499
500  val h_not_hit :: h_false_hit :: h_hit :: Nil = Enum(3)
501  val entry_hit_status = RegInit(VecInit(Seq.fill(FtqSize)(h_not_hit)))
502
503  // modify registers one cycle later to cut critical path
504  val last_cycle_bpu_in = RegNext(bpu_in_fire)
505  val last_cycle_bpu_in_idx = RegNext(bpu_in_resp_idx)
506  val last_cycle_update_target = RegNext(bpu_in_resp.getTarget)
507  val last_cycle_cfiIndex = RegNext(bpu_in_resp.cfiIndex)
508  val last_cycle_bpu_in_stage = RegNext(bpu_in_stage)
509  when (last_cycle_bpu_in) {
510    entry_fetch_status(last_cycle_bpu_in_idx) := f_to_send
511    commitStateQueue(last_cycle_bpu_in_idx) := VecInit(Seq.fill(PredictWidth)(c_invalid))
512    cfiIndex_vec(last_cycle_bpu_in_idx) := last_cycle_cfiIndex
513    mispredict_vec(last_cycle_bpu_in_idx) := WireInit(VecInit(Seq.fill(PredictWidth)(false.B)))
514    update_target(last_cycle_bpu_in_idx) := last_cycle_update_target
515    pred_stage(last_cycle_bpu_in_idx) := last_cycle_bpu_in_stage
516  }
517
518
519  bpuPtr := bpuPtr + enq_fire
520  when (io.toIfu.req.fire && allowToIfu) {
521    ifuPtr := ifuPtrPlus1
522    ifuPtrPlus1 := ifuPtrPlus1 + 1.U
523  }
524
525  // only use ftb result to assign hit status
526  when (bpu_s2_resp.valid) {
527    entry_hit_status(bpu_s2_resp.ftq_idx.value) := Mux(bpu_s2_resp.full_pred.hit, h_hit, h_not_hit)
528  }
529
530
531  io.toIfu.flushFromBpu.s2.valid := bpu_s2_redirect
532  io.toIfu.flushFromBpu.s2.bits := bpu_s2_resp.ftq_idx
533  when (bpu_s2_resp.valid && bpu_s2_resp.hasRedirect) {
534    bpuPtr := bpu_s2_resp.ftq_idx + 1.U
535    // only when ifuPtr runs ahead of bpu s2 resp should we recover it
536    when (!isBefore(ifuPtr, bpu_s2_resp.ftq_idx)) {
537      ifuPtr := bpu_s2_resp.ftq_idx
538      ifuPtrPlus1 := bpu_s2_resp.ftq_idx + 1.U
539    }
540  }
541
542  io.toIfu.flushFromBpu.s3.valid := bpu_s3_redirect
543  io.toIfu.flushFromBpu.s3.bits := bpu_s3_resp.ftq_idx
544  when (bpu_s3_resp.valid && bpu_s3_resp.hasRedirect) {
545    bpuPtr := bpu_s3_resp.ftq_idx + 1.U
546    // only when ifuPtr runs ahead of bpu s2 resp should we recover it
547    when (!isBefore(ifuPtr, bpu_s3_resp.ftq_idx)) {
548      ifuPtr := bpu_s3_resp.ftq_idx
549      ifuPtrPlus1 := bpu_s3_resp.ftq_idx + 1.U
550    }
551  }
552
553  XSError(isBefore(bpuPtr, ifuPtr) && !isFull(bpuPtr, ifuPtr), "\nifuPtr is before bpuPtr!\n")
554
555  // ****************************************************************
556  // **************************** to ifu ****************************
557  // ****************************************************************
558  val bpu_in_bypass_buf = RegEnable(ftq_pc_mem.io.wdata(0), bpu_in_fire)
559  val bpu_in_bypass_ptr = RegNext(bpu_in_resp_ptr)
560  val last_cycle_to_ifu_fire = RegNext(io.toIfu.req.fire)
561
562  // read pc and target
563  ftq_pc_mem.io.raddr.init.init.last := ifuPtr.value
564  ftq_pc_mem.io.raddr.init.last := ifuPtrPlus1.value
565
566  io.toIfu.req.bits.ftqIdx := ifuPtr
567
568
569  val toIfuPcBundle = Wire(new Ftq_RF_Components)
570  val entry_is_to_send = WireInit(entry_fetch_status(ifuPtr.value) === f_to_send)
571  val entry_next_addr = WireInit(update_target(ifuPtr.value))
572  val entry_ftq_offset = WireInit(cfiIndex_vec(ifuPtr.value))
573
574
575  when (last_cycle_bpu_in && bpu_in_bypass_ptr === ifuPtr) {
576    toIfuPcBundle := bpu_in_bypass_buf
577    entry_is_to_send := true.B
578    entry_next_addr := last_cycle_update_target
579    entry_ftq_offset := last_cycle_cfiIndex
580  }.elsewhen (last_cycle_to_ifu_fire) {
581    toIfuPcBundle := ftq_pc_mem.io.rdata.init.last
582    entry_is_to_send := RegNext(entry_fetch_status(ifuPtrPlus1.value) === f_to_send) ||
583                        RegNext(last_cycle_bpu_in && bpu_in_bypass_ptr === (ifuPtrPlus1)) // reduce potential bubbles
584  }.otherwise {
585    toIfuPcBundle := ftq_pc_mem.io.rdata.init.init.last
586    entry_is_to_send := RegNext(entry_fetch_status(ifuPtr.value) === f_to_send)
587  }
588
589  io.toIfu.req.valid := entry_is_to_send && ifuPtr =/= bpuPtr
590  io.toIfu.req.bits.nextStartAddr := entry_next_addr
591  io.toIfu.req.bits.ftqOffset := entry_ftq_offset
592  io.toIfu.req.bits.fromFtqPcBundle(toIfuPcBundle)
593
594  // when fall through is smaller in value than start address, there must be a false hit
595  when (toIfuPcBundle.fallThruError && entry_hit_status(ifuPtr.value) === h_hit) {
596    when (io.toIfu.req.fire &&
597      !(bpu_s2_redirect && bpu_s2_resp.ftq_idx === ifuPtr) &&
598      !(bpu_s3_redirect && bpu_s3_resp.ftq_idx === ifuPtr)
599    ) {
600      entry_hit_status(ifuPtr.value) := h_false_hit
601      // XSError(true.B, "FTB false hit by fallThroughError, startAddr: %x, fallTHru: %x\n", io.toIfu.req.bits.startAddr, io.toIfu.req.bits.nextStartAddr)
602    }
603    XSDebug(true.B, "fallThruError! start:%x, fallThru:%x\n", io.toIfu.req.bits.startAddr, io.toIfu.req.bits.nextStartAddr)
604  }
605
606  XSPerfAccumulate(f"fall_through_error_to_ifu", toIfuPcBundle.fallThruError && entry_hit_status(ifuPtr.value) === h_hit &&
607    io.toIfu.req.fire && !(bpu_s2_redirect && bpu_s2_resp.ftq_idx === ifuPtr) && !(bpu_s3_redirect && bpu_s3_resp.ftq_idx === ifuPtr))
608
609  val ifu_req_should_be_flushed =
610    io.toIfu.flushFromBpu.shouldFlushByStage2(io.toIfu.req.bits.ftqIdx) ||
611    io.toIfu.flushFromBpu.shouldFlushByStage3(io.toIfu.req.bits.ftqIdx)
612
613    when (io.toIfu.req.fire && !ifu_req_should_be_flushed) {
614      entry_fetch_status(ifuPtr.value) := f_sent
615    }
616
617  // *********************************************************************
618  // **************************** wb from ifu ****************************
619  // *********************************************************************
620  val pdWb = io.fromIfu.pdWb
621  val pds = pdWb.bits.pd
622  val ifu_wb_valid = pdWb.valid
623  val ifu_wb_idx = pdWb.bits.ftqIdx.value
624  // read ports:                                                         commit update
625  val ftq_pd_mem = Module(new SyncDataModuleTemplate(new Ftq_pd_Entry, FtqSize, 1, 1))
626  ftq_pd_mem.io.wen(0) := ifu_wb_valid
627  ftq_pd_mem.io.waddr(0) := pdWb.bits.ftqIdx.value
628  ftq_pd_mem.io.wdata(0).fromPdWb(pdWb.bits)
629
630  val hit_pd_valid = entry_hit_status(ifu_wb_idx) === h_hit && ifu_wb_valid
631  val hit_pd_mispred = hit_pd_valid && pdWb.bits.misOffset.valid
632  val hit_pd_mispred_reg = RegNext(hit_pd_mispred, init=false.B)
633  val pd_reg       = RegEnable(pds,             pdWb.valid)
634  val start_pc_reg = RegEnable(pdWb.bits.pc(0), pdWb.valid)
635  val wb_idx_reg   = RegEnable(ifu_wb_idx,      pdWb.valid)
636
637  when (ifu_wb_valid) {
638    val comm_stq_wen = VecInit(pds.map(_.valid).zip(pdWb.bits.instrRange).map{
639      case (v, inRange) => v && inRange
640    })
641    (commitStateQueue(ifu_wb_idx) zip comm_stq_wen).map{
642      case (qe, v) => when (v) { qe := c_valid }
643    }
644  }
645
646  ifuWbPtr := ifuWbPtr + ifu_wb_valid
647
648  ftb_entry_mem.io.raddr.head := ifu_wb_idx
649  val has_false_hit = WireInit(false.B)
650  when (RegNext(hit_pd_valid)) {
651    // check for false hit
652    val pred_ftb_entry = ftb_entry_mem.io.rdata.head
653    val brSlots = pred_ftb_entry.brSlots
654    val tailSlot = pred_ftb_entry.tailSlot
655    // we check cfis that bpu predicted
656
657    // bpu predicted branches but denied by predecode
658    val br_false_hit =
659      brSlots.map{
660        s => s.valid && !(pd_reg(s.offset).valid && pd_reg(s.offset).isBr)
661      }.reduce(_||_) ||
662      (tailSlot.valid && pred_ftb_entry.tailSlot.sharing &&
663        !(pd_reg(tailSlot.offset).valid && pd_reg(tailSlot.offset).isBr))
664
665    val jmpOffset = tailSlot.offset
666    val jmp_pd = pd_reg(jmpOffset)
667    val jal_false_hit = pred_ftb_entry.jmpValid &&
668      ((pred_ftb_entry.isJal  && !(jmp_pd.valid && jmp_pd.isJal)) ||
669       (pred_ftb_entry.isJalr && !(jmp_pd.valid && jmp_pd.isJalr)) ||
670       (pred_ftb_entry.isCall && !(jmp_pd.valid && jmp_pd.isCall)) ||
671       (pred_ftb_entry.isRet  && !(jmp_pd.valid && jmp_pd.isRet))
672      )
673
674    has_false_hit := br_false_hit || jal_false_hit || hit_pd_mispred_reg
675    XSDebug(has_false_hit, "FTB false hit by br or jal or hit_pd, startAddr: %x\n", pdWb.bits.pc(0))
676
677    // assert(!has_false_hit)
678  }
679
680  when (has_false_hit) {
681    entry_hit_status(wb_idx_reg) := h_false_hit
682  }
683
684
685  // **********************************************************************
686  // ***************************** to backend *****************************
687  // **********************************************************************
688  // to backend pc mem / target
689  io.toBackend.pc_mem_wen   := RegNext(last_cycle_bpu_in)
690  io.toBackend.pc_mem_waddr := RegNext(last_cycle_bpu_in_idx)
691  io.toBackend.pc_mem_wdata := RegNext(bpu_in_bypass_buf)
692  io.toBackend.target       := RegNext(last_cycle_update_target)
693
694  // *******************************************************************************
695  // **************************** redirect from backend ****************************
696  // *******************************************************************************
697
698  // redirect read cfiInfo, couples to redirectGen s2
699  ftq_redirect_sram.io.ren.init.last := backendRedirect.valid
700  ftq_redirect_sram.io.raddr.init.last := backendRedirect.bits.ftqIdx.value
701
702  ftb_entry_mem.io.raddr.init.last := backendRedirect.bits.ftqIdx.value
703
704  val stage3CfiInfo = ftq_redirect_sram.io.rdata.init.last
705  val fromBackendRedirect = WireInit(backendRedirectReg)
706  val backendRedirectCfi = fromBackendRedirect.bits.cfiUpdate
707  backendRedirectCfi.fromFtqRedirectSram(stage3CfiInfo)
708
709  val r_ftb_entry = ftb_entry_mem.io.rdata.init.last
710  val r_ftqOffset = fromBackendRedirect.bits.ftqOffset
711
712  when (entry_hit_status(fromBackendRedirect.bits.ftqIdx.value) === h_hit) {
713    backendRedirectCfi.shift := PopCount(r_ftb_entry.getBrMaskByOffset(r_ftqOffset)) +&
714      (backendRedirectCfi.pd.isBr && !r_ftb_entry.brIsSaved(r_ftqOffset) &&
715      !r_ftb_entry.newBrCanNotInsert(r_ftqOffset))
716
717    backendRedirectCfi.addIntoHist := backendRedirectCfi.pd.isBr && (r_ftb_entry.brIsSaved(r_ftqOffset) ||
718        !r_ftb_entry.newBrCanNotInsert(r_ftqOffset))
719  }.otherwise {
720    backendRedirectCfi.shift := (backendRedirectCfi.pd.isBr && backendRedirectCfi.taken).asUInt
721    backendRedirectCfi.addIntoHist := backendRedirectCfi.pd.isBr.asUInt
722  }
723
724
725  // ***************************************************************************
726  // **************************** redirect from ifu ****************************
727  // ***************************************************************************
728  val fromIfuRedirect = WireInit(0.U.asTypeOf(Valid(new Redirect)))
729  fromIfuRedirect.valid := pdWb.valid && pdWb.bits.misOffset.valid && !backendFlush
730  fromIfuRedirect.bits.ftqIdx := pdWb.bits.ftqIdx
731  fromIfuRedirect.bits.ftqOffset := pdWb.bits.misOffset.bits
732  fromIfuRedirect.bits.level := RedirectLevel.flushAfter
733
734  val ifuRedirectCfiUpdate = fromIfuRedirect.bits.cfiUpdate
735  ifuRedirectCfiUpdate.pc := pdWb.bits.pc(pdWb.bits.misOffset.bits)
736  ifuRedirectCfiUpdate.pd := pdWb.bits.pd(pdWb.bits.misOffset.bits)
737  ifuRedirectCfiUpdate.predTaken := cfiIndex_vec(pdWb.bits.ftqIdx.value).valid
738  ifuRedirectCfiUpdate.target := pdWb.bits.target
739  ifuRedirectCfiUpdate.taken := pdWb.bits.cfiOffset.valid
740  ifuRedirectCfiUpdate.isMisPred := pdWb.bits.misOffset.valid
741
742  val ifuRedirectReg = RegNext(fromIfuRedirect, init=0.U.asTypeOf(Valid(new Redirect)))
743  val ifuRedirectToBpu = WireInit(ifuRedirectReg)
744  ifuFlush := fromIfuRedirect.valid || ifuRedirectToBpu.valid
745
746  ftq_redirect_sram.io.ren.head := fromIfuRedirect.valid
747  ftq_redirect_sram.io.raddr.head := fromIfuRedirect.bits.ftqIdx.value
748
749  ftb_entry_mem.io.raddr.head := fromIfuRedirect.bits.ftqIdx.value
750
751  val toBpuCfi = ifuRedirectToBpu.bits.cfiUpdate
752  toBpuCfi.fromFtqRedirectSram(ftq_redirect_sram.io.rdata.head)
753  when (ifuRedirectReg.bits.cfiUpdate.pd.isRet) {
754    toBpuCfi.target := toBpuCfi.rasEntry.retAddr
755  }
756
757  // *********************************************************************
758  // **************************** wb from exu ****************************
759  // *********************************************************************
760
761  backendRedirect := io.fromBackend.redirect
762
763  def extractRedirectInfo(wb: Valid[Redirect]) = {
764    val ftqIdx = wb.bits.ftqIdx.value
765    val ftqOffset = wb.bits.ftqOffset
766    val taken = wb.bits.cfiUpdate.taken
767    val mispred = wb.bits.cfiUpdate.isMisPred
768    (wb.valid, ftqIdx, ftqOffset, taken, mispred)
769  }
770
771  // fix mispredict entry
772  val lastIsMispredict = RegNext(
773    backendRedirect.valid && backendRedirect.bits.level === RedirectLevel.flushAfter, init = false.B
774  )
775
776  def updateCfiInfo(redirect: Valid[Redirect], isBackend: Boolean = true) = {
777    val (r_valid, r_idx, r_offset, r_taken, r_mispred) = extractRedirectInfo(redirect)
778    val cfiIndex_bits_wen = r_valid && r_taken && r_offset < cfiIndex_vec(r_idx).bits
779    val cfiIndex_valid_wen = r_valid && r_offset === cfiIndex_vec(r_idx).bits
780    when (cfiIndex_bits_wen || cfiIndex_valid_wen) {
781      cfiIndex_vec(r_idx).valid := cfiIndex_bits_wen || cfiIndex_valid_wen && r_taken
782    }
783    when (cfiIndex_bits_wen) {
784      cfiIndex_vec(r_idx).bits := r_offset
785    }
786    update_target(r_idx) := redirect.bits.cfiUpdate.target
787    if (isBackend) {
788      mispredict_vec(r_idx)(r_offset) := r_mispred
789    }
790  }
791
792  // write to backend target vec
793  io.toBackend.pd_redirect_waddr.valid := RegNext(fromIfuRedirect.valid)
794  io.toBackend.pd_redirect_waddr.bits  := RegNext(fromIfuRedirect.bits.ftqIdx.value)
795  io.toBackend.pd_redirect_target      := RegNext(fromIfuRedirect.bits.cfiUpdate.target)
796
797  // write to backend target vec
798  io.toBackend.pd_redirect_waddr.valid := RegNext(fromIfuRedirect.valid)
799  io.toBackend.pd_redirect_waddr.bits  := RegNext(fromIfuRedirect.bits.ftqIdx.value)
800  io.toBackend.pd_redirect_target      := RegNext(fromIfuRedirect.bits.cfiUpdate.target)
801
802  when(backendRedirectReg.valid && lastIsMispredict) {
803    updateCfiInfo(backendRedirectReg)
804  }.elsewhen (ifuRedirectToBpu.valid) {
805    updateCfiInfo(ifuRedirectToBpu, isBackend=false)
806  }
807
808  // ***********************************************************************************
809  // **************************** flush ptr and state queue ****************************
810  // ***********************************************************************************
811
812  val redirectVec = VecInit(backendRedirect, fromIfuRedirect)
813
814  // when redirect, we should reset ptrs and status queues
815  when(redirectVec.map(r => r.valid).reduce(_||_)){
816    val r = PriorityMux(redirectVec.map(r => (r.valid -> r.bits)))
817    val notIfu = redirectVec.dropRight(1).map(r => r.valid).reduce(_||_)
818    val (idx, offset, flushItSelf) = (r.ftqIdx, r.ftqOffset, RedirectLevel.flushItself(r.level))
819    val next = idx + 1.U
820    bpuPtr := next
821    ifuPtr := next
822    ifuWbPtr := next
823    ifuPtrPlus1 := idx + 2.U
824    when (notIfu) {
825      commitStateQueue(idx.value).zipWithIndex.foreach({ case (s, i) =>
826        when(i.U > offset || i.U === offset && flushItSelf){
827          s := c_invalid
828        }
829      })
830    }
831  }
832
833  // only the valid bit is actually needed
834  io.toIfu.redirect.bits    := backendRedirect.bits
835  io.toIfu.redirect.valid   := stage2Flush
836
837  // commit
838  for (c <- io.fromBackend.rob_commits) {
839    when(c.valid) {
840      commitStateQueue(c.bits.ftqIdx.value)(c.bits.ftqOffset) := c_commited
841      // TODO: remove this
842      // For instruction fusions, we also update the next instruction
843      when (c.bits.commitType === 4.U) {
844        commitStateQueue(c.bits.ftqIdx.value)(c.bits.ftqOffset + 1.U) := c_commited
845      }.elsewhen(c.bits.commitType === 5.U) {
846        commitStateQueue(c.bits.ftqIdx.value)(c.bits.ftqOffset + 2.U) := c_commited
847      }.elsewhen(c.bits.commitType === 6.U) {
848        val index = (c.bits.ftqIdx + 1.U).value
849        commitStateQueue(index)(0) := c_commited
850      }.elsewhen(c.bits.commitType === 7.U) {
851        val index = (c.bits.ftqIdx + 1.U).value
852        commitStateQueue(index)(1) := c_commited
853      }
854    }
855  }
856
857  // ****************************************************************
858  // **************************** to bpu ****************************
859  // ****************************************************************
860
861  io.toBpu.redirect <> Mux(fromBackendRedirect.valid, fromBackendRedirect, ifuRedirectToBpu)
862
863  val may_have_stall_from_bpu = Wire(Bool())
864  val bpu_ftb_update_stall = RegInit(0.U(2.W)) // 2-cycle stall, so we need 3 states
865  may_have_stall_from_bpu := bpu_ftb_update_stall =/= 0.U
866  val canCommit = commPtr =/= ifuWbPtr && !may_have_stall_from_bpu &&
867    Cat(commitStateQueue(commPtr.value).map(s => {
868      s === c_invalid || s === c_commited
869    })).andR()
870
871  // commit reads
872  ftq_pc_mem.io.raddr.last := commPtr.value
873  val commit_pc_bundle = ftq_pc_mem.io.rdata.last
874  ftq_pd_mem.io.raddr.last := commPtr.value
875  val commit_pd = ftq_pd_mem.io.rdata.last
876  ftq_redirect_sram.io.ren.last := canCommit
877  ftq_redirect_sram.io.raddr.last := commPtr.value
878  val commit_spec_meta = ftq_redirect_sram.io.rdata.last
879  ftq_meta_1r_sram.io.ren(0) := canCommit
880  ftq_meta_1r_sram.io.raddr(0) := commPtr.value
881  val commit_meta = ftq_meta_1r_sram.io.rdata(0)
882  ftb_entry_mem.io.raddr.last := commPtr.value
883  val commit_ftb_entry = ftb_entry_mem.io.rdata.last
884
885  // need one cycle to read mem and srams
886  val do_commit_ptr = RegNext(commPtr)
887  val do_commit = RegNext(canCommit, init=false.B)
888  when (canCommit) { commPtr := commPtr + 1.U }
889  val commit_state = RegNext(commitStateQueue(commPtr.value))
890  val can_commit_cfi = WireInit(cfiIndex_vec(commPtr.value))
891  when (commitStateQueue(commPtr.value)(can_commit_cfi.bits) =/= c_commited) {
892    can_commit_cfi.valid := false.B
893  }
894  val commit_cfi = RegNext(can_commit_cfi)
895
896  val commit_mispredict = VecInit((RegNext(mispredict_vec(commPtr.value)) zip commit_state).map {
897    case (mis, state) => mis && state === c_commited
898  })
899  val can_commit_hit = entry_hit_status(commPtr.value)
900  val commit_hit = RegNext(can_commit_hit)
901  val commit_target = RegNext(update_target(commPtr.value))
902  val commit_stage = RegNext(pred_stage(commPtr.value))
903  val commit_valid = commit_hit === h_hit || commit_cfi.valid // hit or taken
904
905  val to_bpu_hit = can_commit_hit === h_hit || can_commit_hit === h_false_hit
906  switch (bpu_ftb_update_stall) {
907    is (0.U) {
908      when (can_commit_cfi.valid && !to_bpu_hit && canCommit) {
909        bpu_ftb_update_stall := 2.U // 2-cycle stall
910      }
911    }
912    is (2.U) {
913      bpu_ftb_update_stall := 1.U
914    }
915    is (1.U) {
916      bpu_ftb_update_stall := 0.U
917    }
918    is (3.U) {
919      XSError(true.B, "bpu_ftb_update_stall should be 0, 1 or 2")
920    }
921  }
922
923  io.toBpu.update := DontCare
924  io.toBpu.update.valid := commit_valid && do_commit
925  val update = io.toBpu.update.bits
926  update.false_hit   := commit_hit === h_false_hit
927  update.pc          := commit_pc_bundle.startAddr
928  update.meta        := commit_meta.meta
929  update.full_target := commit_target
930  update.from_stage  := commit_stage
931  update.fromFtqRedirectSram(commit_spec_meta)
932
933  val commit_real_hit = commit_hit === h_hit
934  val update_ftb_entry = update.ftb_entry
935
936  val ftbEntryGen = Module(new FTBEntryGen).io
937  ftbEntryGen.start_addr     := commit_pc_bundle.startAddr
938  ftbEntryGen.old_entry      := commit_ftb_entry
939  ftbEntryGen.pd             := commit_pd
940  ftbEntryGen.cfiIndex       := commit_cfi
941  ftbEntryGen.target         := commit_target
942  ftbEntryGen.hit            := commit_real_hit
943  ftbEntryGen.mispredict_vec := commit_mispredict
944
945  update_ftb_entry         := ftbEntryGen.new_entry
946  update.new_br_insert_pos := ftbEntryGen.new_br_insert_pos
947  update.mispred_mask      := ftbEntryGen.mispred_mask
948  update.old_entry         := ftbEntryGen.is_old_entry
949  update.pred_hit          := commit_hit === h_hit || commit_hit === h_false_hit
950
951  update.is_minimal := false.B
952  update.full_pred.fromFtbEntry(ftbEntryGen.new_entry, update.pc)
953  update.full_pred.br_taken_mask  := ftbEntryGen.taken_mask
954  update.full_pred.jalr_target := commit_target
955  update.full_pred.hit := true.B
956  when (update.full_pred.is_jalr) {
957    update.full_pred.targets.last := commit_target
958  }
959
960  // ****************************************************************
961  // *********************** to prefetch ****************************
962  // ****************************************************************
963
964  if(cacheParams.hasPrefetch){
965    val prefetchPtr = RegInit(FtqPtr(false.B, 0.U))
966    prefetchPtr := prefetchPtr + io.toPrefetch.req.fire()
967
968    when (bpu_s2_resp.valid && bpu_s2_resp.hasRedirect && !isBefore(prefetchPtr, bpu_s2_resp.ftq_idx)) {
969      prefetchPtr := bpu_s2_resp.ftq_idx
970    }
971
972    when (bpu_s3_resp.valid && bpu_s3_resp.hasRedirect && !isBefore(prefetchPtr, bpu_s3_resp.ftq_idx)) {
973      prefetchPtr := bpu_s3_resp.ftq_idx
974      // XSError(true.B, "\ns3_redirect mechanism not implemented!\n")
975    }
976
977
978    val prefetch_is_to_send = WireInit(entry_fetch_status(prefetchPtr.value) === f_to_send)
979    val prefetch_addr = WireInit(update_target(prefetchPtr.value))
980
981    when (last_cycle_bpu_in && bpu_in_bypass_ptr === prefetchPtr) {
982      prefetch_is_to_send := true.B
983      prefetch_addr := last_cycle_update_target
984    }
985    io.toPrefetch.req.valid := prefetchPtr =/= bpuPtr && prefetch_is_to_send
986    io.toPrefetch.req.bits.target := prefetch_addr
987
988    when(redirectVec.map(r => r.valid).reduce(_||_)){
989      val r = PriorityMux(redirectVec.map(r => (r.valid -> r.bits)))
990      val next = r.ftqIdx + 1.U
991      prefetchPtr := next
992    }
993
994    XSError(isBefore(bpuPtr, prefetchPtr) && !isFull(bpuPtr, prefetchPtr), "\nprefetchPtr is before bpuPtr!\n")
995    XSError(isBefore(prefetchPtr, ifuPtr) && !isFull(ifuPtr, prefetchPtr), "\nifuPtr is before prefetchPtr!\n")
996  }
997  else {
998    io.toPrefetch.req <> DontCare
999  }
1000
1001  // ******************************************************************************
1002  // **************************** commit perf counters ****************************
1003  // ******************************************************************************
1004
1005  val commit_inst_mask    = VecInit(commit_state.map(c => c === c_commited && do_commit)).asUInt
1006  val commit_mispred_mask = commit_mispredict.asUInt
1007  val commit_not_mispred_mask = ~commit_mispred_mask
1008
1009  val commit_br_mask = commit_pd.brMask.asUInt
1010  val commit_jmp_mask = UIntToOH(commit_pd.jmpOffset) & Fill(PredictWidth, commit_pd.jmpInfo.valid.asTypeOf(UInt(1.W)))
1011  val commit_cfi_mask = (commit_br_mask | commit_jmp_mask)
1012
1013  val mbpInstrs = commit_inst_mask & commit_cfi_mask
1014
1015  val mbpRights = mbpInstrs & commit_not_mispred_mask
1016  val mbpWrongs = mbpInstrs & commit_mispred_mask
1017
1018  io.bpuInfo.bpRight := PopCount(mbpRights)
1019  io.bpuInfo.bpWrong := PopCount(mbpWrongs)
1020
1021  // Cfi Info
1022  for (i <- 0 until PredictWidth) {
1023    val pc = commit_pc_bundle.startAddr + (i * instBytes).U
1024    val v = commit_state(i) === c_commited
1025    val isBr = commit_pd.brMask(i)
1026    val isJmp = commit_pd.jmpInfo.valid && commit_pd.jmpOffset === i.U
1027    val isCfi = isBr || isJmp
1028    val isTaken = commit_cfi.valid && commit_cfi.bits === i.U
1029    val misPred = commit_mispredict(i)
1030    // val ghist = commit_spec_meta.ghist.predHist
1031    val histPtr = commit_spec_meta.histPtr
1032    val predCycle = commit_meta.meta(63, 0)
1033    val target = commit_target
1034
1035    val brIdx = OHToUInt(Reverse(Cat(update_ftb_entry.brValids.zip(update_ftb_entry.brOffset).map{case(v, offset) => v && offset === i.U})))
1036    val inFtbEntry = update_ftb_entry.brValids.zip(update_ftb_entry.brOffset).map{case(v, offset) => v && offset === i.U}.reduce(_||_)
1037    val addIntoHist = ((commit_hit === h_hit) && inFtbEntry) || ((!(commit_hit === h_hit) && i.U === commit_cfi.bits && isBr && commit_cfi.valid))
1038    XSDebug(v && do_commit && isCfi, p"cfi_update: isBr(${isBr}) pc(${Hexadecimal(pc)}) " +
1039    p"taken(${isTaken}) mispred(${misPred}) cycle($predCycle) hist(${histPtr.value}) " +
1040    p"startAddr(${Hexadecimal(commit_pc_bundle.startAddr)}) AddIntoHist(${addIntoHist}) " +
1041    p"brInEntry(${inFtbEntry}) brIdx(${brIdx}) target(${Hexadecimal(target)})\n")
1042  }
1043
1044  val enq = io.fromBpu.resp
1045  val perf_redirect = backendRedirect
1046
1047  XSPerfAccumulate("entry", validEntries)
1048  XSPerfAccumulate("bpu_to_ftq_stall", enq.valid && !enq.ready)
1049  XSPerfAccumulate("mispredictRedirect", perf_redirect.valid && RedirectLevel.flushAfter === perf_redirect.bits.level)
1050  XSPerfAccumulate("replayRedirect", perf_redirect.valid && RedirectLevel.flushItself(perf_redirect.bits.level))
1051  XSPerfAccumulate("predecodeRedirect", fromIfuRedirect.valid)
1052
1053  XSPerfAccumulate("to_ifu_bubble", io.toIfu.req.ready && !io.toIfu.req.valid)
1054
1055  XSPerfAccumulate("to_ifu_stall", io.toIfu.req.valid && !io.toIfu.req.ready)
1056  XSPerfAccumulate("from_bpu_real_bubble", !enq.valid && enq.ready && allowBpuIn)
1057  XSPerfAccumulate("bpu_to_ifu_bubble", bpuPtr === ifuPtr)
1058
1059  val from_bpu = io.fromBpu.resp.bits
1060  def in_entry_len_map_gen(resp: BranchPredictionBundle)(stage: String) = {
1061    assert(!resp.is_minimal)
1062    val entry_len = (resp.ftb_entry.getFallThrough(resp.pc) - resp.pc) >> instOffsetBits
1063    val entry_len_recording_vec = (1 to PredictWidth+1).map(i => entry_len === i.U)
1064    val entry_len_map = (1 to PredictWidth+1).map(i =>
1065      f"${stage}_ftb_entry_len_$i" -> (entry_len_recording_vec(i-1) && resp.valid)
1066    ).foldLeft(Map[String, UInt]())(_+_)
1067    entry_len_map
1068  }
1069  val s2_entry_len_map = in_entry_len_map_gen(from_bpu.s2)("s2")
1070  val s3_entry_len_map = in_entry_len_map_gen(from_bpu.s3)("s3")
1071
1072  val to_ifu = io.toIfu.req.bits
1073
1074
1075
1076  val commit_num_inst_recording_vec = (1 to PredictWidth).map(i => PopCount(commit_inst_mask) === i.U)
1077  val commit_num_inst_map = (1 to PredictWidth).map(i =>
1078    f"commit_num_inst_$i" -> (commit_num_inst_recording_vec(i-1) && do_commit)
1079  ).foldLeft(Map[String, UInt]())(_+_)
1080
1081
1082
1083  val commit_jal_mask  = UIntToOH(commit_pd.jmpOffset) & Fill(PredictWidth, commit_pd.hasJal.asTypeOf(UInt(1.W)))
1084  val commit_jalr_mask = UIntToOH(commit_pd.jmpOffset) & Fill(PredictWidth, commit_pd.hasJalr.asTypeOf(UInt(1.W)))
1085  val commit_call_mask = UIntToOH(commit_pd.jmpOffset) & Fill(PredictWidth, commit_pd.hasCall.asTypeOf(UInt(1.W)))
1086  val commit_ret_mask  = UIntToOH(commit_pd.jmpOffset) & Fill(PredictWidth, commit_pd.hasRet.asTypeOf(UInt(1.W)))
1087
1088
1089  val mbpBRights = mbpRights & commit_br_mask
1090  val mbpJRights = mbpRights & commit_jal_mask
1091  val mbpIRights = mbpRights & commit_jalr_mask
1092  val mbpCRights = mbpRights & commit_call_mask
1093  val mbpRRights = mbpRights & commit_ret_mask
1094
1095  val mbpBWrongs = mbpWrongs & commit_br_mask
1096  val mbpJWrongs = mbpWrongs & commit_jal_mask
1097  val mbpIWrongs = mbpWrongs & commit_jalr_mask
1098  val mbpCWrongs = mbpWrongs & commit_call_mask
1099  val mbpRWrongs = mbpWrongs & commit_ret_mask
1100
1101  val commit_pred_stage = RegNext(pred_stage(commPtr.value))
1102
1103  def pred_stage_map(src: UInt, name: String) = {
1104    (0 until numBpStages).map(i =>
1105      f"${name}_stage_${i+1}" -> PopCount(src.asBools.map(_ && commit_pred_stage === BP_STAGES(i)))
1106    ).foldLeft(Map[String, UInt]())(_+_)
1107  }
1108
1109  val mispred_stage_map      = pred_stage_map(mbpWrongs,  "mispredict")
1110  val br_mispred_stage_map   = pred_stage_map(mbpBWrongs, "br_mispredict")
1111  val jalr_mispred_stage_map = pred_stage_map(mbpIWrongs, "jalr_mispredict")
1112  val correct_stage_map      = pred_stage_map(mbpRights,  "correct")
1113  val br_correct_stage_map   = pred_stage_map(mbpBRights, "br_correct")
1114  val jalr_correct_stage_map = pred_stage_map(mbpIRights, "jalr_correct")
1115
1116  val update_valid = io.toBpu.update.valid
1117  def u(cond: Bool) = update_valid && cond
1118  val ftb_false_hit = u(update.false_hit)
1119  // assert(!ftb_false_hit)
1120  val ftb_hit = u(commit_hit === h_hit)
1121
1122  val ftb_new_entry = u(ftbEntryGen.is_init_entry)
1123  val ftb_new_entry_only_br = ftb_new_entry && !update_ftb_entry.jmpValid
1124  val ftb_new_entry_only_jmp = ftb_new_entry && !update_ftb_entry.brValids(0)
1125  val ftb_new_entry_has_br_and_jmp = ftb_new_entry && update_ftb_entry.brValids(0) && update_ftb_entry.jmpValid
1126
1127  val ftb_old_entry = u(ftbEntryGen.is_old_entry)
1128
1129  val ftb_modified_entry = u(ftbEntryGen.is_new_br || ftbEntryGen.is_jalr_target_modified || ftbEntryGen.is_always_taken_modified)
1130  val ftb_modified_entry_new_br = u(ftbEntryGen.is_new_br)
1131  val ftb_modified_entry_jalr_target_modified = u(ftbEntryGen.is_jalr_target_modified)
1132  val ftb_modified_entry_br_full = ftb_modified_entry && ftbEntryGen.is_br_full
1133  val ftb_modified_entry_always_taken = ftb_modified_entry && ftbEntryGen.is_always_taken_modified
1134
1135  val ftb_entry_len = (ftbEntryGen.new_entry.getFallThrough(update.pc) - update.pc) >> instOffsetBits
1136  val ftb_entry_len_recording_vec = (1 to PredictWidth+1).map(i => ftb_entry_len === i.U)
1137  val ftb_init_entry_len_map = (1 to PredictWidth+1).map(i =>
1138    f"ftb_init_entry_len_$i" -> (ftb_entry_len_recording_vec(i-1) && ftb_new_entry)
1139  ).foldLeft(Map[String, UInt]())(_+_)
1140  val ftb_modified_entry_len_map = (1 to PredictWidth+1).map(i =>
1141    f"ftb_modified_entry_len_$i" -> (ftb_entry_len_recording_vec(i-1) && ftb_modified_entry)
1142  ).foldLeft(Map[String, UInt]())(_+_)
1143
1144  val ftq_occupancy_map = (0 to FtqSize).map(i =>
1145    f"ftq_has_entry_$i" ->( validEntries === i.U)
1146  ).foldLeft(Map[String, UInt]())(_+_)
1147
1148  val perfCountsMap = Map(
1149    "BpInstr" -> PopCount(mbpInstrs),
1150    "BpBInstr" -> PopCount(mbpBRights | mbpBWrongs),
1151    "BpRight"  -> PopCount(mbpRights),
1152    "BpWrong"  -> PopCount(mbpWrongs),
1153    "BpBRight" -> PopCount(mbpBRights),
1154    "BpBWrong" -> PopCount(mbpBWrongs),
1155    "BpJRight" -> PopCount(mbpJRights),
1156    "BpJWrong" -> PopCount(mbpJWrongs),
1157    "BpIRight" -> PopCount(mbpIRights),
1158    "BpIWrong" -> PopCount(mbpIWrongs),
1159    "BpCRight" -> PopCount(mbpCRights),
1160    "BpCWrong" -> PopCount(mbpCWrongs),
1161    "BpRRight" -> PopCount(mbpRRights),
1162    "BpRWrong" -> PopCount(mbpRWrongs),
1163
1164    "ftb_false_hit"                -> PopCount(ftb_false_hit),
1165    "ftb_hit"                      -> PopCount(ftb_hit),
1166    "ftb_new_entry"                -> PopCount(ftb_new_entry),
1167    "ftb_new_entry_only_br"        -> PopCount(ftb_new_entry_only_br),
1168    "ftb_new_entry_only_jmp"       -> PopCount(ftb_new_entry_only_jmp),
1169    "ftb_new_entry_has_br_and_jmp" -> PopCount(ftb_new_entry_has_br_and_jmp),
1170    "ftb_old_entry"                -> PopCount(ftb_old_entry),
1171    "ftb_modified_entry"           -> PopCount(ftb_modified_entry),
1172    "ftb_modified_entry_new_br"    -> PopCount(ftb_modified_entry_new_br),
1173    "ftb_jalr_target_modified"     -> PopCount(ftb_modified_entry_jalr_target_modified),
1174    "ftb_modified_entry_br_full"   -> PopCount(ftb_modified_entry_br_full),
1175    "ftb_modified_entry_always_taken" -> PopCount(ftb_modified_entry_always_taken)
1176  ) ++ ftb_init_entry_len_map ++ ftb_modified_entry_len_map ++ s2_entry_len_map ++
1177  s3_entry_len_map ++ commit_num_inst_map ++ ftq_occupancy_map ++
1178  mispred_stage_map ++ br_mispred_stage_map ++ jalr_mispred_stage_map ++
1179  correct_stage_map ++ br_correct_stage_map ++ jalr_correct_stage_map
1180
1181  for((key, value) <- perfCountsMap) {
1182    XSPerfAccumulate(key, value)
1183  }
1184
1185  // --------------------------- Debug --------------------------------
1186  // XSDebug(enq_fire, p"enq! " + io.fromBpu.resp.bits.toPrintable)
1187  XSDebug(io.toIfu.req.fire, p"fire to ifu " + io.toIfu.req.bits.toPrintable)
1188  XSDebug(do_commit, p"deq! [ptr] $do_commit_ptr\n")
1189  XSDebug(true.B, p"[bpuPtr] $bpuPtr, [ifuPtr] $ifuPtr, [ifuWbPtr] $ifuWbPtr [commPtr] $commPtr\n")
1190  XSDebug(true.B, p"[in] v:${io.fromBpu.resp.valid} r:${io.fromBpu.resp.ready} " +
1191    p"[out] v:${io.toIfu.req.valid} r:${io.toIfu.req.ready}\n")
1192  XSDebug(do_commit, p"[deq info] cfiIndex: $commit_cfi, $commit_pc_bundle, target: ${Hexadecimal(commit_target)}\n")
1193
1194  //   def ubtbCheck(commit: FtqEntry, predAns: Seq[PredictorAnswer], isWrong: Bool) = {
1195  //     commit.valids.zip(commit.pd).zip(predAns).zip(commit.takens).map {
1196  //       case (((valid, pd), ans), taken) =>
1197  //       Mux(valid && pd.isBr,
1198  //         isWrong ^ Mux(ans.hit.asBool,
1199  //           Mux(ans.taken.asBool, taken && ans.target === commitEntry.target,
1200  //           !taken),
1201  //         !taken),
1202  //       false.B)
1203  //     }
1204  //   }
1205
1206  //   def btbCheck(commit: FtqEntry, predAns: Seq[PredictorAnswer], isWrong: Bool) = {
1207  //     commit.valids.zip(commit.pd).zip(predAns).zip(commit.takens).map {
1208  //       case (((valid, pd), ans), taken) =>
1209  //       Mux(valid && pd.isBr,
1210  //         isWrong ^ Mux(ans.hit.asBool,
1211  //           Mux(ans.taken.asBool, taken && ans.target === commitEntry.target,
1212  //           !taken),
1213  //         !taken),
1214  //       false.B)
1215  //     }
1216  //   }
1217
1218  //   def tageCheck(commit: FtqEntry, predAns: Seq[PredictorAnswer], isWrong: Bool) = {
1219  //     commit.valids.zip(commit.pd).zip(predAns).zip(commit.takens).map {
1220  //       case (((valid, pd), ans), taken) =>
1221  //       Mux(valid && pd.isBr,
1222  //         isWrong ^ (ans.taken.asBool === taken),
1223  //       false.B)
1224  //     }
1225  //   }
1226
1227  //   def loopCheck(commit: FtqEntry, predAns: Seq[PredictorAnswer], isWrong: Bool) = {
1228  //     commit.valids.zip(commit.pd).zip(predAns).zip(commit.takens).map {
1229  //       case (((valid, pd), ans), taken) =>
1230  //       Mux(valid && (pd.isBr) && ans.hit.asBool,
1231  //         isWrong ^ (!taken),
1232  //           false.B)
1233  //     }
1234  //   }
1235
1236  //   def rasCheck(commit: FtqEntry, predAns: Seq[PredictorAnswer], isWrong: Bool) = {
1237  //     commit.valids.zip(commit.pd).zip(predAns).zip(commit.takens).map {
1238  //       case (((valid, pd), ans), taken) =>
1239  //       Mux(valid && pd.isRet.asBool /*&& taken*/ && ans.hit.asBool,
1240  //         isWrong ^ (ans.target === commitEntry.target),
1241  //           false.B)
1242  //     }
1243  //   }
1244
1245  //   val ubtbRights = ubtbCheck(commitEntry, commitEntry.metas.map(_.ubtbAns), false.B)
1246  //   val ubtbWrongs = ubtbCheck(commitEntry, commitEntry.metas.map(_.ubtbAns), true.B)
1247  //   // btb and ubtb pred jal and jalr as well
1248  //   val btbRights = btbCheck(commitEntry, commitEntry.metas.map(_.btbAns), false.B)
1249  //   val btbWrongs = btbCheck(commitEntry, commitEntry.metas.map(_.btbAns), true.B)
1250  //   val tageRights = tageCheck(commitEntry, commitEntry.metas.map(_.tageAns), false.B)
1251  //   val tageWrongs = tageCheck(commitEntry, commitEntry.metas.map(_.tageAns), true.B)
1252
1253  //   val loopRights = loopCheck(commitEntry, commitEntry.metas.map(_.loopAns), false.B)
1254  //   val loopWrongs = loopCheck(commitEntry, commitEntry.metas.map(_.loopAns), true.B)
1255
1256  //   val rasRights = rasCheck(commitEntry, commitEntry.metas.map(_.rasAns), false.B)
1257  //   val rasWrongs = rasCheck(commitEntry, commitEntry.metas.map(_.rasAns), true.B)
1258
1259  val perfEvents = Seq(
1260    ("bpu_s2_redirect        ", bpu_s2_redirect                                                             ),
1261    ("bpu_s3_redirect        ", bpu_s3_redirect                                                             ),
1262    ("bpu_to_ftq_stall       ", enq.valid && ~enq.ready                                                     ),
1263    ("mispredictRedirect     ", perf_redirect.valid && RedirectLevel.flushAfter === perf_redirect.bits.level),
1264    ("replayRedirect         ", perf_redirect.valid && RedirectLevel.flushItself(perf_redirect.bits.level)  ),
1265    ("predecodeRedirect      ", fromIfuRedirect.valid                                                       ),
1266    ("to_ifu_bubble          ", io.toIfu.req.ready && !io.toIfu.req.valid                                   ),
1267    ("from_bpu_real_bubble   ", !enq.valid && enq.ready && allowBpuIn                                       ),
1268    ("BpInstr                ", PopCount(mbpInstrs)                                                         ),
1269    ("BpBInstr               ", PopCount(mbpBRights | mbpBWrongs)                                           ),
1270    ("BpRight                ", PopCount(mbpRights)                                                         ),
1271    ("BpWrong                ", PopCount(mbpWrongs)                                                         ),
1272    ("BpBRight               ", PopCount(mbpBRights)                                                        ),
1273    ("BpBWrong               ", PopCount(mbpBWrongs)                                                        ),
1274    ("BpJRight               ", PopCount(mbpJRights)                                                        ),
1275    ("BpJWrong               ", PopCount(mbpJWrongs)                                                        ),
1276    ("BpIRight               ", PopCount(mbpIRights)                                                        ),
1277    ("BpIWrong               ", PopCount(mbpIWrongs)                                                        ),
1278    ("BpCRight               ", PopCount(mbpCRights)                                                        ),
1279    ("BpCWrong               ", PopCount(mbpCWrongs)                                                        ),
1280    ("BpRRight               ", PopCount(mbpRRights)                                                        ),
1281    ("BpRWrong               ", PopCount(mbpRWrongs)                                                        ),
1282    ("ftb_false_hit          ", PopCount(ftb_false_hit)                                                     ),
1283    ("ftb_hit                ", PopCount(ftb_hit)                                                           ),
1284  )
1285  generatePerfEvent()
1286}
1287