xref: /XiangShan/src/main/scala/xiangshan/backend/rename/BusyTable.scala (revision a2ecedc92043c002bd9a2fef6d6436da988dedcc)
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.backend.rename
18
19import org.chipsalliance.cde.config.Parameters
20import chisel3._
21import chisel3.util._
22import xiangshan._
23import utils._
24import utility._
25import xiangshan.backend.Bundles._
26import xiangshan.backend.datapath.WbConfig._
27import xiangshan.backend.issue.SchdBlockParams
28import xiangshan.backend.datapath.{DataSource}
29
30class BusyTableReadIO(implicit p: Parameters) extends XSBundle {
31  val req = Input(UInt(PhyRegIdxWidth.W))
32  val resp = Output(Bool())
33  val loadDependency = Vec(LoadPipelineWidth, Output(UInt(LoadDependencyWidth.W)))
34}
35
36
37class VlBusyTableReadIO(implicit p: Parameters) extends XSBundle {
38  val is_nonzero = Output(Bool())
39  val is_vlmax = Output(Bool())
40}
41
42class BusyTable(numReadPorts: Int, numWritePorts: Int, numPhyPregs: Int, pregWB: PregWB)(implicit p: Parameters) extends XSModule with HasPerfEvents {
43  val io = IO(new Bundle() {
44    // set preg state to busy
45    val allocPregs = Vec(RenameWidth, Flipped(ValidIO(UInt(PhyRegIdxWidth.W))))
46    // set preg state to ready (write back regfile + rob walk)
47    val wbPregs = Vec(numWritePorts, Flipped(ValidIO(UInt(PhyRegIdxWidth.W))))
48    // fast wakeup
49    val wakeUpInt = Flipped(backendParams.intSchdParams.get.genIQWakeUpOutValidBundle)
50    val wakeUpFp = Flipped(backendParams.fpSchdParams.get.genIQWakeUpOutValidBundle)
51    val wakeUpVec = Flipped(backendParams.vfSchdParams.get.genIQWakeUpOutValidBundle)
52    val wakeUpMem = Flipped(backendParams.memSchdParams.get.genIQWakeUpOutValidBundle)
53    // cancelFromDatapath
54    val og0Cancel = Input(ExuVec())
55    // cancelFromMem
56    val ldCancel = Vec(backendParams.LdExuCnt, Flipped(new LoadCancelIO))
57    // read preg state
58    val read = Vec(numReadPorts, new BusyTableReadIO)
59  })
60
61  val allExuParams = backendParams.allExuParams
62  val intBusyTableNeedLoadCancel = allExuParams.map(x =>
63    x.needLoadDependency && x.writeIntRf && x.iqWakeUpSourcePairs.map(y => y.sink.getExuParam(allExuParams).readIntRf).foldLeft(false)(_ || _)
64  ).reduce(_ || _)
65  val fpBusyTableNeedLoadCancel = false
66  val vfBusyTableNeedLoadCancel = allExuParams.map(x =>
67    x.needLoadDependency && x.writeVfRf && x.iqWakeUpSourcePairs.map(y => y.sink.getExuParam(allExuParams).readVecRf).foldLeft(false)(_ || _)
68  ).reduce(_ || _)
69  val v0BusyTableNeedLoadCancel = allExuParams.map(x =>
70    x.needLoadDependency && x.writeV0Rf && x.iqWakeUpSourcePairs.map(y => y.sink.getExuParam(allExuParams).readVecRf).foldLeft(false)(_ || _)
71  ).reduce(_ || _)
72  val vlBusyTableNeedLoadCancel = allExuParams.map(x =>
73    x.needLoadDependency && x.writeVlRf && x.iqWakeUpSourcePairs.map(y => y.sink.getExuParam(allExuParams).readVlRf).foldLeft(false)(_ || _)
74  ).reduce(_ || _)
75  val needLoadCancel = pregWB match {
76    case IntWB(_, _) => intBusyTableNeedLoadCancel
77    case FpWB(_, _) => fpBusyTableNeedLoadCancel
78    case VfWB(_, _) => vfBusyTableNeedLoadCancel
79    case V0WB(_, _) => v0BusyTableNeedLoadCancel
80    case VlWB(_, _) => vlBusyTableNeedLoadCancel
81    case _ => throw new IllegalArgumentException(s"WbConfig ${pregWB} is not permitted")
82  }
83  if (!needLoadCancel) println(s"[BusyTable]: WbConfig ${pregWB} busyTable don't need loadCancel")
84  val loadCancel = if (needLoadCancel) io.ldCancel else 0.U.asTypeOf(io.ldCancel)
85  val allWakeUp = io.wakeUpInt ++ io.wakeUpFp ++ io.wakeUpVec ++ io.wakeUpMem
86  val wakeUpIn = pregWB match {
87    case IntWB(_, _) => allWakeUp.filter{x => x.bits.params.writeIntRf && (x.bits.params.hasLoadExu || x.bits.params.hasAluFu)}
88    case FpWB(_, _) => allWakeUp.filter{x => x.bits.params.writeFpRf && !x.bits.params.hasLoadExu}
89    case VfWB(_, _) => allWakeUp.filter(_.bits.params.writeVfRf)
90    case V0WB(_, _) => allWakeUp.filter(_.bits.params.writeV0Rf)
91    case VlWB(_, _) => allWakeUp.filter(_.bits.params.writeVlRf)
92    case _ => throw new IllegalArgumentException(s"WbConfig ${pregWB} is not permitted")
93  }
94  val loadDependency = RegInit(0.U.asTypeOf(Vec(numPhyPregs, Vec(LoadPipelineWidth, UInt(LoadDependencyWidth.W)))))
95  val shiftLoadDependency = Wire(Vec(wakeUpIn.size, Vec(LoadPipelineWidth, UInt(LoadDependencyWidth.W))))
96  val tableUpdate = Wire(Vec(numPhyPregs, Bool()))
97  val wakeupOHVec = Wire(Vec(numPhyPregs, UInt(wakeUpIn.size.W)))
98
99  def reqVecToMask(rVec: Vec[Valid[UInt]]): UInt = {
100    ParallelOR(rVec.map(v => Mux(v.valid, UIntToOH(v.bits), 0.U)))
101  }
102
103  shiftLoadDependency.zip(wakeUpIn).map{ case (deps, wakeup) =>
104    if (wakeup.bits.params.hasLoadExu) {
105      deps.zipWithIndex.map{ case (dep, i) =>
106        if (backendParams.getLdExuIdx(wakeup.bits.params) == i) dep := 1.U
107        else dep := 0.U
108      }
109    }
110    else {
111      deps.zip(wakeup.bits.loadDependency).map{ case (sink, source) =>
112        sink := source << 1
113      }
114    }
115  }
116
117  wakeupOHVec.zipWithIndex.foreach{ case (wakeupOH, idx) =>
118    val tmp = pregWB match {
119      case IntWB(_, _) => wakeUpIn.map(x => x.valid && x.bits.rfWen  && UIntToOH(x.bits.pdest)(idx) && !LoadShouldCancel(Some(x.bits.loadDependency), loadCancel) && !(x.bits.is0Lat && io.og0Cancel(x.bits.params.exuIdx)))
120      case FpWB(_, _)  => wakeUpIn.map(x => x.valid && x.bits.fpWen  && UIntToOH(x.bits.pdest)(idx) && !LoadShouldCancel(Some(x.bits.loadDependency), loadCancel) && !(x.bits.is0Lat && io.og0Cancel(x.bits.params.exuIdx)))
121      case VfWB(_, _)  => wakeUpIn.map(x => x.valid && x.bits.vecWen && UIntToOH(x.bits.pdest)(idx) && !LoadShouldCancel(Some(x.bits.loadDependency), loadCancel) && !(x.bits.is0Lat && io.og0Cancel(x.bits.params.exuIdx)))
122      case V0WB(_, _)  => wakeUpIn.map(x => x.valid && x.bits.v0Wen  && UIntToOH(x.bits.pdest)(idx) && !LoadShouldCancel(Some(x.bits.loadDependency), loadCancel) && !(x.bits.is0Lat && io.og0Cancel(x.bits.params.exuIdx)))
123      case VlWB(_, _)  => wakeUpIn.map(x => x.valid && x.bits.vlWen  && UIntToOH(x.bits.pdest)(idx) && !LoadShouldCancel(Some(x.bits.loadDependency), loadCancel) && !(x.bits.is0Lat && io.og0Cancel(x.bits.params.exuIdx)))
124      case _ => throw new IllegalArgumentException(s"WbConfig ${pregWB} is not permitted")
125    }
126    wakeupOH := (if (wakeUpIn.nonEmpty) VecInit(tmp.toSeq).asUInt else 0.U)
127  }
128  val wbMask = reqVecToMask(io.wbPregs)
129  val allocMask = reqVecToMask(io.allocPregs)
130  val wakeUpMask = VecInit(wakeupOHVec.map(_.orR).toSeq).asUInt
131  val ldCancelMask = loadDependency.map(x => LoadShouldCancel(Some(x), loadCancel))
132
133  loadDependency.zipWithIndex.foreach{ case (ldDp, idx) =>
134    when(wakeUpMask(idx)) {
135      ldDp := (if (wakeUpIn.nonEmpty) Mux1H(wakeupOHVec(idx), shiftLoadDependency) else 0.U.asTypeOf(ldDp))
136    }
137      .elsewhen(ldDp.map(x => x.orR).reduce(_ | _)) {
138        ldDp := VecInit(ldDp.map(x => x << 1))
139      }
140  }
141
142  /*
143  we can ensure that the following conditions are mutually exclusive
144  wakeUp and cancel (same pdest) may arrive at the same cycle
145  for a pdest:
146    rename alloc => wakeUp / cancel => ... => wakeUp / cancel => wakeUp
147  or
148    rename alloc => wbMask  //TODO we still need wbMask because wakeUp signal is partial now
149  in wakeUpMask, we filter ogCancel and loadTransCancel at the same cycle
150   */
151  val table = VecInit((0 until numPhyPregs).zip(tableUpdate).map{ case (idx, update) =>
152    RegEnable(update, 0.U(1.W), allocMask(idx) || ldCancelMask(idx) || wakeUpMask(idx) || wbMask(idx))
153  }).asUInt
154
155  tableUpdate.zipWithIndex.foreach{ case (update, idx) =>
156    when(wakeUpMask(idx) || wbMask(idx)) {
157      update := false.B                                   //ready
158    }
159      .elsewhen(allocMask(idx) || ldCancelMask(idx)) {
160        update := true.B                                    //busy
161        if (idx == 0 && pregWB.isInstanceOf[IntWB]) {
162          // Int RegFile 0 is always ready
163          update := false.B
164        }
165      }
166      .otherwise {
167        update := table(idx)
168      }
169  }
170
171  io.read.foreach{ case res =>
172    val readBypass = VecInit(io.allocPregs.map(x => x.valid && x.bits === res.req))
173    res.resp := !(table(res.req) || readBypass.asUInt.orR)
174    res.loadDependency := (if (needLoadCancel) loadDependency(res.req) else 0.U.asTypeOf(res.loadDependency))
175  }
176
177  val oddTable = table.asBools.zipWithIndex.filter(_._2 % 2 == 1).map(_._1)
178  val evenTable = table.asBools.zipWithIndex.filter(_._2 % 2 == 0).map(_._1)
179  val busyCount = RegNext(RegNext(PopCount(oddTable)) + RegNext(PopCount(evenTable)))
180
181  XSPerfAccumulate("busy_count", PopCount(table))
182
183  val perfEvents = Seq(
184    ("bt_std_freelist_1_4_valid", busyCount < (numPhyPregs / 4).U                                        ),
185    ("bt_std_freelist_2_4_valid", busyCount > (numPhyPregs / 4).U && busyCount <= (numPhyPregs / 2).U    ),
186    ("bt_std_freelist_3_4_valid", busyCount > (numPhyPregs / 2).U && busyCount <= (numPhyPregs * 3 / 4).U),
187    ("bt_std_freelist_4_4_valid", busyCount > (numPhyPregs * 3 / 4).U                                    )
188  )
189  generatePerfEvent()
190
191}
192
193class VlBusyTable(numReadPorts: Int, numWritePorts: Int, numPhyPregs: Int, pregWB: PregWB)(implicit p: Parameters) extends BusyTable(numReadPorts, numWritePorts, numPhyPregs, pregWB) {
194
195  val io_vl_Wb = IO(new Bundle() {
196    val vlWriteBackInfo = new Bundle {
197      val vlFromIntIsZero  = Input(Bool())
198      val vlFromIntIsVlmax = Input(Bool())
199      val vlFromVfIsZero   = Input(Bool())
200      val vlFromVfIsVlmax  = Input(Bool())
201    }
202  })
203  val io_vl_read = IO(new Bundle() {
204    val vlReadInfo = Vec(numReadPorts, new VlBusyTableReadIO)
205  })
206
207  var intSchdVlWbPort = p(XSCoreParamsKey).intSchdVlWbPort
208  var vfSchdVlWbPort = p(XSCoreParamsKey).vfSchdVlWbPort
209
210  val nonzeroTableUpdate = Wire(Vec(numPhyPregs, Bool()))
211  val vlmaxTableUpdate = Wire(Vec(numPhyPregs, Bool()))
212
213  val intVlWb = Mux(io.wbPregs(intSchdVlWbPort).valid, UIntToOH(io.wbPregs(intSchdVlWbPort).bits), 0.U)
214  val vfVlWb = Mux(io.wbPregs(vfSchdVlWbPort).valid, UIntToOH(io.wbPregs(vfSchdVlWbPort).bits), 0.U)
215  // when other ports write back, we cannot know the vl value, so we should set the vl table to busy
216  val otherPortsWb = io.wbPregs.zipWithIndex.filter(x => x._2 != intSchdVlWbPort && x._2 != vfSchdVlWbPort).map(x => Mux(x._1.valid, UIntToOH(x._1.bits), 0.U)).reduce(_ | _)
217
218  val nonzeroTable = VecInit((0 until numPhyPregs).zip(nonzeroTableUpdate).map{ case (idx, update) =>
219    RegEnable(update, 0.U(1.W), allocMask(idx) || ldCancelMask(idx) || intVlWb(idx) || vfVlWb(idx) || otherPortsWb(idx))
220  }).asUInt
221  val vlmaxTable = VecInit((0 until numPhyPregs).zip(vlmaxTableUpdate).map{ case (idx, update) =>
222    RegEnable(update, 0.U(1.W), allocMask(idx) || ldCancelMask(idx) || intVlWb(idx) || vfVlWb(idx) || otherPortsWb(idx))
223  }).asUInt
224
225
226  nonzeroTableUpdate.zipWithIndex.foreach{ case (update, idx) =>
227    when(intVlWb(idx)) {
228      // int schd vl write back, check whether the vl is zero
229      update := io_vl_Wb.vlWriteBackInfo.vlFromIntIsZero
230    }.elsewhen(vfVlWb(idx)) {
231      // vf schd vl write back, check whether the vl is zero
232      update := io_vl_Wb.vlWriteBackInfo.vlFromVfIsZero
233    }.elsewhen(otherPortsWb(idx) || allocMask(idx) || ldCancelMask(idx)) {
234      update := true.B
235    }.otherwise {
236      update := nonzeroTable(idx)
237    }
238  }
239
240  vlmaxTableUpdate.zipWithIndex.foreach{ case (update, idx) =>
241    when(intVlWb(idx)) {
242      // int schd vl write back, check whether the vl is vlmax
243      update := !io_vl_Wb.vlWriteBackInfo.vlFromIntIsVlmax
244    }.elsewhen(vfVlWb(idx)) {
245      // vf schd vl write back, check whether the vl is vlmax
246      update := !io_vl_Wb.vlWriteBackInfo.vlFromVfIsVlmax
247    }.elsewhen(otherPortsWb(idx) || allocMask(idx) || ldCancelMask(idx)) {
248      update := true.B
249    }.otherwise {
250      update := vlmaxTable(idx)
251    }
252  }
253
254  io_vl_read.vlReadInfo.zip(io.read).foreach{ case (vlRes, res) =>
255    vlRes.is_nonzero := !nonzeroTable(res.req)
256    vlRes.is_vlmax := !vlmaxTable(res.req)
257  }
258}
259
260