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