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