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