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