xref: /XiangShan/src/main/scala/xiangshan/frontend/NewFtq.scala (revision 708ceed4afe43fb0ea3a52407e46b2794c573634)
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 chipsalliance.rocketchip.config.Parameters
20import chisel3._
21import chisel3.util._
22import utils.{AsyncDataModuleTemplate, CircularQueuePtr, DataModuleTemplate, HasCircularQueuePtrHelper, SRAMTemplate, SyncDataModuleTemplate, XSDebug, XSPerfAccumulate, XSError}
23import xiangshan._
24import scala.tools.nsc.doc.model.Val
25import utils.{ParallelPriorityMux, ParallelPriorityEncoder}
26import xiangshan.backend.{CtrlToFtqIO}
27import firrtl.annotations.MemoryLoadFileType
28
29class FtqPtr(implicit p: Parameters) extends CircularQueuePtr[FtqPtr](
30  p => p(XSCoreParamsKey).FtqSize
31){
32  override def cloneType = (new FtqPtr).asInstanceOf[this.type]
33}
34
35object FtqPtr {
36  def apply(f: Bool, v: UInt)(implicit p: Parameters): FtqPtr = {
37    val ptr = Wire(new FtqPtr)
38    ptr.flag := f
39    ptr.value := v
40    ptr
41  }
42  def inverse(ptr: FtqPtr)(implicit p: Parameters): FtqPtr = {
43    apply(!ptr.flag, ptr.value)
44  }
45}
46
47class FtqNRSRAM[T <: Data](gen: T, numRead: Int)(implicit p: Parameters) extends XSModule {
48
49  val io = IO(new Bundle() {
50    val raddr = Input(Vec(numRead, UInt(log2Up(FtqSize).W)))
51    val ren = Input(Vec(numRead, Bool()))
52    val rdata = Output(Vec(numRead, gen))
53    val waddr = Input(UInt(log2Up(FtqSize).W))
54    val wen = Input(Bool())
55    val wdata = Input(gen)
56  })
57
58  for(i <- 0 until numRead){
59    val sram = Module(new SRAMTemplate(gen, FtqSize))
60    sram.io.r.req.valid := io.ren(i)
61    sram.io.r.req.bits.setIdx := io.raddr(i)
62    io.rdata(i) := sram.io.r.resp.data(0)
63    sram.io.w.req.valid := io.wen
64    sram.io.w.req.bits.setIdx := io.waddr
65    sram.io.w.req.bits.data := VecInit(io.wdata)
66  }
67
68}
69
70class Ftq_RF_Components(implicit p: Parameters) extends XSBundle with BPUUtils {
71  // TODO: move pftAddr, oversize, carry to another mem
72  val startAddr = UInt(VAddrBits.W)
73  val nextRangeAddr = UInt(VAddrBits.W)
74  val pftAddr = UInt((log2Ceil(PredictWidth)+1).W)
75  val isNextMask = Vec(PredictWidth, Bool())
76  val oversize = Bool()
77  val carry = Bool()
78  def getPc(offset: UInt) = {
79    def getHigher(pc: UInt) = pc(VAddrBits-1, log2Ceil(PredictWidth)+instOffsetBits)
80    def getOffset(pc: UInt) = pc(log2Ceil(PredictWidth)+instOffsetBits-1, instOffsetBits)
81    Cat(getHigher(Mux(isNextMask(offset), nextRangeAddr, startAddr)),
82        getOffset(startAddr)+offset, 0.U(instOffsetBits.W))
83  }
84  def getFallThrough() = {
85    getFallThroughAddr(this.startAddr, this.carry, this.pftAddr)
86  }
87  def fallThroughError() = {
88    !carry && startAddr(instOffsetBits+log2Ceil(PredictWidth), instOffsetBits) > pftAddr
89  }
90  def fromBranchPrediction(resp: BranchPredictionBundle) = {
91    this.startAddr := resp.pc
92    this.nextRangeAddr := resp.pc + (FetchWidth * 4).U
93    this.pftAddr := resp.ftb_entry.pftAddr
94    this.isNextMask := VecInit((0 until PredictWidth).map(i =>
95      (resp.pc(log2Ceil(PredictWidth), 1) +& i.U)(log2Ceil(PredictWidth)).asBool()
96    ))
97    this.oversize := resp.ftb_entry.oversize
98    this.carry := resp.ftb_entry.carry
99    this
100  }
101  override def toPrintable: Printable = {
102    p"startAddr:${Hexadecimal(startAddr)}, fallThru:${Hexadecimal(getFallThrough())}"
103  }
104}
105
106class Ftq_pd_Entry(implicit p: Parameters) extends XSBundle {
107  val brMask = Vec(PredictWidth, Bool())
108  val jmpInfo = ValidUndirectioned(Vec(3, Bool()))
109  val jmpOffset = UInt(log2Ceil(PredictWidth).W)
110  val jalTarget = UInt(VAddrBits.W)
111  val rvcMask = Vec(PredictWidth, Bool())
112  def hasJal  = jmpInfo.valid && !jmpInfo.bits(0)
113  def hasJalr = jmpInfo.valid && jmpInfo.bits(0)
114  def hasCall = jmpInfo.valid && jmpInfo.bits(1)
115  def hasRet  = jmpInfo.valid && jmpInfo.bits(2)
116
117  def fromPdWb(pdWb: PredecodeWritebackBundle) = {
118    val pds = pdWb.pd
119    this.brMask := VecInit(pds.map(pd => pd.isBr && pd.valid))
120    this.jmpInfo.valid := VecInit(pds.map(pd => (pd.isJal || pd.isJalr) && pd.valid)).asUInt.orR
121    this.jmpInfo.bits := ParallelPriorityMux(pds.map(pd => (pd.isJal || pd.isJalr) && pd.valid),
122                                             pds.map(pd => VecInit(pd.isJalr, pd.isCall, pd.isRet)))
123    this.jmpOffset := ParallelPriorityEncoder(pds.map(pd => (pd.isJal || pd.isJalr) && pd.valid))
124    this.rvcMask := VecInit(pds.map(pd => pd.isRVC))
125    this.jalTarget := pdWb.jalTarget
126  }
127
128  def toPd(offset: UInt) = {
129    require(offset.getWidth == log2Ceil(PredictWidth))
130    val pd = Wire(new PreDecodeInfo)
131    pd.valid := true.B
132    pd.isRVC := rvcMask(offset)
133    val isBr = brMask(offset)
134    val isJalr = offset === jmpOffset && jmpInfo.valid && jmpInfo.bits(0)
135    pd.brType := Cat(offset === jmpOffset && jmpInfo.valid, isJalr || isBr)
136    pd.isCall := offset === jmpOffset && jmpInfo.valid && jmpInfo.bits(1)
137    pd.isRet  := offset === jmpOffset && jmpInfo.valid && jmpInfo.bits(2)
138    pd
139  }
140}
141
142
143
144class Ftq_Redirect_SRAMEntry(implicit p: Parameters) extends XSBundle with HasBPUConst {
145  val rasSp = UInt(log2Ceil(RasSize).W)
146  val rasEntry = new RASEntry
147  val specCnt = Vec(numBr, UInt(10.W))
148  val ghist = new GlobalHistory
149  val phist = UInt(PathHistoryLength.W)
150  val phNewBit = UInt(1.W)
151
152  def fromBranchPrediction(resp: BranchPredictionBundle) = {
153    this.rasSp := resp.rasSp
154    this.rasEntry := resp.rasTop
155    this.specCnt := resp.specCnt
156    this.ghist := resp.ghist
157    this.phist := resp.phist
158    this.phNewBit := resp.pc(instOffsetBits)
159    this
160  }
161}
162
163class Ftq_1R_SRAMEntry(implicit p: Parameters) extends XSBundle with HasBPUConst {
164  val meta = UInt(MaxMetaLength.W)
165}
166
167class Ftq_Pred_Info(implicit p: Parameters) extends XSBundle {
168  val target = UInt(VAddrBits.W)
169  val cfiIndex = ValidUndirectioned(UInt(log2Ceil(PredictWidth).W))
170}
171
172class FtqEntry(implicit p: Parameters) extends XSBundle with HasBPUConst {
173  val startAddr = UInt(VAddrBits.W)
174  val fallThruAddr = UInt(VAddrBits.W)
175  val isNextMask = Vec(PredictWidth, Bool())
176
177  val meta = UInt(MaxMetaLength.W)
178
179  val rasSp = UInt(log2Ceil(RasSize).W)
180  val rasEntry = new RASEntry
181  val hist = new GlobalHistory
182  val specCnt = Vec(numBr, UInt(10.W))
183
184  val valids = Vec(PredictWidth, Bool())
185  val brMask = Vec(PredictWidth, Bool())
186  // isJalr, isCall, isRet
187  val jmpInfo = ValidUndirectioned(Vec(3, Bool()))
188  val jmpOffset = UInt(log2Ceil(PredictWidth).W)
189
190  val mispredVec = Vec(PredictWidth, Bool())
191  val cfiIndex = ValidUndirectioned(UInt(log2Ceil(PredictWidth).W))
192  val target = UInt(VAddrBits.W)
193}
194
195class FtqRead[T <: Data](private val gen: T)(implicit p: Parameters) extends XSBundle {
196  val ptr = Output(new FtqPtr)
197  val offset = Output(UInt(log2Ceil(PredictWidth).W))
198  val data = Input(gen)
199  def apply(ptr: FtqPtr, offset: UInt) = {
200    this.ptr := ptr
201    this.offset := offset
202    this.data
203  }
204  override def cloneType = (new FtqRead(gen)).asInstanceOf[this.type]
205}
206
207
208class FtqToBpuIO(implicit p: Parameters) extends XSBundle {
209  val redirect = Valid(new BranchPredictionRedirect)
210  val update = Valid(new BranchPredictionUpdate)
211  val enq_ptr = Output(new FtqPtr)
212}
213
214class FtqToIfuIO(implicit p: Parameters) extends XSBundle with HasCircularQueuePtrHelper {
215  val req = Decoupled(new FetchRequestBundle)
216  val redirect = Valid(new Redirect)
217  val flushFromBpu = new Bundle {
218    // when ifu pipeline is not stalled,
219    // a packet from bpu s3 can reach f1 at most
220    val s2 = Valid(new FtqPtr)
221    val s3 = Valid(new FtqPtr)
222    def shouldFlushBy(src: Valid[FtqPtr], idx_to_flush: FtqPtr) = {
223      src.valid && !isAfter(src.bits, idx_to_flush)
224    }
225    def shouldFlushByStage2(idx: FtqPtr) = shouldFlushBy(s2, idx)
226    def shouldFlushByStage3(idx: FtqPtr) = shouldFlushBy(s3, idx)
227  }
228}
229
230trait HasBackendRedirectInfo extends HasXSParameter {
231  def numRedirect = exuParameters.JmpCnt + exuParameters.AluCnt + 1
232  def isLoadReplay(r: Valid[Redirect]) = r.bits.flushItself()
233}
234
235class FtqToCtrlIO(implicit p: Parameters) extends XSBundle with HasBackendRedirectInfo {
236  val pc_reads = Vec(1 + numRedirect + 1 + 1, Flipped(new FtqRead(UInt(VAddrBits.W))))
237  val target_read = Flipped(new FtqRead(UInt(VAddrBits.W)))
238  def getJumpPcRead = pc_reads.head
239  def getRedirectPcRead = VecInit(pc_reads.tail.dropRight(2))
240  def getMemPredPcRead = pc_reads.init.last
241  def getRoqFlushPcRead = pc_reads.last
242}
243
244
245class FTBEntryGen(implicit p: Parameters) extends XSModule with HasBackendRedirectInfo with HasBPUParameter {
246  val io = IO(new Bundle {
247    val start_addr = Input(UInt(VAddrBits.W))
248    val old_entry = Input(new FTBEntry)
249    val pd = Input(new Ftq_pd_Entry)
250    val cfiIndex = Flipped(Valid(UInt(log2Ceil(PredictWidth).W)))
251    val target = Input(UInt(VAddrBits.W))
252    val hit = Input(Bool())
253    val mispredict_vec = Input(Vec(PredictWidth, Bool()))
254
255    val new_entry = Output(new FTBEntry)
256    val new_br_insert_pos = Output(Vec(numBr, Bool()))
257    val taken_mask = Output(Vec(numBr, Bool()))
258    val mispred_mask = Output(Vec(numBr+1, Bool()))
259
260    // for perf counters
261    val is_init_entry = Output(Bool())
262    val is_old_entry = Output(Bool())
263    val is_new_br = Output(Bool())
264    val is_jalr_target_modified = Output(Bool())
265    val is_always_taken_modified = Output(Bool())
266    val is_br_full = Output(Bool())
267  })
268
269  // no mispredictions detected at predecode
270  val hit = io.hit
271  val pd = io.pd
272
273  val init_entry = WireInit(0.U.asTypeOf(new FTBEntry))
274
275
276  val cfi_is_br = pd.brMask(io.cfiIndex.bits) && io.cfiIndex.valid
277  val entry_has_jmp = pd.jmpInfo.valid
278  val new_jmp_is_jal  = entry_has_jmp && !pd.jmpInfo.bits(0) && io.cfiIndex.valid
279  val new_jmp_is_jalr = entry_has_jmp &&  pd.jmpInfo.bits(0) && io.cfiIndex.valid
280  val new_jmp_is_call = entry_has_jmp &&  pd.jmpInfo.bits(1) && io.cfiIndex.valid
281  val new_jmp_is_ret  = entry_has_jmp &&  pd.jmpInfo.bits(2) && io.cfiIndex.valid
282  val last_jmp_rvi = entry_has_jmp && pd.jmpOffset === (PredictWidth-1).U && !pd.rvcMask.last
283  val last_br_rvi = cfi_is_br && io.cfiIndex.bits === (PredictWidth-1).U && !pd.rvcMask.last
284
285  val cfi_is_jal = io.cfiIndex.bits === pd.jmpOffset && new_jmp_is_jal
286  val cfi_is_jalr = io.cfiIndex.bits === pd.jmpOffset && new_jmp_is_jalr
287
288  def carryPos = log2Ceil(PredictWidth)+instOffsetBits+1
289  def getLower(pc: UInt) = pc(carryPos-1, instOffsetBits)
290  // if not hit, establish a new entry
291  init_entry.valid := true.B
292  // tag is left for ftb to assign
293  init_entry.brValids(0) := cfi_is_br
294  init_entry.brOffset(0) := io.cfiIndex.bits
295  init_entry.setByBrTarget(0, io.start_addr, io.target)
296  init_entry.always_taken(0) := cfi_is_br // set to always taken on init
297  init_entry.always_taken(1) := false.B
298  init_entry.jmpOffset := pd.jmpOffset
299  init_entry.jmpValid := new_jmp_is_jal || new_jmp_is_jalr
300  init_entry.setByJmpTarget(io.start_addr, Mux(cfi_is_jalr, io.target, pd.jalTarget))
301  val jmpPft = getLower(io.start_addr) +& pd.jmpOffset +& Mux(pd.rvcMask(pd.jmpOffset), 1.U, 2.U)
302  init_entry.pftAddr := Mux(entry_has_jmp, jmpPft, getLower(io.start_addr) + ((FetchWidth*4)>>instOffsetBits).U + Mux(last_br_rvi, 1.U, 0.U))
303  init_entry.carry   := Mux(entry_has_jmp, jmpPft(carryPos-instOffsetBits), io.start_addr(carryPos-1) || (io.start_addr(carryPos-2, instOffsetBits).andR && last_br_rvi))
304  init_entry.isJalr := new_jmp_is_jalr
305  init_entry.isCall := new_jmp_is_call
306  init_entry.isRet  := new_jmp_is_ret
307  init_entry.last_is_rvc := Mux(entry_has_jmp, pd.rvcMask(pd.jmpOffset), pd.rvcMask.last)
308
309  init_entry.oversize := last_br_rvi || last_jmp_rvi
310
311  // if hit, check whether a new cfi(only br is possible) is detected
312  val oe = io.old_entry
313  val br_recorded_vec = VecInit((oe.brValids zip oe.brOffset).map {
314    case (v, off) => v && (off === io.cfiIndex.bits)
315  })
316  val br_recorded = br_recorded_vec.asUInt.orR
317  val is_new_br = cfi_is_br && !br_recorded
318  val br_full = oe.brValids.asUInt.andR // all slots have brs
319  val new_br_offset = io.cfiIndex.bits
320  // vec(i) means new br will be inserted BEFORE old br(i)
321  val new_br_insert_onehot = VecInit((0 until numBr).map{
322    i => i match {
323      case 0 => !oe.brValids(0) || new_br_offset < oe.brOffset(0)
324      case idx => oe.brValids(idx-1) && new_br_offset > oe.brOffset(idx-1) &&
325        (!oe.brValids(idx) || new_br_offset < oe.brOffset(idx))
326    }
327  })
328
329  val old_entry_modified = WireInit(io.old_entry)
330  val (new_br_lower, new_br_tar_stat) = old_entry_modified.getBrLowerStatByTarget(io.start_addr, io.target)
331  for (i <- 0 until numBr) {
332    old_entry_modified.brOffset(i)  :=  Mux(new_br_insert_onehot(i), new_br_offset,
333                                          Mux(oe.brOffset(i) < new_br_offset, oe.brOffset(i),
334                                            (if (i != 0) oe.brOffset(i-1) else oe.brOffset(i))))
335    old_entry_modified.brLowers(i) :=  Mux(new_br_insert_onehot(i), new_br_lower,
336                                          Mux(oe.brOffset(i) < new_br_offset, oe.brLowers(i),
337                                            (if (i != 0) oe.brLowers(i-1) else oe.brLowers(i))))
338    old_entry_modified.brTarStats(i) := Mux(new_br_insert_onehot(i), new_br_tar_stat,
339                                          Mux(oe.brOffset(i) < new_br_offset, oe.brTarStats(i),
340                                            (if (i != 0) oe.brTarStats(i-1) else oe.brTarStats(i))))
341    old_entry_modified.always_taken(i) := Mux(new_br_insert_onehot(i), true.B,
342                                            Mux(oe.brOffset(i) < new_br_offset, false.B,
343                                              (if (i != 0) oe.always_taken(i-1) else oe.always_taken(i))))
344  }
345  old_entry_modified.brValids := VecInit((oe.brValids zip new_br_insert_onehot).map{case (v1, v2) => v1 || v2})
346
347  // in this case, pft_addr should be the addrs of the last br in packet
348  val pft_need_to_change = is_new_br && br_full
349  // it should either be the given last br or the new br
350  when (pft_need_to_change) {
351    val new_pft_offset = Mux(new_br_insert_onehot.asUInt.orR, oe.brOffset.last, new_br_offset)
352    old_entry_modified.pftAddr := getLower(io.start_addr) + new_pft_offset
353    old_entry_modified.last_is_rvc := pd.rvcMask(new_pft_offset - 1.U) // TODO: fix this
354    old_entry_modified.carry := (getLower(io.start_addr) +& new_pft_offset).head(1).asBool
355    old_entry_modified.oversize := false.B
356    old_entry_modified.jmpValid := false.B
357    old_entry_modified.isCall := false.B
358    old_entry_modified.isRet := false.B
359  }
360
361  val old_entry_jmp_target_modified = WireInit(oe)
362  val old_target = oe.getJmpTarget(io.start_addr)
363  val jalr_target_modified = cfi_is_jalr && (old_target =/= io.target) // TODO: pass full jalr target
364  when (jalr_target_modified) {
365    old_entry_jmp_target_modified.setByJmpTarget(io.start_addr, io.target)
366    old_entry_jmp_target_modified.always_taken := 0.U.asTypeOf(Vec(numBr, Bool()))
367  }
368
369  val old_entry_always_taken = WireInit(oe)
370  val always_taken_modified_vec = Wire(Vec(numBr, Bool())) // whether modified or not
371  for (i <- 0 until numBr) {
372    old_entry_always_taken.always_taken(i) :=
373      oe.always_taken(i) && io.cfiIndex.valid && oe.brValids(i) && io.cfiIndex.bits === oe.brOffset(i)
374    always_taken_modified_vec(i) := oe.always_taken(i) && !(io.cfiIndex.valid && oe.brValids(i) && io.cfiIndex.bits === oe.brOffset(i))
375  }
376  val always_taken_modified = always_taken_modified_vec.reduce(_||_)
377
378
379
380  val derived_from_old_entry =
381    Mux(is_new_br, old_entry_modified,
382      Mux(jalr_target_modified, old_entry_jmp_target_modified, old_entry_always_taken))
383
384
385  io.new_entry := Mux(!hit, init_entry, derived_from_old_entry)
386
387  io.new_br_insert_pos := new_br_insert_onehot
388  io.taken_mask := VecInit((io.new_entry.brOffset zip io.new_entry.brValids).map{
389    case (off, v) => io.cfiIndex.bits === off && io.cfiIndex.valid && v
390  })
391  for (i <- 0 until numBr) {
392    io.mispred_mask(i) := io.new_entry.brValids(i) && io.mispredict_vec(io.new_entry.brOffset(i))
393  }
394  io.mispred_mask.last := io.new_entry.jmpValid && io.mispredict_vec(pd.jmpOffset)
395
396  // for perf counters
397  io.is_init_entry := !hit
398  io.is_old_entry := hit && !is_new_br && !jalr_target_modified && !always_taken_modified
399  io.is_new_br := hit && is_new_br
400  io.is_jalr_target_modified := hit && jalr_target_modified
401  io.is_always_taken_modified := hit && always_taken_modified
402  io.is_br_full := hit && is_new_br && br_full
403}
404
405class Ftq(implicit p: Parameters) extends XSModule with HasCircularQueuePtrHelper
406  with HasBackendRedirectInfo with BPUUtils with HasBPUConst {
407  val io = IO(new Bundle {
408    val fromBpu = Flipped(new BpuToFtqIO)
409    val fromIfu = Flipped(new IfuToFtqIO)
410    val fromBackend = Flipped(new CtrlToFtqIO)
411
412    val toBpu = new FtqToBpuIO
413    val toIfu = new FtqToIfuIO
414    val toBackend = new FtqToCtrlIO
415
416    val bpuInfo = new Bundle {
417      val bpRight = Output(UInt(XLEN.W))
418      val bpWrong = Output(UInt(XLEN.W))
419    }
420  })
421  io.bpuInfo := DontCare
422
423  val roqFlush = io.fromBackend.roqFlush
424  val stage2Redirect = io.fromBackend.stage2Redirect
425  val stage3Redirect = io.fromBackend.stage3Redirect
426
427  val stage2Flush = stage2Redirect.valid || roqFlush.valid
428  val backendFlush = stage2Flush || RegNext(stage2Flush)
429  val ifuFlush = Wire(Bool())
430
431  val flush = stage2Flush || RegNext(stage2Flush)
432
433  val allowBpuIn, allowToIfu = WireInit(false.B)
434  val flushToIfu = !allowToIfu
435  allowBpuIn := !ifuFlush && !roqFlush.valid && !stage2Redirect.valid && !stage3Redirect.valid
436  allowToIfu := !ifuFlush && !roqFlush.valid && !stage2Redirect.valid && !stage3Redirect.valid
437
438  val bpuPtr, ifuPtr, ifuWbPtr, commPtr = RegInit(FtqPtr(false.B, 0.U))
439  val validEntries = distanceBetween(bpuPtr, commPtr)
440
441  // **********************************************************************
442  // **************************** enq from bpu ****************************
443  // **********************************************************************
444  val new_entry_ready = validEntries < FtqSize.U
445  io.fromBpu.resp.ready := new_entry_ready
446
447  val bpu_s2_resp = io.fromBpu.resp.bits.s2
448  val bpu_s3_resp = io.fromBpu.resp.bits.s3
449  val bpu_s2_redirect = bpu_s2_resp.valid && bpu_s2_resp.hasRedirect
450  val bpu_s3_redirect = bpu_s3_resp.valid && bpu_s3_resp.hasRedirect
451
452  io.toBpu.enq_ptr := bpuPtr
453  val enq_fire = io.fromBpu.resp.fire() && allowBpuIn // from bpu s1
454  val bpu_in_fire = (io.fromBpu.resp.fire() || bpu_s2_redirect || bpu_s3_redirect) && allowBpuIn
455
456  val bpu_in_resp = WireInit(io.fromBpu.resp.bits.selectedResp)
457  val bpu_in_stage = WireInit(io.fromBpu.resp.bits.selectedRespIdx)
458  val bpu_in_resp_ptr = Mux(bpu_in_stage === BP_S1, bpuPtr, bpu_in_resp.ftq_idx)
459  val bpu_in_resp_idx = bpu_in_resp_ptr.value
460
461  // read ports:                            jumpPc + redirects + loadPred + roqFlush + ifuReq1 + ifuReq2 + commitUpdate
462  val ftq_pc_mem = Module(new SyncDataModuleTemplate(new Ftq_RF_Components, FtqSize, 1+numRedirect+2+1+1+1, 1))
463  // resp from uBTB
464  ftq_pc_mem.io.wen(0) := bpu_in_fire
465  ftq_pc_mem.io.waddr(0) := bpu_in_resp_idx
466  ftq_pc_mem.io.wdata(0).fromBranchPrediction(bpu_in_resp)
467
468  //                                                            ifuRedirect + backendRedirect + commit
469  val ftq_redirect_sram = Module(new FtqNRSRAM(new Ftq_Redirect_SRAMEntry, 1+1+1))
470  // these info is intended to enq at the last stage of bpu
471  ftq_redirect_sram.io.wen := io.fromBpu.resp.bits.lastStage.valid
472  ftq_redirect_sram.io.waddr := io.fromBpu.resp.bits.lastStage.ftq_idx.value
473  ftq_redirect_sram.io.wdata.fromBranchPrediction(io.fromBpu.resp.bits.lastStage)
474
475  val ftq_meta_1r_sram = Module(new FtqNRSRAM(new Ftq_1R_SRAMEntry, 1))
476  // these info is intended to enq at the last stage of bpu
477  ftq_meta_1r_sram.io.wen := io.fromBpu.resp.bits.lastStage.valid
478  ftq_meta_1r_sram.io.waddr := io.fromBpu.resp.bits.lastStage.ftq_idx.value
479  ftq_meta_1r_sram.io.wdata.meta := io.fromBpu.resp.bits.meta
480  //                                                            ifuRedirect + backendRedirect + commit
481  val ftb_entry_mem = Module(new SyncDataModuleTemplate(new FTBEntry, FtqSize, 1+1+1, 1))
482  ftb_entry_mem.io.wen(0) := io.fromBpu.resp.bits.lastStage.valid
483  ftb_entry_mem.io.waddr(0) := io.fromBpu.resp.bits.lastStage.ftq_idx.value
484  ftb_entry_mem.io.wdata(0) := io.fromBpu.resp.bits.lastStage.ftb_entry
485
486
487  // multi-write
488  val update_target = Reg(Vec(FtqSize, UInt(VAddrBits.W)))
489  val cfiIndex_vec = Reg(Vec(FtqSize, ValidUndirectioned(UInt(log2Ceil(PredictWidth).W))))
490  val mispredict_vec = Reg(Vec(FtqSize, Vec(PredictWidth, Bool())))
491  val pred_stage = Reg(Vec(FtqSize, UInt(2.W)))
492
493  val c_invalid :: c_valid :: c_commited :: Nil = Enum(3)
494  val commitStateQueue = RegInit(VecInit(Seq.fill(FtqSize) {
495    VecInit(Seq.fill(PredictWidth)(c_invalid))
496  }))
497
498  val f_to_send :: f_sent :: Nil = Enum(2)
499  val entry_fetch_status = RegInit(VecInit(Seq.fill(FtqSize)(f_sent)))
500
501  val h_not_hit :: h_false_hit :: h_hit :: Nil = Enum(3)
502  val entry_hit_status = RegInit(VecInit(Seq.fill(FtqSize)(h_not_hit)))
503
504
505  when (bpu_in_fire) {
506    entry_fetch_status(bpu_in_resp_idx) := f_to_send
507    commitStateQueue(bpu_in_resp_idx) := VecInit(Seq.fill(PredictWidth)(c_invalid))
508    cfiIndex_vec(bpu_in_resp_idx) := bpu_in_resp.genCfiIndex
509    mispredict_vec(bpu_in_resp_idx) := WireInit(VecInit(Seq.fill(PredictWidth)(false.B)))
510    update_target(bpu_in_resp_idx) := bpu_in_resp.target
511    pred_stage(bpu_in_resp_idx) := bpu_in_stage
512  }
513
514  bpuPtr := bpuPtr + enq_fire
515  ifuPtr := ifuPtr + io.toIfu.req.fire
516
517  // only use ftb result to assign hit status
518  when (bpu_s2_resp.valid) {
519    entry_hit_status(bpu_s2_resp.ftq_idx.value) := Mux(bpu_s2_resp.preds.hit, h_hit, h_not_hit)
520  }
521
522
523  io.toIfu.flushFromBpu.s2.valid := bpu_s2_resp.valid && bpu_s2_resp.hasRedirect
524  io.toIfu.flushFromBpu.s2.bits := bpu_s2_resp.ftq_idx
525  when (bpu_s2_resp.valid && bpu_s2_resp.hasRedirect) {
526    bpuPtr := bpu_s2_resp.ftq_idx + 1.U
527    // only when ifuPtr runs ahead of bpu s2 resp should we recover it
528    when (!isBefore(ifuPtr, bpu_s2_resp.ftq_idx)) {
529      ifuPtr := bpu_s2_resp.ftq_idx
530    }
531  }
532
533  io.toIfu.flushFromBpu.s3.valid := bpu_s3_resp.valid && bpu_s3_resp.hasRedirect
534  io.toIfu.flushFromBpu.s3.bits := bpu_s3_resp.ftq_idx
535  when (bpu_s3_resp.valid && bpu_s3_resp.hasRedirect) {
536    bpuPtr := bpu_s3_resp.ftq_idx + 1.U
537    // only when ifuPtr runs ahead of bpu s2 resp should we recover it
538    when (!isBefore(ifuPtr, bpu_s3_resp.ftq_idx)) {
539      ifuPtr := bpu_s3_resp.ftq_idx
540    }
541    XSError(true.B, "\ns3_redirect mechanism not implemented!\n")
542  }
543
544  XSError(isBefore(bpuPtr, ifuPtr) && !isFull(bpuPtr, ifuPtr), "\nifuPtr is before bpuPtr!\n")
545
546  // ****************************************************************
547  // **************************** to ifu ****************************
548  // ****************************************************************
549  val bpu_in_bypass_buf = RegEnable(ftq_pc_mem.io.wdata(0), enable=bpu_in_fire)
550  val bpu_in_bypass_ptr = RegNext(bpu_in_resp_ptr)
551  val last_cycle_bpu_in = RegNext(bpu_in_fire)
552  val last_cycle_to_ifu_fire = RegNext(io.toIfu.req.fire)
553
554  // read pc and target
555  ftq_pc_mem.io.raddr.init.init.last := ifuPtr.value
556  ftq_pc_mem.io.raddr.init.last := (ifuPtr+1.U).value
557
558  val toIfuReq = Wire(chiselTypeOf(io.toIfu.req))
559
560  toIfuReq.valid := allowToIfu && entry_fetch_status(ifuPtr.value) === f_to_send && ifuPtr =/= bpuPtr
561  toIfuReq.bits.ftqIdx := ifuPtr
562  toIfuReq.bits.target := update_target(ifuPtr.value)
563  toIfuReq.bits.ftqOffset := cfiIndex_vec(ifuPtr.value)
564  toIfuReq.bits.fallThruError  := false.B
565
566  when (last_cycle_bpu_in && bpu_in_bypass_ptr === ifuPtr) {
567    toIfuReq.bits.fromFtqPcBundle(bpu_in_bypass_buf)
568  }.elsewhen (last_cycle_to_ifu_fire) {
569    toIfuReq.bits.fromFtqPcBundle(ftq_pc_mem.io.rdata.init.last)
570  }.otherwise {
571    toIfuReq.bits.fromFtqPcBundle(ftq_pc_mem.io.rdata.init.init.last)
572  }
573
574  io.toIfu.req <> toIfuReq
575
576  // when fall through is smaller in value than start address, there must be a false hit
577  when (toIfuReq.bits.fallThroughError() && entry_hit_status(ifuPtr.value) === h_hit) {
578    when (io.toIfu.req.fire &&
579      !(bpu_s2_redirect && bpu_s2_resp.ftq_idx === ifuPtr) &&
580      !(bpu_s3_redirect && bpu_s3_resp.ftq_idx === ifuPtr)
581    ) {
582      entry_hit_status(ifuPtr.value) := h_false_hit
583      XSDebug(true.B, "FTB false hit by fallThroughError, startAddr: %x, fallTHru: %x\n", toIfuReq.bits.startAddr, toIfuReq.bits.fallThruAddr)
584    }
585    io.toIfu.req.bits.fallThruAddr   := toIfuReq.bits.startAddr + (FetchWidth*4).U
586    io.toIfu.req.bits.fallThruError  := true.B
587    XSDebug(true.B, "fallThruError! start:%x, fallThru:%x\n", toIfuReq.bits.startAddr, toIfuReq.bits.fallThruAddr)
588  }
589
590  val ifu_req_should_be_flushed =
591    io.toIfu.flushFromBpu.shouldFlushByStage2(toIfuReq.bits.ftqIdx) ||
592    io.toIfu.flushFromBpu.shouldFlushByStage3(toIfuReq.bits.ftqIdx)
593
594  when (io.toIfu.req.fire && !ifu_req_should_be_flushed) {
595    entry_fetch_status(ifuPtr.value) := f_sent
596  }
597
598
599  // *********************************************************************
600  // **************************** wb from ifu ****************************
601  // *********************************************************************
602  val pdWb = io.fromIfu.pdWb
603  val pds = pdWb.bits.pd
604  val ifu_wb_valid = pdWb.valid
605  val ifu_wb_idx = pdWb.bits.ftqIdx.value
606  // read ports:                                                         commit update
607  val ftq_pd_mem = Module(new SyncDataModuleTemplate(new Ftq_pd_Entry, FtqSize, 1, 1))
608  ftq_pd_mem.io.wen(0) := ifu_wb_valid
609  ftq_pd_mem.io.waddr(0) := pdWb.bits.ftqIdx.value
610  ftq_pd_mem.io.wdata(0).fromPdWb(pdWb.bits)
611
612  val hit_pd_valid = entry_hit_status(ifu_wb_idx) === h_hit && ifu_wb_valid
613  val hit_pd_mispred = hit_pd_valid && pdWb.bits.misOffset.valid
614  val hit_pd_mispred_reg = RegNext(hit_pd_mispred, init=false.B)
615  val pd_reg       = RegEnable(pds,             enable = pdWb.valid)
616  val start_pc_reg = RegEnable(pdWb.bits.pc(0), enable = pdWb.valid)
617  val wb_idx_reg   = RegEnable(ifu_wb_idx,      enable = pdWb.valid)
618
619  when (ifu_wb_valid) {
620    val comm_stq_wen = VecInit(pds.map(_.valid).zip(pdWb.bits.instrRange).map{
621      case (v, inRange) => v && inRange
622    })
623    (commitStateQueue(ifu_wb_idx) zip comm_stq_wen).map{
624      case (qe, v) => when (v) { qe := c_valid }
625    }
626  }
627
628  ifuWbPtr := ifuWbPtr + ifu_wb_valid
629
630  ftb_entry_mem.io.raddr.head := ifu_wb_idx
631  val has_false_hit = WireInit(false.B)
632  when (RegNext(hit_pd_valid)) {
633    // check for false hit
634    val pred_ftb_entry = ftb_entry_mem.io.rdata.head
635    // we check cfis that bpu predicted
636    val br_false_hit = (pred_ftb_entry.brValids zip pred_ftb_entry.brOffset).map{
637      case (v, offset) => v && !(pd_reg(offset).valid && pd_reg(offset).isBr)
638    }.reduce(_||_)
639
640    val jmpOffset = pred_ftb_entry.jmpOffset
641    val jmp_pd = pd_reg(jmpOffset)
642    val jal_false_hit = pred_ftb_entry.jmpValid &&
643      ((pred_ftb_entry.isJal  && !(jmp_pd.valid && jmp_pd.isJal)) ||
644       (pred_ftb_entry.isJalr && !(jmp_pd.valid && jmp_pd.isJalr)) ||
645       (pred_ftb_entry.isCall && !(jmp_pd.valid && jmp_pd.isCall)) ||
646       (pred_ftb_entry.isRet  && !(jmp_pd.valid && jmp_pd.isRet))
647      )
648
649    has_false_hit := br_false_hit || jal_false_hit || hit_pd_mispred_reg
650    XSDebug(has_false_hit, "FTB false hit by br or jal or hit_pd, startAddr: %x\n", pdWb.bits.pc(0))
651
652    // assert(!has_false_hit)
653  }
654
655  when (has_false_hit) {
656    entry_hit_status(wb_idx_reg) := h_false_hit
657  }
658
659
660  // **********************************************************************
661  // **************************** backend read ****************************
662  // **********************************************************************
663
664  // pc reads
665  for ((req, i) <- io.toBackend.pc_reads.zipWithIndex) {
666    ftq_pc_mem.io.raddr(i) := req.ptr.value
667    req.data := ftq_pc_mem.io.rdata(i).getPc(RegNext(req.offset))
668  }
669  // target read
670  io.toBackend.target_read.data := RegNext(update_target(io.toBackend.target_read.ptr.value))
671
672  // *******************************************************************************
673  // **************************** redirect from backend ****************************
674  // *******************************************************************************
675
676  // redirect read cfiInfo, couples to redirectGen s2
677  ftq_redirect_sram.io.ren.init.last := io.fromBackend.stage2Redirect.valid
678  ftq_redirect_sram.io.raddr.init.last := io.fromBackend.stage2Redirect.bits.ftqIdx.value
679
680  ftb_entry_mem.io.raddr.init.last := io.fromBackend.stage2Redirect.bits.ftqIdx.value
681
682  val stage3CfiInfo = ftq_redirect_sram.io.rdata.init.last
683  val fromBackendRedirect = WireInit(io.fromBackend.stage3Redirect)
684  val backendRedirectCfi = fromBackendRedirect.bits.cfiUpdate
685  backendRedirectCfi.fromFtqRedirectSram(stage3CfiInfo)
686
687  val r_ftb_entry = ftb_entry_mem.io.rdata.init.last
688  val r_ftqOffset = fromBackendRedirect.bits.ftqOffset
689
690  when (entry_hit_status(fromBackendRedirect.bits.ftqIdx.value) === h_hit) {
691    backendRedirectCfi.shift := PopCount(r_ftb_entry.getBrMaskByOffset(r_ftqOffset)) +&
692      (backendRedirectCfi.pd.isBr && !r_ftb_entry.brIsSaved(r_ftqOffset) &&
693      !(r_ftb_entry.brValids(numBr-1) && r_ftqOffset > r_ftb_entry.brOffset(numBr-1)))
694
695    backendRedirectCfi.addIntoHist := backendRedirectCfi.pd.isBr && (r_ftb_entry.brIsSaved(r_ftqOffset) ||
696        !(r_ftb_entry.brValids(numBr-1) && r_ftqOffset > r_ftb_entry.brOffset(numBr-1)))
697  }.otherwise {
698    backendRedirectCfi.shift := (backendRedirectCfi.pd.isBr && backendRedirectCfi.taken).asUInt
699    backendRedirectCfi.addIntoHist := backendRedirectCfi.pd.isBr.asUInt
700  }
701
702
703  // ***************************************************************************
704  // **************************** redirect from ifu ****************************
705  // ***************************************************************************
706  val fromIfuRedirect = WireInit(0.U.asTypeOf(Valid(new Redirect)))
707  fromIfuRedirect.valid := pdWb.valid && pdWb.bits.misOffset.valid && !backendFlush
708  fromIfuRedirect.bits.ftqIdx := pdWb.bits.ftqIdx
709  fromIfuRedirect.bits.ftqOffset := pdWb.bits.misOffset.bits
710  fromIfuRedirect.bits.level := RedirectLevel.flushAfter
711
712  val ifuRedirectCfiUpdate = fromIfuRedirect.bits.cfiUpdate
713  ifuRedirectCfiUpdate.pc := pdWb.bits.pc(pdWb.bits.misOffset.bits)
714  ifuRedirectCfiUpdate.pd := pdWb.bits.pd(pdWb.bits.misOffset.bits)
715  ifuRedirectCfiUpdate.predTaken := cfiIndex_vec(pdWb.bits.ftqIdx.value).valid
716  ifuRedirectCfiUpdate.target := pdWb.bits.target
717  ifuRedirectCfiUpdate.taken := pdWb.bits.cfiOffset.valid
718  ifuRedirectCfiUpdate.isMisPred := pdWb.bits.misOffset.valid
719
720  val ifuRedirectReg = RegNext(fromIfuRedirect, init=0.U.asTypeOf(Valid(new Redirect)))
721  val ifuRedirectToBpu = WireInit(ifuRedirectReg)
722  ifuFlush := fromIfuRedirect.valid || ifuRedirectToBpu.valid
723
724  ftq_redirect_sram.io.ren.head := fromIfuRedirect.valid
725  ftq_redirect_sram.io.raddr.head := fromIfuRedirect.bits.ftqIdx.value
726
727  ftb_entry_mem.io.raddr.head := fromIfuRedirect.bits.ftqIdx.value
728
729  val toBpuCfi = ifuRedirectToBpu.bits.cfiUpdate
730  toBpuCfi.fromFtqRedirectSram(ftq_redirect_sram.io.rdata.head)
731  when (ifuRedirectReg.bits.cfiUpdate.pd.isRet) {
732    toBpuCfi.target := toBpuCfi.rasEntry.retAddr
733  }
734
735  // *********************************************************************
736  // **************************** wb from exu ****************************
737  // *********************************************************************
738
739  def extractRedirectInfo(wb: Valid[Redirect]) = {
740    val ftqIdx = wb.bits.ftqIdx.value
741    val ftqOffset = wb.bits.ftqOffset
742    val taken = wb.bits.cfiUpdate.taken
743    val mispred = wb.bits.cfiUpdate.isMisPred
744    (wb.valid, ftqIdx, ftqOffset, taken, mispred)
745  }
746
747  // fix mispredict entry
748  val lastIsMispredict = RegNext(
749    stage2Redirect.valid && stage2Redirect.bits.level === RedirectLevel.flushAfter, init = false.B
750  )
751
752  def updateCfiInfo(redirect: Valid[Redirect], isBackend: Boolean = true) = {
753    val (r_valid, r_idx, r_offset, r_taken, r_mispred) = extractRedirectInfo(redirect)
754    val cfiIndex_bits_wen = r_valid && r_taken && r_offset < cfiIndex_vec(r_idx).bits
755    val cfiIndex_valid_wen = r_valid && r_offset === cfiIndex_vec(r_idx).bits
756    when (cfiIndex_bits_wen || cfiIndex_valid_wen) {
757      cfiIndex_vec(r_idx).valid := cfiIndex_bits_wen || cfiIndex_valid_wen && r_taken
758    }
759    when (cfiIndex_bits_wen) {
760      cfiIndex_vec(r_idx).bits := r_offset
761    }
762    update_target(r_idx) := redirect.bits.cfiUpdate.target
763    if (isBackend) {
764      mispredict_vec(r_idx)(r_offset) := r_mispred
765    }
766  }
767
768  when(stage3Redirect.valid && lastIsMispredict) {
769    updateCfiInfo(stage3Redirect)
770  }.elsewhen (ifuRedirectToBpu.valid) {
771    updateCfiInfo(ifuRedirectToBpu, isBackend=false)
772  }
773
774  // ***********************************************************************************
775  // **************************** flush ptr and state queue ****************************
776  // ***********************************************************************************
777
778  class RedirectInfo extends Bundle {
779    val valid = Bool()
780    val ftqIdx = new FtqPtr
781    val ftqOffset = UInt(4.W)
782    val flushItSelf = Bool()
783    def apply(redirect: Valid[Redirect]) = {
784      this.valid := redirect.valid
785      this.ftqIdx := redirect.bits.ftqIdx
786      this.ftqOffset := redirect.bits.ftqOffset
787      this.flushItSelf := RedirectLevel.flushItself(redirect.bits.level)
788      this
789    }
790  }
791  val redirectVec = Wire(Vec(3, new RedirectInfo))
792  val roqRedirect = Wire(Valid(new Redirect))
793  roqRedirect := DontCare
794  roqRedirect.valid := roqFlush.valid
795  roqRedirect.bits.ftqIdx := roqFlush.bits.ftqIdx
796  roqRedirect.bits.ftqOffset := roqFlush.bits.ftqOffset
797  roqRedirect.bits.level := RedirectLevel.flush
798
799  redirectVec.zip(Seq(roqRedirect, stage2Redirect, fromIfuRedirect)).map {
800    case (ve, r) => ve(r)
801  }
802
803  // when redirect, we should reset ptrs and status queues
804  when(redirectVec.map(r => r.valid).reduce(_||_)){
805    val r = PriorityMux(redirectVec.map(r => (r.valid -> r)))
806    val notIfu = redirectVec.dropRight(1).map(r => r.valid).reduce(_||_)
807    val (idx, offset, flushItSelf) = (r.ftqIdx, r.ftqOffset, r.flushItSelf)
808    val next = idx + 1.U
809    bpuPtr := next
810    ifuPtr := next
811    ifuWbPtr := next
812    when (notIfu) {
813      commitStateQueue(idx.value).zipWithIndex.foreach({ case (s, i) =>
814        when(i.U > offset || i.U === offset && flushItSelf){
815          s := c_invalid
816        }
817      })
818    }
819  }
820
821  // only the valid bit is actually needed
822  io.toIfu.redirect := DontCare
823  io.toIfu.redirect.valid := stage2Flush
824
825  // commit
826  for (c <- io.fromBackend.roq_commits) {
827    when(c.valid) {
828      commitStateQueue(c.bits.ftqIdx.value)(c.bits.ftqOffset) := c_commited
829      // TODO: remove this
830      // For instruction fusions, we also update the next instruction
831      when (c.bits.isFused === 1.U) {
832        commitStateQueue(c.bits.ftqIdx.value)(c.bits.ftqOffset + 1.U) := c_commited
833      }.elsewhen(c.bits.isFused === 2.U) {
834        commitStateQueue(c.bits.ftqIdx.value)(c.bits.ftqOffset + 2.U) := c_commited
835      }.elsewhen(c.bits.isFused === 3.U) {
836        val index = (c.bits.ftqIdx + 1.U).value
837        commitStateQueue(index)(0) := c_commited
838      }.elsewhen(c.bits.isFused === 4.U) {
839        val index = (c.bits.ftqIdx + 1.U).value
840        commitStateQueue(index)(1) := c_commited
841      }
842    }
843  }
844
845  // ****************************************************************
846  // **************************** to bpu ****************************
847  // ****************************************************************
848
849  io.toBpu.redirect <> Mux(fromBackendRedirect.valid, fromBackendRedirect, ifuRedirectToBpu)
850
851  val canCommit = commPtr =/= ifuWbPtr &&
852    Cat(commitStateQueue(commPtr.value).map(s => {
853      s === c_invalid || s === c_commited
854    })).andR()
855
856  // commit reads
857  ftq_pc_mem.io.raddr.last := commPtr.value
858  val commit_pc_bundle = ftq_pc_mem.io.rdata.last
859  ftq_pd_mem.io.raddr.last := commPtr.value
860  val commit_pd = ftq_pd_mem.io.rdata.last
861  ftq_redirect_sram.io.ren.last := canCommit
862  ftq_redirect_sram.io.raddr.last := commPtr.value
863  val commit_spec_meta = ftq_redirect_sram.io.rdata.last
864  ftq_meta_1r_sram.io.ren(0) := canCommit
865  ftq_meta_1r_sram.io.raddr(0) := commPtr.value
866  val commit_meta = ftq_meta_1r_sram.io.rdata(0)
867  ftb_entry_mem.io.raddr.last := commPtr.value
868  val commit_ftb_entry = ftb_entry_mem.io.rdata.last
869
870  // need one cycle to read mem and srams
871  val do_commit = RegNext(canCommit, init=false.B)
872  val do_commit_ptr = RegNext(commPtr)
873  when (canCommit) { commPtr := commPtr + 1.U }
874  val commit_state = RegNext(commitStateQueue(commPtr.value))
875  val commit_cfi = WireInit(RegNext(cfiIndex_vec(commPtr.value)))
876  when (commit_state(commit_cfi.bits) =/= c_commited) {
877    commit_cfi.valid := false.B
878  }
879
880  val commit_mispredict = VecInit((RegNext(mispredict_vec(commPtr.value)) zip commit_state).map {
881    case (mis, state) => mis && state === c_commited
882  })
883  val commit_hit = RegNext(entry_hit_status(commPtr.value))
884  val commit_target = RegNext(update_target(commPtr.value))
885  val commit_valid = commit_hit === h_hit || commit_cfi.valid // hit or taken
886
887
888  io.toBpu.update := DontCare
889  io.toBpu.update.valid := commit_valid && do_commit
890  val update = io.toBpu.update.bits
891  update.false_hit   := commit_hit === h_false_hit
892  update.pc          := commit_pc_bundle.startAddr
893  update.preds.hit   := commit_hit === h_hit || commit_hit === h_false_hit
894  update.meta        := commit_meta.meta
895  update.full_target := commit_target
896  update.fromFtqRedirectSram(commit_spec_meta)
897
898  val commit_real_hit = commit_hit === h_hit
899  val update_ftb_entry = update.ftb_entry
900
901  val ftbEntryGen = Module(new FTBEntryGen).io
902  ftbEntryGen.start_addr     := commit_pc_bundle.startAddr
903  ftbEntryGen.old_entry      := commit_ftb_entry
904  ftbEntryGen.pd             := commit_pd
905  ftbEntryGen.cfiIndex       := commit_cfi
906  ftbEntryGen.target         := commit_target
907  ftbEntryGen.hit            := commit_real_hit
908  ftbEntryGen.mispredict_vec := commit_mispredict
909
910  update_ftb_entry         := ftbEntryGen.new_entry
911  update.new_br_insert_pos := ftbEntryGen.new_br_insert_pos
912  update.mispred_mask      := ftbEntryGen.mispred_mask
913  update.old_entry         := ftbEntryGen.is_old_entry
914  update.preds.taken_mask  := ftbEntryGen.taken_mask
915
916  // ******************************************************************************
917  // **************************** commit perf counters ****************************
918  // ******************************************************************************
919
920  val commit_inst_mask    = VecInit(commit_state.map(c => c === c_commited && do_commit)).asUInt
921  val commit_mispred_mask = commit_mispredict.asUInt
922  val commit_not_mispred_mask = ~commit_mispred_mask
923
924  val commit_br_mask = commit_pd.brMask.asUInt
925  val commit_jmp_mask = UIntToOH(commit_pd.jmpOffset) & Fill(PredictWidth, commit_pd.jmpInfo.valid.asTypeOf(UInt(1.W)))
926  val commit_cfi_mask = (commit_br_mask | commit_jmp_mask)
927
928  val mbpInstrs = commit_inst_mask & commit_cfi_mask
929
930  val mbpRights = mbpInstrs & commit_not_mispred_mask
931  val mbpWrongs = mbpInstrs & commit_mispred_mask
932
933  io.bpuInfo.bpRight := PopCount(mbpRights)
934  io.bpuInfo.bpWrong := PopCount(mbpWrongs)
935
936  // Cfi Info
937  for (i <- 0 until PredictWidth) {
938    val pc = commit_pc_bundle.startAddr + (i * instBytes).U
939    val v = commit_state(i) === c_commited
940    val isBr = commit_pd.brMask(i)
941    val isJmp = commit_pd.jmpInfo.valid && commit_pd.jmpOffset === i.U
942    val isCfi = isBr || isJmp
943    val isTaken = commit_cfi.valid && commit_cfi.bits === i.U
944    val misPred = commit_mispredict(i)
945    val ghist = commit_spec_meta.ghist.predHist
946    val predCycle = commit_meta.meta(63, 0)
947    val target = commit_target
948
949    val brIdx = OHToUInt(Reverse(Cat(update_ftb_entry.brValids.zip(update_ftb_entry.brOffset).map{case(v, offset) => v && offset === i.U})))
950    val inFtbEntry = update_ftb_entry.brValids.zip(update_ftb_entry.brOffset).map{case(v, offset) => v && offset === i.U}.reduce(_||_)
951    val addIntoHist = ((commit_hit === h_hit) && inFtbEntry) || ((!(commit_hit === h_hit) && i.U === commit_cfi.bits && isBr && commit_cfi.valid))
952    XSDebug(v && do_commit && isCfi, p"cfi_update: isBr(${isBr}) pc(${Hexadecimal(pc)}) " +
953    p"taken(${isTaken}) mispred(${misPred}) cycle($predCycle) hist(${Hexadecimal(ghist)}) " +
954    p"startAddr(${Hexadecimal(commit_pc_bundle.startAddr)}) AddIntoHist(${addIntoHist}) " +
955    p"brInEntry(${inFtbEntry}) brIdx(${brIdx}) target(${Hexadecimal(target)})\n")
956  }
957
958  val enq = io.fromBpu.resp
959  val perf_redirect = io.fromBackend.stage2Redirect
960
961  XSPerfAccumulate("entry", validEntries)
962  XSPerfAccumulate("bpu_to_ftq_stall", enq.valid && !enq.ready)
963  XSPerfAccumulate("mispredictRedirect", perf_redirect.valid && RedirectLevel.flushAfter === perf_redirect.bits.level)
964  XSPerfAccumulate("replayRedirect", perf_redirect.valid && RedirectLevel.flushItself(perf_redirect.bits.level))
965  XSPerfAccumulate("predecodeRedirect", fromIfuRedirect.valid)
966
967  XSPerfAccumulate("to_ifu_bubble", io.toIfu.req.ready && !io.toIfu.req.valid)
968
969  XSPerfAccumulate("to_ifu_stall", io.toIfu.req.valid && !io.toIfu.req.ready)
970  XSPerfAccumulate("from_bpu_real_bubble", !enq.valid && enq.ready && allowBpuIn)
971
972  val from_bpu = io.fromBpu.resp.bits
973  def in_entry_len_map_gen(resp: BranchPredictionBundle)(stage: String) = {
974    val entry_len = (resp.ftb_entry.getFallThrough(resp.pc) - resp.pc) >> instOffsetBits
975    val entry_len_recording_vec = (1 to PredictWidth+1).map(i => entry_len === i.U)
976    val entry_len_map = (1 to PredictWidth+1).map(i =>
977      f"${stage}_ftb_entry_len_$i" -> (entry_len_recording_vec(i-1) && resp.valid)
978    ).foldLeft(Map[String, UInt]())(_+_)
979    entry_len_map
980  }
981  val s1_entry_len_map = in_entry_len_map_gen(from_bpu.s1)("s1")
982  val s2_entry_len_map = in_entry_len_map_gen(from_bpu.s2)("s2")
983  val s3_entry_len_map = in_entry_len_map_gen(from_bpu.s3)("s3")
984
985  val to_ifu = io.toIfu.req.bits
986  val to_ifu_entry_len = (to_ifu.fallThruAddr - to_ifu.startAddr) >> instOffsetBits
987  val to_ifu_entry_len_recording_vec = (1 to PredictWidth+1).map(i => to_ifu_entry_len === i.U)
988  val to_ifu_entry_len_map = (1 to PredictWidth+1).map(i =>
989    f"to_ifu_ftb_entry_len_$i" -> (to_ifu_entry_len_recording_vec(i-1) && io.toIfu.req.fire)
990  ).foldLeft(Map[String, UInt]())(_+_)
991
992
993
994  val commit_num_inst_recording_vec = (1 to PredictWidth).map(i => PopCount(commit_inst_mask) === i.U)
995  val commit_num_inst_map = (1 to PredictWidth).map(i =>
996    f"commit_num_inst_$i" -> (commit_num_inst_recording_vec(i-1) && do_commit)
997  ).foldLeft(Map[String, UInt]())(_+_)
998
999
1000
1001  val commit_jal_mask  = UIntToOH(commit_pd.jmpOffset) & Fill(PredictWidth, commit_pd.hasJal.asTypeOf(UInt(1.W)))
1002  val commit_jalr_mask = UIntToOH(commit_pd.jmpOffset) & Fill(PredictWidth, commit_pd.hasJalr.asTypeOf(UInt(1.W)))
1003  val commit_call_mask = UIntToOH(commit_pd.jmpOffset) & Fill(PredictWidth, commit_pd.hasCall.asTypeOf(UInt(1.W)))
1004  val commit_ret_mask  = UIntToOH(commit_pd.jmpOffset) & Fill(PredictWidth, commit_pd.hasRet.asTypeOf(UInt(1.W)))
1005
1006
1007  val mbpBRights = mbpRights & commit_br_mask
1008  val mbpJRights = mbpRights & commit_jal_mask
1009  val mbpIRights = mbpRights & commit_jalr_mask
1010  val mbpCRights = mbpRights & commit_call_mask
1011  val mbpRRights = mbpRights & commit_ret_mask
1012
1013  val mbpBWrongs = mbpWrongs & commit_br_mask
1014  val mbpJWrongs = mbpWrongs & commit_jal_mask
1015  val mbpIWrongs = mbpWrongs & commit_jalr_mask
1016  val mbpCWrongs = mbpWrongs & commit_call_mask
1017  val mbpRWrongs = mbpWrongs & commit_ret_mask
1018
1019  val update_valid = io.toBpu.update.valid
1020  def u(cond: Bool) = update_valid && cond
1021  val ftb_false_hit = u(update.false_hit)
1022  // assert(!ftb_false_hit)
1023  val ftb_hit = u(commit_hit === h_hit)
1024
1025  val ftb_new_entry = u(ftbEntryGen.is_init_entry)
1026  val ftb_new_entry_only_br = ftb_new_entry && !update.ftb_entry.jmpValid
1027  val ftb_new_entry_only_jmp = ftb_new_entry && !update.ftb_entry.brValids(0)
1028  val ftb_new_entry_has_br_and_jmp = ftb_new_entry && update.ftb_entry.brValids(0) && update.ftb_entry.jmpValid
1029
1030  val ftb_old_entry = u(ftbEntryGen.is_old_entry)
1031
1032  val ftb_modified_entry = u(ftbEntryGen.is_new_br || ftbEntryGen.is_jalr_target_modified || ftbEntryGen.is_always_taken_modified)
1033  val ftb_modified_entry_new_br = u(ftbEntryGen.is_new_br)
1034  val ftb_modified_entry_jalr_target_modified = u(ftbEntryGen.is_jalr_target_modified)
1035  val ftb_modified_entry_br_full = ftb_modified_entry && ftbEntryGen.is_br_full
1036  val ftb_modified_entry_always_taken = ftb_modified_entry && ftbEntryGen.is_always_taken_modified
1037
1038  val ftb_entry_len = (ftbEntryGen.new_entry.getFallThrough(update.pc) - update.pc) >> instOffsetBits
1039  val ftb_entry_len_recording_vec = (1 to PredictWidth+1).map(i => ftb_entry_len === i.U)
1040  val ftb_init_entry_len_map = (1 to PredictWidth+1).map(i =>
1041    f"ftb_init_entry_len_$i" -> (ftb_entry_len_recording_vec(i-1) && ftb_new_entry)
1042  ).foldLeft(Map[String, UInt]())(_+_)
1043  val ftb_modified_entry_len_map = (1 to PredictWidth+1).map(i =>
1044    f"ftb_modified_entry_len_$i" -> (ftb_entry_len_recording_vec(i-1) && ftb_modified_entry)
1045  ).foldLeft(Map[String, UInt]())(_+_)
1046
1047  val ftq_occupancy_map = (0 to FtqSize).map(i =>
1048    f"ftq_has_entry_$i" ->( validEntries === i.U)
1049  ).foldLeft(Map[String, UInt]())(_+_)
1050
1051  val perfCountsMap = Map(
1052    "BpInstr" -> PopCount(mbpInstrs),
1053    "BpBInstr" -> PopCount(mbpBRights | mbpBWrongs),
1054    "BpRight"  -> PopCount(mbpRights),
1055    "BpWrong"  -> PopCount(mbpWrongs),
1056    "BpBRight" -> PopCount(mbpBRights),
1057    "BpBWrong" -> PopCount(mbpBWrongs),
1058    "BpJRight" -> PopCount(mbpJRights),
1059    "BpJWrong" -> PopCount(mbpJWrongs),
1060    "BpIRight" -> PopCount(mbpIRights),
1061    "BpIWrong" -> PopCount(mbpIWrongs),
1062    "BpCRight" -> PopCount(mbpCRights),
1063    "BpCWrong" -> PopCount(mbpCWrongs),
1064    "BpRRight" -> PopCount(mbpRRights),
1065    "BpRWrong" -> PopCount(mbpRWrongs),
1066
1067    "ftb_false_hit"                -> PopCount(ftb_false_hit),
1068    "ftb_hit"                      -> PopCount(ftb_hit),
1069    "ftb_new_entry"                -> PopCount(ftb_new_entry),
1070    "ftb_new_entry_only_br"        -> PopCount(ftb_new_entry_only_br),
1071    "ftb_new_entry_only_jmp"       -> PopCount(ftb_new_entry_only_jmp),
1072    "ftb_new_entry_has_br_and_jmp" -> PopCount(ftb_new_entry_has_br_and_jmp),
1073    "ftb_old_entry"                -> PopCount(ftb_old_entry),
1074    "ftb_modified_entry"           -> PopCount(ftb_modified_entry),
1075    "ftb_modified_entry_new_br"    -> PopCount(ftb_modified_entry_new_br),
1076    "ftb_jalr_target_modified"     -> PopCount(ftb_modified_entry_jalr_target_modified),
1077    "ftb_modified_entry_br_full"   -> PopCount(ftb_modified_entry_br_full),
1078    "ftb_modified_entry_always_taken" -> PopCount(ftb_modified_entry_always_taken)
1079  ) ++ ftb_init_entry_len_map ++ ftb_modified_entry_len_map ++ s1_entry_len_map ++
1080  s2_entry_len_map ++ s3_entry_len_map ++
1081  to_ifu_entry_len_map ++ commit_num_inst_map ++ ftq_occupancy_map
1082
1083  for((key, value) <- perfCountsMap) {
1084    XSPerfAccumulate(key, value)
1085  }
1086
1087  // --------------------------- Debug --------------------------------
1088  // XSDebug(enq_fire, p"enq! " + io.fromBpu.resp.bits.toPrintable)
1089  XSDebug(io.toIfu.req.fire, p"fire to ifu " + io.toIfu.req.bits.toPrintable)
1090  XSDebug(do_commit, p"deq! [ptr] $do_commit_ptr\n")
1091  XSDebug(true.B, p"[bpuPtr] $bpuPtr, [ifuPtr] $ifuPtr, [ifuWbPtr] $ifuWbPtr [commPtr] $commPtr\n")
1092  XSDebug(true.B, p"[in] v:${io.fromBpu.resp.valid} r:${io.fromBpu.resp.ready} " +
1093    p"[out] v:${io.toIfu.req.valid} r:${io.toIfu.req.ready}\n")
1094  XSDebug(do_commit, p"[deq info] cfiIndex: $commit_cfi, $commit_pc_bundle, target: ${Hexadecimal(commit_target)}\n")
1095
1096  //   def ubtbCheck(commit: FtqEntry, predAns: Seq[PredictorAnswer], isWrong: Bool) = {
1097  //     commit.valids.zip(commit.pd).zip(predAns).zip(commit.takens).map {
1098  //       case (((valid, pd), ans), taken) =>
1099  //       Mux(valid && pd.isBr,
1100  //         isWrong ^ Mux(ans.hit.asBool,
1101  //           Mux(ans.taken.asBool, taken && ans.target === commitEntry.target,
1102  //           !taken),
1103  //         !taken),
1104  //       false.B)
1105  //     }
1106  //   }
1107
1108  //   def btbCheck(commit: FtqEntry, predAns: Seq[PredictorAnswer], isWrong: Bool) = {
1109  //     commit.valids.zip(commit.pd).zip(predAns).zip(commit.takens).map {
1110  //       case (((valid, pd), ans), taken) =>
1111  //       Mux(valid && pd.isBr,
1112  //         isWrong ^ Mux(ans.hit.asBool,
1113  //           Mux(ans.taken.asBool, taken && ans.target === commitEntry.target,
1114  //           !taken),
1115  //         !taken),
1116  //       false.B)
1117  //     }
1118  //   }
1119
1120  //   def tageCheck(commit: FtqEntry, predAns: Seq[PredictorAnswer], isWrong: Bool) = {
1121  //     commit.valids.zip(commit.pd).zip(predAns).zip(commit.takens).map {
1122  //       case (((valid, pd), ans), taken) =>
1123  //       Mux(valid && pd.isBr,
1124  //         isWrong ^ (ans.taken.asBool === taken),
1125  //       false.B)
1126  //     }
1127  //   }
1128
1129  //   def loopCheck(commit: FtqEntry, predAns: Seq[PredictorAnswer], isWrong: Bool) = {
1130  //     commit.valids.zip(commit.pd).zip(predAns).zip(commit.takens).map {
1131  //       case (((valid, pd), ans), taken) =>
1132  //       Mux(valid && (pd.isBr) && ans.hit.asBool,
1133  //         isWrong ^ (!taken),
1134  //           false.B)
1135  //     }
1136  //   }
1137
1138  //   def rasCheck(commit: FtqEntry, predAns: Seq[PredictorAnswer], isWrong: Bool) = {
1139  //     commit.valids.zip(commit.pd).zip(predAns).zip(commit.takens).map {
1140  //       case (((valid, pd), ans), taken) =>
1141  //       Mux(valid && pd.isRet.asBool /*&& taken*/ && ans.hit.asBool,
1142  //         isWrong ^ (ans.target === commitEntry.target),
1143  //           false.B)
1144  //     }
1145  //   }
1146
1147  //   val ubtbRights = ubtbCheck(commitEntry, commitEntry.metas.map(_.ubtbAns), false.B)
1148  //   val ubtbWrongs = ubtbCheck(commitEntry, commitEntry.metas.map(_.ubtbAns), true.B)
1149  //   // btb and ubtb pred jal and jalr as well
1150  //   val btbRights = btbCheck(commitEntry, commitEntry.metas.map(_.btbAns), false.B)
1151  //   val btbWrongs = btbCheck(commitEntry, commitEntry.metas.map(_.btbAns), true.B)
1152  //   val tageRights = tageCheck(commitEntry, commitEntry.metas.map(_.tageAns), false.B)
1153  //   val tageWrongs = tageCheck(commitEntry, commitEntry.metas.map(_.tageAns), true.B)
1154
1155  //   val loopRights = loopCheck(commitEntry, commitEntry.metas.map(_.loopAns), false.B)
1156  //   val loopWrongs = loopCheck(commitEntry, commitEntry.metas.map(_.loopAns), true.B)
1157
1158  //   val rasRights = rasCheck(commitEntry, commitEntry.metas.map(_.rasAns), false.B)
1159  //   val rasWrongs = rasCheck(commitEntry, commitEntry.metas.map(_.rasAns), true.B)
1160
1161}