xref: /XiangShan/src/main/scala/xiangshan/frontend/FauFTB.scala (revision 03426fe221938ab0f0d83457b4c77734b35fdc71)
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 scala.{Tuple2 => &}
23import utility._
24import xiangshan._
25
26trait FauFTBParams extends HasXSParameter with HasBPUConst {
27  val numWays = 32
28  val tagSize = 16
29
30  val TAR_STAT_SZ = 2
31  def TAR_FIT     = 0.U(TAR_STAT_SZ.W)
32  def TAR_OVF     = 1.U(TAR_STAT_SZ.W)
33  def TAR_UDF     = 2.U(TAR_STAT_SZ.W)
34
35  def BR_OFFSET_LEN  = 12
36  def JMP_OFFSET_LEN = 20
37
38  def getTag(pc: UInt) = pc(tagSize + instOffsetBits - 1, instOffsetBits)
39}
40
41class FauFTBEntry(implicit p: Parameters) extends FTBEntry()(p) {}
42
43class FauFTBWay(implicit p: Parameters) extends XSModule with FauFTBParams {
44  val io = IO(new Bundle {
45    val req_tag        = Input(UInt(tagSize.W))
46    val resp           = Output(new FauFTBEntry)
47    val resp_hit       = Output(Bool())
48    val update_req_tag = Input(UInt(tagSize.W))
49    val update_hit     = Output(Bool())
50    val write_valid    = Input(Bool())
51    val write_entry    = Input(new FauFTBEntry)
52    val write_tag      = Input(UInt(tagSize.W))
53    val tag_read       = Output(UInt(tagSize.W))
54  })
55
56  val data  = Reg(new FauFTBEntry)
57  val tag   = Reg(UInt(tagSize.W))
58  val valid = RegInit(false.B)
59
60  io.resp     := data
61  io.resp_hit := tag === io.req_tag && valid
62  // write bypass to avoid multiple hit
63  io.update_hit := ((tag === io.update_req_tag) && valid) ||
64    ((io.write_tag === io.update_req_tag) && io.write_valid)
65  io.tag_read := tag
66
67  when(io.write_valid) {
68    when(!valid) {
69      valid := true.B
70    }
71    tag  := io.write_tag
72    data := io.write_entry
73  }
74}
75
76class FauFTB(implicit p: Parameters) extends BasePredictor with FauFTBParams {
77
78  class FauFTBMeta(implicit p: Parameters) extends XSBundle with FauFTBParams {
79    val pred_way = if (!env.FPGAPlatform) Some(UInt(log2Ceil(numWays).W)) else None
80    val hit      = Bool()
81  }
82  val resp_meta             = Wire(new FauFTBMeta)
83  override val meta_size    = resp_meta.getWidth
84  override val is_fast_pred = true
85
86  val ways = Seq.tabulate(numWays)(w => Module(new FauFTBWay))
87  // numWays * numBr
88  val ctrs                = Seq.tabulate(numWays)(w => Seq.tabulate(numBr)(b => RegInit(2.U(2.W))))
89  val replacer            = ReplacementPolicy.fromString("plru", numWays)
90  val replacer_touch_ways = Wire(Vec(2, Valid(UInt(log2Ceil(numWays).W))))
91
92  // pred req
93  ways.foreach(_.io.req_tag := getTag(s1_pc_dup(0)))
94
95  // pred resp
96  val s1_hit_oh              = VecInit(ways.map(_.io.resp_hit)).asUInt
97  val s1_hit                 = s1_hit_oh.orR
98  val s1_hit_way             = OHToUInt(s1_hit_oh)
99  val s1_possible_full_preds = Wire(Vec(numWays, new FullBranchPrediction(isNotS3 = true)))
100
101  val s1_all_entries = VecInit(ways.map(_.io.resp))
102  for (c & fp & e <- ctrs zip s1_possible_full_preds zip s1_all_entries) {
103    fp.hit      := DontCare
104    fp.multiHit := false.B
105    fp.fromFtbEntry(e, s1_pc_dup(0))
106    for (i <- 0 until numBr) {
107      fp.br_taken_mask(i) := c(i)(1) || e.strong_bias(i)
108    }
109  }
110  val s1_hit_full_pred   = Mux1H(s1_hit_oh, s1_possible_full_preds)
111  val s1_hit_fauftbentry = Mux1H(s1_hit_oh, s1_all_entries)
112  XSError(PopCount(s1_hit_oh) > 1.U, "fauftb has multiple hits!\n")
113  val fauftb_enable = RegNext(io.ctrl.ubtb_enable)
114  io.out.s1.full_pred.map(_ := s1_hit_full_pred)
115  io.out.s1.full_pred.map(_.hit := s1_hit && fauftb_enable)
116  io.fauftb_entry_out     := s1_hit_fauftbentry
117  io.fauftb_entry_hit_out := s1_hit && fauftb_enable
118
119  // Illegal check for FTB entry reading
120  val s1_pc_startLower = Cat(0.U(1.W), s1_pc_dup(0)(instOffsetBits + log2Ceil(PredictWidth) - 1, instOffsetBits))
121  val uftb_entry_endLowerwithCarry = Cat(s1_hit_fauftbentry.carry, s1_hit_fauftbentry.pftAddr)
122  val fallThroughErr               = s1_pc_startLower + PredictWidth.U >= uftb_entry_endLowerwithCarry
123  when(io.s1_fire(0) && s1_hit) {
124    assert(fallThroughErr, s"FauFTB read entry fallThrough address error!")
125  }
126
127  // assign metas
128  io.out.last_stage_meta := resp_meta.asUInt
129  resp_meta.hit          := RegEnable(RegEnable(s1_hit, io.s1_fire(0)), io.s2_fire(0))
130  if (resp_meta.pred_way.isDefined) {
131    resp_meta.pred_way.get := RegEnable(RegEnable(s1_hit_way, io.s1_fire(0)), io.s2_fire(0))
132  }
133
134  // pred update replacer state
135  val s1_fire = io.s1_fire(0)
136  replacer_touch_ways(0).valid := RegNext(s1_fire(0) && s1_hit)
137  replacer_touch_ways(0).bits  := RegEnable(s1_hit_way, s1_fire(0) && s1_hit)
138
139  /********************** update ***********************/
140  // s0: update_valid, read and tag comparison
141  // s1: alloc_way and write
142
143  // s0
144  val u_valid = RegNext(io.update.valid, init = false.B)
145  val u_bits  = RegEnable(io.update.bits, io.update.valid)
146
147  // The pc register has been moved outside of predictor, pc field of update bundle and other update data are not in the same stage
148  // so io.update.bits.pc is used directly here
149  val u_pc = io.update.bits.pc
150
151  val u_meta   = u_bits.meta.asTypeOf(new FauFTBMeta)
152  val u_s0_tag = getTag(u_pc)
153  ways.foreach(_.io.update_req_tag := u_s0_tag)
154  val u_s0_hit_oh = VecInit(ways.map(_.io.update_hit)).asUInt
155  val u_s0_hit    = u_s0_hit_oh.orR
156  val u_s0_br_update_valids =
157    VecInit((0 until numBr).map(w =>
158      u_bits.ftb_entry.brValids(w) && u_valid && !u_bits.ftb_entry.strong_bias(w) &&
159        !(PriorityEncoder(u_bits.br_taken_mask) < w.U)
160    ))
161
162  // s1
163  val u_s1_valid            = RegNext(u_valid)
164  val u_s1_tag              = RegEnable(u_s0_tag, u_valid)
165  val u_s1_hit_oh           = RegEnable(u_s0_hit_oh, u_valid)
166  val u_s1_hit              = RegEnable(u_s0_hit, u_valid)
167  val u_s1_alloc_way        = replacer.way
168  val u_s1_write_way_oh     = Mux(u_s1_hit, u_s1_hit_oh, UIntToOH(u_s1_alloc_way))
169  val u_s1_ftb_entry        = RegEnable(u_bits.ftb_entry, u_valid)
170  val u_s1_ways_write_valid = VecInit((0 until numWays).map(w => u_s1_write_way_oh(w).asBool && u_s1_valid))
171  for (w <- 0 until numWays) {
172    ways(w).io.write_valid := u_s1_ways_write_valid(w)
173    ways(w).io.write_tag   := u_s1_tag
174    ways(w).io.write_entry := u_s1_ftb_entry
175  }
176
177  // Illegal check for FTB entry writing
178  val uftb_write_pc          = RegEnable(u_pc, u_valid)
179  val uftb_write_fallThrough = u_s1_ftb_entry.getFallThrough(uftb_write_pc)
180  when(u_s1_valid && u_s1_hit) {
181    assert(
182      uftb_write_pc + (FetchWidth * 4).U >= uftb_write_fallThrough,
183      s"FauFTB write entry fallThrough address error!"
184    )
185  }
186
187  // update saturating counters
188  val u_s1_br_update_valids = RegEnable(u_s0_br_update_valids, u_valid)
189  val u_s1_br_takens        = RegEnable(u_bits.br_taken_mask, u_valid)
190  for (w <- 0 until numWays) {
191    when(u_s1_ways_write_valid(w)) {
192      for (br <- 0 until numBr) {
193        when(u_s1_br_update_valids(br)) {
194          ctrs(w)(br) := satUpdate(ctrs(w)(br), 2, u_s1_br_takens(br))
195        }
196      }
197    }
198  }
199
200  // commit update replacer state
201  replacer_touch_ways(1).valid := u_s1_valid
202  replacer_touch_ways(1).bits  := OHToUInt(u_s1_write_way_oh)
203
204  /******** update replacer *********/
205  replacer.access(replacer_touch_ways)
206
207  /********************** perf counters **********************/
208  val s0_fire_next_cycle = RegNext(io.s0_fire(0))
209  val u_pred_hit_way_map = (0 until numWays).map(w => s0_fire_next_cycle && s1_hit && s1_hit_way === w.U)
210  XSPerfAccumulate("uftb_read_hits", s0_fire_next_cycle && s1_hit)
211  XSPerfAccumulate("uftb_read_misses", s0_fire_next_cycle && !s1_hit)
212  XSPerfAccumulate("uftb_commit_hits", u_valid && u_meta.hit)
213  XSPerfAccumulate("uftb_commit_misses", u_valid && !u_meta.hit)
214  XSPerfAccumulate("uftb_commit_read_hit_pred_miss", u_valid && !u_meta.hit && u_s0_hit_oh.orR)
215  for (w <- 0 until numWays) {
216    XSPerfAccumulate(f"uftb_pred_hit_way_${w}", u_pred_hit_way_map(w))
217    XSPerfAccumulate(f"uftb_replace_way_${w}", !u_s1_hit && u_s1_alloc_way === w.U)
218  }
219
220  if (u_meta.pred_way.isDefined) {
221    val u_commit_hit_way_map = (0 until numWays).map(w => u_valid && u_meta.hit && u_meta.pred_way.get === w.U)
222    for (w <- 0 until numWays) {
223      XSPerfAccumulate(f"uftb_commit_hit_way_${w}", u_commit_hit_way_map(w))
224    }
225  }
226
227  override val perfEvents = Seq(
228    ("fauftb_commit_hit       ", u_valid && u_meta.hit),
229    ("fauftb_commit_miss      ", u_valid && !u_meta.hit)
230  )
231  generatePerfEvent()
232
233}
234