xref: /XiangShan/src/main/scala/xiangshan/frontend/NewFtq.scala (revision 6639e9a467468f4e1b05a25a5de4500772aedeb1)
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*
17* Acknowledgement
18*
19* This implementation is inspired by several key papers:
20* [1] Glenn Reinman, Todd Austin, and Brad Calder. "[A scalable front-end architecture for fast instruction delivery.]
21* (https://doi.org/10.1109/ISCA.1999.765954)" 26th International Symposium on Computer Architecture (ISCA). 1999.
22*
23***************************************************************************************/
24
25package xiangshan.frontend
26
27import chisel3._
28import chisel3.util._
29import org.chipsalliance.cde.config.Parameters
30import utility._
31import utility.ChiselDB
32import utils._
33import xiangshan._
34import xiangshan.backend.CtrlToFtqIO
35import xiangshan.backend.decode.ImmUnion
36import xiangshan.frontend.icache._
37
38class FtqDebugBundle extends Bundle {
39  val pc        = UInt(39.W)
40  val target    = UInt(39.W)
41  val isBr      = Bool()
42  val isJmp     = Bool()
43  val isCall    = Bool()
44  val isRet     = Bool()
45  val misPred   = Bool()
46  val isTaken   = Bool()
47  val predStage = UInt(2.W)
48}
49
50class FtqPtr(entries: Int) extends CircularQueuePtr[FtqPtr](
51      entries
52    ) {
53  def this()(implicit p: Parameters) = this(p(XSCoreParamsKey).FtqSize)
54}
55
56object FtqPtr {
57  def apply(f: Bool, v: UInt)(implicit p: Parameters): FtqPtr = {
58    val ptr = Wire(new FtqPtr)
59    ptr.flag  := f
60    ptr.value := v
61    ptr
62  }
63  def inverse(ptr: FtqPtr)(implicit p: Parameters): FtqPtr =
64    apply(!ptr.flag, ptr.value)
65}
66
67class FtqNRSRAM[T <: Data](gen: T, numRead: Int)(implicit p: Parameters) extends XSModule {
68
69  val io = IO(new Bundle() {
70    val raddr = Input(Vec(numRead, UInt(log2Up(FtqSize).W)))
71    val ren   = Input(Vec(numRead, Bool()))
72    val rdata = Output(Vec(numRead, gen))
73    val waddr = Input(UInt(log2Up(FtqSize).W))
74    val wen   = Input(Bool())
75    val wdata = Input(gen)
76  })
77
78  for (i <- 0 until numRead) {
79    val sram = Module(new SRAMTemplate(gen, FtqSize, withClockGate = true))
80    sram.io.r.req.valid       := io.ren(i)
81    sram.io.r.req.bits.setIdx := io.raddr(i)
82    io.rdata(i)               := sram.io.r.resp.data(0)
83    sram.io.w.req.valid       := io.wen
84    sram.io.w.req.bits.setIdx := io.waddr
85    sram.io.w.req.bits.data   := VecInit(io.wdata)
86  }
87
88}
89
90class Ftq_RF_Components(implicit p: Parameters) extends XSBundle with BPUUtils {
91  val startAddr     = UInt(VAddrBits.W)
92  val nextLineAddr  = UInt(VAddrBits.W)
93  val isNextMask    = Vec(PredictWidth, Bool())
94  val fallThruError = Bool()
95  // val carry = Bool()
96  def getPc(offset: UInt) = {
97    def getHigher(pc: UInt) = pc(VAddrBits - 1, log2Ceil(PredictWidth) + instOffsetBits + 1)
98    def getOffset(pc: UInt) = pc(log2Ceil(PredictWidth) + instOffsetBits, instOffsetBits)
99    Cat(
100      getHigher(Mux(isNextMask(offset) && startAddr(log2Ceil(PredictWidth) + instOffsetBits), nextLineAddr, startAddr)),
101      getOffset(startAddr) + offset,
102      0.U(instOffsetBits.W)
103    )
104  }
105  def fromBranchPrediction(resp: BranchPredictionBundle) = {
106    def carryPos(addr: UInt) = addr(instOffsetBits + log2Ceil(PredictWidth) + 1)
107    this.startAddr    := resp.pc(3)
108    this.nextLineAddr := resp.pc(3) + (FetchWidth * 4 * 2).U // may be broken on other configs
109    this.isNextMask := VecInit((0 until PredictWidth).map(i =>
110      (resp.pc(3)(log2Ceil(PredictWidth), 1) +& i.U)(log2Ceil(PredictWidth)).asBool
111    ))
112    this.fallThruError := resp.fallThruError(3)
113    this
114  }
115  override def toPrintable: Printable =
116    p"startAddr:${Hexadecimal(startAddr)}"
117}
118
119class Ftq_pd_Entry(implicit p: Parameters) extends XSBundle {
120  val brMask    = Vec(PredictWidth, Bool())
121  val jmpInfo   = ValidUndirectioned(Vec(3, Bool()))
122  val jmpOffset = UInt(log2Ceil(PredictWidth).W)
123  val jalTarget = UInt(VAddrBits.W)
124  val rvcMask   = Vec(PredictWidth, Bool())
125  def hasJal    = jmpInfo.valid && !jmpInfo.bits(0)
126  def hasJalr   = jmpInfo.valid && jmpInfo.bits(0)
127  def hasCall   = jmpInfo.valid && jmpInfo.bits(1)
128  def hasRet    = jmpInfo.valid && jmpInfo.bits(2)
129
130  def fromPdWb(pdWb: PredecodeWritebackBundle) = {
131    val pds = pdWb.pd
132    this.brMask        := VecInit(pds.map(pd => pd.isBr && pd.valid))
133    this.jmpInfo.valid := VecInit(pds.map(pd => (pd.isJal || pd.isJalr) && pd.valid)).asUInt.orR
134    this.jmpInfo.bits := ParallelPriorityMux(
135      pds.map(pd => (pd.isJal || pd.isJalr) && pd.valid),
136      pds.map(pd => VecInit(pd.isJalr, pd.isCall, pd.isRet))
137    )
138    this.jmpOffset := ParallelPriorityEncoder(pds.map(pd => (pd.isJal || pd.isJalr) && pd.valid))
139    this.rvcMask   := VecInit(pds.map(pd => pd.isRVC))
140    this.jalTarget := pdWb.jalTarget
141  }
142
143  def toPd(offset: UInt) = {
144    require(offset.getWidth == log2Ceil(PredictWidth))
145    val pd = Wire(new PreDecodeInfo)
146    pd.valid := true.B
147    pd.isRVC := rvcMask(offset)
148    val isBr   = brMask(offset)
149    val isJalr = offset === jmpOffset && jmpInfo.valid && jmpInfo.bits(0)
150    pd.brType := Cat(offset === jmpOffset && jmpInfo.valid, isJalr || isBr)
151    pd.isCall := offset === jmpOffset && jmpInfo.valid && jmpInfo.bits(1)
152    pd.isRet  := offset === jmpOffset && jmpInfo.valid && jmpInfo.bits(2)
153    pd
154  }
155}
156
157class PrefetchPtrDB(implicit p: Parameters) extends Bundle {
158  val fromFtqPtr = UInt(log2Up(p(XSCoreParamsKey).FtqSize).W)
159  val fromIfuPtr = UInt(log2Up(p(XSCoreParamsKey).FtqSize).W)
160}
161
162class Ftq_Redirect_SRAMEntry(implicit p: Parameters) extends SpeculativeInfo {
163  val sc_disagree = if (!env.FPGAPlatform) Some(Vec(numBr, Bool())) else None
164}
165
166class Ftq_1R_SRAMEntry(implicit p: Parameters) extends XSBundle with HasBPUConst {
167  val meta      = UInt(MaxMetaLength.W)
168  val ftb_entry = new FTBEntry
169}
170
171class Ftq_Pred_Info(implicit p: Parameters) extends XSBundle {
172  val target   = UInt(VAddrBits.W)
173  val cfiIndex = ValidUndirectioned(UInt(log2Ceil(PredictWidth).W))
174}
175
176class FtqRead[T <: Data](private val gen: T)(implicit p: Parameters) extends XSBundle {
177  val valid  = Output(Bool())
178  val ptr    = Output(new FtqPtr)
179  val offset = Output(UInt(log2Ceil(PredictWidth).W))
180  val data   = Input(gen)
181  def apply(valid: Bool, ptr: FtqPtr, offset: UInt) = {
182    this.valid  := valid
183    this.ptr    := ptr
184    this.offset := offset
185    this.data
186  }
187}
188
189class FtqToBpuIO(implicit p: Parameters) extends XSBundle {
190  val redirect       = Valid(new BranchPredictionRedirect)
191  val update         = Valid(new BranchPredictionUpdate)
192  val enq_ptr        = Output(new FtqPtr)
193  val redirctFromIFU = Output(Bool())
194}
195
196class BpuFlushInfo(implicit p: Parameters) extends XSBundle with HasCircularQueuePtrHelper {
197  // when ifu pipeline is not stalled,
198  // a packet from bpu s3 can reach f1 at most
199  val s2 = Valid(new FtqPtr)
200  val s3 = Valid(new FtqPtr)
201  def shouldFlushBy(src: Valid[FtqPtr], idx_to_flush: FtqPtr) =
202    src.valid && !isAfter(src.bits, idx_to_flush)
203  def shouldFlushByStage2(idx: FtqPtr) = shouldFlushBy(s2, idx)
204  def shouldFlushByStage3(idx: FtqPtr) = shouldFlushBy(s3, idx)
205}
206
207class FtqToIfuIO(implicit p: Parameters) extends XSBundle {
208  val req              = Decoupled(new FetchRequestBundle)
209  val redirect         = Valid(new BranchPredictionRedirect)
210  val topdown_redirect = Valid(new BranchPredictionRedirect)
211  val flushFromBpu     = new BpuFlushInfo
212}
213
214class FtqToICacheIO(implicit p: Parameters) extends XSBundle {
215  // NOTE: req.bits must be prepare in T cycle
216  // while req.valid is set true in T + 1 cycle
217  val req = Decoupled(new FtqToICacheRequestBundle)
218}
219
220class FtqToPrefetchIO(implicit p: Parameters) extends XSBundle {
221  val req              = Decoupled(new FtqICacheInfo)
222  val flushFromBpu     = new BpuFlushInfo
223  val backendException = UInt(ExceptionType.width.W)
224}
225
226trait HasBackendRedirectInfo extends HasXSParameter {
227  def isLoadReplay(r: Valid[Redirect]) = r.bits.flushItself()
228}
229
230class FtqToCtrlIO(implicit p: Parameters) extends XSBundle with HasBackendRedirectInfo {
231  // write to backend pc mem
232  val pc_mem_wen   = Output(Bool())
233  val pc_mem_waddr = Output(UInt(log2Ceil(FtqSize).W))
234  val pc_mem_wdata = Output(new Ftq_RF_Components)
235  // newest target
236  val newest_entry_en     = Output(Bool())
237  val newest_entry_target = Output(UInt(VAddrBits.W))
238  val newest_entry_ptr    = Output(new FtqPtr)
239}
240
241class FTBEntryGen(implicit p: Parameters) extends XSModule with HasBackendRedirectInfo with HasBPUParameter {
242  val io = IO(new Bundle {
243    val start_addr     = Input(UInt(VAddrBits.W))
244    val old_entry      = Input(new FTBEntry)
245    val pd             = Input(new Ftq_pd_Entry)
246    val cfiIndex       = Flipped(Valid(UInt(log2Ceil(PredictWidth).W)))
247    val target         = Input(UInt(VAddrBits.W))
248    val hit            = Input(Bool())
249    val mispredict_vec = Input(Vec(PredictWidth, Bool()))
250
251    val new_entry         = Output(new FTBEntry)
252    val new_br_insert_pos = Output(Vec(numBr, Bool()))
253    val taken_mask        = Output(Vec(numBr, Bool()))
254    val jmp_taken         = Output(Bool())
255    val mispred_mask      = Output(Vec(numBr + 1, Bool()))
256
257    // for perf counters
258    val is_init_entry           = Output(Bool())
259    val is_old_entry            = Output(Bool())
260    val is_new_br               = Output(Bool())
261    val is_jalr_target_modified = Output(Bool())
262    val is_strong_bias_modified = Output(Bool())
263    val is_br_full              = Output(Bool())
264  })
265
266  // no mispredictions detected at predecode
267  val hit = io.hit
268  val pd  = io.pd
269
270  val init_entry = WireInit(0.U.asTypeOf(new FTBEntry))
271
272  val cfi_is_br       = pd.brMask(io.cfiIndex.bits) && io.cfiIndex.valid
273  val entry_has_jmp   = pd.jmpInfo.valid
274  val new_jmp_is_jal  = entry_has_jmp && !pd.jmpInfo.bits(0) && io.cfiIndex.valid
275  val new_jmp_is_jalr = entry_has_jmp && pd.jmpInfo.bits(0) && io.cfiIndex.valid
276  val new_jmp_is_call = entry_has_jmp && pd.jmpInfo.bits(1) && io.cfiIndex.valid
277  val new_jmp_is_ret  = entry_has_jmp && pd.jmpInfo.bits(2) && io.cfiIndex.valid
278  val last_jmp_rvi    = entry_has_jmp && pd.jmpOffset === (PredictWidth - 1).U && !pd.rvcMask.last
279  // val last_br_rvi = cfi_is_br && io.cfiIndex.bits === (PredictWidth-1).U && !pd.rvcMask.last
280
281  val cfi_is_jal  = io.cfiIndex.bits === pd.jmpOffset && new_jmp_is_jal
282  val cfi_is_jalr = io.cfiIndex.bits === pd.jmpOffset && new_jmp_is_jalr
283
284  def carryPos = log2Ceil(PredictWidth) + instOffsetBits
285  def getLower(pc: UInt) = pc(carryPos - 1, instOffsetBits)
286  // if not hit, establish a new entry
287  init_entry.valid := true.B
288  // tag is left for ftb to assign
289
290  // case br
291  val init_br_slot = init_entry.getSlotForBr(0)
292  when(cfi_is_br) {
293    init_br_slot.valid  := true.B
294    init_br_slot.offset := io.cfiIndex.bits
295    init_br_slot.setLowerStatByTarget(io.start_addr, io.target, numBr == 1)
296    init_entry.strong_bias(0) := true.B // set to strong bias on init
297  }
298
299  // case jmp
300  when(entry_has_jmp) {
301    init_entry.tailSlot.offset := pd.jmpOffset
302    init_entry.tailSlot.valid  := new_jmp_is_jal || new_jmp_is_jalr
303    init_entry.tailSlot.setLowerStatByTarget(io.start_addr, Mux(cfi_is_jalr, io.target, pd.jalTarget), isShare = false)
304    init_entry.strong_bias(numBr - 1) := new_jmp_is_jalr // set strong bias for the jalr on init
305  }
306
307  val jmpPft = getLower(io.start_addr) +& pd.jmpOffset +& Mux(pd.rvcMask(pd.jmpOffset), 1.U, 2.U)
308  init_entry.pftAddr := Mux(entry_has_jmp && !last_jmp_rvi, jmpPft, getLower(io.start_addr))
309  init_entry.carry   := Mux(entry_has_jmp && !last_jmp_rvi, jmpPft(carryPos - instOffsetBits), true.B)
310  init_entry.isJalr  := new_jmp_is_jalr
311  init_entry.isCall  := new_jmp_is_call
312  init_entry.isRet   := new_jmp_is_ret
313  // that means fall thru points to the middle of an inst
314  init_entry.last_may_be_rvi_call := pd.jmpOffset === (PredictWidth - 1).U && !pd.rvcMask(pd.jmpOffset)
315
316  // if hit, check whether a new cfi(only br is possible) is detected
317  val oe              = io.old_entry
318  val br_recorded_vec = oe.getBrRecordedVec(io.cfiIndex.bits)
319  val br_recorded     = br_recorded_vec.asUInt.orR
320  val is_new_br       = cfi_is_br && !br_recorded
321  val new_br_offset   = io.cfiIndex.bits
322  // vec(i) means new br will be inserted BEFORE old br(i)
323  val allBrSlotsVec = oe.allSlotsForBr
324  val new_br_insert_onehot = VecInit((0 until numBr).map {
325    i =>
326      i match {
327        case 0 =>
328          !allBrSlotsVec(0).valid || new_br_offset < allBrSlotsVec(0).offset
329        case idx =>
330          allBrSlotsVec(idx - 1).valid && new_br_offset > allBrSlotsVec(idx - 1).offset &&
331          (!allBrSlotsVec(idx).valid || new_br_offset < allBrSlotsVec(idx).offset)
332      }
333  })
334
335  val old_entry_modified = WireInit(io.old_entry)
336  for (i <- 0 until numBr) {
337    val slot = old_entry_modified.allSlotsForBr(i)
338    when(new_br_insert_onehot(i)) {
339      slot.valid  := true.B
340      slot.offset := new_br_offset
341      slot.setLowerStatByTarget(io.start_addr, io.target, i == numBr - 1)
342      old_entry_modified.strong_bias(i) := true.B
343    }.elsewhen(new_br_offset > oe.allSlotsForBr(i).offset) {
344      old_entry_modified.strong_bias(i) := false.B
345      // all other fields remain unchanged
346    }.otherwise {
347      // case i == 0, remain unchanged
348      if (i != 0) {
349        val noNeedToMoveFromFormerSlot = (i == numBr - 1).B && !oe.brSlots.last.valid
350        when(!noNeedToMoveFromFormerSlot) {
351          slot.fromAnotherSlot(oe.allSlotsForBr(i - 1))
352          old_entry_modified.strong_bias(i) := oe.strong_bias(i)
353        }
354      }
355    }
356  }
357
358  // two circumstances:
359  // 1. oe: | br | j  |, new br should be in front of j, thus addr of j should be new pft
360  // 2. oe: | br | br |, new br could be anywhere between, thus new pft is the addr of either
361  //        the previous last br or the new br
362  val may_have_to_replace = oe.noEmptySlotForNewBr
363  val pft_need_to_change  = is_new_br && may_have_to_replace
364  // it should either be the given last br or the new br
365  when(pft_need_to_change) {
366    val new_pft_offset =
367      Mux(!new_br_insert_onehot.asUInt.orR, new_br_offset, oe.allSlotsForBr.last.offset)
368
369    // set jmp to invalid
370    old_entry_modified.pftAddr              := getLower(io.start_addr) + new_pft_offset
371    old_entry_modified.carry                := (getLower(io.start_addr) +& new_pft_offset).head(1).asBool
372    old_entry_modified.last_may_be_rvi_call := false.B
373    old_entry_modified.isCall               := false.B
374    old_entry_modified.isRet                := false.B
375    old_entry_modified.isJalr               := false.B
376  }
377
378  val old_entry_jmp_target_modified = WireInit(oe)
379  val old_target      = oe.tailSlot.getTarget(io.start_addr) // may be wrong because we store only 20 lowest bits
380  val old_tail_is_jmp = !oe.tailSlot.sharing
381  val jalr_target_modified = cfi_is_jalr && (old_target =/= io.target) && old_tail_is_jmp // TODO: pass full jalr target
382  when(jalr_target_modified) {
383    old_entry_jmp_target_modified.setByJmpTarget(io.start_addr, io.target)
384    old_entry_jmp_target_modified.strong_bias := 0.U.asTypeOf(Vec(numBr, Bool()))
385  }
386
387  val old_entry_strong_bias    = WireInit(oe)
388  val strong_bias_modified_vec = Wire(Vec(numBr, Bool())) // whether modified or not
389  for (i <- 0 until numBr) {
390    when(br_recorded_vec(0)) {
391      old_entry_strong_bias.strong_bias(0) :=
392        oe.strong_bias(0) && io.cfiIndex.valid && oe.brValids(0) && io.cfiIndex.bits === oe.brOffset(0)
393    }.elsewhen(br_recorded_vec(numBr - 1)) {
394      old_entry_strong_bias.strong_bias(0) := false.B
395      old_entry_strong_bias.strong_bias(numBr - 1) :=
396        oe.strong_bias(numBr - 1) && io.cfiIndex.valid && oe.brValids(numBr - 1) && io.cfiIndex.bits === oe.brOffset(
397          numBr - 1
398        )
399    }
400    strong_bias_modified_vec(i) := oe.strong_bias(i) && oe.brValids(i) && !old_entry_strong_bias.strong_bias(i)
401  }
402  val strong_bias_modified = strong_bias_modified_vec.reduce(_ || _)
403
404  val derived_from_old_entry =
405    Mux(is_new_br, old_entry_modified, Mux(jalr_target_modified, old_entry_jmp_target_modified, old_entry_strong_bias))
406
407  io.new_entry := Mux(!hit, init_entry, derived_from_old_entry)
408
409  io.new_br_insert_pos := new_br_insert_onehot
410  io.taken_mask := VecInit((io.new_entry.brOffset zip io.new_entry.brValids).map {
411    case (off, v) => io.cfiIndex.bits === off && io.cfiIndex.valid && v
412  })
413  io.jmp_taken := io.new_entry.jmpValid && io.new_entry.tailSlot.offset === io.cfiIndex.bits
414  for (i <- 0 until numBr) {
415    io.mispred_mask(i) := io.new_entry.brValids(i) && io.mispredict_vec(io.new_entry.brOffset(i))
416  }
417  io.mispred_mask.last := io.new_entry.jmpValid && io.mispredict_vec(pd.jmpOffset)
418
419  // for perf counters
420  io.is_init_entry           := !hit
421  io.is_old_entry            := hit && !is_new_br && !jalr_target_modified && !strong_bias_modified
422  io.is_new_br               := hit && is_new_br
423  io.is_jalr_target_modified := hit && jalr_target_modified
424  io.is_strong_bias_modified := hit && strong_bias_modified
425  io.is_br_full              := hit && is_new_br && may_have_to_replace
426}
427
428class FtqPcMemWrapper(numOtherReads: Int)(implicit p: Parameters) extends XSModule with HasBackendRedirectInfo {
429  val io = IO(new Bundle {
430    val ifuPtr_w           = Input(new FtqPtr)
431    val ifuPtrPlus1_w      = Input(new FtqPtr)
432    val ifuPtrPlus2_w      = Input(new FtqPtr)
433    val pfPtr_w            = Input(new FtqPtr)
434    val pfPtrPlus1_w       = Input(new FtqPtr)
435    val commPtr_w          = Input(new FtqPtr)
436    val commPtrPlus1_w     = Input(new FtqPtr)
437    val ifuPtr_rdata       = Output(new Ftq_RF_Components)
438    val ifuPtrPlus1_rdata  = Output(new Ftq_RF_Components)
439    val ifuPtrPlus2_rdata  = Output(new Ftq_RF_Components)
440    val pfPtr_rdata        = Output(new Ftq_RF_Components)
441    val pfPtrPlus1_rdata   = Output(new Ftq_RF_Components)
442    val commPtr_rdata      = Output(new Ftq_RF_Components)
443    val commPtrPlus1_rdata = Output(new Ftq_RF_Components)
444
445    val wen   = Input(Bool())
446    val waddr = Input(UInt(log2Ceil(FtqSize).W))
447    val wdata = Input(new Ftq_RF_Components)
448  })
449
450  val num_pc_read = numOtherReads + 5
451  val mem         = Module(new SyncDataModuleTemplate(new Ftq_RF_Components, FtqSize, num_pc_read, 1, "FtqPC"))
452  mem.io.wen(0)   := io.wen
453  mem.io.waddr(0) := io.waddr
454  mem.io.wdata(0) := io.wdata
455
456  // read one cycle ahead for ftq local reads
457  val raddr_vec = VecInit(Seq(
458    io.ifuPtr_w.value,
459    io.ifuPtrPlus1_w.value,
460    io.ifuPtrPlus2_w.value,
461    io.pfPtr_w.value,
462    io.pfPtrPlus1_w.value,
463    io.commPtrPlus1_w.value,
464    io.commPtr_w.value
465  ))
466
467  mem.io.raddr := raddr_vec
468
469  io.ifuPtr_rdata       := mem.io.rdata.dropRight(6).last
470  io.ifuPtrPlus1_rdata  := mem.io.rdata.dropRight(5).last
471  io.ifuPtrPlus2_rdata  := mem.io.rdata.dropRight(4).last
472  io.pfPtr_rdata        := mem.io.rdata.dropRight(3).last
473  io.pfPtrPlus1_rdata   := mem.io.rdata.dropRight(2).last
474  io.commPtrPlus1_rdata := mem.io.rdata.dropRight(1).last
475  io.commPtr_rdata      := mem.io.rdata.last
476}
477
478class Ftq(implicit p: Parameters) extends XSModule with HasCircularQueuePtrHelper
479    with HasBackendRedirectInfo with BPUUtils with HasBPUConst with HasPerfEvents
480    with HasICacheParameters {
481  val io = IO(new Bundle {
482    val fromBpu     = Flipped(new BpuToFtqIO)
483    val fromIfu     = Flipped(new IfuToFtqIO)
484    val fromBackend = Flipped(new CtrlToFtqIO)
485
486    val toBpu       = new FtqToBpuIO
487    val toIfu       = new FtqToIfuIO
488    val toICache    = new FtqToICacheIO
489    val toBackend   = new FtqToCtrlIO
490    val toPrefetch  = new FtqToPrefetchIO
491    val icacheFlush = Output(Bool())
492
493    val bpuInfo = new Bundle {
494      val bpRight = Output(UInt(XLEN.W))
495      val bpWrong = Output(UInt(XLEN.W))
496    }
497
498    val mmioCommitRead = Flipped(new mmioCommitRead)
499
500    // for perf
501    val ControlBTBMissBubble = Output(Bool())
502    val TAGEMissBubble       = Output(Bool())
503    val SCMissBubble         = Output(Bool())
504    val ITTAGEMissBubble     = Output(Bool())
505    val RASMissBubble        = Output(Bool())
506  })
507  io.bpuInfo := DontCare
508
509  val topdown_stage = RegInit(0.U.asTypeOf(new FrontendTopDownBundle))
510  // only driven by clock, not valid-ready
511  topdown_stage                  := io.fromBpu.resp.bits.topdown_info
512  io.toIfu.req.bits.topdown_info := topdown_stage
513
514  val ifuRedirected = RegInit(VecInit(Seq.fill(FtqSize)(false.B)))
515
516  // io.fromBackend.ftqIdxAhead: bju(BjuCnt) + ldReplay + exception
517  val ftqIdxAhead = VecInit(Seq.tabulate(FtqRedirectAheadNum)(i => io.fromBackend.ftqIdxAhead(i))) // only bju
518  val ftqIdxSelOH = io.fromBackend.ftqIdxSelOH.bits(FtqRedirectAheadNum - 1, 0)
519
520  val aheadValid         = ftqIdxAhead.map(_.valid).reduce(_ | _) && !io.fromBackend.redirect.valid
521  val realAhdValid       = io.fromBackend.redirect.valid && (ftqIdxSelOH > 0.U) && RegNext(aheadValid)
522  val backendRedirect    = Wire(Valid(new BranchPredictionRedirect))
523  val backendRedirectReg = Wire(Valid(new BranchPredictionRedirect))
524  backendRedirectReg.valid := RegNext(Mux(realAhdValid, false.B, backendRedirect.valid))
525  backendRedirectReg.bits  := RegEnable(backendRedirect.bits, backendRedirect.valid)
526  val fromBackendRedirect = Wire(Valid(new BranchPredictionRedirect))
527  fromBackendRedirect := Mux(realAhdValid, backendRedirect, backendRedirectReg)
528
529  val stage2Flush  = backendRedirect.valid
530  val backendFlush = stage2Flush || RegNext(stage2Flush)
531  val ifuFlush     = Wire(Bool())
532
533  val flush = stage2Flush || RegNext(stage2Flush)
534
535  val allowBpuIn, allowToIfu = WireInit(false.B)
536  val flushToIfu             = !allowToIfu
537  allowBpuIn := !ifuFlush && !backendRedirect.valid && !backendRedirectReg.valid
538  allowToIfu := !ifuFlush && !backendRedirect.valid && !backendRedirectReg.valid
539
540  def copyNum                                              = 5
541  val bpuPtr, ifuPtr, pfPtr, ifuWbPtr, commPtr, robCommPtr = RegInit(FtqPtr(false.B, 0.U))
542  val ifuPtrPlus1                                          = RegInit(FtqPtr(false.B, 1.U))
543  val ifuPtrPlus2                                          = RegInit(FtqPtr(false.B, 2.U))
544  val pfPtrPlus1                                           = RegInit(FtqPtr(false.B, 1.U))
545  val commPtrPlus1                                         = RegInit(FtqPtr(false.B, 1.U))
546  val copied_ifu_ptr                                       = Seq.fill(copyNum)(RegInit(FtqPtr(false.B, 0.U)))
547  val copied_bpu_ptr                                       = Seq.fill(copyNum)(RegInit(FtqPtr(false.B, 0.U)))
548  require(FtqSize >= 4)
549  val ifuPtr_write       = WireInit(ifuPtr)
550  val ifuPtrPlus1_write  = WireInit(ifuPtrPlus1)
551  val ifuPtrPlus2_write  = WireInit(ifuPtrPlus2)
552  val pfPtr_write        = WireInit(pfPtr)
553  val pfPtrPlus1_write   = WireInit(pfPtrPlus1)
554  val ifuWbPtr_write     = WireInit(ifuWbPtr)
555  val commPtr_write      = WireInit(commPtr)
556  val commPtrPlus1_write = WireInit(commPtrPlus1)
557  val robCommPtr_write   = WireInit(robCommPtr)
558  ifuPtr       := ifuPtr_write
559  ifuPtrPlus1  := ifuPtrPlus1_write
560  ifuPtrPlus2  := ifuPtrPlus2_write
561  pfPtr        := pfPtr_write
562  pfPtrPlus1   := pfPtrPlus1_write
563  ifuWbPtr     := ifuWbPtr_write
564  commPtr      := commPtr_write
565  commPtrPlus1 := commPtrPlus1_write
566  copied_ifu_ptr.map { ptr =>
567    ptr := ifuPtr_write
568    dontTouch(ptr)
569  }
570  robCommPtr := robCommPtr_write
571  val validEntries = distanceBetween(bpuPtr, commPtr)
572  val canCommit    = Wire(Bool())
573
574  // Instruction page fault and instruction access fault are sent from backend with redirect requests.
575  // When IPF and IAF are sent, backendPcFaultIfuPtr points to the FTQ entry whose first instruction
576  // raises IPF or IAF, which is ifuWbPtr_write or IfuPtr_write.
577  // Only when IFU has written back that FTQ entry can backendIpf and backendIaf be false because this
578  // makes sure that IAF and IPF are correctly raised instead of being flushed by redirect requests.
579  val backendException  = RegInit(ExceptionType.none)
580  val backendPcFaultPtr = RegInit(FtqPtr(false.B, 0.U))
581  when(fromBackendRedirect.valid) {
582    backendException := ExceptionType.fromOH(
583      has_pf = fromBackendRedirect.bits.cfiUpdate.backendIPF,
584      has_gpf = fromBackendRedirect.bits.cfiUpdate.backendIGPF,
585      has_af = fromBackendRedirect.bits.cfiUpdate.backendIAF
586    )
587    when(
588      fromBackendRedirect.bits.cfiUpdate.backendIPF || fromBackendRedirect.bits.cfiUpdate.backendIGPF ||
589        fromBackendRedirect.bits.cfiUpdate.backendIAF
590    ) {
591      backendPcFaultPtr := ifuWbPtr_write
592    }
593  }.elsewhen(ifuWbPtr =/= backendPcFaultPtr) {
594    backendException := ExceptionType.none
595  }
596
597  // **********************************************************************
598  // **************************** enq from bpu ****************************
599  // **********************************************************************
600  val new_entry_ready = validEntries < FtqSize.U || canCommit
601  io.fromBpu.resp.ready := new_entry_ready
602
603  val bpu_s2_resp     = io.fromBpu.resp.bits.s2
604  val bpu_s3_resp     = io.fromBpu.resp.bits.s3
605  val bpu_s2_redirect = bpu_s2_resp.valid(3) && bpu_s2_resp.hasRedirect(3)
606  val bpu_s3_redirect = bpu_s3_resp.valid(3) && bpu_s3_resp.hasRedirect(3)
607
608  io.toBpu.enq_ptr := bpuPtr
609  val enq_fire    = io.fromBpu.resp.fire && allowBpuIn // from bpu s1
610  val bpu_in_fire = (io.fromBpu.resp.fire || bpu_s2_redirect || bpu_s3_redirect) && allowBpuIn
611
612  val bpu_in_resp     = io.fromBpu.resp.bits.selectedResp
613  val bpu_in_stage    = io.fromBpu.resp.bits.selectedRespIdxForFtq
614  val bpu_in_resp_ptr = Mux(bpu_in_stage === BP_S1, bpuPtr, bpu_in_resp.ftq_idx)
615  val bpu_in_resp_idx = bpu_in_resp_ptr.value
616
617  // read ports:      pfReq1 + pfReq2 ++  ifuReq1 + ifuReq2 + ifuReq3 + commitUpdate2 + commitUpdate
618  val ftq_pc_mem = Module(new FtqPcMemWrapper(2))
619  // resp from uBTB
620  ftq_pc_mem.io.wen   := bpu_in_fire
621  ftq_pc_mem.io.waddr := bpu_in_resp_idx
622  ftq_pc_mem.io.wdata.fromBranchPrediction(bpu_in_resp)
623
624  //                                                            ifuRedirect + backendRedirect + commit
625  val ftq_redirect_mem = Module(new SyncDataModuleTemplate(
626    new Ftq_Redirect_SRAMEntry,
627    FtqSize,
628    IfuRedirectNum + FtqRedirectAheadNum + 1,
629    1,
630    hasRen = true
631  ))
632  // these info is intended to enq at the last stage of bpu
633  ftq_redirect_mem.io.wen(0)   := io.fromBpu.resp.bits.lastStage.valid(3)
634  ftq_redirect_mem.io.waddr(0) := io.fromBpu.resp.bits.lastStage.ftq_idx.value
635  ftq_redirect_mem.io.wdata(0) := io.fromBpu.resp.bits.last_stage_spec_info
636  println(f"ftq redirect MEM: entry ${ftq_redirect_mem.io.wdata(0).getWidth} * ${FtqSize} * 3")
637
638  val ftq_meta_1r_sram = Module(new FtqNRSRAM(new Ftq_1R_SRAMEntry, 1))
639  // these info is intended to enq at the last stage of bpu
640  ftq_meta_1r_sram.io.wen             := io.fromBpu.resp.bits.lastStage.valid(3)
641  ftq_meta_1r_sram.io.waddr           := io.fromBpu.resp.bits.lastStage.ftq_idx.value
642  ftq_meta_1r_sram.io.wdata.meta      := io.fromBpu.resp.bits.last_stage_meta
643  ftq_meta_1r_sram.io.wdata.ftb_entry := io.fromBpu.resp.bits.last_stage_ftb_entry
644  //                                                            ifuRedirect + backendRedirect (commit moved to ftq_meta_1r_sram)
645  val ftb_entry_mem = Module(new SyncDataModuleTemplate(
646    new FTBEntry_FtqMem,
647    FtqSize,
648    IfuRedirectNum + FtqRedirectAheadNum,
649    1,
650    hasRen = true
651  ))
652  ftb_entry_mem.io.wen(0)   := io.fromBpu.resp.bits.lastStage.valid(3)
653  ftb_entry_mem.io.waddr(0) := io.fromBpu.resp.bits.lastStage.ftq_idx.value
654  ftb_entry_mem.io.wdata(0) := io.fromBpu.resp.bits.last_stage_ftb_entry
655
656  // multi-write
657  val update_target = Reg(Vec(FtqSize, UInt(VAddrBits.W))) // could be taken target or fallThrough //TODO: remove this
658  val newest_entry_target          = Reg(UInt(VAddrBits.W))
659  val newest_entry_target_modified = RegInit(false.B)
660  val newest_entry_ptr             = Reg(new FtqPtr)
661  val newest_entry_ptr_modified    = RegInit(false.B)
662  val cfiIndex_vec                 = Reg(Vec(FtqSize, ValidUndirectioned(UInt(log2Ceil(PredictWidth).W))))
663  val mispredict_vec               = Reg(Vec(FtqSize, Vec(PredictWidth, Bool())))
664  val pred_stage                   = Reg(Vec(FtqSize, UInt(2.W)))
665  val pred_s1_cycle                = if (!env.FPGAPlatform) Some(Reg(Vec(FtqSize, UInt(64.W)))) else None
666
667  val c_empty :: c_toCommit :: c_committed :: c_flushed :: Nil = Enum(4)
668  val commitStateQueueReg = RegInit(VecInit(Seq.fill(FtqSize) {
669    VecInit(Seq.fill(PredictWidth)(c_empty))
670  }))
671  val commitStateQueueEnable = WireInit(VecInit(Seq.fill(FtqSize)(false.B)))
672  val commitStateQueueNext   = WireInit(commitStateQueueReg)
673
674  for (f <- 0 until FtqSize) {
675    when(commitStateQueueEnable(f)) {
676      commitStateQueueReg(f) := commitStateQueueNext(f)
677    }
678  }
679
680  val f_to_send :: f_sent :: Nil = Enum(2)
681  val entry_fetch_status         = RegInit(VecInit(Seq.fill(FtqSize)(f_sent)))
682
683  val h_not_hit :: h_false_hit :: h_hit :: Nil = Enum(3)
684  val entry_hit_status                         = RegInit(VecInit(Seq.fill(FtqSize)(h_not_hit)))
685
686  // modify registers one cycle later to cut critical path
687  val last_cycle_bpu_in       = RegNext(bpu_in_fire)
688  val last_cycle_bpu_in_ptr   = RegEnable(bpu_in_resp_ptr, bpu_in_fire)
689  val last_cycle_bpu_in_idx   = last_cycle_bpu_in_ptr.value
690  val last_cycle_bpu_target   = RegEnable(bpu_in_resp.getTarget(3), bpu_in_fire)
691  val last_cycle_cfiIndex     = RegEnable(bpu_in_resp.cfiIndex(3), bpu_in_fire)
692  val last_cycle_bpu_in_stage = RegEnable(bpu_in_stage, bpu_in_fire)
693
694  def extra_copyNum_for_commitStateQueue = 2
695  val copied_last_cycle_bpu_in =
696    VecInit(Seq.fill(copyNum + extra_copyNum_for_commitStateQueue)(RegNext(bpu_in_fire)))
697  val copied_last_cycle_bpu_in_ptr_for_ftq =
698    VecInit(Seq.fill(extra_copyNum_for_commitStateQueue)(RegEnable(bpu_in_resp_ptr, bpu_in_fire)))
699
700  newest_entry_target_modified := false.B
701  newest_entry_ptr_modified    := false.B
702  when(last_cycle_bpu_in) {
703    entry_fetch_status(last_cycle_bpu_in_idx) := f_to_send
704    cfiIndex_vec(last_cycle_bpu_in_idx)       := last_cycle_cfiIndex
705    pred_stage(last_cycle_bpu_in_idx)         := last_cycle_bpu_in_stage
706
707    update_target(last_cycle_bpu_in_idx) := last_cycle_bpu_target // TODO: remove this
708    newest_entry_target_modified         := true.B
709    newest_entry_target                  := last_cycle_bpu_target
710    newest_entry_ptr_modified            := true.B
711    newest_entry_ptr                     := last_cycle_bpu_in_ptr
712  }
713
714  // reduce fanout by delay write for a cycle
715  when(RegNext(last_cycle_bpu_in)) {
716    mispredict_vec(RegEnable(last_cycle_bpu_in_idx, last_cycle_bpu_in)) :=
717      WireInit(VecInit(Seq.fill(PredictWidth)(false.B)))
718  }
719
720  // record s1 pred cycles
721  pred_s1_cycle.map { vec =>
722    when(bpu_in_fire && (bpu_in_stage === BP_S1)) {
723      vec(bpu_in_resp_ptr.value) := bpu_in_resp.full_pred(0).predCycle.getOrElse(0.U)
724    }
725  }
726
727  // reduce fanout using copied last_cycle_bpu_in and copied last_cycle_bpu_in_ptr
728  val copied_last_cycle_bpu_in_for_ftq = copied_last_cycle_bpu_in.takeRight(extra_copyNum_for_commitStateQueue)
729  copied_last_cycle_bpu_in_for_ftq.zip(copied_last_cycle_bpu_in_ptr_for_ftq).zipWithIndex.map {
730    case ((in, ptr), i) =>
731      when(in) {
732        val perSetEntries = FtqSize / extra_copyNum_for_commitStateQueue // 32
733        require(FtqSize % extra_copyNum_for_commitStateQueue == 0)
734        for (j <- 0 until perSetEntries) {
735          when(ptr.value === (i * perSetEntries + j).U) {
736            commitStateQueueNext(i * perSetEntries + j) := VecInit(Seq.fill(PredictWidth)(c_empty))
737            // Clock gating optimization, use 1 gate cell to control a row
738            commitStateQueueEnable(i * perSetEntries + j) := true.B
739          }
740        }
741      }
742  }
743
744  bpuPtr := bpuPtr + enq_fire
745  copied_bpu_ptr.map(_ := bpuPtr + enq_fire)
746  when(io.toIfu.req.fire && allowToIfu) {
747    ifuPtr_write      := ifuPtrPlus1
748    ifuPtrPlus1_write := ifuPtrPlus2
749    ifuPtrPlus2_write := ifuPtrPlus2 + 1.U
750  }
751  when(io.toPrefetch.req.fire && allowToIfu) {
752    pfPtr_write      := pfPtrPlus1
753    pfPtrPlus1_write := pfPtrPlus1 + 1.U
754  }
755
756  // only use ftb result to assign hit status
757  when(bpu_s2_resp.valid(3)) {
758    entry_hit_status(bpu_s2_resp.ftq_idx.value) := Mux(bpu_s2_resp.full_pred(3).hit, h_hit, h_not_hit)
759  }
760
761  io.toIfu.flushFromBpu.s2.valid      := bpu_s2_redirect
762  io.toIfu.flushFromBpu.s2.bits       := bpu_s2_resp.ftq_idx
763  io.toPrefetch.flushFromBpu.s2.valid := bpu_s2_redirect
764  io.toPrefetch.flushFromBpu.s2.bits  := bpu_s2_resp.ftq_idx
765  when(bpu_s2_redirect) {
766    bpuPtr := bpu_s2_resp.ftq_idx + 1.U
767    copied_bpu_ptr.map(_ := bpu_s2_resp.ftq_idx + 1.U)
768    // only when ifuPtr runs ahead of bpu s2 resp should we recover it
769    when(!isBefore(ifuPtr, bpu_s2_resp.ftq_idx)) {
770      ifuPtr_write      := bpu_s2_resp.ftq_idx
771      ifuPtrPlus1_write := bpu_s2_resp.ftq_idx + 1.U
772      ifuPtrPlus2_write := bpu_s2_resp.ftq_idx + 2.U
773    }
774    when(!isBefore(pfPtr, bpu_s2_resp.ftq_idx)) {
775      pfPtr_write      := bpu_s2_resp.ftq_idx
776      pfPtrPlus1_write := bpu_s2_resp.ftq_idx + 1.U
777    }
778  }
779
780  io.toIfu.flushFromBpu.s3.valid      := bpu_s3_redirect
781  io.toIfu.flushFromBpu.s3.bits       := bpu_s3_resp.ftq_idx
782  io.toPrefetch.flushFromBpu.s3.valid := bpu_s3_redirect
783  io.toPrefetch.flushFromBpu.s3.bits  := bpu_s3_resp.ftq_idx
784  when(bpu_s3_redirect) {
785    bpuPtr := bpu_s3_resp.ftq_idx + 1.U
786    copied_bpu_ptr.map(_ := bpu_s3_resp.ftq_idx + 1.U)
787    // only when ifuPtr runs ahead of bpu s2 resp should we recover it
788    when(!isBefore(ifuPtr, bpu_s3_resp.ftq_idx)) {
789      ifuPtr_write      := bpu_s3_resp.ftq_idx
790      ifuPtrPlus1_write := bpu_s3_resp.ftq_idx + 1.U
791      ifuPtrPlus2_write := bpu_s3_resp.ftq_idx + 2.U
792    }
793    when(!isBefore(pfPtr, bpu_s3_resp.ftq_idx)) {
794      pfPtr_write      := bpu_s3_resp.ftq_idx
795      pfPtrPlus1_write := bpu_s3_resp.ftq_idx + 1.U
796    }
797  }
798
799  XSError(isBefore(bpuPtr, ifuPtr) && !isFull(bpuPtr, ifuPtr), "\nifuPtr is before bpuPtr!\n")
800  XSError(isBefore(bpuPtr, pfPtr) && !isFull(bpuPtr, pfPtr), "\npfPtr is before bpuPtr!\n")
801  XSError(isBefore(ifuWbPtr, commPtr) && !isFull(ifuWbPtr, commPtr), "\ncommPtr is before ifuWbPtr!\n")
802
803  (0 until copyNum).map(i => XSError(copied_bpu_ptr(i) =/= bpuPtr, "\ncopiedBpuPtr is different from bpuPtr!\n"))
804
805  // ****************************************************************
806  // **************************** to ifu ****************************
807  // ****************************************************************
808  // 0  for ifu, and 1-4 for ICache
809  val bpu_in_bypass_buf         = RegEnable(ftq_pc_mem.io.wdata, bpu_in_fire)
810  val copied_bpu_in_bypass_buf  = VecInit(Seq.fill(copyNum)(RegEnable(ftq_pc_mem.io.wdata, bpu_in_fire)))
811  val bpu_in_bypass_buf_for_ifu = bpu_in_bypass_buf
812  val bpu_in_bypass_ptr         = RegEnable(bpu_in_resp_ptr, bpu_in_fire)
813  val last_cycle_to_ifu_fire    = RegNext(io.toIfu.req.fire)
814  val last_cycle_to_pf_fire     = RegNext(io.toPrefetch.req.fire)
815
816  val copied_bpu_in_bypass_ptr      = VecInit(Seq.fill(copyNum)(RegEnable(bpu_in_resp_ptr, bpu_in_fire)))
817  val copied_last_cycle_to_ifu_fire = VecInit(Seq.fill(copyNum)(RegNext(io.toIfu.req.fire)))
818
819  // read pc and target
820  ftq_pc_mem.io.ifuPtr_w       := ifuPtr_write
821  ftq_pc_mem.io.ifuPtrPlus1_w  := ifuPtrPlus1_write
822  ftq_pc_mem.io.ifuPtrPlus2_w  := ifuPtrPlus2_write
823  ftq_pc_mem.io.pfPtr_w        := pfPtr_write
824  ftq_pc_mem.io.pfPtrPlus1_w   := pfPtrPlus1_write
825  ftq_pc_mem.io.commPtr_w      := commPtr_write
826  ftq_pc_mem.io.commPtrPlus1_w := commPtrPlus1_write
827
828  io.toIfu.req.bits.ftqIdx := ifuPtr
829
830  val toICachePcBundle               = Wire(Vec(copyNum, new Ftq_RF_Components))
831  val toICacheEntryToSend            = Wire(Vec(copyNum, Bool()))
832  val nextCycleToPrefetchPcBundle    = Wire(new Ftq_RF_Components)
833  val nextCycleToPrefetchEntryToSend = Wire(Bool())
834  val toPrefetchPcBundle             = RegNext(nextCycleToPrefetchPcBundle)
835  val toPrefetchEntryToSend          = RegNext(nextCycleToPrefetchEntryToSend)
836  val toIfuPcBundle                  = Wire(new Ftq_RF_Components)
837  val entry_is_to_send               = WireInit(entry_fetch_status(ifuPtr.value) === f_to_send)
838  val entry_ftq_offset               = WireInit(cfiIndex_vec(ifuPtr.value))
839  val entry_next_addr                = Wire(UInt(VAddrBits.W))
840
841  val pc_mem_ifu_ptr_rdata   = VecInit(Seq.fill(copyNum)(RegNext(ftq_pc_mem.io.ifuPtr_rdata)))
842  val pc_mem_ifu_plus1_rdata = VecInit(Seq.fill(copyNum)(RegNext(ftq_pc_mem.io.ifuPtrPlus1_rdata)))
843  val diff_entry_next_addr   = WireInit(update_target(ifuPtr.value)) // TODO: remove this
844
845  val copied_ifu_plus1_to_send = VecInit(Seq.fill(copyNum)(RegNext(
846    entry_fetch_status(ifuPtrPlus1.value) === f_to_send
847  ) || RegNext(last_cycle_bpu_in && bpu_in_bypass_ptr === ifuPtrPlus1)))
848  val copied_ifu_ptr_to_send = VecInit(Seq.fill(copyNum)(RegNext(
849    entry_fetch_status(ifuPtr.value) === f_to_send
850  ) || RegNext(last_cycle_bpu_in && bpu_in_bypass_ptr === ifuPtr)))
851
852  for (i <- 0 until copyNum) {
853    when(copied_last_cycle_bpu_in(i) && copied_bpu_in_bypass_ptr(i) === copied_ifu_ptr(i)) {
854      toICachePcBundle(i)    := copied_bpu_in_bypass_buf(i)
855      toICacheEntryToSend(i) := true.B
856    }.elsewhen(copied_last_cycle_to_ifu_fire(i)) {
857      toICachePcBundle(i)    := pc_mem_ifu_plus1_rdata(i)
858      toICacheEntryToSend(i) := copied_ifu_plus1_to_send(i)
859    }.otherwise {
860      toICachePcBundle(i)    := pc_mem_ifu_ptr_rdata(i)
861      toICacheEntryToSend(i) := copied_ifu_ptr_to_send(i)
862    }
863  }
864
865  // Calculate requests sent to prefetcher one cycle in advance to cut critical path
866  when(bpu_in_fire && bpu_in_resp_ptr === pfPtr_write) {
867    nextCycleToPrefetchPcBundle    := ftq_pc_mem.io.wdata
868    nextCycleToPrefetchEntryToSend := true.B
869  }.elsewhen(io.toPrefetch.req.fire) {
870    nextCycleToPrefetchPcBundle := ftq_pc_mem.io.pfPtrPlus1_rdata
871    nextCycleToPrefetchEntryToSend := entry_fetch_status(pfPtrPlus1.value) === f_to_send ||
872      last_cycle_bpu_in && bpu_in_bypass_ptr === pfPtrPlus1
873  }.otherwise {
874    nextCycleToPrefetchPcBundle := ftq_pc_mem.io.pfPtr_rdata
875    nextCycleToPrefetchEntryToSend := entry_fetch_status(pfPtr.value) === f_to_send ||
876      last_cycle_bpu_in && bpu_in_bypass_ptr === pfPtr // reduce potential bubbles
877  }
878
879  // TODO: reconsider target address bypass logic
880  when(last_cycle_bpu_in && bpu_in_bypass_ptr === ifuPtr) {
881    toIfuPcBundle        := bpu_in_bypass_buf_for_ifu
882    entry_is_to_send     := true.B
883    entry_next_addr      := last_cycle_bpu_target
884    entry_ftq_offset     := last_cycle_cfiIndex
885    diff_entry_next_addr := last_cycle_bpu_target // TODO: remove this
886  }.elsewhen(last_cycle_to_ifu_fire) {
887    toIfuPcBundle := RegNext(ftq_pc_mem.io.ifuPtrPlus1_rdata)
888    entry_is_to_send := RegNext(entry_fetch_status(ifuPtrPlus1.value) === f_to_send) ||
889      RegNext(last_cycle_bpu_in && bpu_in_bypass_ptr === ifuPtrPlus1) // reduce potential bubbles
890    entry_next_addr := Mux(
891      last_cycle_bpu_in && bpu_in_bypass_ptr === ifuPtrPlus1,
892      bpu_in_bypass_buf_for_ifu.startAddr,
893      Mux(ifuPtr === newest_entry_ptr, newest_entry_target, RegNext(ftq_pc_mem.io.ifuPtrPlus2_rdata.startAddr))
894    ) // ifuPtr+2
895  }.otherwise {
896    toIfuPcBundle := RegNext(ftq_pc_mem.io.ifuPtr_rdata)
897    entry_is_to_send := RegNext(entry_fetch_status(ifuPtr.value) === f_to_send) ||
898      RegNext(last_cycle_bpu_in && bpu_in_bypass_ptr === ifuPtr) // reduce potential bubbles
899    entry_next_addr := Mux(
900      last_cycle_bpu_in && bpu_in_bypass_ptr === ifuPtrPlus1,
901      bpu_in_bypass_buf_for_ifu.startAddr,
902      Mux(ifuPtr === newest_entry_ptr, newest_entry_target, RegNext(ftq_pc_mem.io.ifuPtrPlus1_rdata.startAddr))
903    ) // ifuPtr+1
904  }
905
906  io.toIfu.req.valid              := entry_is_to_send && ifuPtr =/= bpuPtr
907  io.toIfu.req.bits.nextStartAddr := entry_next_addr
908  io.toIfu.req.bits.ftqOffset     := entry_ftq_offset
909  io.toIfu.req.bits.fromFtqPcBundle(toIfuPcBundle)
910
911  io.toICache.req.valid := entry_is_to_send && ifuPtr =/= bpuPtr
912  io.toICache.req.bits.readValid.zipWithIndex.map { case (copy, i) =>
913    copy := toICacheEntryToSend(i) && copied_ifu_ptr(i) =/= copied_bpu_ptr(i)
914  }
915  io.toICache.req.bits.pcMemRead.zipWithIndex.foreach { case (copy, i) =>
916    copy.fromFtqPcBundle(toICachePcBundle(i))
917    copy.ftqIdx := ifuPtr
918  }
919  io.toICache.req.bits.backendException := ExceptionType.hasException(backendException) && backendPcFaultPtr === ifuPtr
920
921  io.toPrefetch.req.valid := toPrefetchEntryToSend && pfPtr =/= bpuPtr
922  io.toPrefetch.req.bits.fromFtqPcBundle(toPrefetchPcBundle)
923  io.toPrefetch.req.bits.ftqIdx  := pfPtr
924  io.toPrefetch.backendException := Mux(backendPcFaultPtr === pfPtr, backendException, ExceptionType.none)
925  // io.toICache.req.bits.bypassSelect := last_cycle_bpu_in && bpu_in_bypass_ptr === ifuPtr
926  // io.toICache.req.bits.bpuBypassWrite.zipWithIndex.map{case(bypassWrtie, i) =>
927  //   bypassWrtie.startAddr := bpu_in_bypass_buf.tail(i).startAddr
928  //   bypassWrtie.nextlineStart := bpu_in_bypass_buf.tail(i).nextLineAddr
929  // }
930
931  // TODO: remove this
932  XSError(
933    io.toIfu.req.valid && diff_entry_next_addr =/= entry_next_addr,
934    p"\nifu_req_target wrong! ifuPtr: ${ifuPtr}, entry_next_addr: ${Hexadecimal(entry_next_addr)} diff_entry_next_addr: ${Hexadecimal(diff_entry_next_addr)}\n"
935  )
936
937  // when fall through is smaller in value than start address, there must be a false hit
938  when(toIfuPcBundle.fallThruError && entry_hit_status(ifuPtr.value) === h_hit) {
939    when(io.toIfu.req.fire &&
940      !(bpu_s2_redirect && bpu_s2_resp.ftq_idx === ifuPtr) &&
941      !(bpu_s3_redirect && bpu_s3_resp.ftq_idx === ifuPtr)) {
942      entry_hit_status(ifuPtr.value) := h_false_hit
943      // XSError(true.B, "FTB false hit by fallThroughError, startAddr: %x, fallTHru: %x\n", io.toIfu.req.bits.startAddr, io.toIfu.req.bits.nextStartAddr)
944    }
945    XSDebug(
946      true.B,
947      "fallThruError! start:%x, fallThru:%x\n",
948      io.toIfu.req.bits.startAddr,
949      io.toIfu.req.bits.nextStartAddr
950    )
951  }
952
953  XSPerfAccumulate(
954    f"fall_through_error_to_ifu",
955    toIfuPcBundle.fallThruError && entry_hit_status(ifuPtr.value) === h_hit &&
956      io.toIfu.req.fire && !(bpu_s2_redirect && bpu_s2_resp.ftq_idx === ifuPtr) && !(bpu_s3_redirect && bpu_s3_resp.ftq_idx === ifuPtr)
957  )
958
959  val ifu_req_should_be_flushed =
960    io.toIfu.flushFromBpu.shouldFlushByStage2(io.toIfu.req.bits.ftqIdx) ||
961      io.toIfu.flushFromBpu.shouldFlushByStage3(io.toIfu.req.bits.ftqIdx)
962
963  when(io.toIfu.req.fire && !ifu_req_should_be_flushed) {
964    entry_fetch_status(ifuPtr.value) := f_sent
965  }
966
967  // *********************************************************************
968  // **************************** wb from ifu ****************************
969  // *********************************************************************
970  val pdWb         = io.fromIfu.pdWb
971  val pds          = pdWb.bits.pd
972  val ifu_wb_valid = pdWb.valid
973  val ifu_wb_idx   = pdWb.bits.ftqIdx.value
974  // read ports:                                                         commit update
975  val ftq_pd_mem =
976    Module(new SyncDataModuleTemplate(new Ftq_pd_Entry, FtqSize, FtqRedirectAheadNum + 1, 1, hasRen = true))
977  ftq_pd_mem.io.wen(0)   := ifu_wb_valid
978  ftq_pd_mem.io.waddr(0) := pdWb.bits.ftqIdx.value
979  ftq_pd_mem.io.wdata(0).fromPdWb(pdWb.bits)
980
981  val hit_pd_valid       = entry_hit_status(ifu_wb_idx) === h_hit && ifu_wb_valid
982  val hit_pd_mispred     = hit_pd_valid && pdWb.bits.misOffset.valid
983  val hit_pd_mispred_reg = RegNext(hit_pd_mispred, init = false.B)
984  val pd_reg             = RegEnable(pds, pdWb.valid)
985  val start_pc_reg       = RegEnable(pdWb.bits.pc(0), pdWb.valid)
986  val wb_idx_reg         = RegEnable(ifu_wb_idx, pdWb.valid)
987
988  when(ifu_wb_valid) {
989    val comm_stq_wen = VecInit(pds.map(_.valid).zip(pdWb.bits.instrRange).map {
990      case (v, inRange) => v && inRange
991    })
992    commitStateQueueEnable(ifu_wb_idx) := true.B
993    (commitStateQueueNext(ifu_wb_idx) zip comm_stq_wen).map {
994      case (qe, v) => when(v) {
995          qe := c_toCommit
996        }
997    }
998  }
999
1000  when(ifu_wb_valid) {
1001    ifuWbPtr_write := ifuWbPtr + 1.U
1002  }
1003
1004  XSError(ifu_wb_valid && isAfter(pdWb.bits.ftqIdx, ifuPtr), "IFU returned a predecode before its req, check IFU")
1005
1006  ftb_entry_mem.io.ren.get.head := ifu_wb_valid
1007  ftb_entry_mem.io.raddr.head   := ifu_wb_idx
1008  val has_false_hit = WireInit(false.B)
1009  when(RegNext(hit_pd_valid)) {
1010    // check for false hit
1011    val pred_ftb_entry = ftb_entry_mem.io.rdata.head
1012    val brSlots        = pred_ftb_entry.brSlots
1013    val tailSlot       = pred_ftb_entry.tailSlot
1014    // we check cfis that bpu predicted
1015
1016    // bpu predicted branches but denied by predecode
1017    val br_false_hit =
1018      brSlots.map {
1019        s => s.valid && !(pd_reg(s.offset).valid && pd_reg(s.offset).isBr)
1020      }.reduce(_ || _) ||
1021        (tailSlot.valid && pred_ftb_entry.tailSlot.sharing &&
1022          !(pd_reg(tailSlot.offset).valid && pd_reg(tailSlot.offset).isBr))
1023
1024    val jmpOffset = tailSlot.offset
1025    val jmp_pd    = pd_reg(jmpOffset)
1026    val jal_false_hit = pred_ftb_entry.jmpValid &&
1027      ((pred_ftb_entry.isJal && !(jmp_pd.valid && jmp_pd.isJal)) ||
1028        (pred_ftb_entry.isJalr && !(jmp_pd.valid && jmp_pd.isJalr)) ||
1029        (pred_ftb_entry.isCall && !(jmp_pd.valid && jmp_pd.isCall)) ||
1030        (pred_ftb_entry.isRet && !(jmp_pd.valid && jmp_pd.isRet)))
1031
1032    has_false_hit := br_false_hit || jal_false_hit || hit_pd_mispred_reg
1033    XSDebug(has_false_hit, "FTB false hit by br or jal or hit_pd, startAddr: %x\n", pdWb.bits.pc(0))
1034
1035    // assert(!has_false_hit)
1036  }
1037
1038  when(has_false_hit) {
1039    entry_hit_status(wb_idx_reg) := h_false_hit
1040  }
1041
1042  // *******************************************************************************
1043  // **************************** redirect from backend ****************************
1044  // *******************************************************************************
1045
1046  // redirect read cfiInfo, couples to redirectGen s2
1047  // ftqIdxAhead(0-3) => ftq_redirect_mem(1-4), reuse ftq_redirect_mem(1)
1048  val ftq_redirect_rdata = Wire(Vec(FtqRedirectAheadNum, new Ftq_Redirect_SRAMEntry))
1049  val ftb_redirect_rdata = Wire(Vec(FtqRedirectAheadNum, new FTBEntry_FtqMem))
1050
1051  val ftq_pd_rdata = Wire(Vec(FtqRedirectAheadNum, new Ftq_pd_Entry))
1052  for (i <- 1 until FtqRedirectAheadNum) {
1053    ftq_redirect_mem.io.ren.get(i + IfuRedirectNum) := ftqIdxAhead(i).valid
1054    ftq_redirect_mem.io.raddr(i + IfuRedirectNum)   := ftqIdxAhead(i).bits.value
1055    ftb_entry_mem.io.ren.get(i + IfuRedirectNum)    := ftqIdxAhead(i).valid
1056    ftb_entry_mem.io.raddr(i + IfuRedirectNum)      := ftqIdxAhead(i).bits.value
1057
1058    ftq_pd_mem.io.ren.get(i) := ftqIdxAhead(i).valid
1059    ftq_pd_mem.io.raddr(i)   := ftqIdxAhead(i).bits.value
1060  }
1061  ftq_redirect_mem.io.ren.get(IfuRedirectNum) := Mux(aheadValid, ftqIdxAhead(0).valid, backendRedirect.valid)
1062  ftq_redirect_mem.io.raddr(IfuRedirectNum) := Mux(
1063    aheadValid,
1064    ftqIdxAhead(0).bits.value,
1065    backendRedirect.bits.ftqIdx.value
1066  )
1067  ftb_entry_mem.io.ren.get(IfuRedirectNum) := Mux(aheadValid, ftqIdxAhead(0).valid, backendRedirect.valid)
1068  ftb_entry_mem.io.raddr(IfuRedirectNum) := Mux(
1069    aheadValid,
1070    ftqIdxAhead(0).bits.value,
1071    backendRedirect.bits.ftqIdx.value
1072  )
1073
1074  ftq_pd_mem.io.ren.get(0) := Mux(aheadValid, ftqIdxAhead(0).valid, backendRedirect.valid)
1075  ftq_pd_mem.io.raddr(0)   := Mux(aheadValid, ftqIdxAhead(0).bits.value, backendRedirect.bits.ftqIdx.value)
1076
1077  for (i <- 0 until FtqRedirectAheadNum) {
1078    ftq_redirect_rdata(i) := ftq_redirect_mem.io.rdata(i + IfuRedirectNum)
1079    ftb_redirect_rdata(i) := ftb_entry_mem.io.rdata(i + IfuRedirectNum)
1080
1081    ftq_pd_rdata(i) := ftq_pd_mem.io.rdata(i)
1082  }
1083  val stage3CfiInfo =
1084    Mux(realAhdValid, Mux1H(ftqIdxSelOH, ftq_redirect_rdata), ftq_redirect_mem.io.rdata(IfuRedirectNum))
1085  val stage3PdInfo       = Mux(realAhdValid, Mux1H(ftqIdxSelOH, ftq_pd_rdata), ftq_pd_mem.io.rdata(0))
1086  val backendRedirectCfi = fromBackendRedirect.bits.cfiUpdate
1087  backendRedirectCfi.fromFtqRedirectSram(stage3CfiInfo)
1088  backendRedirectCfi.pd := stage3PdInfo.toPd(fromBackendRedirect.bits.ftqOffset)
1089
1090  val r_ftb_entry = Mux(realAhdValid, Mux1H(ftqIdxSelOH, ftb_redirect_rdata), ftb_entry_mem.io.rdata(IfuRedirectNum))
1091  val r_ftqOffset = fromBackendRedirect.bits.ftqOffset
1092
1093  backendRedirectCfi.br_hit := r_ftb_entry.brIsSaved(r_ftqOffset)
1094  backendRedirectCfi.jr_hit := r_ftb_entry.isJalr && r_ftb_entry.tailSlot.offset === r_ftqOffset
1095  // FIXME: not portable
1096  val sc_disagree = stage3CfiInfo.sc_disagree.getOrElse(VecInit(Seq.fill(numBr)(false.B)))
1097  backendRedirectCfi.sc_hit := backendRedirectCfi.br_hit && Mux(
1098    r_ftb_entry.brSlots(0).offset === r_ftqOffset,
1099    sc_disagree(0),
1100    sc_disagree(1)
1101  )
1102
1103  when(entry_hit_status(fromBackendRedirect.bits.ftqIdx.value) === h_hit) {
1104    backendRedirectCfi.shift := PopCount(r_ftb_entry.getBrMaskByOffset(r_ftqOffset)) +&
1105      (backendRedirectCfi.pd.isBr && !r_ftb_entry.brIsSaved(r_ftqOffset) &&
1106        !r_ftb_entry.newBrCanNotInsert(r_ftqOffset))
1107
1108    backendRedirectCfi.addIntoHist := backendRedirectCfi.pd.isBr && (r_ftb_entry.brIsSaved(r_ftqOffset) ||
1109      !r_ftb_entry.newBrCanNotInsert(r_ftqOffset))
1110  }.otherwise {
1111    backendRedirectCfi.shift       := (backendRedirectCfi.pd.isBr && backendRedirectCfi.taken).asUInt
1112    backendRedirectCfi.addIntoHist := backendRedirectCfi.pd.isBr.asUInt
1113  }
1114
1115  // ***************************************************************************
1116  // **************************** redirect from ifu ****************************
1117  // ***************************************************************************
1118  val fromIfuRedirect = WireInit(0.U.asTypeOf(Valid(new BranchPredictionRedirect)))
1119  fromIfuRedirect.valid              := pdWb.valid && pdWb.bits.misOffset.valid && !backendFlush
1120  fromIfuRedirect.bits.ftqIdx        := pdWb.bits.ftqIdx
1121  fromIfuRedirect.bits.ftqOffset     := pdWb.bits.misOffset.bits
1122  fromIfuRedirect.bits.level         := RedirectLevel.flushAfter
1123  fromIfuRedirect.bits.BTBMissBubble := true.B
1124  fromIfuRedirect.bits.debugIsMemVio := false.B
1125  fromIfuRedirect.bits.debugIsCtrl   := false.B
1126
1127  val ifuRedirectCfiUpdate = fromIfuRedirect.bits.cfiUpdate
1128  ifuRedirectCfiUpdate.pc        := pdWb.bits.pc(pdWb.bits.misOffset.bits)
1129  ifuRedirectCfiUpdate.pd        := pdWb.bits.pd(pdWb.bits.misOffset.bits)
1130  ifuRedirectCfiUpdate.predTaken := cfiIndex_vec(pdWb.bits.ftqIdx.value).valid
1131  ifuRedirectCfiUpdate.target    := pdWb.bits.target
1132  ifuRedirectCfiUpdate.taken     := pdWb.bits.cfiOffset.valid
1133  ifuRedirectCfiUpdate.isMisPred := pdWb.bits.misOffset.valid
1134
1135  val ifuRedirectReg   = RegNextWithEnable(fromIfuRedirect, hasInit = true)
1136  val ifuRedirectToBpu = WireInit(ifuRedirectReg)
1137  ifuFlush := fromIfuRedirect.valid || ifuRedirectToBpu.valid
1138
1139  ftq_redirect_mem.io.ren.get.head := fromIfuRedirect.valid
1140  ftq_redirect_mem.io.raddr.head   := fromIfuRedirect.bits.ftqIdx.value
1141
1142  val toBpuCfi = ifuRedirectToBpu.bits.cfiUpdate
1143  toBpuCfi.fromFtqRedirectSram(ftq_redirect_mem.io.rdata.head)
1144  when(ifuRedirectReg.bits.cfiUpdate.pd.isRet && ifuRedirectReg.bits.cfiUpdate.pd.valid) {
1145    toBpuCfi.target := toBpuCfi.topAddr
1146  }
1147
1148  when(ifuRedirectReg.valid) {
1149    ifuRedirected(ifuRedirectReg.bits.ftqIdx.value) := true.B
1150  }.elsewhen(RegNext(pdWb.valid)) {
1151    // if pdWb and no redirect, set to false
1152    ifuRedirected(last_cycle_bpu_in_ptr.value) := false.B
1153  }
1154
1155  // **********************************************************************
1156  // ***************************** to backend *****************************
1157  // **********************************************************************
1158  // to backend pc mem / target
1159  io.toBackend.pc_mem_wen   := RegNext(last_cycle_bpu_in)
1160  io.toBackend.pc_mem_waddr := RegEnable(last_cycle_bpu_in_idx, last_cycle_bpu_in)
1161  io.toBackend.pc_mem_wdata := RegEnable(bpu_in_bypass_buf_for_ifu, last_cycle_bpu_in)
1162
1163  // num cycle is fixed
1164  val newest_entry_en: Bool = RegNext(last_cycle_bpu_in || backendRedirect.valid || ifuRedirectToBpu.valid)
1165  io.toBackend.newest_entry_en     := RegNext(newest_entry_en)
1166  io.toBackend.newest_entry_ptr    := RegEnable(newest_entry_ptr, newest_entry_en)
1167  io.toBackend.newest_entry_target := RegEnable(newest_entry_target, newest_entry_en)
1168
1169  // *********************************************************************
1170  // **************************** wb from exu ****************************
1171  // *********************************************************************
1172
1173  backendRedirect.valid := io.fromBackend.redirect.valid
1174  backendRedirect.bits.connectRedirect(io.fromBackend.redirect.bits)
1175  backendRedirect.bits.BTBMissBubble := false.B
1176
1177  def extractRedirectInfo(wb: Valid[Redirect]) = {
1178    val ftqPtr    = wb.bits.ftqIdx
1179    val ftqOffset = wb.bits.ftqOffset
1180    val taken     = wb.bits.cfiUpdate.taken
1181    val mispred   = wb.bits.cfiUpdate.isMisPred
1182    (wb.valid, ftqPtr, ftqOffset, taken, mispred)
1183  }
1184
1185  // fix mispredict entry
1186  val lastIsMispredict = RegNext(
1187    backendRedirect.valid && backendRedirect.bits.level === RedirectLevel.flushAfter,
1188    init = false.B
1189  )
1190
1191  def updateCfiInfo(redirect: Valid[Redirect], isBackend: Boolean = true) = {
1192    val (r_valid, r_ptr, r_offset, r_taken, r_mispred) = extractRedirectInfo(redirect)
1193    val r_idx                                          = r_ptr.value
1194    val cfiIndex_bits_wen                              = r_valid && r_taken && r_offset < cfiIndex_vec(r_idx).bits
1195    val cfiIndex_valid_wen                             = r_valid && r_offset === cfiIndex_vec(r_idx).bits
1196    when(cfiIndex_bits_wen || cfiIndex_valid_wen) {
1197      cfiIndex_vec(r_idx).valid := cfiIndex_bits_wen || cfiIndex_valid_wen && r_taken
1198    }.elsewhen(r_valid && !r_taken && r_offset =/= cfiIndex_vec(r_idx).bits) {
1199      cfiIndex_vec(r_idx).valid := false.B
1200    }
1201    when(cfiIndex_bits_wen) {
1202      cfiIndex_vec(r_idx).bits := r_offset
1203    }
1204    newest_entry_target_modified := true.B
1205    newest_entry_target          := redirect.bits.cfiUpdate.target
1206    newest_entry_ptr_modified    := true.B
1207    newest_entry_ptr             := r_ptr
1208
1209    update_target(r_idx) := redirect.bits.cfiUpdate.target // TODO: remove this
1210    if (isBackend) {
1211      mispredict_vec(r_idx)(r_offset) := r_mispred
1212    }
1213  }
1214
1215  when(fromBackendRedirect.valid) {
1216    updateCfiInfo(fromBackendRedirect)
1217  }.elsewhen(ifuRedirectToBpu.valid) {
1218    updateCfiInfo(ifuRedirectToBpu, isBackend = false)
1219  }
1220
1221  when(fromBackendRedirect.valid) {
1222    when(fromBackendRedirect.bits.ControlRedirectBubble) {
1223      when(fromBackendRedirect.bits.ControlBTBMissBubble) {
1224        topdown_stage.reasons(TopDownCounters.BTBMissBubble.id)                  := true.B
1225        io.toIfu.req.bits.topdown_info.reasons(TopDownCounters.BTBMissBubble.id) := true.B
1226      }.elsewhen(fromBackendRedirect.bits.TAGEMissBubble) {
1227        topdown_stage.reasons(TopDownCounters.TAGEMissBubble.id)                  := true.B
1228        io.toIfu.req.bits.topdown_info.reasons(TopDownCounters.TAGEMissBubble.id) := true.B
1229      }.elsewhen(fromBackendRedirect.bits.SCMissBubble) {
1230        topdown_stage.reasons(TopDownCounters.SCMissBubble.id)                  := true.B
1231        io.toIfu.req.bits.topdown_info.reasons(TopDownCounters.SCMissBubble.id) := true.B
1232      }.elsewhen(fromBackendRedirect.bits.ITTAGEMissBubble) {
1233        topdown_stage.reasons(TopDownCounters.ITTAGEMissBubble.id)                  := true.B
1234        io.toIfu.req.bits.topdown_info.reasons(TopDownCounters.ITTAGEMissBubble.id) := true.B
1235      }.elsewhen(fromBackendRedirect.bits.RASMissBubble) {
1236        topdown_stage.reasons(TopDownCounters.RASMissBubble.id)                  := true.B
1237        io.toIfu.req.bits.topdown_info.reasons(TopDownCounters.RASMissBubble.id) := true.B
1238      }
1239
1240    }.elsewhen(backendRedirect.bits.MemVioRedirectBubble) {
1241      topdown_stage.reasons(TopDownCounters.MemVioRedirectBubble.id)                  := true.B
1242      io.toIfu.req.bits.topdown_info.reasons(TopDownCounters.MemVioRedirectBubble.id) := true.B
1243    }.otherwise {
1244      topdown_stage.reasons(TopDownCounters.OtherRedirectBubble.id)                  := true.B
1245      io.toIfu.req.bits.topdown_info.reasons(TopDownCounters.OtherRedirectBubble.id) := true.B
1246    }
1247  }.elsewhen(ifuRedirectReg.valid) {
1248    topdown_stage.reasons(TopDownCounters.BTBMissBubble.id)                  := true.B
1249    io.toIfu.req.bits.topdown_info.reasons(TopDownCounters.BTBMissBubble.id) := true.B
1250  }
1251
1252  io.ControlBTBMissBubble := fromBackendRedirect.bits.ControlBTBMissBubble
1253  io.TAGEMissBubble       := fromBackendRedirect.bits.TAGEMissBubble
1254  io.SCMissBubble         := fromBackendRedirect.bits.SCMissBubble
1255  io.ITTAGEMissBubble     := fromBackendRedirect.bits.ITTAGEMissBubble
1256  io.RASMissBubble        := fromBackendRedirect.bits.RASMissBubble
1257
1258  // ***********************************************************************************
1259  // **************************** flush ptr and state queue ****************************
1260  // ***********************************************************************************
1261
1262  val redirectVec = VecInit(backendRedirect, fromIfuRedirect)
1263
1264  // when redirect, we should reset ptrs and status queues
1265  io.icacheFlush := redirectVec.map(r => r.valid).reduce(_ || _)
1266  XSPerfAccumulate("icacheFlushFromBackend", backendRedirect.valid)
1267  XSPerfAccumulate("icacheFlushFromIFU", fromIfuRedirect.valid)
1268  when(redirectVec.map(r => r.valid).reduce(_ || _)) {
1269    val r                          = PriorityMux(redirectVec.map(r => r.valid -> r.bits))
1270    val notIfu                     = redirectVec.dropRight(1).map(r => r.valid).reduce(_ || _)
1271    val (idx, offset, flushItSelf) = (r.ftqIdx, r.ftqOffset, RedirectLevel.flushItself(r.level))
1272    val next                       = idx + 1.U
1273    bpuPtr := next
1274    copied_bpu_ptr.map(_ := next)
1275    ifuPtr_write      := next
1276    ifuWbPtr_write    := next
1277    ifuPtrPlus1_write := idx + 2.U
1278    ifuPtrPlus2_write := idx + 3.U
1279    pfPtr_write       := next
1280    pfPtrPlus1_write  := idx + 2.U
1281  }
1282  when(RegNext(redirectVec.map(r => r.valid).reduce(_ || _))) {
1283    val r                          = PriorityMux(redirectVec.map(r => r.valid -> r.bits))
1284    val notIfu                     = redirectVec.dropRight(1).map(r => r.valid).reduce(_ || _)
1285    val (idx, offset, flushItSelf) = (r.ftqIdx, r.ftqOffset, RedirectLevel.flushItself(r.level))
1286    when(RegNext(notIfu)) {
1287      commitStateQueueEnable(RegNext(idx.value)) := true.B
1288      commitStateQueueNext(RegNext(idx.value)).zipWithIndex.foreach { case (s, i) =>
1289        when(i.U > RegNext(offset)) {
1290          s := c_empty
1291        }
1292        when(i.U === RegNext(offset) && RegNext(flushItSelf)) {
1293          s := c_flushed
1294        }
1295      }
1296    }
1297  }
1298
1299  // only the valid bit is actually needed
1300  io.toIfu.redirect.bits    := backendRedirect.bits
1301  io.toIfu.redirect.valid   := stage2Flush
1302  io.toIfu.topdown_redirect := fromBackendRedirect
1303
1304  // commit
1305  for (c <- io.fromBackend.rob_commits) {
1306    when(c.valid) {
1307      commitStateQueueEnable(c.bits.ftqIdx.value)                 := true.B
1308      commitStateQueueNext(c.bits.ftqIdx.value)(c.bits.ftqOffset) := c_committed
1309      // TODO: remove this
1310      // For instruction fusions, we also update the next instruction
1311      when(c.bits.commitType === 4.U) {
1312        commitStateQueueNext(c.bits.ftqIdx.value)(c.bits.ftqOffset + 1.U) := c_committed
1313      }.elsewhen(c.bits.commitType === 5.U) {
1314        commitStateQueueNext(c.bits.ftqIdx.value)(c.bits.ftqOffset + 2.U) := c_committed
1315      }.elsewhen(c.bits.commitType === 6.U) {
1316        val index = (c.bits.ftqIdx + 1.U).value
1317        commitStateQueueEnable(index)  := true.B
1318        commitStateQueueNext(index)(0) := c_committed
1319      }.elsewhen(c.bits.commitType === 7.U) {
1320        val index = (c.bits.ftqIdx + 1.U).value
1321        commitStateQueueEnable(index)  := true.B
1322        commitStateQueueNext(index)(1) := c_committed
1323      }
1324    }
1325  }
1326
1327  // ****************************************************************
1328  // **************************** to bpu ****************************
1329  // ****************************************************************
1330
1331  io.toBpu.redirctFromIFU := ifuRedirectToBpu.valid
1332  io.toBpu.redirect       := Mux(fromBackendRedirect.valid, fromBackendRedirect, ifuRedirectToBpu)
1333  val dummy_s1_pred_cycle_vec = VecInit(List.tabulate(FtqSize)(_ => 0.U(64.W)))
1334  val redirect_latency =
1335    GTimer() - pred_s1_cycle.getOrElse(dummy_s1_pred_cycle_vec)(io.toBpu.redirect.bits.ftqIdx.value) + 1.U
1336  XSPerfHistogram("backend_redirect_latency", redirect_latency, fromBackendRedirect.valid, 0, 60, 1)
1337  XSPerfHistogram(
1338    "ifu_redirect_latency",
1339    redirect_latency,
1340    !fromBackendRedirect.valid && ifuRedirectToBpu.valid,
1341    0,
1342    60,
1343    1
1344  )
1345
1346  XSError(
1347    io.toBpu.redirect.valid && isBefore(io.toBpu.redirect.bits.ftqIdx, commPtr),
1348    "Ftq received a redirect after its commit, check backend or replay"
1349  )
1350
1351  val may_have_stall_from_bpu = Wire(Bool())
1352  val bpu_ftb_update_stall    = RegInit(0.U(2.W)) // 2-cycle stall, so we need 3 states
1353  may_have_stall_from_bpu := bpu_ftb_update_stall =/= 0.U
1354
1355  val validInstructions       = commitStateQueueReg(commPtr.value).map(s => s === c_toCommit || s === c_committed)
1356  val lastInstructionStatus   = PriorityMux(validInstructions.reverse.zip(commitStateQueueReg(commPtr.value).reverse))
1357  val firstInstructionFlushed = commitStateQueueReg(commPtr.value)(0) === c_flushed
1358  canCommit := commPtr =/= ifuWbPtr && !may_have_stall_from_bpu &&
1359    (isAfter(robCommPtr, commPtr) ||
1360      validInstructions.reduce(_ || _) && lastInstructionStatus === c_committed)
1361  val canMoveCommPtr = commPtr =/= ifuWbPtr && !may_have_stall_from_bpu &&
1362    (isAfter(robCommPtr, commPtr) ||
1363      validInstructions.reduce(_ || _) && lastInstructionStatus === c_committed ||
1364      firstInstructionFlushed)
1365
1366  when(io.fromBackend.rob_commits.map(_.valid).reduce(_ | _)) {
1367    robCommPtr_write := ParallelPriorityMux(
1368      io.fromBackend.rob_commits.map(_.valid).reverse,
1369      io.fromBackend.rob_commits.map(_.bits.ftqIdx).reverse
1370    )
1371  }.elsewhen(isAfter(commPtr, robCommPtr)) {
1372    robCommPtr_write := commPtr
1373  }.otherwise {
1374    robCommPtr_write := robCommPtr
1375  }
1376
1377  /**
1378    *************************************************************************************
1379    * MMIO instruction fetch is allowed only if MMIO is the oldest instruction.
1380    *************************************************************************************
1381    */
1382  val mmioReadPtr = io.mmioCommitRead.mmioFtqPtr
1383  val mmioLastCommit = isAfter(commPtr, mmioReadPtr) ||
1384    commPtr === mmioReadPtr && validInstructions.reduce(_ || _) && lastInstructionStatus === c_committed
1385  io.mmioCommitRead.mmioLastCommit := RegNext(mmioLastCommit)
1386
1387  // commit reads
1388  val commit_pc_bundle = RegNext(ftq_pc_mem.io.commPtr_rdata)
1389  val commit_target =
1390    Mux(
1391      RegNext(commPtr === newest_entry_ptr),
1392      RegEnable(newest_entry_target, newest_entry_target_modified),
1393      RegNext(ftq_pc_mem.io.commPtrPlus1_rdata.startAddr)
1394    )
1395  ftq_pd_mem.io.ren.get.last := canCommit
1396  ftq_pd_mem.io.raddr.last   := commPtr.value
1397  val commit_pd = ftq_pd_mem.io.rdata.last
1398  ftq_redirect_mem.io.ren.get.last := canCommit
1399  ftq_redirect_mem.io.raddr.last   := commPtr.value
1400  val commit_spec_meta = ftq_redirect_mem.io.rdata.last
1401  ftq_meta_1r_sram.io.ren(0)   := canCommit
1402  ftq_meta_1r_sram.io.raddr(0) := commPtr.value
1403  val commit_meta      = ftq_meta_1r_sram.io.rdata(0).meta
1404  val commit_ftb_entry = ftq_meta_1r_sram.io.rdata(0).ftb_entry
1405
1406  // need one cycle to read mem and srams
1407  val do_commit_ptr = RegEnable(commPtr, canCommit)
1408  val do_commit     = RegNext(canCommit, init = false.B)
1409  when(canMoveCommPtr) {
1410    commPtr_write      := commPtrPlus1
1411    commPtrPlus1_write := commPtrPlus1 + 1.U
1412  }
1413  val commit_state   = RegEnable(commitStateQueueReg(commPtr.value), canCommit)
1414  val can_commit_cfi = WireInit(cfiIndex_vec(commPtr.value))
1415  val do_commit_cfi  = WireInit(cfiIndex_vec(do_commit_ptr.value))
1416  //
1417  // when (commitStateQueue(commPtr.value)(can_commit_cfi.bits) =/= c_commited) {
1418  //  can_commit_cfi.valid := false.B
1419  // }
1420  val commit_cfi = RegEnable(can_commit_cfi, canCommit)
1421  val debug_cfi  = commitStateQueueReg(do_commit_ptr.value)(do_commit_cfi.bits) =/= c_committed && do_commit_cfi.valid
1422
1423  val commit_mispredict: Vec[Bool] =
1424    VecInit((RegEnable(mispredict_vec(commPtr.value), canCommit) zip commit_state).map {
1425      case (mis, state) => mis && state === c_committed
1426    })
1427  val commit_instCommited: Vec[Bool] = VecInit(commit_state.map(_ === c_committed)) // [PredictWidth]
1428  val can_commit_hit     = entry_hit_status(commPtr.value)
1429  val commit_hit         = RegEnable(can_commit_hit, canCommit)
1430  val diff_commit_target = RegEnable(update_target(commPtr.value), canCommit) // TODO: remove this
1431  val commit_stage       = RegEnable(pred_stage(commPtr.value), canCommit)
1432  val commit_valid       = commit_hit === h_hit || commit_cfi.valid           // hit or taken
1433
1434  val to_bpu_hit = can_commit_hit === h_hit || can_commit_hit === h_false_hit
1435  switch(bpu_ftb_update_stall) {
1436    is(0.U) {
1437      when(can_commit_cfi.valid && !to_bpu_hit && canCommit) {
1438        bpu_ftb_update_stall := 2.U // 2-cycle stall
1439      }
1440    }
1441    is(2.U) {
1442      bpu_ftb_update_stall := 1.U
1443    }
1444    is(1.U) {
1445      bpu_ftb_update_stall := 0.U
1446    }
1447    is(3.U) {
1448      XSError(true.B, "bpu_ftb_update_stall should be 0, 1 or 2")
1449    }
1450  }
1451
1452  // TODO: remove this
1453  XSError(do_commit && diff_commit_target =/= commit_target, "\ncommit target should be the same as update target\n")
1454
1455  // update latency stats
1456  val update_latency = GTimer() - pred_s1_cycle.getOrElse(dummy_s1_pred_cycle_vec)(do_commit_ptr.value) + 1.U
1457  XSPerfHistogram("bpu_update_latency", update_latency, io.toBpu.update.valid, 0, 64, 2)
1458
1459  io.toBpu.update       := DontCare
1460  io.toBpu.update.valid := commit_valid && do_commit
1461  val update = io.toBpu.update.bits
1462  update.false_hit   := commit_hit === h_false_hit
1463  update.pc          := commit_pc_bundle.startAddr
1464  update.meta        := commit_meta
1465  update.cfi_idx     := commit_cfi
1466  update.full_target := commit_target
1467  update.from_stage  := commit_stage
1468  update.spec_info   := commit_spec_meta
1469  XSError(commit_valid && do_commit && debug_cfi, "\ncommit cfi can be non c_commited\n")
1470
1471  val commit_real_hit  = commit_hit === h_hit
1472  val update_ftb_entry = update.ftb_entry
1473
1474  val ftbEntryGen = Module(new FTBEntryGen).io
1475  ftbEntryGen.start_addr     := commit_pc_bundle.startAddr
1476  ftbEntryGen.old_entry      := commit_ftb_entry
1477  ftbEntryGen.pd             := commit_pd
1478  ftbEntryGen.cfiIndex       := commit_cfi
1479  ftbEntryGen.target         := commit_target
1480  ftbEntryGen.hit            := commit_real_hit
1481  ftbEntryGen.mispredict_vec := commit_mispredict
1482
1483  update_ftb_entry         := ftbEntryGen.new_entry
1484  update.new_br_insert_pos := ftbEntryGen.new_br_insert_pos
1485  update.mispred_mask      := ftbEntryGen.mispred_mask
1486  update.old_entry         := ftbEntryGen.is_old_entry
1487  update.pred_hit          := commit_hit === h_hit || commit_hit === h_false_hit
1488  update.br_taken_mask     := ftbEntryGen.taken_mask
1489  update.br_committed := (ftbEntryGen.new_entry.brValids zip ftbEntryGen.new_entry.brOffset) map {
1490    case (valid, offset) => valid && commit_instCommited(offset)
1491  }
1492  update.jmp_taken := ftbEntryGen.jmp_taken
1493
1494  // update.full_pred.fromFtbEntry(ftbEntryGen.new_entry, update.pc)
1495  // update.full_pred.jalr_target := commit_target
1496  // update.full_pred.hit := true.B
1497  // when (update.full_pred.is_jalr) {
1498  //   update.full_pred.targets.last := commit_target
1499  // }
1500
1501  // ******************************************************************************
1502  // **************************** commit perf counters ****************************
1503  // ******************************************************************************
1504
1505  val commit_inst_mask        = VecInit(commit_state.map(c => c === c_committed && do_commit)).asUInt
1506  val commit_mispred_mask     = commit_mispredict.asUInt
1507  val commit_not_mispred_mask = ~commit_mispred_mask
1508
1509  val commit_br_mask  = commit_pd.brMask.asUInt
1510  val commit_jmp_mask = UIntToOH(commit_pd.jmpOffset) & Fill(PredictWidth, commit_pd.jmpInfo.valid.asTypeOf(UInt(1.W)))
1511  val commit_cfi_mask = commit_br_mask | commit_jmp_mask
1512
1513  val mbpInstrs = commit_inst_mask & commit_cfi_mask
1514
1515  val mbpRights = mbpInstrs & commit_not_mispred_mask
1516  val mbpWrongs = mbpInstrs & commit_mispred_mask
1517
1518  io.bpuInfo.bpRight := PopCount(mbpRights)
1519  io.bpuInfo.bpWrong := PopCount(mbpWrongs)
1520
1521  val hartId           = p(XSCoreParamsKey).HartId
1522  val isWriteFTQTable  = Constantin.createRecord(s"isWriteFTQTable$hartId")
1523  val ftqBranchTraceDB = ChiselDB.createTable(s"FTQTable$hartId", new FtqDebugBundle)
1524  // Cfi Info
1525  for (i <- 0 until PredictWidth) {
1526    val pc      = commit_pc_bundle.startAddr + (i * instBytes).U
1527    val v       = commit_state(i) === c_committed
1528    val isBr    = commit_pd.brMask(i)
1529    val isJmp   = commit_pd.jmpInfo.valid && commit_pd.jmpOffset === i.U
1530    val isCfi   = isBr || isJmp
1531    val isTaken = commit_cfi.valid && commit_cfi.bits === i.U
1532    val misPred = commit_mispredict(i)
1533    // val ghist = commit_spec_meta.ghist.predHist
1534    val histPtr   = commit_spec_meta.histPtr
1535    val predCycle = commit_meta(63, 0)
1536    val target    = commit_target
1537
1538    val brIdx = OHToUInt(Reverse(Cat(update_ftb_entry.brValids.zip(update_ftb_entry.brOffset).map { case (v, offset) =>
1539      v && offset === i.U
1540    })))
1541    val inFtbEntry = update_ftb_entry.brValids.zip(update_ftb_entry.brOffset).map { case (v, offset) =>
1542      v && offset === i.U
1543    }.reduce(_ || _)
1544    val addIntoHist =
1545      ((commit_hit === h_hit) && inFtbEntry) || (!(commit_hit === h_hit) && i.U === commit_cfi.bits && isBr && commit_cfi.valid)
1546    XSDebug(
1547      v && do_commit && isCfi,
1548      p"cfi_update: isBr(${isBr}) pc(${Hexadecimal(pc)}) " +
1549        p"taken(${isTaken}) mispred(${misPred}) cycle($predCycle) hist(${histPtr.value}) " +
1550        p"startAddr(${Hexadecimal(commit_pc_bundle.startAddr)}) AddIntoHist(${addIntoHist}) " +
1551        p"brInEntry(${inFtbEntry}) brIdx(${brIdx}) target(${Hexadecimal(target)})\n"
1552    )
1553
1554    val logbundle = Wire(new FtqDebugBundle)
1555    logbundle.pc        := pc
1556    logbundle.target    := target
1557    logbundle.isBr      := isBr
1558    logbundle.isJmp     := isJmp
1559    logbundle.isCall    := isJmp && commit_pd.hasCall
1560    logbundle.isRet     := isJmp && commit_pd.hasRet
1561    logbundle.misPred   := misPred
1562    logbundle.isTaken   := isTaken
1563    logbundle.predStage := commit_stage
1564
1565    ftqBranchTraceDB.log(
1566      data = logbundle /* hardware of type T */,
1567      en = isWriteFTQTable.orR && v && do_commit && isCfi,
1568      site = "FTQ" + p(XSCoreParamsKey).HartId.toString,
1569      clock = clock,
1570      reset = reset
1571    )
1572  }
1573
1574  val enq           = io.fromBpu.resp
1575  val perf_redirect = backendRedirect
1576
1577  XSPerfAccumulate("entry", validEntries)
1578  XSPerfAccumulate("bpu_to_ftq_stall", enq.valid && !enq.ready)
1579  XSPerfAccumulate("mispredictRedirect", perf_redirect.valid && RedirectLevel.flushAfter === perf_redirect.bits.level)
1580  XSPerfAccumulate("replayRedirect", perf_redirect.valid && RedirectLevel.flushItself(perf_redirect.bits.level))
1581  XSPerfAccumulate("predecodeRedirect", fromIfuRedirect.valid)
1582
1583  XSPerfAccumulate("to_ifu_bubble", io.toIfu.req.ready && !io.toIfu.req.valid)
1584
1585  XSPerfAccumulate("to_ifu_stall", io.toIfu.req.valid && !io.toIfu.req.ready)
1586  XSPerfAccumulate("from_bpu_real_bubble", !enq.valid && enq.ready && allowBpuIn)
1587  XSPerfAccumulate("bpu_to_ifu_bubble", bpuPtr === ifuPtr)
1588  XSPerfAccumulate(
1589    "bpu_to_ifu_bubble_when_ftq_full",
1590    (bpuPtr === ifuPtr) && isFull(bpuPtr, commPtr) && io.toIfu.req.ready
1591  )
1592
1593  XSPerfAccumulate("redirectAhead_ValidNum", ftqIdxAhead.map(_.valid).reduce(_ | _))
1594  XSPerfAccumulate("fromBackendRedirect_ValidNum", io.fromBackend.redirect.valid)
1595  XSPerfAccumulate("toBpuRedirect_ValidNum", io.toBpu.redirect.valid)
1596
1597  val from_bpu = io.fromBpu.resp.bits
1598  val to_ifu   = io.toIfu.req.bits
1599
1600  XSPerfHistogram("commit_num_inst", PopCount(commit_inst_mask), do_commit, 0, PredictWidth + 1, 1)
1601
1602  val commit_jal_mask  = UIntToOH(commit_pd.jmpOffset) & Fill(PredictWidth, commit_pd.hasJal.asTypeOf(UInt(1.W)))
1603  val commit_jalr_mask = UIntToOH(commit_pd.jmpOffset) & Fill(PredictWidth, commit_pd.hasJalr.asTypeOf(UInt(1.W)))
1604  val commit_call_mask = UIntToOH(commit_pd.jmpOffset) & Fill(PredictWidth, commit_pd.hasCall.asTypeOf(UInt(1.W)))
1605  val commit_ret_mask  = UIntToOH(commit_pd.jmpOffset) & Fill(PredictWidth, commit_pd.hasRet.asTypeOf(UInt(1.W)))
1606
1607  val mbpBRights = mbpRights & commit_br_mask
1608  val mbpJRights = mbpRights & commit_jal_mask
1609  val mbpIRights = mbpRights & commit_jalr_mask
1610  val mbpCRights = mbpRights & commit_call_mask
1611  val mbpRRights = mbpRights & commit_ret_mask
1612
1613  val mbpBWrongs = mbpWrongs & commit_br_mask
1614  val mbpJWrongs = mbpWrongs & commit_jal_mask
1615  val mbpIWrongs = mbpWrongs & commit_jalr_mask
1616  val mbpCWrongs = mbpWrongs & commit_call_mask
1617  val mbpRWrongs = mbpWrongs & commit_ret_mask
1618
1619  val commit_pred_stage = RegNext(pred_stage(commPtr.value))
1620
1621  def pred_stage_map(src: UInt, name: String) =
1622    (0 until numBpStages).map(i =>
1623      f"${name}_stage_${i + 1}" -> PopCount(src.asBools.map(_ && commit_pred_stage === BP_STAGES(i)))
1624    ).foldLeft(Map[String, UInt]())(_ + _)
1625
1626  val mispred_stage_map      = pred_stage_map(mbpWrongs, "mispredict")
1627  val br_mispred_stage_map   = pred_stage_map(mbpBWrongs, "br_mispredict")
1628  val jalr_mispred_stage_map = pred_stage_map(mbpIWrongs, "jalr_mispredict")
1629  val correct_stage_map      = pred_stage_map(mbpRights, "correct")
1630  val br_correct_stage_map   = pred_stage_map(mbpBRights, "br_correct")
1631  val jalr_correct_stage_map = pred_stage_map(mbpIRights, "jalr_correct")
1632
1633  val update_valid = io.toBpu.update.valid
1634  def u(cond: Bool) = update_valid && cond
1635  val ftb_false_hit = u(update.false_hit)
1636  // assert(!ftb_false_hit)
1637  val ftb_hit = u(commit_hit === h_hit)
1638
1639  val ftb_new_entry                = u(ftbEntryGen.is_init_entry)
1640  val ftb_new_entry_only_br        = ftb_new_entry && !update_ftb_entry.jmpValid
1641  val ftb_new_entry_only_jmp       = ftb_new_entry && !update_ftb_entry.brValids(0)
1642  val ftb_new_entry_has_br_and_jmp = ftb_new_entry && update_ftb_entry.brValids(0) && update_ftb_entry.jmpValid
1643
1644  val ftb_old_entry = u(ftbEntryGen.is_old_entry)
1645
1646  val ftb_modified_entry =
1647    u(ftbEntryGen.is_new_br || ftbEntryGen.is_jalr_target_modified || ftbEntryGen.is_strong_bias_modified)
1648  val ftb_modified_entry_new_br               = u(ftbEntryGen.is_new_br)
1649  val ftb_modified_entry_ifu_redirected       = u(ifuRedirected(do_commit_ptr.value))
1650  val ftb_modified_entry_jalr_target_modified = u(ftbEntryGen.is_jalr_target_modified)
1651  val ftb_modified_entry_br_full              = ftb_modified_entry && ftbEntryGen.is_br_full
1652  val ftb_modified_entry_strong_bias          = ftb_modified_entry && ftbEntryGen.is_strong_bias_modified
1653
1654  def getFtbEntryLen(pc: UInt, entry: FTBEntry) = (entry.getFallThrough(pc) - pc) >> instOffsetBits
1655  val gen_ftb_entry_len = getFtbEntryLen(update.pc, ftbEntryGen.new_entry)
1656  XSPerfHistogram("ftb_init_entry_len", gen_ftb_entry_len, ftb_new_entry, 0, PredictWidth + 1, 1)
1657  XSPerfHistogram("ftb_modified_entry_len", gen_ftb_entry_len, ftb_modified_entry, 0, PredictWidth + 1, 1)
1658  val s3_ftb_entry_len = getFtbEntryLen(from_bpu.s3.pc(0), from_bpu.last_stage_ftb_entry)
1659  XSPerfHistogram("s3_ftb_entry_len", s3_ftb_entry_len, from_bpu.s3.valid(0), 0, PredictWidth + 1, 1)
1660
1661  XSPerfHistogram("ftq_has_entry", validEntries, true.B, 0, FtqSize + 1, 1)
1662
1663  val perfCountsMap = Map(
1664    "BpInstr"                        -> PopCount(mbpInstrs),
1665    "BpBInstr"                       -> PopCount(mbpBRights | mbpBWrongs),
1666    "BpRight"                        -> PopCount(mbpRights),
1667    "BpWrong"                        -> PopCount(mbpWrongs),
1668    "BpBRight"                       -> PopCount(mbpBRights),
1669    "BpBWrong"                       -> PopCount(mbpBWrongs),
1670    "BpJRight"                       -> PopCount(mbpJRights),
1671    "BpJWrong"                       -> PopCount(mbpJWrongs),
1672    "BpIRight"                       -> PopCount(mbpIRights),
1673    "BpIWrong"                       -> PopCount(mbpIWrongs),
1674    "BpCRight"                       -> PopCount(mbpCRights),
1675    "BpCWrong"                       -> PopCount(mbpCWrongs),
1676    "BpRRight"                       -> PopCount(mbpRRights),
1677    "BpRWrong"                       -> PopCount(mbpRWrongs),
1678    "ftb_false_hit"                  -> PopCount(ftb_false_hit),
1679    "ftb_hit"                        -> PopCount(ftb_hit),
1680    "ftb_new_entry"                  -> PopCount(ftb_new_entry),
1681    "ftb_new_entry_only_br"          -> PopCount(ftb_new_entry_only_br),
1682    "ftb_new_entry_only_jmp"         -> PopCount(ftb_new_entry_only_jmp),
1683    "ftb_new_entry_has_br_and_jmp"   -> PopCount(ftb_new_entry_has_br_and_jmp),
1684    "ftb_old_entry"                  -> PopCount(ftb_old_entry),
1685    "ftb_modified_entry"             -> PopCount(ftb_modified_entry),
1686    "ftb_modified_entry_new_br"      -> PopCount(ftb_modified_entry_new_br),
1687    "ftb_jalr_target_modified"       -> PopCount(ftb_modified_entry_jalr_target_modified),
1688    "ftb_modified_entry_br_full"     -> PopCount(ftb_modified_entry_br_full),
1689    "ftb_modified_entry_strong_bias" -> PopCount(ftb_modified_entry_strong_bias)
1690  ) ++ mispred_stage_map ++ br_mispred_stage_map ++ jalr_mispred_stage_map ++
1691    correct_stage_map ++ br_correct_stage_map ++ jalr_correct_stage_map
1692
1693  for ((key, value) <- perfCountsMap) {
1694    XSPerfAccumulate(key, value)
1695  }
1696
1697  // --------------------------- Debug --------------------------------
1698  // XSDebug(enq_fire, p"enq! " + io.fromBpu.resp.bits.toPrintable)
1699  XSDebug(io.toIfu.req.fire, p"fire to ifu " + io.toIfu.req.bits.toPrintable)
1700  XSDebug(do_commit, p"deq! [ptr] $do_commit_ptr\n")
1701  XSDebug(true.B, p"[bpuPtr] $bpuPtr, [ifuPtr] $ifuPtr, [ifuWbPtr] $ifuWbPtr [commPtr] $commPtr\n")
1702  XSDebug(
1703    true.B,
1704    p"[in] v:${io.fromBpu.resp.valid} r:${io.fromBpu.resp.ready} " +
1705      p"[out] v:${io.toIfu.req.valid} r:${io.toIfu.req.ready}\n"
1706  )
1707  XSDebug(do_commit, p"[deq info] cfiIndex: $commit_cfi, $commit_pc_bundle, target: ${Hexadecimal(commit_target)}\n")
1708
1709  //   def ubtbCheck(commit: FtqEntry, predAns: Seq[PredictorAnswer], isWrong: Bool) = {
1710  //     commit.valids.zip(commit.pd).zip(predAns).zip(commit.takens).map {
1711  //       case (((valid, pd), ans), taken) =>
1712  //       Mux(valid && pd.isBr,
1713  //         isWrong ^ Mux(ans.hit.asBool,
1714  //           Mux(ans.taken.asBool, taken && ans.target === commitEntry.target,
1715  //           !taken),
1716  //         !taken),
1717  //       false.B)
1718  //     }
1719  //   }
1720
1721  //   def btbCheck(commit: FtqEntry, predAns: Seq[PredictorAnswer], isWrong: Bool) = {
1722  //     commit.valids.zip(commit.pd).zip(predAns).zip(commit.takens).map {
1723  //       case (((valid, pd), ans), taken) =>
1724  //       Mux(valid && pd.isBr,
1725  //         isWrong ^ Mux(ans.hit.asBool,
1726  //           Mux(ans.taken.asBool, taken && ans.target === commitEntry.target,
1727  //           !taken),
1728  //         !taken),
1729  //       false.B)
1730  //     }
1731  //   }
1732
1733  //   def tageCheck(commit: FtqEntry, predAns: Seq[PredictorAnswer], isWrong: Bool) = {
1734  //     commit.valids.zip(commit.pd).zip(predAns).zip(commit.takens).map {
1735  //       case (((valid, pd), ans), taken) =>
1736  //       Mux(valid && pd.isBr,
1737  //         isWrong ^ (ans.taken.asBool === taken),
1738  //       false.B)
1739  //     }
1740  //   }
1741
1742  //   def loopCheck(commit: FtqEntry, predAns: Seq[PredictorAnswer], isWrong: Bool) = {
1743  //     commit.valids.zip(commit.pd).zip(predAns).zip(commit.takens).map {
1744  //       case (((valid, pd), ans), taken) =>
1745  //       Mux(valid && (pd.isBr) && ans.hit.asBool,
1746  //         isWrong ^ (!taken),
1747  //           false.B)
1748  //     }
1749  //   }
1750
1751  //   def rasCheck(commit: FtqEntry, predAns: Seq[PredictorAnswer], isWrong: Bool) = {
1752  //     commit.valids.zip(commit.pd).zip(predAns).zip(commit.takens).map {
1753  //       case (((valid, pd), ans), taken) =>
1754  //       Mux(valid && pd.isRet.asBool /*&& taken*/ && ans.hit.asBool,
1755  //         isWrong ^ (ans.target === commitEntry.target),
1756  //           false.B)
1757  //     }
1758  //   }
1759
1760  //   val ubtbRights = ubtbCheck(commitEntry, commitEntry.metas.map(_.ubtbAns), false.B)
1761  //   val ubtbWrongs = ubtbCheck(commitEntry, commitEntry.metas.map(_.ubtbAns), true.B)
1762  //   // btb and ubtb pred jal and jalr as well
1763  //   val btbRights = btbCheck(commitEntry, commitEntry.metas.map(_.btbAns), false.B)
1764  //   val btbWrongs = btbCheck(commitEntry, commitEntry.metas.map(_.btbAns), true.B)
1765  //   val tageRights = tageCheck(commitEntry, commitEntry.metas.map(_.tageAns), false.B)
1766  //   val tageWrongs = tageCheck(commitEntry, commitEntry.metas.map(_.tageAns), true.B)
1767
1768  //   val loopRights = loopCheck(commitEntry, commitEntry.metas.map(_.loopAns), false.B)
1769  //   val loopWrongs = loopCheck(commitEntry, commitEntry.metas.map(_.loopAns), true.B)
1770
1771  //   val rasRights = rasCheck(commitEntry, commitEntry.metas.map(_.rasAns), false.B)
1772  //   val rasWrongs = rasCheck(commitEntry, commitEntry.metas.map(_.rasAns), true.B)
1773
1774  val perfEvents = Seq(
1775    ("bpu_s2_redirect        ", bpu_s2_redirect),
1776    ("bpu_s3_redirect        ", bpu_s3_redirect),
1777    ("bpu_to_ftq_stall       ", enq.valid && ~enq.ready),
1778    ("mispredictRedirect     ", perf_redirect.valid && RedirectLevel.flushAfter === perf_redirect.bits.level),
1779    ("replayRedirect         ", perf_redirect.valid && RedirectLevel.flushItself(perf_redirect.bits.level)),
1780    ("predecodeRedirect      ", fromIfuRedirect.valid),
1781    ("to_ifu_bubble          ", io.toIfu.req.ready && !io.toIfu.req.valid),
1782    ("from_bpu_real_bubble   ", !enq.valid && enq.ready && allowBpuIn),
1783    ("BpInstr                ", PopCount(mbpInstrs)),
1784    ("BpBInstr               ", PopCount(mbpBRights | mbpBWrongs)),
1785    ("BpRight                ", PopCount(mbpRights)),
1786    ("BpWrong                ", PopCount(mbpWrongs)),
1787    ("BpBRight               ", PopCount(mbpBRights)),
1788    ("BpBWrong               ", PopCount(mbpBWrongs)),
1789    ("BpJRight               ", PopCount(mbpJRights)),
1790    ("BpJWrong               ", PopCount(mbpJWrongs)),
1791    ("BpIRight               ", PopCount(mbpIRights)),
1792    ("BpIWrong               ", PopCount(mbpIWrongs)),
1793    ("BpCRight               ", PopCount(mbpCRights)),
1794    ("BpCWrong               ", PopCount(mbpCWrongs)),
1795    ("BpRRight               ", PopCount(mbpRRights)),
1796    ("BpRWrong               ", PopCount(mbpRWrongs)),
1797    ("ftb_false_hit          ", PopCount(ftb_false_hit)),
1798    ("ftb_hit                ", PopCount(ftb_hit))
1799  )
1800  generatePerfEvent()
1801}
1802