xref: /XiangShan/src/main/scala/xiangshan/frontend/NewFtq.scala (revision 6ce1096467c77f9a13c9ea0e57b870cd1fb67270)
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 utils._
23import utility._
24import xiangshan._
25import xiangshan.frontend.icache._
26import xiangshan.backend.CtrlToFtqIO
27import xiangshan.backend.decode.ImmUnion
28import utility.ChiselDB
29
30class FtqDebugBundle extends Bundle {
31  val pc = UInt(39.W)
32  val target = UInt(39.W)
33  val isBr = Bool()
34  val isJmp = Bool()
35  val isCall = Bool()
36  val isRet = Bool()
37  val misPred = Bool()
38  val isTaken = Bool()
39  val predStage = UInt(2.W)
40}
41
42class FtqPtr(entries: Int) extends CircularQueuePtr[FtqPtr](
43  entries
44){
45  def this()(implicit p: Parameters) = this(p(XSCoreParamsKey).FtqSize)
46}
47
48object FtqPtr {
49  def apply(f: Bool, v: UInt)(implicit p: Parameters): FtqPtr = {
50    val ptr = Wire(new FtqPtr)
51    ptr.flag := f
52    ptr.value := v
53    ptr
54  }
55  def inverse(ptr: FtqPtr)(implicit p: Parameters): FtqPtr = {
56    apply(!ptr.flag, ptr.value)
57  }
58}
59
60class FtqNRSRAM[T <: Data](gen: T, numRead: Int)(implicit p: Parameters) extends XSModule {
61
62  val io = IO(new Bundle() {
63    val raddr = Input(Vec(numRead, UInt(log2Up(FtqSize).W)))
64    val ren = Input(Vec(numRead, Bool()))
65    val rdata = Output(Vec(numRead, gen))
66    val waddr = Input(UInt(log2Up(FtqSize).W))
67    val wen = Input(Bool())
68    val wdata = Input(gen)
69  })
70
71  for(i <- 0 until numRead){
72    val sram = Module(new SRAMTemplate(gen, FtqSize))
73    sram.io.r.req.valid := io.ren(i)
74    sram.io.r.req.bits.setIdx := io.raddr(i)
75    io.rdata(i) := sram.io.r.resp.data(0)
76    sram.io.w.req.valid := io.wen
77    sram.io.w.req.bits.setIdx := io.waddr
78    sram.io.w.req.bits.data := VecInit(io.wdata)
79  }
80
81}
82
83class Ftq_RF_Components(implicit p: Parameters) extends XSBundle with BPUUtils {
84  val startAddr = UInt(VAddrBits.W)
85  val nextLineAddr = UInt(VAddrBits.W)
86  val isNextMask = Vec(PredictWidth, Bool())
87  val fallThruError = Bool()
88  // val carry = Bool()
89  def getPc(offset: UInt) = {
90    def getHigher(pc: UInt) = pc(VAddrBits-1, log2Ceil(PredictWidth)+instOffsetBits+1)
91    def getOffset(pc: UInt) = pc(log2Ceil(PredictWidth)+instOffsetBits, instOffsetBits)
92    Cat(getHigher(Mux(isNextMask(offset) && startAddr(log2Ceil(PredictWidth)+instOffsetBits), nextLineAddr, startAddr)),
93        getOffset(startAddr)+offset, 0.U(instOffsetBits.W))
94  }
95  def fromBranchPrediction(resp: BranchPredictionBundle) = {
96    def carryPos(addr: UInt) = addr(instOffsetBits+log2Ceil(PredictWidth)+1)
97    this.startAddr := resp.pc(3)
98    this.nextLineAddr := resp.pc(3) + (FetchWidth * 4 * 2).U // may be broken on other configs
99    this.isNextMask := VecInit((0 until PredictWidth).map(i =>
100      (resp.pc(3)(log2Ceil(PredictWidth), 1) +& i.U)(log2Ceil(PredictWidth)).asBool
101    ))
102    this.fallThruError := resp.fallThruError(3)
103    this
104  }
105  override def toPrintable: Printable = {
106    p"startAddr:${Hexadecimal(startAddr)}"
107  }
108}
109
110class Ftq_pd_Entry(implicit p: Parameters) extends XSBundle {
111  val brMask = Vec(PredictWidth, Bool())
112  val jmpInfo = ValidUndirectioned(Vec(3, Bool()))
113  val jmpOffset = UInt(log2Ceil(PredictWidth).W)
114  val jalTarget = UInt(VAddrBits.W)
115  val rvcMask = Vec(PredictWidth, Bool())
116  def hasJal  = jmpInfo.valid && !jmpInfo.bits(0)
117  def hasJalr = jmpInfo.valid && jmpInfo.bits(0)
118  def hasCall = jmpInfo.valid && jmpInfo.bits(1)
119  def hasRet  = jmpInfo.valid && jmpInfo.bits(2)
120
121  def fromPdWb(pdWb: PredecodeWritebackBundle) = {
122    val pds = pdWb.pd
123    this.brMask := VecInit(pds.map(pd => pd.isBr && pd.valid))
124    this.jmpInfo.valid := VecInit(pds.map(pd => (pd.isJal || pd.isJalr) && pd.valid)).asUInt.orR
125    this.jmpInfo.bits := ParallelPriorityMux(pds.map(pd => (pd.isJal || pd.isJalr) && pd.valid),
126                                             pds.map(pd => VecInit(pd.isJalr, pd.isCall, pd.isRet)))
127    this.jmpOffset := ParallelPriorityEncoder(pds.map(pd => (pd.isJal || pd.isJalr) && pd.valid))
128    this.rvcMask := VecInit(pds.map(pd => pd.isRVC))
129    this.jalTarget := pdWb.jalTarget
130  }
131
132  def toPd(offset: UInt) = {
133    require(offset.getWidth == log2Ceil(PredictWidth))
134    val pd = Wire(new PreDecodeInfo)
135    pd.valid := true.B
136    pd.isRVC := rvcMask(offset)
137    val isBr = brMask(offset)
138    val isJalr = offset === jmpOffset && jmpInfo.valid && jmpInfo.bits(0)
139    pd.brType := Cat(offset === jmpOffset && jmpInfo.valid, isJalr || isBr)
140    pd.isCall := offset === jmpOffset && jmpInfo.valid && jmpInfo.bits(1)
141    pd.isRet  := offset === jmpOffset && jmpInfo.valid && jmpInfo.bits(2)
142    pd
143  }
144}
145
146
147
148class Ftq_Redirect_SRAMEntry(implicit p: Parameters) extends SpeculativeInfo {}
149
150class Ftq_1R_SRAMEntry(implicit p: Parameters) extends XSBundle with HasBPUConst {
151  val meta = UInt(MaxMetaLength.W)
152}
153
154class Ftq_Pred_Info(implicit p: Parameters) extends XSBundle {
155  val target = UInt(VAddrBits.W)
156  val cfiIndex = ValidUndirectioned(UInt(log2Ceil(PredictWidth).W))
157}
158
159
160class FtqRead[T <: Data](private val gen: T)(implicit p: Parameters) extends XSBundle {
161  val ptr = Output(new FtqPtr)
162  val offset = Output(UInt(log2Ceil(PredictWidth).W))
163  val data = Input(gen)
164  def apply(ptr: FtqPtr, offset: UInt) = {
165    this.ptr := ptr
166    this.offset := offset
167    this.data
168  }
169}
170
171
172class FtqToBpuIO(implicit p: Parameters) extends XSBundle {
173  val redirect = Valid(new BranchPredictionRedirect)
174  val update = Valid(new BranchPredictionUpdate)
175  val enq_ptr = Output(new FtqPtr)
176}
177
178class FtqToIfuIO(implicit p: Parameters) extends XSBundle with HasCircularQueuePtrHelper {
179  val req = Decoupled(new FetchRequestBundle)
180  val redirect = Valid(new BranchPredictionRedirect)
181  val topdown_redirect = Valid(new BranchPredictionRedirect)
182  val flushFromBpu = new Bundle {
183    // when ifu pipeline is not stalled,
184    // a packet from bpu s3 can reach f1 at most
185    val s2 = Valid(new FtqPtr)
186    val s3 = Valid(new FtqPtr)
187    def shouldFlushBy(src: Valid[FtqPtr], idx_to_flush: FtqPtr) = {
188      src.valid && !isAfter(src.bits, idx_to_flush)
189    }
190    def shouldFlushByStage2(idx: FtqPtr) = shouldFlushBy(s2, idx)
191    def shouldFlushByStage3(idx: FtqPtr) = shouldFlushBy(s3, idx)
192  }
193}
194
195class FtqToICacheIO(implicit p: Parameters) extends XSBundle with HasCircularQueuePtrHelper {
196  //NOTE: req.bits must be prepare in T cycle
197  // while req.valid is set true in T + 1 cycle
198  val req = Decoupled(new FtqToICacheRequestBundle)
199}
200
201trait HasBackendRedirectInfo extends HasXSParameter {
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  // newest target
211  val newest_entry_target = Output(UInt(VAddrBits.W))
212  val newest_entry_ptr = Output(new FtqPtr)
213}
214
215
216class FTBEntryGen(implicit p: Parameters) extends XSModule with HasBackendRedirectInfo with HasBPUParameter {
217  val io = IO(new Bundle {
218    val start_addr = Input(UInt(VAddrBits.W))
219    val old_entry = Input(new FTBEntry)
220    val pd = Input(new Ftq_pd_Entry)
221    val cfiIndex = Flipped(Valid(UInt(log2Ceil(PredictWidth).W)))
222    val target = Input(UInt(VAddrBits.W))
223    val hit = Input(Bool())
224    val mispredict_vec = Input(Vec(PredictWidth, Bool()))
225
226    val new_entry = Output(new FTBEntry)
227    val new_br_insert_pos = Output(Vec(numBr, Bool()))
228    val taken_mask = Output(Vec(numBr, Bool()))
229    val jmp_taken = Output(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  io.jmp_taken := io.new_entry.jmpValid && io.new_entry.tailSlot.offset === io.cfiIndex.bits
385  for (i <- 0 until numBr) {
386    io.mispred_mask(i) := io.new_entry.brValids(i) && io.mispredict_vec(io.new_entry.brOffset(i))
387  }
388  io.mispred_mask.last := io.new_entry.jmpValid && io.mispredict_vec(pd.jmpOffset)
389
390  // for perf counters
391  io.is_init_entry := !hit
392  io.is_old_entry := hit && !is_new_br && !jalr_target_modified && !always_taken_modified
393  io.is_new_br := hit && is_new_br
394  io.is_jalr_target_modified := hit && jalr_target_modified
395  io.is_always_taken_modified := hit && always_taken_modified
396  io.is_br_full := hit && is_new_br && may_have_to_replace
397}
398
399class FtqPcMemWrapper(numOtherReads: Int)(implicit p: Parameters) extends XSModule with HasBackendRedirectInfo {
400  val io = IO(new Bundle {
401    val ifuPtr_w       = Input(new FtqPtr)
402    val ifuPtrPlus1_w  = Input(new FtqPtr)
403    val ifuPtrPlus2_w  = Input(new FtqPtr)
404    val commPtr_w      = Input(new FtqPtr)
405    val commPtrPlus1_w = Input(new FtqPtr)
406    val ifuPtr_rdata       = Output(new Ftq_RF_Components)
407    val ifuPtrPlus1_rdata  = Output(new Ftq_RF_Components)
408    val ifuPtrPlus2_rdata  = Output(new Ftq_RF_Components)
409    val commPtr_rdata      = Output(new Ftq_RF_Components)
410    val commPtrPlus1_rdata = Output(new Ftq_RF_Components)
411
412    val other_raddrs = Input(Vec(numOtherReads, UInt(log2Ceil(FtqSize).W)))
413    val other_rdatas = Output(Vec(numOtherReads, new Ftq_RF_Components))
414
415    val wen = Input(Bool())
416    val waddr = Input(UInt(log2Ceil(FtqSize).W))
417    val wdata = Input(new Ftq_RF_Components)
418  })
419
420  val num_pc_read = numOtherReads + 5
421  val mem = Module(new SyncDataModuleTemplate(new Ftq_RF_Components, FtqSize,
422    num_pc_read, 1, "FtqPC"))
423  mem.io.wen(0)   := io.wen
424  mem.io.waddr(0) := io.waddr
425  mem.io.wdata(0) := io.wdata
426
427  // read one cycle ahead for ftq local reads
428  val raddr_vec = VecInit(io.other_raddrs ++
429    Seq(io.ifuPtr_w.value, io.ifuPtrPlus1_w.value, io.ifuPtrPlus2_w.value, io.commPtrPlus1_w.value, io.commPtr_w.value))
430
431  mem.io.raddr := raddr_vec
432
433  io.other_rdatas       := mem.io.rdata.dropRight(5)
434  io.ifuPtr_rdata       := mem.io.rdata.dropRight(4).last
435  io.ifuPtrPlus1_rdata  := mem.io.rdata.dropRight(3).last
436  io.ifuPtrPlus2_rdata  := mem.io.rdata.dropRight(2).last
437  io.commPtrPlus1_rdata := mem.io.rdata.dropRight(1).last
438  io.commPtr_rdata      := mem.io.rdata.last
439}
440
441class Ftq(implicit p: Parameters) extends XSModule with HasCircularQueuePtrHelper
442  with HasBackendRedirectInfo with BPUUtils with HasBPUConst with HasPerfEvents
443  with HasICacheParameters{
444  val io = IO(new Bundle {
445    val fromBpu = Flipped(new BpuToFtqIO)
446    val fromIfu = Flipped(new IfuToFtqIO)
447    val fromBackend = Flipped(new CtrlToFtqIO)
448
449    val toBpu = new FtqToBpuIO
450    val toIfu = new FtqToIfuIO
451    val toICache = new FtqToICacheIO
452    val toBackend = new FtqToCtrlIO
453
454    val toPrefetch = new FtqPrefechBundle
455
456    val bpuInfo = new Bundle {
457      val bpRight = Output(UInt(XLEN.W))
458      val bpWrong = Output(UInt(XLEN.W))
459    }
460
461    val mmioCommitRead = Flipped(new mmioCommitRead)
462
463    // for perf
464    val ControlBTBMissBubble = Output(Bool())
465    val TAGEMissBubble = Output(Bool())
466    val SCMissBubble = Output(Bool())
467    val ITTAGEMissBubble = Output(Bool())
468    val RASMissBubble = Output(Bool())
469  })
470  io.bpuInfo := DontCare
471
472  val topdown_stage = RegInit(0.U.asTypeOf(new FrontendTopDownBundle))
473  dontTouch(topdown_stage)
474  // only driven by clock, not valid-ready
475  topdown_stage := io.fromBpu.resp.bits.topdown_info
476  io.toIfu.req.bits.topdown_info := topdown_stage
477
478  val ifuRedirected = RegInit(VecInit(Seq.fill(FtqSize)(false.B)))
479
480  val backendRedirect = Wire(Valid(new BranchPredictionRedirect))
481  when(io.fromBackend.redirect.valid) {
482    assert(RegNext(io.fromBackend.ftqIdxAhead.map(_.valid).reduce(_|_)))
483    assert(io.fromBackend.ftqIdxSelOH.valid)
484    assert(PopCount(io.fromBackend.ftqIdxSelOH.bits) === 1.U)
485  }
486
487  val stage2Flush = backendRedirect.valid
488  val backendFlush = stage2Flush || RegNext(stage2Flush)
489  val ifuFlush = Wire(Bool())
490
491  val flush = stage2Flush || RegNext(stage2Flush)
492
493  val allowBpuIn, allowToIfu = WireInit(false.B)
494  val flushToIfu = !allowToIfu
495  allowBpuIn := !ifuFlush && !backendRedirect.valid
496  allowToIfu := !ifuFlush && !backendRedirect.valid
497
498  def copyNum = 5
499  val bpuPtr, ifuPtr, ifuWbPtr, commPtr, robCommPtr = RegInit(FtqPtr(false.B, 0.U))
500  val ifuPtrPlus1 = RegInit(FtqPtr(false.B, 1.U))
501  val ifuPtrPlus2 = RegInit(FtqPtr(false.B, 2.U))
502  val commPtrPlus1 = RegInit(FtqPtr(false.B, 1.U))
503  val copied_ifu_ptr = Seq.fill(copyNum)(RegInit(FtqPtr(false.B, 0.U)))
504  val copied_bpu_ptr = Seq.fill(copyNum)(RegInit(FtqPtr(false.B, 0.U)))
505  require(FtqSize >= 4)
506  val ifuPtr_write       = WireInit(ifuPtr)
507  val ifuPtrPlus1_write  = WireInit(ifuPtrPlus1)
508  val ifuPtrPlus2_write  = WireInit(ifuPtrPlus2)
509  val ifuWbPtr_write     = WireInit(ifuWbPtr)
510  val commPtr_write      = WireInit(commPtr)
511  val commPtrPlus1_write = WireInit(commPtrPlus1)
512  val robCommPtr_write   = WireInit(robCommPtr)
513  ifuPtr       := ifuPtr_write
514  ifuPtrPlus1  := ifuPtrPlus1_write
515  ifuPtrPlus2  := ifuPtrPlus2_write
516  ifuWbPtr     := ifuWbPtr_write
517  commPtr      := commPtr_write
518  commPtrPlus1 := commPtrPlus1_write
519  copied_ifu_ptr.map{ptr =>
520    ptr := ifuPtr_write
521    dontTouch(ptr)
522  }
523  robCommPtr   := robCommPtr_write
524  val validEntries = distanceBetween(bpuPtr, commPtr)
525  val canCommit = Wire(Bool())
526
527  // **********************************************************************
528  // **************************** enq from bpu ****************************
529  // **********************************************************************
530  val new_entry_ready = validEntries < FtqSize.U || canCommit
531  io.fromBpu.resp.ready := new_entry_ready
532
533  val bpu_s2_resp = io.fromBpu.resp.bits.s2
534  val bpu_s3_resp = io.fromBpu.resp.bits.s3
535  val bpu_s2_redirect = bpu_s2_resp.valid(3) && bpu_s2_resp.hasRedirect(3)
536  val bpu_s3_redirect = bpu_s3_resp.valid(3) && bpu_s3_resp.hasRedirect(3)
537
538  io.toBpu.enq_ptr := bpuPtr
539  val enq_fire = io.fromBpu.resp.fire && allowBpuIn // from bpu s1
540  val bpu_in_fire = (io.fromBpu.resp.fire || bpu_s2_redirect || bpu_s3_redirect) && allowBpuIn
541
542  val bpu_in_resp = io.fromBpu.resp.bits.selectedResp
543  val bpu_in_stage = io.fromBpu.resp.bits.selectedRespIdxForFtq
544  val bpu_in_resp_ptr = Mux(bpu_in_stage === BP_S1, bpuPtr, bpu_in_resp.ftq_idx)
545  val bpu_in_resp_idx = bpu_in_resp_ptr.value
546
547  // read ports:      prefetchReq ++  ifuReq1 + ifuReq2 + ifuReq3 + commitUpdate2 + commitUpdate
548  val ftq_pc_mem = Module(new FtqPcMemWrapper(1))
549  // resp from uBTB
550  ftq_pc_mem.io.wen := bpu_in_fire
551  ftq_pc_mem.io.waddr := bpu_in_resp_idx
552  ftq_pc_mem.io.wdata.fromBranchPrediction(bpu_in_resp)
553
554  //                                                            ifuRedirect + backendRedirect + commit
555  val ftq_redirect_sram = Module(new FtqNRSRAM(new Ftq_Redirect_SRAMEntry, 1+BackendRedirectNum+1))
556  // these info is intended to enq at the last stage of bpu
557  ftq_redirect_sram.io.wen := io.fromBpu.resp.bits.lastStage.valid(3)
558  ftq_redirect_sram.io.waddr := io.fromBpu.resp.bits.lastStage.ftq_idx.value
559  ftq_redirect_sram.io.wdata := io.fromBpu.resp.bits.last_stage_spec_info
560  println(f"ftq redirect SRAM: entry ${ftq_redirect_sram.io.wdata.getWidth} * ${FtqSize} * 3")
561  println(f"ftq redirect SRAM: ahead fh ${ftq_redirect_sram.io.wdata.afhob.getWidth} * ${FtqSize} * 3")
562
563  val ftq_meta_1r_sram = Module(new FtqNRSRAM(new Ftq_1R_SRAMEntry, 1))
564  // these info is intended to enq at the last stage of bpu
565  ftq_meta_1r_sram.io.wen := io.fromBpu.resp.bits.lastStage.valid(3)
566  ftq_meta_1r_sram.io.waddr := io.fromBpu.resp.bits.lastStage.ftq_idx.value
567  ftq_meta_1r_sram.io.wdata.meta := io.fromBpu.resp.bits.last_stage_meta
568  //                                                            ifuRedirect + backendRedirect + commit
569  val ftb_entry_mem = Module(new SyncDataModuleTemplate(new FTBEntry, FtqSize, 1+BackendRedirectNum+1, 1))
570  ftb_entry_mem.io.wen(0) := io.fromBpu.resp.bits.lastStage.valid(3)
571  ftb_entry_mem.io.waddr(0) := io.fromBpu.resp.bits.lastStage.ftq_idx.value
572  ftb_entry_mem.io.wdata(0) := io.fromBpu.resp.bits.last_stage_ftb_entry
573
574
575  // multi-write
576  val update_target = Reg(Vec(FtqSize, UInt(VAddrBits.W))) // could be taken target or fallThrough //TODO: remove this
577  val newest_entry_target = Reg(UInt(VAddrBits.W))
578  val newest_entry_ptr = Reg(new FtqPtr)
579  val cfiIndex_vec = Reg(Vec(FtqSize, ValidUndirectioned(UInt(log2Ceil(PredictWidth).W))))
580  val mispredict_vec = Reg(Vec(FtqSize, Vec(PredictWidth, Bool())))
581  val pred_stage = Reg(Vec(FtqSize, UInt(2.W)))
582  val pred_s1_cycle = if (!env.FPGAPlatform) Some(Reg(Vec(FtqSize, UInt(64.W)))) else None
583
584  val c_invalid :: c_valid :: c_commited :: Nil = Enum(3)
585  val commitStateQueue = RegInit(VecInit(Seq.fill(FtqSize) {
586    VecInit(Seq.fill(PredictWidth)(c_invalid))
587  }))
588
589  val f_to_send :: f_sent :: Nil = Enum(2)
590  val entry_fetch_status = RegInit(VecInit(Seq.fill(FtqSize)(f_sent)))
591
592  val h_not_hit :: h_false_hit :: h_hit :: Nil = Enum(3)
593  val entry_hit_status = RegInit(VecInit(Seq.fill(FtqSize)(h_not_hit)))
594
595  // modify registers one cycle later to cut critical path
596  val last_cycle_bpu_in = RegNext(bpu_in_fire)
597  val last_cycle_bpu_in_ptr = RegNext(bpu_in_resp_ptr)
598  val last_cycle_bpu_in_idx = last_cycle_bpu_in_ptr.value
599  val last_cycle_bpu_target = RegNext(bpu_in_resp.getTarget(3))
600  val last_cycle_cfiIndex = RegNext(bpu_in_resp.cfiIndex(3))
601  val last_cycle_bpu_in_stage = RegNext(bpu_in_stage)
602
603  def extra_copyNum_for_commitStateQueue = 2
604  val copied_last_cycle_bpu_in = VecInit(Seq.fill(copyNum+extra_copyNum_for_commitStateQueue)(RegNext(bpu_in_fire)))
605  val copied_last_cycle_bpu_in_ptr_for_ftq = VecInit(Seq.fill(extra_copyNum_for_commitStateQueue)(RegNext(bpu_in_resp_ptr)))
606
607  when (last_cycle_bpu_in) {
608    entry_fetch_status(last_cycle_bpu_in_idx) := f_to_send
609    cfiIndex_vec(last_cycle_bpu_in_idx) := last_cycle_cfiIndex
610    pred_stage(last_cycle_bpu_in_idx) := last_cycle_bpu_in_stage
611
612    update_target(last_cycle_bpu_in_idx) := last_cycle_bpu_target // TODO: remove this
613    newest_entry_target := last_cycle_bpu_target
614    newest_entry_ptr := last_cycle_bpu_in_ptr
615  }
616
617  // reduce fanout by delay write for a cycle
618  when (RegNext(last_cycle_bpu_in)) {
619    mispredict_vec(RegNext(last_cycle_bpu_in_idx)) := WireInit(VecInit(Seq.fill(PredictWidth)(false.B)))
620  }
621
622  // record s1 pred cycles
623  pred_s1_cycle.map(vec => {
624    when (bpu_in_fire && (bpu_in_stage === BP_S1)) {
625      vec(bpu_in_resp_ptr.value) := bpu_in_resp.full_pred(0).predCycle.getOrElse(0.U)
626    }
627  })
628
629  // reduce fanout using copied last_cycle_bpu_in and copied last_cycle_bpu_in_ptr
630  val copied_last_cycle_bpu_in_for_ftq = copied_last_cycle_bpu_in.takeRight(extra_copyNum_for_commitStateQueue)
631  copied_last_cycle_bpu_in_for_ftq.zip(copied_last_cycle_bpu_in_ptr_for_ftq).zipWithIndex.map {
632    case ((in, ptr), i) =>
633      when (in) {
634        val perSetEntries = FtqSize / extra_copyNum_for_commitStateQueue // 32
635        require(FtqSize % extra_copyNum_for_commitStateQueue == 0)
636        for (j <- 0 until perSetEntries) {
637          when (ptr.value === (i*perSetEntries+j).U) {
638            commitStateQueue(i*perSetEntries+j) := VecInit(Seq.fill(PredictWidth)(c_invalid))
639          }
640        }
641      }
642  }
643
644  // num cycle is fixed
645  io.toBackend.newest_entry_ptr := RegNext(newest_entry_ptr)
646  io.toBackend.newest_entry_target := RegNext(newest_entry_target)
647
648
649  bpuPtr := bpuPtr + enq_fire
650  copied_bpu_ptr.map(_ := bpuPtr + enq_fire)
651  when (io.toIfu.req.fire && allowToIfu) {
652    ifuPtr_write := ifuPtrPlus1
653    ifuPtrPlus1_write := ifuPtrPlus2
654    ifuPtrPlus2_write := ifuPtrPlus2 + 1.U
655  }
656
657  // only use ftb result to assign hit status
658  when (bpu_s2_resp.valid(3)) {
659    entry_hit_status(bpu_s2_resp.ftq_idx.value) := Mux(bpu_s2_resp.full_pred(3).hit, h_hit, h_not_hit)
660  }
661
662
663  io.toIfu.flushFromBpu.s2.valid := bpu_s2_redirect
664  io.toIfu.flushFromBpu.s2.bits := bpu_s2_resp.ftq_idx
665  when (bpu_s2_redirect) {
666    bpuPtr := bpu_s2_resp.ftq_idx + 1.U
667    copied_bpu_ptr.map(_ := bpu_s2_resp.ftq_idx + 1.U)
668    // only when ifuPtr runs ahead of bpu s2 resp should we recover it
669    when (!isBefore(ifuPtr, bpu_s2_resp.ftq_idx)) {
670      ifuPtr_write := bpu_s2_resp.ftq_idx
671      ifuPtrPlus1_write := bpu_s2_resp.ftq_idx + 1.U
672      ifuPtrPlus2_write := bpu_s2_resp.ftq_idx + 2.U
673    }
674  }
675
676  io.toIfu.flushFromBpu.s3.valid := bpu_s3_redirect
677  io.toIfu.flushFromBpu.s3.bits := bpu_s3_resp.ftq_idx
678  when (bpu_s3_redirect) {
679    bpuPtr := bpu_s3_resp.ftq_idx + 1.U
680    copied_bpu_ptr.map(_ := bpu_s3_resp.ftq_idx + 1.U)
681    // only when ifuPtr runs ahead of bpu s2 resp should we recover it
682    when (!isBefore(ifuPtr, bpu_s3_resp.ftq_idx)) {
683      ifuPtr_write := bpu_s3_resp.ftq_idx
684      ifuPtrPlus1_write := bpu_s3_resp.ftq_idx + 1.U
685      ifuPtrPlus2_write := bpu_s3_resp.ftq_idx + 2.U
686    }
687  }
688
689  XSError(isBefore(bpuPtr, ifuPtr) && !isFull(bpuPtr, ifuPtr), "\nifuPtr is before bpuPtr!\n")
690  XSError(isBefore(ifuWbPtr, commPtr) && !isFull(ifuWbPtr, commPtr), "\ncommPtr is before ifuWbPtr!\n")
691
692  (0 until copyNum).map{i =>
693    XSError(copied_bpu_ptr(i) =/= bpuPtr, "\ncopiedBpuPtr is different from bpuPtr!\n")
694  }
695
696  // ****************************************************************
697  // **************************** to ifu ****************************
698  // ****************************************************************
699  // 0  for ifu, and 1-4 for ICache
700  val bpu_in_bypass_buf = RegEnable(ftq_pc_mem.io.wdata, bpu_in_fire)
701  val copied_bpu_in_bypass_buf = VecInit(Seq.fill(copyNum)(RegEnable(ftq_pc_mem.io.wdata, bpu_in_fire)))
702  val bpu_in_bypass_buf_for_ifu = bpu_in_bypass_buf
703  val bpu_in_bypass_ptr = RegNext(bpu_in_resp_ptr)
704  val last_cycle_to_ifu_fire = RegNext(io.toIfu.req.fire)
705
706  val copied_bpu_in_bypass_ptr = VecInit(Seq.fill(copyNum)(RegNext(bpu_in_resp_ptr)))
707  val copied_last_cycle_to_ifu_fire = VecInit(Seq.fill(copyNum)(RegNext(io.toIfu.req.fire)))
708
709  // read pc and target
710  ftq_pc_mem.io.ifuPtr_w       := ifuPtr_write
711  ftq_pc_mem.io.ifuPtrPlus1_w  := ifuPtrPlus1_write
712  ftq_pc_mem.io.ifuPtrPlus2_w  := ifuPtrPlus2_write
713  ftq_pc_mem.io.commPtr_w      := commPtr_write
714  ftq_pc_mem.io.commPtrPlus1_w := commPtrPlus1_write
715
716
717  io.toIfu.req.bits.ftqIdx := ifuPtr
718
719  val toICachePcBundle = Wire(Vec(copyNum,new Ftq_RF_Components))
720  val toICacheEntryToSend = Wire(Vec(copyNum,Bool()))
721  val toIfuPcBundle = Wire(new Ftq_RF_Components)
722  val entry_is_to_send = WireInit(entry_fetch_status(ifuPtr.value) === f_to_send)
723  val entry_ftq_offset = WireInit(cfiIndex_vec(ifuPtr.value))
724  val entry_next_addr  = Wire(UInt(VAddrBits.W))
725
726  val pc_mem_ifu_ptr_rdata   = VecInit(Seq.fill(copyNum)(RegNext(ftq_pc_mem.io.ifuPtr_rdata)))
727  val pc_mem_ifu_plus1_rdata = VecInit(Seq.fill(copyNum)(RegNext(ftq_pc_mem.io.ifuPtrPlus1_rdata)))
728  val diff_entry_next_addr = WireInit(update_target(ifuPtr.value)) //TODO: remove this
729
730  val copied_ifu_plus1_to_send = VecInit(Seq.fill(copyNum)(RegNext(entry_fetch_status(ifuPtrPlus1.value) === f_to_send) || RegNext(last_cycle_bpu_in && bpu_in_bypass_ptr === (ifuPtrPlus1))))
731  val copied_ifu_ptr_to_send   = VecInit(Seq.fill(copyNum)(RegNext(entry_fetch_status(ifuPtr.value) === f_to_send) || RegNext(last_cycle_bpu_in && bpu_in_bypass_ptr === ifuPtr)))
732
733  for(i <- 0 until copyNum){
734    when(copied_last_cycle_bpu_in(i) && copied_bpu_in_bypass_ptr(i) === copied_ifu_ptr(i)){
735      toICachePcBundle(i) := copied_bpu_in_bypass_buf(i)
736      toICacheEntryToSend(i)   := true.B
737    }.elsewhen(copied_last_cycle_to_ifu_fire(i)){
738      toICachePcBundle(i) := pc_mem_ifu_plus1_rdata(i)
739      toICacheEntryToSend(i)   := copied_ifu_plus1_to_send(i)
740    }.otherwise{
741      toICachePcBundle(i) := pc_mem_ifu_ptr_rdata(i)
742      toICacheEntryToSend(i)   := copied_ifu_ptr_to_send(i)
743    }
744  }
745
746  // TODO: reconsider target address bypass logic
747  when (last_cycle_bpu_in && bpu_in_bypass_ptr === ifuPtr) {
748    toIfuPcBundle := bpu_in_bypass_buf_for_ifu
749    entry_is_to_send := true.B
750    entry_next_addr := last_cycle_bpu_target
751    entry_ftq_offset := last_cycle_cfiIndex
752    diff_entry_next_addr := last_cycle_bpu_target // TODO: remove this
753  }.elsewhen (last_cycle_to_ifu_fire) {
754    toIfuPcBundle := RegNext(ftq_pc_mem.io.ifuPtrPlus1_rdata)
755    entry_is_to_send := RegNext(entry_fetch_status(ifuPtrPlus1.value) === f_to_send) ||
756                        RegNext(last_cycle_bpu_in && bpu_in_bypass_ptr === (ifuPtrPlus1)) // reduce potential bubbles
757    entry_next_addr := Mux(last_cycle_bpu_in && bpu_in_bypass_ptr === (ifuPtrPlus1),
758                          bpu_in_bypass_buf_for_ifu.startAddr,
759                          Mux(ifuPtr === newest_entry_ptr,
760                            newest_entry_target,
761                            RegNext(ftq_pc_mem.io.ifuPtrPlus2_rdata.startAddr))) // ifuPtr+2
762  }.otherwise {
763    toIfuPcBundle := RegNext(ftq_pc_mem.io.ifuPtr_rdata)
764    entry_is_to_send := RegNext(entry_fetch_status(ifuPtr.value) === f_to_send) ||
765                        RegNext(last_cycle_bpu_in && bpu_in_bypass_ptr === ifuPtr) // reduce potential bubbles
766    entry_next_addr := Mux(last_cycle_bpu_in && bpu_in_bypass_ptr === (ifuPtrPlus1),
767                          bpu_in_bypass_buf_for_ifu.startAddr,
768                          Mux(ifuPtr === newest_entry_ptr,
769                            newest_entry_target,
770                            RegNext(ftq_pc_mem.io.ifuPtrPlus1_rdata.startAddr))) // ifuPtr+1
771  }
772
773  io.toIfu.req.valid := entry_is_to_send && ifuPtr =/= bpuPtr
774  io.toIfu.req.bits.nextStartAddr := entry_next_addr
775  io.toIfu.req.bits.ftqOffset := entry_ftq_offset
776  io.toIfu.req.bits.fromFtqPcBundle(toIfuPcBundle)
777
778  io.toICache.req.valid := entry_is_to_send && ifuPtr =/= bpuPtr
779  io.toICache.req.bits.readValid.zipWithIndex.map{case(copy, i) => copy := toICacheEntryToSend(i) && copied_ifu_ptr(i) =/= copied_bpu_ptr(i)}
780  io.toICache.req.bits.pcMemRead.zipWithIndex.map{case(copy,i) => copy.fromFtqPcBundle(toICachePcBundle(i))}
781  // io.toICache.req.bits.bypassSelect := last_cycle_bpu_in && bpu_in_bypass_ptr === ifuPtr
782  // io.toICache.req.bits.bpuBypassWrite.zipWithIndex.map{case(bypassWrtie, i) =>
783  //   bypassWrtie.startAddr := bpu_in_bypass_buf.tail(i).startAddr
784  //   bypassWrtie.nextlineStart := bpu_in_bypass_buf.tail(i).nextLineAddr
785  // }
786
787  // TODO: remove this
788  XSError(io.toIfu.req.valid && diff_entry_next_addr =/= entry_next_addr,
789          p"\nifu_req_target wrong! ifuPtr: ${ifuPtr}, entry_next_addr: ${Hexadecimal(entry_next_addr)} diff_entry_next_addr: ${Hexadecimal(diff_entry_next_addr)}\n")
790
791  // when fall through is smaller in value than start address, there must be a false hit
792  when (toIfuPcBundle.fallThruError && entry_hit_status(ifuPtr.value) === h_hit) {
793    when (io.toIfu.req.fire &&
794      !(bpu_s2_redirect && bpu_s2_resp.ftq_idx === ifuPtr) &&
795      !(bpu_s3_redirect && bpu_s3_resp.ftq_idx === ifuPtr)
796    ) {
797      entry_hit_status(ifuPtr.value) := h_false_hit
798      // XSError(true.B, "FTB false hit by fallThroughError, startAddr: %x, fallTHru: %x\n", io.toIfu.req.bits.startAddr, io.toIfu.req.bits.nextStartAddr)
799    }
800    XSDebug(true.B, "fallThruError! start:%x, fallThru:%x\n", io.toIfu.req.bits.startAddr, io.toIfu.req.bits.nextStartAddr)
801  }
802
803  XSPerfAccumulate(f"fall_through_error_to_ifu", toIfuPcBundle.fallThruError && entry_hit_status(ifuPtr.value) === h_hit &&
804    io.toIfu.req.fire && !(bpu_s2_redirect && bpu_s2_resp.ftq_idx === ifuPtr) && !(bpu_s3_redirect && bpu_s3_resp.ftq_idx === ifuPtr))
805
806  val ifu_req_should_be_flushed =
807    io.toIfu.flushFromBpu.shouldFlushByStage2(io.toIfu.req.bits.ftqIdx) ||
808    io.toIfu.flushFromBpu.shouldFlushByStage3(io.toIfu.req.bits.ftqIdx)
809
810    when (io.toIfu.req.fire && !ifu_req_should_be_flushed) {
811      entry_fetch_status(ifuPtr.value) := f_sent
812    }
813
814  // *********************************************************************
815  // **************************** wb from ifu ****************************
816  // *********************************************************************
817  val pdWb = io.fromIfu.pdWb
818  val pds = pdWb.bits.pd
819  val ifu_wb_valid = pdWb.valid
820  val ifu_wb_idx = pdWb.bits.ftqIdx.value
821  // read ports:                                                         commit update
822  val ftq_pd_mem = Module(new SyncDataModuleTemplate(new Ftq_pd_Entry, FtqSize, 1, 1))
823  ftq_pd_mem.io.wen(0) := ifu_wb_valid
824  ftq_pd_mem.io.waddr(0) := pdWb.bits.ftqIdx.value
825  ftq_pd_mem.io.wdata(0).fromPdWb(pdWb.bits)
826
827  val hit_pd_valid = entry_hit_status(ifu_wb_idx) === h_hit && ifu_wb_valid
828  val hit_pd_mispred = hit_pd_valid && pdWb.bits.misOffset.valid
829  val hit_pd_mispred_reg = RegNext(hit_pd_mispred, init=false.B)
830  val pd_reg       = RegEnable(pds,             pdWb.valid)
831  val start_pc_reg = RegEnable(pdWb.bits.pc(0), pdWb.valid)
832  val wb_idx_reg   = RegEnable(ifu_wb_idx,      pdWb.valid)
833
834  when (ifu_wb_valid) {
835    val comm_stq_wen = VecInit(pds.map(_.valid).zip(pdWb.bits.instrRange).map{
836      case (v, inRange) => v && inRange
837    })
838    (commitStateQueue(ifu_wb_idx) zip comm_stq_wen).map{
839      case (qe, v) => when (v) { qe := c_valid }
840    }
841  }
842
843  when (ifu_wb_valid) {
844    ifuWbPtr_write := ifuWbPtr + 1.U
845  }
846
847  XSError(ifu_wb_valid && isAfter(pdWb.bits.ftqIdx, ifuPtr), "IFU returned a predecode before its req, check IFU")
848
849  ftb_entry_mem.io.raddr.head := ifu_wb_idx
850  val has_false_hit = WireInit(false.B)
851  when (RegNext(hit_pd_valid)) {
852    // check for false hit
853    val pred_ftb_entry = ftb_entry_mem.io.rdata.head
854    val brSlots = pred_ftb_entry.brSlots
855    val tailSlot = pred_ftb_entry.tailSlot
856    // we check cfis that bpu predicted
857
858    // bpu predicted branches but denied by predecode
859    val br_false_hit =
860      brSlots.map{
861        s => s.valid && !(pd_reg(s.offset).valid && pd_reg(s.offset).isBr)
862      }.reduce(_||_) ||
863      (tailSlot.valid && pred_ftb_entry.tailSlot.sharing &&
864        !(pd_reg(tailSlot.offset).valid && pd_reg(tailSlot.offset).isBr))
865
866    val jmpOffset = tailSlot.offset
867    val jmp_pd = pd_reg(jmpOffset)
868    val jal_false_hit = pred_ftb_entry.jmpValid &&
869      ((pred_ftb_entry.isJal  && !(jmp_pd.valid && jmp_pd.isJal)) ||
870       (pred_ftb_entry.isJalr && !(jmp_pd.valid && jmp_pd.isJalr)) ||
871       (pred_ftb_entry.isCall && !(jmp_pd.valid && jmp_pd.isCall)) ||
872       (pred_ftb_entry.isRet  && !(jmp_pd.valid && jmp_pd.isRet))
873      )
874
875    has_false_hit := br_false_hit || jal_false_hit || hit_pd_mispred_reg
876    XSDebug(has_false_hit, "FTB false hit by br or jal or hit_pd, startAddr: %x\n", pdWb.bits.pc(0))
877
878    // assert(!has_false_hit)
879  }
880
881  when (has_false_hit) {
882    entry_hit_status(wb_idx_reg) := h_false_hit
883  }
884
885
886  // **********************************************************************
887  // ***************************** to backend *****************************
888  // **********************************************************************
889  // to backend pc mem / target
890  io.toBackend.pc_mem_wen   := RegNext(last_cycle_bpu_in)
891  io.toBackend.pc_mem_waddr := RegNext(last_cycle_bpu_in_idx)
892  io.toBackend.pc_mem_wdata := RegNext(bpu_in_bypass_buf_for_ifu)
893
894  // *******************************************************************************
895  // **************************** redirect from backend ****************************
896  // *******************************************************************************
897
898  // redirect read cfiInfo, couples to redirectGen s2
899  val ftq_redirect_rdata = Wire(Vec(BackendRedirectNum, new Ftq_Redirect_SRAMEntry))
900  val ftb_redirect_rdata = Wire(Vec(BackendRedirectNum, new FTBEntry))
901  for (i <- 0 until BackendRedirectNum) {
902    ftq_redirect_sram.io.ren(i + 1) := io.fromBackend.ftqIdxAhead(i).valid
903    ftq_redirect_sram.io.raddr(i + 1) := io.fromBackend.ftqIdxAhead(i).bits.value
904    ftb_entry_mem.io.raddr(i + 1)     := io.fromBackend.ftqIdxAhead(i).bits.value
905
906    ftq_redirect_rdata(i) := ftq_redirect_sram.io.rdata(i + 1)
907    ftb_redirect_rdata(i) := ftb_entry_mem.io.rdata(i + 1)
908  }
909  val stage3CfiInfo = Mux1H(io.fromBackend.ftqIdxSelOH.bits, ftq_redirect_rdata)
910  val fromBackendRedirect = WireInit(backendRedirect)
911  val backendRedirectCfi = fromBackendRedirect.bits.cfiUpdate
912  backendRedirectCfi.fromFtqRedirectSram(stage3CfiInfo)
913
914
915  val r_ftb_entry = Mux1H(io.fromBackend.ftqIdxSelOH.bits, ftb_redirect_rdata)
916  val r_ftqOffset = fromBackendRedirect.bits.ftqOffset
917
918  backendRedirectCfi.br_hit := r_ftb_entry.brIsSaved(r_ftqOffset)
919  backendRedirectCfi.jr_hit := r_ftb_entry.isJalr && r_ftb_entry.tailSlot.offset === r_ftqOffset
920  backendRedirectCfi.sc_hit := backendRedirectCfi.br_hit && Mux(r_ftb_entry.brSlots(0).offset === r_ftqOffset,
921      r_ftb_entry.brSlots(0).sc, r_ftb_entry.tailSlot.sc)
922
923  when (entry_hit_status(fromBackendRedirect.bits.ftqIdx.value) === h_hit) {
924    backendRedirectCfi.shift := PopCount(r_ftb_entry.getBrMaskByOffset(r_ftqOffset)) +&
925      (backendRedirectCfi.pd.isBr && !r_ftb_entry.brIsSaved(r_ftqOffset) &&
926      !r_ftb_entry.newBrCanNotInsert(r_ftqOffset))
927
928    backendRedirectCfi.addIntoHist := backendRedirectCfi.pd.isBr && (r_ftb_entry.brIsSaved(r_ftqOffset) ||
929        !r_ftb_entry.newBrCanNotInsert(r_ftqOffset))
930  }.otherwise {
931    backendRedirectCfi.shift := (backendRedirectCfi.pd.isBr && backendRedirectCfi.taken).asUInt
932    backendRedirectCfi.addIntoHist := backendRedirectCfi.pd.isBr.asUInt
933  }
934
935
936  // ***************************************************************************
937  // **************************** redirect from ifu ****************************
938  // ***************************************************************************
939  val fromIfuRedirect = WireInit(0.U.asTypeOf(Valid(new BranchPredictionRedirect)))
940  fromIfuRedirect.valid := pdWb.valid && pdWb.bits.misOffset.valid && !backendFlush
941  fromIfuRedirect.bits.ftqIdx := pdWb.bits.ftqIdx
942  fromIfuRedirect.bits.ftqOffset := pdWb.bits.misOffset.bits
943  fromIfuRedirect.bits.level := RedirectLevel.flushAfter
944  fromIfuRedirect.bits.BTBMissBubble := true.B
945  fromIfuRedirect.bits.debugIsMemVio := false.B
946  fromIfuRedirect.bits.debugIsCtrl := false.B
947
948  val ifuRedirectCfiUpdate = fromIfuRedirect.bits.cfiUpdate
949  ifuRedirectCfiUpdate.pc := pdWb.bits.pc(pdWb.bits.misOffset.bits)
950  ifuRedirectCfiUpdate.pd := pdWb.bits.pd(pdWb.bits.misOffset.bits)
951  ifuRedirectCfiUpdate.predTaken := cfiIndex_vec(pdWb.bits.ftqIdx.value).valid
952  ifuRedirectCfiUpdate.target := pdWb.bits.target
953  ifuRedirectCfiUpdate.taken := pdWb.bits.cfiOffset.valid
954  ifuRedirectCfiUpdate.isMisPred := pdWb.bits.misOffset.valid
955
956  val ifuRedirectReg = RegNext(fromIfuRedirect, init=0.U.asTypeOf(Valid(new BranchPredictionRedirect)))
957  val ifuRedirectToBpu = WireInit(ifuRedirectReg)
958  ifuFlush := fromIfuRedirect.valid || ifuRedirectToBpu.valid
959
960  ftq_redirect_sram.io.ren.head := fromIfuRedirect.valid
961  ftq_redirect_sram.io.raddr.head := fromIfuRedirect.bits.ftqIdx.value
962
963  ftb_entry_mem.io.raddr.head := fromIfuRedirect.bits.ftqIdx.value
964
965  val toBpuCfi = ifuRedirectToBpu.bits.cfiUpdate
966  toBpuCfi.fromFtqRedirectSram(ftq_redirect_sram.io.rdata.head)
967  when (ifuRedirectReg.bits.cfiUpdate.pd.isRet && ifuRedirectReg.bits.cfiUpdate.pd.valid) {
968    toBpuCfi.target := toBpuCfi.topAddr
969  }
970
971  when (ifuRedirectReg.valid) {
972    ifuRedirected(ifuRedirectReg.bits.ftqIdx.value) := true.B
973  } .elsewhen(RegNext(pdWb.valid)) {
974    // if pdWb and no redirect, set to false
975    ifuRedirected(last_cycle_bpu_in_ptr.value) := false.B
976  }
977
978  // *********************************************************************
979  // **************************** wb from exu ****************************
980  // *********************************************************************
981
982  backendRedirect.valid := io.fromBackend.redirect.valid
983  backendRedirect.bits.connectRedirect(io.fromBackend.redirect.bits)
984  backendRedirect.bits.BTBMissBubble := false.B
985
986
987  def extractRedirectInfo(wb: Valid[Redirect]) = {
988    val ftqPtr = wb.bits.ftqIdx
989    val ftqOffset = wb.bits.ftqOffset
990    val taken = wb.bits.cfiUpdate.taken
991    val mispred = wb.bits.cfiUpdate.isMisPred
992    (wb.valid, ftqPtr, ftqOffset, taken, mispred)
993  }
994
995  // fix mispredict entry
996  val lastIsMispredict = RegNext(
997    backendRedirect.valid && backendRedirect.bits.level === RedirectLevel.flushAfter, init = false.B
998  )
999
1000  def updateCfiInfo(redirect: Valid[Redirect], isBackend: Boolean = true) = {
1001    val (r_valid, r_ptr, r_offset, r_taken, r_mispred) = extractRedirectInfo(redirect)
1002    val r_idx = r_ptr.value
1003    val cfiIndex_bits_wen = r_valid && r_taken && r_offset < cfiIndex_vec(r_idx).bits
1004    val cfiIndex_valid_wen = r_valid && r_offset === cfiIndex_vec(r_idx).bits
1005    when (cfiIndex_bits_wen || cfiIndex_valid_wen) {
1006      cfiIndex_vec(r_idx).valid := cfiIndex_bits_wen || cfiIndex_valid_wen && r_taken
1007    } .elsewhen (r_valid && !r_taken && r_offset =/= cfiIndex_vec(r_idx).bits) {
1008      cfiIndex_vec(r_idx).valid :=false.B
1009    }
1010    when (cfiIndex_bits_wen) {
1011      cfiIndex_vec(r_idx).bits := r_offset
1012    }
1013    newest_entry_target := redirect.bits.cfiUpdate.target
1014    newest_entry_ptr := r_ptr
1015    update_target(r_idx) := redirect.bits.cfiUpdate.target // TODO: remove this
1016    if (isBackend) {
1017      mispredict_vec(r_idx)(r_offset) := r_mispred
1018    }
1019  }
1020
1021  when(backendRedirect.valid) {
1022    updateCfiInfo(backendRedirect)
1023  }.elsewhen (ifuRedirectToBpu.valid) {
1024    updateCfiInfo(ifuRedirectToBpu, isBackend=false)
1025  }
1026
1027  when (backendRedirect.valid) {
1028    when (backendRedirect.bits.ControlRedirectBubble) {
1029      when (fromBackendRedirect.bits.ControlBTBMissBubble) {
1030        topdown_stage.reasons(TopDownCounters.BTBMissBubble.id) := true.B
1031        io.toIfu.req.bits.topdown_info.reasons(TopDownCounters.BTBMissBubble.id) := true.B
1032      } .elsewhen (fromBackendRedirect.bits.TAGEMissBubble) {
1033        topdown_stage.reasons(TopDownCounters.TAGEMissBubble.id) := true.B
1034        io.toIfu.req.bits.topdown_info.reasons(TopDownCounters.TAGEMissBubble.id) := true.B
1035      } .elsewhen (fromBackendRedirect.bits.SCMissBubble) {
1036        topdown_stage.reasons(TopDownCounters.SCMissBubble.id) := true.B
1037        io.toIfu.req.bits.topdown_info.reasons(TopDownCounters.SCMissBubble.id) := true.B
1038      } .elsewhen (fromBackendRedirect.bits.ITTAGEMissBubble) {
1039        topdown_stage.reasons(TopDownCounters.ITTAGEMissBubble.id) := true.B
1040        io.toIfu.req.bits.topdown_info.reasons(TopDownCounters.ITTAGEMissBubble.id) := true.B
1041      } .elsewhen (fromBackendRedirect.bits.RASMissBubble) {
1042        topdown_stage.reasons(TopDownCounters.RASMissBubble.id) := true.B
1043        io.toIfu.req.bits.topdown_info.reasons(TopDownCounters.RASMissBubble.id) := true.B
1044      }
1045
1046
1047    } .elsewhen (backendRedirect.bits.MemVioRedirectBubble) {
1048      topdown_stage.reasons(TopDownCounters.MemVioRedirectBubble.id) := true.B
1049      io.toIfu.req.bits.topdown_info.reasons(TopDownCounters.MemVioRedirectBubble.id) := true.B
1050    } .otherwise {
1051      topdown_stage.reasons(TopDownCounters.OtherRedirectBubble.id) := true.B
1052      io.toIfu.req.bits.topdown_info.reasons(TopDownCounters.OtherRedirectBubble.id) := true.B
1053    }
1054  } .elsewhen (ifuRedirectReg.valid) {
1055    topdown_stage.reasons(TopDownCounters.BTBMissBubble.id) := true.B
1056    io.toIfu.req.bits.topdown_info.reasons(TopDownCounters.BTBMissBubble.id) := true.B
1057  }
1058
1059  io.ControlBTBMissBubble := fromBackendRedirect.bits.ControlBTBMissBubble
1060  io.TAGEMissBubble := fromBackendRedirect.bits.TAGEMissBubble
1061  io.SCMissBubble := fromBackendRedirect.bits.SCMissBubble
1062  io.ITTAGEMissBubble := fromBackendRedirect.bits.ITTAGEMissBubble
1063  io.RASMissBubble := fromBackendRedirect.bits.RASMissBubble
1064
1065  // ***********************************************************************************
1066  // **************************** flush ptr and state queue ****************************
1067  // ***********************************************************************************
1068
1069  val redirectVec = VecInit(backendRedirect, fromIfuRedirect)
1070
1071  // when redirect, we should reset ptrs and status queues
1072  when(redirectVec.map(r => r.valid).reduce(_||_)){
1073    val r = PriorityMux(redirectVec.map(r => (r.valid -> r.bits)))
1074    val notIfu = redirectVec.dropRight(1).map(r => r.valid).reduce(_||_)
1075    val (idx, offset, flushItSelf) = (r.ftqIdx, r.ftqOffset, RedirectLevel.flushItself(r.level))
1076    val next = idx + 1.U
1077    bpuPtr := next
1078    copied_bpu_ptr.map(_ := next)
1079    ifuPtr_write := next
1080    ifuWbPtr_write := next
1081    ifuPtrPlus1_write := idx + 2.U
1082    ifuPtrPlus2_write := idx + 3.U
1083
1084  }
1085  when(RegNext(redirectVec.map(r => r.valid).reduce(_||_))){
1086    val r = PriorityMux(redirectVec.map(r => (r.valid -> r.bits)))
1087    val notIfu = redirectVec.dropRight(1).map(r => r.valid).reduce(_||_)
1088    val (idx, offset, flushItSelf) = (r.ftqIdx, r.ftqOffset, RedirectLevel.flushItself(r.level))
1089    when (RegNext(notIfu)) {
1090      commitStateQueue(RegNext(idx.value)).zipWithIndex.foreach({ case (s, i) =>
1091        when(i.U > RegNext(offset) || i.U === RegNext(offset) && RegNext(flushItSelf)){
1092          s := c_invalid
1093        }
1094      })
1095    }
1096  }
1097
1098
1099  // only the valid bit is actually needed
1100  io.toIfu.redirect.bits    := backendRedirect.bits
1101  io.toIfu.redirect.valid   := stage2Flush
1102  io.toIfu.topdown_redirect := fromBackendRedirect
1103
1104  // commit
1105  for (c <- io.fromBackend.rob_commits) {
1106    when(c.valid) {
1107      commitStateQueue(c.bits.ftqIdx.value)(c.bits.ftqOffset) := c_commited
1108      // TODO: remove this
1109      // For instruction fusions, we also update the next instruction
1110      when (c.bits.commitType === 4.U) {
1111        commitStateQueue(c.bits.ftqIdx.value)(c.bits.ftqOffset + 1.U) := c_commited
1112      }.elsewhen(c.bits.commitType === 5.U) {
1113        commitStateQueue(c.bits.ftqIdx.value)(c.bits.ftqOffset + 2.U) := c_commited
1114      }.elsewhen(c.bits.commitType === 6.U) {
1115        val index = (c.bits.ftqIdx + 1.U).value
1116        commitStateQueue(index)(0) := c_commited
1117      }.elsewhen(c.bits.commitType === 7.U) {
1118        val index = (c.bits.ftqIdx + 1.U).value
1119        commitStateQueue(index)(1) := c_commited
1120      }
1121    }
1122  }
1123
1124  robCommPtr_write := Mux(io.fromBackend.rob_commits.map(_.valid).reduce(_ | _), ParallelPriorityMux(io.fromBackend.rob_commits.map(_.valid).reverse, io.fromBackend.rob_commits.map(_.bits.ftqIdx).reverse), robCommPtr)
1125
1126  // ****************************************************************
1127  // **************************** to bpu ****************************
1128  // ****************************************************************
1129
1130  io.toBpu.redirect := Mux(fromBackendRedirect.valid, fromBackendRedirect, ifuRedirectToBpu)
1131  val dummy_s1_pred_cycle_vec = VecInit(List.tabulate(FtqSize)(_=>0.U(64.W)))
1132  val redirect_latency = GTimer() - pred_s1_cycle.getOrElse(dummy_s1_pred_cycle_vec)(io.toBpu.redirect.bits.ftqIdx.value) + 1.U
1133  XSPerfHistogram("backend_redirect_latency", redirect_latency, fromBackendRedirect.valid, 0, 60, 1)
1134  XSPerfHistogram("ifu_redirect_latency", redirect_latency, !fromBackendRedirect.valid && ifuRedirectToBpu.valid, 0, 60, 1)
1135
1136  XSError(io.toBpu.redirect.valid && isBefore(io.toBpu.redirect.bits.ftqIdx, commPtr), "Ftq received a redirect after its commit, check backend or replay")
1137
1138  val may_have_stall_from_bpu = Wire(Bool())
1139  val bpu_ftb_update_stall = RegInit(0.U(2.W)) // 2-cycle stall, so we need 3 states
1140  may_have_stall_from_bpu := bpu_ftb_update_stall =/= 0.U
1141  val notInvalidSeq = commitStateQueue(commPtr.value).map(s => s =/= c_invalid).reverse
1142  // Todo: @huxuan check it
1143  //  canCommit := commPtr =/= ifuWbPtr && !may_have_stall_from_bpu &&
1144  //    Cat(commitStateQueue(commPtr.value).map(s => {
1145  //      s === c_invalid || s === c_commited
1146  //    })).andR
1147  canCommit := commPtr =/= ifuWbPtr && !may_have_stall_from_bpu &&
1148    (isAfter(robCommPtr, commPtr) || PriorityMuxDefault(notInvalidSeq.zip(commitStateQueue(commPtr.value).reverse), c_invalid) === c_commited)
1149
1150  val mmioReadPtr = io.mmioCommitRead.mmioFtqPtr
1151  val mmioLastCommit = isBefore(commPtr, mmioReadPtr) && (isAfter(ifuPtr,mmioReadPtr)  ||  mmioReadPtr ===   ifuPtr) &&
1152                       Cat(commitStateQueue(mmioReadPtr.value).map(s => { s === c_invalid || s === c_commited})).andR
1153  io.mmioCommitRead.mmioLastCommit := RegNext(mmioLastCommit)
1154
1155  // commit reads
1156  val commit_pc_bundle = RegNext(ftq_pc_mem.io.commPtr_rdata)
1157  val commit_target =
1158    Mux(RegNext(commPtr === newest_entry_ptr),
1159      RegNext(newest_entry_target),
1160      RegNext(ftq_pc_mem.io.commPtrPlus1_rdata.startAddr))
1161  ftq_pd_mem.io.raddr.last := commPtr.value
1162  val commit_pd = ftq_pd_mem.io.rdata.last
1163  ftq_redirect_sram.io.ren.last := canCommit
1164  ftq_redirect_sram.io.raddr.last := commPtr.value
1165  val commit_spec_meta = ftq_redirect_sram.io.rdata.last
1166  ftq_meta_1r_sram.io.ren(0) := canCommit
1167  ftq_meta_1r_sram.io.raddr(0) := commPtr.value
1168  val commit_meta = ftq_meta_1r_sram.io.rdata(0)
1169  ftb_entry_mem.io.raddr.last := commPtr.value
1170  val commit_ftb_entry = ftb_entry_mem.io.rdata.last
1171
1172  // need one cycle to read mem and srams
1173  val do_commit_ptr = RegNext(commPtr)
1174  val do_commit = RegNext(canCommit, init=false.B)
1175  when (canCommit) {
1176    commPtr_write := commPtrPlus1
1177    commPtrPlus1_write := commPtrPlus1 + 1.U
1178  }
1179  val commit_state = RegNext(commitStateQueue(commPtr.value))
1180  val can_commit_cfi = WireInit(cfiIndex_vec(commPtr.value))
1181  val do_commit_cfi = WireInit(cfiIndex_vec(do_commit_ptr.value))
1182  //
1183  //when (commitStateQueue(commPtr.value)(can_commit_cfi.bits) =/= c_commited) {
1184  //  can_commit_cfi.valid := false.B
1185  //}
1186  val commit_cfi = RegNext(can_commit_cfi)
1187  val debug_cfi = commitStateQueue(do_commit_ptr.value)(do_commit_cfi.bits) =/= c_commited && do_commit_cfi.valid
1188
1189  val commit_mispredict  : Vec[Bool] = VecInit((RegNext(mispredict_vec(commPtr.value)) zip commit_state).map {
1190    case (mis, state) => mis && state === c_commited
1191  })
1192  val commit_instCommited: Vec[Bool] = VecInit(commit_state.map(_ === c_commited)) // [PredictWidth]
1193  val can_commit_hit                 = entry_hit_status(commPtr.value)
1194  val commit_hit                     = RegNext(can_commit_hit)
1195  val diff_commit_target             = RegNext(update_target(commPtr.value)) // TODO: remove this
1196  val commit_stage                   = RegNext(pred_stage(commPtr.value))
1197  val commit_valid                   = commit_hit === h_hit || commit_cfi.valid // hit or taken
1198
1199  val to_bpu_hit = can_commit_hit === h_hit || can_commit_hit === h_false_hit
1200  switch (bpu_ftb_update_stall) {
1201    is (0.U) {
1202      when (can_commit_cfi.valid && !to_bpu_hit && canCommit) {
1203        bpu_ftb_update_stall := 2.U // 2-cycle stall
1204      }
1205    }
1206    is (2.U) {
1207      bpu_ftb_update_stall := 1.U
1208    }
1209    is (1.U) {
1210      bpu_ftb_update_stall := 0.U
1211    }
1212    is (3.U) {
1213      XSError(true.B, "bpu_ftb_update_stall should be 0, 1 or 2")
1214    }
1215  }
1216
1217  // TODO: remove this
1218  XSError(do_commit && diff_commit_target =/= commit_target, "\ncommit target should be the same as update target\n")
1219
1220  // update latency stats
1221  val update_latency = GTimer() - pred_s1_cycle.getOrElse(dummy_s1_pred_cycle_vec)(do_commit_ptr.value) + 1.U
1222  XSPerfHistogram("bpu_update_latency", update_latency, io.toBpu.update.valid, 0, 64, 2)
1223
1224  io.toBpu.update := DontCare
1225  io.toBpu.update.valid := commit_valid && do_commit
1226  val update = io.toBpu.update.bits
1227  update.false_hit   := commit_hit === h_false_hit
1228  update.pc          := commit_pc_bundle.startAddr
1229  update.meta        := commit_meta.meta
1230  update.cfi_idx     := commit_cfi
1231  update.full_target := commit_target
1232  update.from_stage  := commit_stage
1233  update.spec_info   := commit_spec_meta
1234  XSError(commit_valid && do_commit && debug_cfi, "\ncommit cfi can be non c_commited\n")
1235
1236  val commit_real_hit = commit_hit === h_hit
1237  val update_ftb_entry = update.ftb_entry
1238
1239  val ftbEntryGen = Module(new FTBEntryGen).io
1240  ftbEntryGen.start_addr     := commit_pc_bundle.startAddr
1241  ftbEntryGen.old_entry      := commit_ftb_entry
1242  ftbEntryGen.pd             := commit_pd
1243  ftbEntryGen.cfiIndex       := commit_cfi
1244  ftbEntryGen.target         := commit_target
1245  ftbEntryGen.hit            := commit_real_hit
1246  ftbEntryGen.mispredict_vec := commit_mispredict
1247
1248  update_ftb_entry         := ftbEntryGen.new_entry
1249  update.new_br_insert_pos := ftbEntryGen.new_br_insert_pos
1250  update.mispred_mask      := ftbEntryGen.mispred_mask
1251  update.old_entry         := ftbEntryGen.is_old_entry
1252  update.pred_hit          := commit_hit === h_hit || commit_hit === h_false_hit
1253  update.br_taken_mask     := ftbEntryGen.taken_mask
1254  update.br_committed      := (ftbEntryGen.new_entry.brValids zip ftbEntryGen.new_entry.brOffset) map {
1255    case (valid, offset) => valid && commit_instCommited(offset)
1256  }
1257  update.jmp_taken         := ftbEntryGen.jmp_taken
1258
1259  // update.full_pred.fromFtbEntry(ftbEntryGen.new_entry, update.pc)
1260  // update.full_pred.jalr_target := commit_target
1261  // update.full_pred.hit := true.B
1262  // when (update.full_pred.is_jalr) {
1263  //   update.full_pred.targets.last := commit_target
1264  // }
1265
1266  // ****************************************************************
1267  // *********************** to prefetch ****************************
1268  // ****************************************************************
1269
1270  ftq_pc_mem.io.other_raddrs(0) := DontCare
1271  if(cacheParams.enableICachePrefetch){
1272    val prefetchPtr = RegInit(FtqPtr(false.B, 0.U))
1273    val diff_prefetch_addr = WireInit(update_target(prefetchPtr.value)) //TODO: remove this
1274    // TODO : MUST WIDER
1275    prefetchPtr := prefetchPtr + io.toPrefetch.req.fire
1276
1277    val prefetch_too_late = (isBefore(prefetchPtr, ifuPtr) && !isFull(ifuPtr, prefetchPtr)) || (prefetchPtr === ifuPtr)
1278    when(prefetch_too_late){
1279      when(prefetchPtr =/= bpuPtr){
1280        prefetchPtr := bpuPtr - 1.U
1281      }.otherwise{
1282        prefetchPtr := ifuPtr
1283      }
1284    }
1285
1286    ftq_pc_mem.io.other_raddrs(0) := prefetchPtr.value
1287
1288    when (bpu_s2_redirect && !isBefore(prefetchPtr, bpu_s2_resp.ftq_idx)) {
1289      prefetchPtr := bpu_s2_resp.ftq_idx
1290    }
1291
1292    when (bpu_s3_redirect && !isBefore(prefetchPtr, bpu_s3_resp.ftq_idx)) {
1293      prefetchPtr := bpu_s3_resp.ftq_idx
1294      // XSError(true.B, "\ns3_redirect mechanism not implemented!\n")
1295    }
1296
1297
1298    val prefetch_is_to_send = WireInit(entry_fetch_status(prefetchPtr.value) === f_to_send)
1299    val prefetch_addr = Wire(UInt(VAddrBits.W))
1300
1301    when (last_cycle_bpu_in && bpu_in_bypass_ptr === prefetchPtr) {
1302      prefetch_is_to_send := true.B
1303      prefetch_addr := last_cycle_bpu_target
1304      diff_prefetch_addr := last_cycle_bpu_target // TODO: remove this
1305    }.otherwise{
1306      prefetch_addr := RegNext( ftq_pc_mem.io.other_rdatas(0).startAddr)
1307    }
1308    io.toPrefetch.req.valid := prefetchPtr =/= bpuPtr && prefetch_is_to_send
1309    io.toPrefetch.req.bits.target := prefetch_addr
1310
1311    when(redirectVec.map(r => r.valid).reduce(_||_)){
1312      val r = PriorityMux(redirectVec.map(r => (r.valid -> r.bits)))
1313      val next = r.ftqIdx + 1.U
1314      prefetchPtr := next
1315    }
1316
1317    // TODO: remove this
1318    // XSError(io.toPrefetch.req.valid && diff_prefetch_addr =/= prefetch_addr,
1319    //         f"\nprefetch_req_target wrong! prefetchPtr: ${prefetchPtr}, prefetch_addr: ${Hexadecimal(prefetch_addr)} diff_prefetch_addr: ${Hexadecimal(diff_prefetch_addr)}\n")
1320
1321
1322    XSError(isBefore(bpuPtr, prefetchPtr) && !isFull(bpuPtr, prefetchPtr), "\nprefetchPtr is before bpuPtr!\n")
1323//    XSError(isBefore(prefetchPtr, ifuPtr) && !isFull(ifuPtr, prefetchPtr), "\nifuPtr is before prefetchPtr!\n")
1324  }
1325  else {
1326    io.toPrefetch.req <> DontCare
1327  }
1328
1329  // ******************************************************************************
1330  // **************************** commit perf counters ****************************
1331  // ******************************************************************************
1332
1333  val commit_inst_mask    = VecInit(commit_state.map(c => c === c_commited && do_commit)).asUInt
1334  val commit_mispred_mask = commit_mispredict.asUInt
1335  val commit_not_mispred_mask = ~commit_mispred_mask
1336
1337  val commit_br_mask = commit_pd.brMask.asUInt
1338  val commit_jmp_mask = UIntToOH(commit_pd.jmpOffset) & Fill(PredictWidth, commit_pd.jmpInfo.valid.asTypeOf(UInt(1.W)))
1339  val commit_cfi_mask = (commit_br_mask | commit_jmp_mask)
1340
1341  val mbpInstrs = commit_inst_mask & commit_cfi_mask
1342
1343  val mbpRights = mbpInstrs & commit_not_mispred_mask
1344  val mbpWrongs = mbpInstrs & commit_mispred_mask
1345
1346  io.bpuInfo.bpRight := PopCount(mbpRights)
1347  io.bpuInfo.bpWrong := PopCount(mbpWrongs)
1348
1349  val isWriteFTQTable = WireInit(Constantin.createRecord("isWriteFTQTable" + p(XSCoreParamsKey).HartId.toString))
1350  val ftqBranchTraceDB = ChiselDB.createTable("FTQTable" + p(XSCoreParamsKey).HartId.toString, new FtqDebugBundle)
1351  // Cfi Info
1352  for (i <- 0 until PredictWidth) {
1353    val pc = commit_pc_bundle.startAddr + (i * instBytes).U
1354    val v = commit_state(i) === c_commited
1355    val isBr = commit_pd.brMask(i)
1356    val isJmp = commit_pd.jmpInfo.valid && commit_pd.jmpOffset === i.U
1357    val isCfi = isBr || isJmp
1358    val isTaken = commit_cfi.valid && commit_cfi.bits === i.U
1359    val misPred = commit_mispredict(i)
1360    // val ghist = commit_spec_meta.ghist.predHist
1361    val histPtr = commit_spec_meta.histPtr
1362    val predCycle = commit_meta.meta(63, 0)
1363    val target = commit_target
1364
1365    val brIdx = OHToUInt(Reverse(Cat(update_ftb_entry.brValids.zip(update_ftb_entry.brOffset).map{case(v, offset) => v && offset === i.U})))
1366    val inFtbEntry = update_ftb_entry.brValids.zip(update_ftb_entry.brOffset).map{case(v, offset) => v && offset === i.U}.reduce(_||_)
1367    val addIntoHist = ((commit_hit === h_hit) && inFtbEntry) || ((!(commit_hit === h_hit) && i.U === commit_cfi.bits && isBr && commit_cfi.valid))
1368    XSDebug(v && do_commit && isCfi, p"cfi_update: isBr(${isBr}) pc(${Hexadecimal(pc)}) " +
1369    p"taken(${isTaken}) mispred(${misPred}) cycle($predCycle) hist(${histPtr.value}) " +
1370    p"startAddr(${Hexadecimal(commit_pc_bundle.startAddr)}) AddIntoHist(${addIntoHist}) " +
1371    p"brInEntry(${inFtbEntry}) brIdx(${brIdx}) target(${Hexadecimal(target)})\n")
1372
1373    val logbundle = Wire(new FtqDebugBundle)
1374    logbundle.pc := pc
1375    logbundle.target := target
1376    logbundle.isBr := isBr
1377    logbundle.isJmp := isJmp
1378    logbundle.isCall := isJmp && commit_pd.hasCall
1379    logbundle.isRet := isJmp && commit_pd.hasRet
1380    logbundle.misPred := misPred
1381    logbundle.isTaken := isTaken
1382    logbundle.predStage := commit_stage
1383
1384    ftqBranchTraceDB.log(
1385      data = logbundle /* hardware of type T */,
1386      en = isWriteFTQTable.orR && v && do_commit && isCfi,
1387      site = "FTQ" + p(XSCoreParamsKey).HartId.toString,
1388      clock = clock,
1389      reset = reset
1390    )
1391  }
1392
1393  val enq = io.fromBpu.resp
1394  val perf_redirect = backendRedirect
1395
1396  XSPerfAccumulate("entry", validEntries)
1397  XSPerfAccumulate("bpu_to_ftq_stall", enq.valid && !enq.ready)
1398  XSPerfAccumulate("mispredictRedirect", perf_redirect.valid && RedirectLevel.flushAfter === perf_redirect.bits.level)
1399  XSPerfAccumulate("replayRedirect", perf_redirect.valid && RedirectLevel.flushItself(perf_redirect.bits.level))
1400  XSPerfAccumulate("predecodeRedirect", fromIfuRedirect.valid)
1401
1402  XSPerfAccumulate("to_ifu_bubble", io.toIfu.req.ready && !io.toIfu.req.valid)
1403
1404  XSPerfAccumulate("to_ifu_stall", io.toIfu.req.valid && !io.toIfu.req.ready)
1405  XSPerfAccumulate("from_bpu_real_bubble", !enq.valid && enq.ready && allowBpuIn)
1406  XSPerfAccumulate("bpu_to_ifu_bubble", bpuPtr === ifuPtr)
1407  XSPerfAccumulate("bpu_to_ifu_bubble_when_ftq_full", (bpuPtr === ifuPtr) && isFull(bpuPtr, commPtr) && io.toIfu.req.ready)
1408
1409  XSPerfAccumulate("redirectAhead_ValidNum", io.fromBackend.ftqIdxAhead.map(_.valid).reduce(_|_))
1410  XSPerfAccumulate("fromBackendRedirect_ValidNum", io.fromBackend.redirect.valid)
1411  XSPerfAccumulate("toBpuRedirect_ValidNum", io.toBpu.redirect.valid)
1412
1413  val from_bpu = io.fromBpu.resp.bits
1414  val to_ifu = io.toIfu.req.bits
1415
1416
1417  XSPerfHistogram("commit_num_inst", PopCount(commit_inst_mask), do_commit, 0, PredictWidth+1, 1)
1418
1419
1420
1421
1422  val commit_jal_mask  = UIntToOH(commit_pd.jmpOffset) & Fill(PredictWidth, commit_pd.hasJal.asTypeOf(UInt(1.W)))
1423  val commit_jalr_mask = UIntToOH(commit_pd.jmpOffset) & Fill(PredictWidth, commit_pd.hasJalr.asTypeOf(UInt(1.W)))
1424  val commit_call_mask = UIntToOH(commit_pd.jmpOffset) & Fill(PredictWidth, commit_pd.hasCall.asTypeOf(UInt(1.W)))
1425  val commit_ret_mask  = UIntToOH(commit_pd.jmpOffset) & Fill(PredictWidth, commit_pd.hasRet.asTypeOf(UInt(1.W)))
1426
1427
1428  val mbpBRights = mbpRights & commit_br_mask
1429  val mbpJRights = mbpRights & commit_jal_mask
1430  val mbpIRights = mbpRights & commit_jalr_mask
1431  val mbpCRights = mbpRights & commit_call_mask
1432  val mbpRRights = mbpRights & commit_ret_mask
1433
1434  val mbpBWrongs = mbpWrongs & commit_br_mask
1435  val mbpJWrongs = mbpWrongs & commit_jal_mask
1436  val mbpIWrongs = mbpWrongs & commit_jalr_mask
1437  val mbpCWrongs = mbpWrongs & commit_call_mask
1438  val mbpRWrongs = mbpWrongs & commit_ret_mask
1439
1440  val commit_pred_stage = RegNext(pred_stage(commPtr.value))
1441
1442  def pred_stage_map(src: UInt, name: String) = {
1443    (0 until numBpStages).map(i =>
1444      f"${name}_stage_${i+1}" -> PopCount(src.asBools.map(_ && commit_pred_stage === BP_STAGES(i)))
1445    ).foldLeft(Map[String, UInt]())(_+_)
1446  }
1447
1448  val mispred_stage_map      = pred_stage_map(mbpWrongs,  "mispredict")
1449  val br_mispred_stage_map   = pred_stage_map(mbpBWrongs, "br_mispredict")
1450  val jalr_mispred_stage_map = pred_stage_map(mbpIWrongs, "jalr_mispredict")
1451  val correct_stage_map      = pred_stage_map(mbpRights,  "correct")
1452  val br_correct_stage_map   = pred_stage_map(mbpBRights, "br_correct")
1453  val jalr_correct_stage_map = pred_stage_map(mbpIRights, "jalr_correct")
1454
1455  val update_valid = io.toBpu.update.valid
1456  def u(cond: Bool) = update_valid && cond
1457  val ftb_false_hit = u(update.false_hit)
1458  // assert(!ftb_false_hit)
1459  val ftb_hit = u(commit_hit === h_hit)
1460
1461  val ftb_new_entry = u(ftbEntryGen.is_init_entry)
1462  val ftb_new_entry_only_br = ftb_new_entry && !update_ftb_entry.jmpValid
1463  val ftb_new_entry_only_jmp = ftb_new_entry && !update_ftb_entry.brValids(0)
1464  val ftb_new_entry_has_br_and_jmp = ftb_new_entry && update_ftb_entry.brValids(0) && update_ftb_entry.jmpValid
1465
1466  val ftb_old_entry = u(ftbEntryGen.is_old_entry)
1467
1468  val ftb_modified_entry = u(ftbEntryGen.is_new_br || ftbEntryGen.is_jalr_target_modified || ftbEntryGen.is_always_taken_modified)
1469  val ftb_modified_entry_new_br = u(ftbEntryGen.is_new_br)
1470  val ftb_modified_entry_ifu_redirected = u(ifuRedirected(do_commit_ptr.value))
1471  val ftb_modified_entry_jalr_target_modified = u(ftbEntryGen.is_jalr_target_modified)
1472  val ftb_modified_entry_br_full = ftb_modified_entry && ftbEntryGen.is_br_full
1473  val ftb_modified_entry_always_taken = ftb_modified_entry && ftbEntryGen.is_always_taken_modified
1474
1475  def getFtbEntryLen(pc: UInt, entry: FTBEntry) = (entry.getFallThrough(pc) - pc) >> instOffsetBits
1476  val gen_ftb_entry_len = getFtbEntryLen(update.pc, ftbEntryGen.new_entry)
1477  XSPerfHistogram("ftb_init_entry_len", gen_ftb_entry_len, ftb_new_entry, 0, PredictWidth+1, 1)
1478  XSPerfHistogram("ftb_modified_entry_len", gen_ftb_entry_len, ftb_modified_entry, 0, PredictWidth+1, 1)
1479  val s3_ftb_entry_len = getFtbEntryLen(from_bpu.s3.pc(0), from_bpu.last_stage_ftb_entry)
1480  XSPerfHistogram("s3_ftb_entry_len", s3_ftb_entry_len, from_bpu.s3.valid(0), 0, PredictWidth+1, 1)
1481
1482  XSPerfHistogram("ftq_has_entry", validEntries, true.B, 0, FtqSize+1, 1)
1483
1484  val perfCountsMap = Map(
1485    "BpInstr" -> PopCount(mbpInstrs),
1486    "BpBInstr" -> PopCount(mbpBRights | mbpBWrongs),
1487    "BpRight"  -> PopCount(mbpRights),
1488    "BpWrong"  -> PopCount(mbpWrongs),
1489    "BpBRight" -> PopCount(mbpBRights),
1490    "BpBWrong" -> PopCount(mbpBWrongs),
1491    "BpJRight" -> PopCount(mbpJRights),
1492    "BpJWrong" -> PopCount(mbpJWrongs),
1493    "BpIRight" -> PopCount(mbpIRights),
1494    "BpIWrong" -> PopCount(mbpIWrongs),
1495    "BpCRight" -> PopCount(mbpCRights),
1496    "BpCWrong" -> PopCount(mbpCWrongs),
1497    "BpRRight" -> PopCount(mbpRRights),
1498    "BpRWrong" -> PopCount(mbpRWrongs),
1499
1500    "ftb_false_hit"                -> PopCount(ftb_false_hit),
1501    "ftb_hit"                      -> PopCount(ftb_hit),
1502    "ftb_new_entry"                -> PopCount(ftb_new_entry),
1503    "ftb_new_entry_only_br"        -> PopCount(ftb_new_entry_only_br),
1504    "ftb_new_entry_only_jmp"       -> PopCount(ftb_new_entry_only_jmp),
1505    "ftb_new_entry_has_br_and_jmp" -> PopCount(ftb_new_entry_has_br_and_jmp),
1506    "ftb_old_entry"                -> PopCount(ftb_old_entry),
1507    "ftb_modified_entry"           -> PopCount(ftb_modified_entry),
1508    "ftb_modified_entry_new_br"    -> PopCount(ftb_modified_entry_new_br),
1509    "ftb_jalr_target_modified"     -> PopCount(ftb_modified_entry_jalr_target_modified),
1510    "ftb_modified_entry_br_full"   -> PopCount(ftb_modified_entry_br_full),
1511    "ftb_modified_entry_always_taken" -> PopCount(ftb_modified_entry_always_taken)
1512  ) ++ mispred_stage_map ++ br_mispred_stage_map ++ jalr_mispred_stage_map ++
1513       correct_stage_map ++ br_correct_stage_map ++ jalr_correct_stage_map
1514
1515  for((key, value) <- perfCountsMap) {
1516    XSPerfAccumulate(key, value)
1517  }
1518
1519  // --------------------------- Debug --------------------------------
1520  // XSDebug(enq_fire, p"enq! " + io.fromBpu.resp.bits.toPrintable)
1521  XSDebug(io.toIfu.req.fire, p"fire to ifu " + io.toIfu.req.bits.toPrintable)
1522  XSDebug(do_commit, p"deq! [ptr] $do_commit_ptr\n")
1523  XSDebug(true.B, p"[bpuPtr] $bpuPtr, [ifuPtr] $ifuPtr, [ifuWbPtr] $ifuWbPtr [commPtr] $commPtr\n")
1524  XSDebug(true.B, p"[in] v:${io.fromBpu.resp.valid} r:${io.fromBpu.resp.ready} " +
1525    p"[out] v:${io.toIfu.req.valid} r:${io.toIfu.req.ready}\n")
1526  XSDebug(do_commit, p"[deq info] cfiIndex: $commit_cfi, $commit_pc_bundle, target: ${Hexadecimal(commit_target)}\n")
1527
1528  //   def ubtbCheck(commit: FtqEntry, predAns: Seq[PredictorAnswer], isWrong: Bool) = {
1529  //     commit.valids.zip(commit.pd).zip(predAns).zip(commit.takens).map {
1530  //       case (((valid, pd), ans), taken) =>
1531  //       Mux(valid && pd.isBr,
1532  //         isWrong ^ Mux(ans.hit.asBool,
1533  //           Mux(ans.taken.asBool, taken && ans.target === commitEntry.target,
1534  //           !taken),
1535  //         !taken),
1536  //       false.B)
1537  //     }
1538  //   }
1539
1540  //   def btbCheck(commit: FtqEntry, predAns: Seq[PredictorAnswer], isWrong: Bool) = {
1541  //     commit.valids.zip(commit.pd).zip(predAns).zip(commit.takens).map {
1542  //       case (((valid, pd), ans), taken) =>
1543  //       Mux(valid && pd.isBr,
1544  //         isWrong ^ Mux(ans.hit.asBool,
1545  //           Mux(ans.taken.asBool, taken && ans.target === commitEntry.target,
1546  //           !taken),
1547  //         !taken),
1548  //       false.B)
1549  //     }
1550  //   }
1551
1552  //   def tageCheck(commit: FtqEntry, predAns: Seq[PredictorAnswer], isWrong: Bool) = {
1553  //     commit.valids.zip(commit.pd).zip(predAns).zip(commit.takens).map {
1554  //       case (((valid, pd), ans), taken) =>
1555  //       Mux(valid && pd.isBr,
1556  //         isWrong ^ (ans.taken.asBool === taken),
1557  //       false.B)
1558  //     }
1559  //   }
1560
1561  //   def loopCheck(commit: FtqEntry, predAns: Seq[PredictorAnswer], isWrong: Bool) = {
1562  //     commit.valids.zip(commit.pd).zip(predAns).zip(commit.takens).map {
1563  //       case (((valid, pd), ans), taken) =>
1564  //       Mux(valid && (pd.isBr) && ans.hit.asBool,
1565  //         isWrong ^ (!taken),
1566  //           false.B)
1567  //     }
1568  //   }
1569
1570  //   def rasCheck(commit: FtqEntry, predAns: Seq[PredictorAnswer], isWrong: Bool) = {
1571  //     commit.valids.zip(commit.pd).zip(predAns).zip(commit.takens).map {
1572  //       case (((valid, pd), ans), taken) =>
1573  //       Mux(valid && pd.isRet.asBool /*&& taken*/ && ans.hit.asBool,
1574  //         isWrong ^ (ans.target === commitEntry.target),
1575  //           false.B)
1576  //     }
1577  //   }
1578
1579  //   val ubtbRights = ubtbCheck(commitEntry, commitEntry.metas.map(_.ubtbAns), false.B)
1580  //   val ubtbWrongs = ubtbCheck(commitEntry, commitEntry.metas.map(_.ubtbAns), true.B)
1581  //   // btb and ubtb pred jal and jalr as well
1582  //   val btbRights = btbCheck(commitEntry, commitEntry.metas.map(_.btbAns), false.B)
1583  //   val btbWrongs = btbCheck(commitEntry, commitEntry.metas.map(_.btbAns), true.B)
1584  //   val tageRights = tageCheck(commitEntry, commitEntry.metas.map(_.tageAns), false.B)
1585  //   val tageWrongs = tageCheck(commitEntry, commitEntry.metas.map(_.tageAns), true.B)
1586
1587  //   val loopRights = loopCheck(commitEntry, commitEntry.metas.map(_.loopAns), false.B)
1588  //   val loopWrongs = loopCheck(commitEntry, commitEntry.metas.map(_.loopAns), true.B)
1589
1590  //   val rasRights = rasCheck(commitEntry, commitEntry.metas.map(_.rasAns), false.B)
1591  //   val rasWrongs = rasCheck(commitEntry, commitEntry.metas.map(_.rasAns), true.B)
1592
1593  val perfEvents = Seq(
1594    ("bpu_s2_redirect        ", bpu_s2_redirect                                                             ),
1595    ("bpu_s3_redirect        ", bpu_s3_redirect                                                             ),
1596    ("bpu_to_ftq_stall       ", enq.valid && ~enq.ready                                                     ),
1597    ("mispredictRedirect     ", perf_redirect.valid && RedirectLevel.flushAfter === perf_redirect.bits.level),
1598    ("replayRedirect         ", perf_redirect.valid && RedirectLevel.flushItself(perf_redirect.bits.level)  ),
1599    ("predecodeRedirect      ", fromIfuRedirect.valid                                                       ),
1600    ("to_ifu_bubble          ", io.toIfu.req.ready && !io.toIfu.req.valid                                   ),
1601    ("from_bpu_real_bubble   ", !enq.valid && enq.ready && allowBpuIn                                       ),
1602    ("BpInstr                ", PopCount(mbpInstrs)                                                         ),
1603    ("BpBInstr               ", PopCount(mbpBRights | mbpBWrongs)                                           ),
1604    ("BpRight                ", PopCount(mbpRights)                                                         ),
1605    ("BpWrong                ", PopCount(mbpWrongs)                                                         ),
1606    ("BpBRight               ", PopCount(mbpBRights)                                                        ),
1607    ("BpBWrong               ", PopCount(mbpBWrongs)                                                        ),
1608    ("BpJRight               ", PopCount(mbpJRights)                                                        ),
1609    ("BpJWrong               ", PopCount(mbpJWrongs)                                                        ),
1610    ("BpIRight               ", PopCount(mbpIRights)                                                        ),
1611    ("BpIWrong               ", PopCount(mbpIWrongs)                                                        ),
1612    ("BpCRight               ", PopCount(mbpCRights)                                                        ),
1613    ("BpCWrong               ", PopCount(mbpCWrongs)                                                        ),
1614    ("BpRRight               ", PopCount(mbpRRights)                                                        ),
1615    ("BpRWrong               ", PopCount(mbpRWrongs)                                                        ),
1616    ("ftb_false_hit          ", PopCount(ftb_false_hit)                                                     ),
1617    ("ftb_hit                ", PopCount(ftb_hit)                                                           ),
1618  )
1619  generatePerfEvent()
1620}
1621