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