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