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***************************************************************************************/ 16package xiangshan.mem 17 18import chisel3._ 19import chisel3.util._ 20import chipsalliance.rocketchip.config._ 21import xiangshan._ 22import xiangshan.backend.rob.{RobPtr, RobLsqIO} 23import xiangshan.ExceptionNO._ 24import xiangshan.cache._ 25import utils._ 26import utility._ 27 28class VirtualLoadQueue(implicit p: Parameters) extends XSModule 29 with HasDCacheParameters 30 with HasCircularQueuePtrHelper 31 with HasLoadHelper 32 with HasPerfEvents 33{ 34 val io = IO(new Bundle() { 35 val redirect = Flipped(Valid(new Redirect)) 36 val enq = new LqEnqIO 37 val loadIn = Vec(LoadPipelineWidth, Flipped(DecoupledIO(new LqWriteBundle))) 38 val ldWbPtr = Output(new LqPtr) 39 val lqFull = Output(Bool()) 40 val lqDeq = Output(UInt(log2Up(CommitWidth + 1).W)) 41 val lqCancelCnt = Output(UInt(log2Up(VirtualLoadQueueSize+1).W)) 42 }) 43 44 println("VirtualLoadQueue: size: " + VirtualLoadQueueSize) 45 // VirtualLoadQueue field 46 // +-----------+---------+-------+ 47 // | Allocated | MicroOp | Flags | 48 // +-----------+---------+-------+ 49 // Allocated : entry has been allocated already 50 // MicroOp : inst's microOp 51 // Flags : load flags 52 val allocated = RegInit(VecInit(List.fill(VirtualLoadQueueSize)(false.B))) // The control signals need to explicitly indicate the initial value 53 val uop = Reg(Vec(VirtualLoadQueueSize, new MicroOp)) 54 val addrvalid = RegInit(VecInit(List.fill(VirtualLoadQueueSize)(false.B))) // non-mmio addr is valid 55 val datavalid = RegInit(VecInit(List.fill(VirtualLoadQueueSize)(false.B))) // non-mmio data is valid 56 57 /** 58 * used for debug 59 */ 60 val debug_mmio = Reg(Vec(VirtualLoadQueueSize, Bool())) // mmio: inst is an mmio inst 61 val debug_paddr = Reg(Vec(VirtualLoadQueueSize, UInt(PAddrBits.W))) // mmio: inst's paddr 62 63 // maintain pointers 64 val enqPtrExt = RegInit(VecInit((0 until io.enq.req.length).map(_.U.asTypeOf(new LqPtr)))) 65 val enqPtr = enqPtrExt(0).value 66 val deqPtr = Wire(new LqPtr) 67 val deqPtrNext = Wire(new LqPtr) 68 69 /** 70 * update pointer 71 */ 72 val lastCycleRedirect = RegNext(io.redirect) 73 val lastLastCycleRedirect = RegNext(lastCycleRedirect) 74 75 val validCount = distanceBetween(enqPtrExt(0), deqPtr) 76 val allowEnqueue = validCount <= (VirtualLoadQueueSize - LoadPipelineWidth).U 77 val canEnqueue = io.enq.req.map(_.valid) 78 val needCancel = WireInit(VecInit((0 until VirtualLoadQueueSize).map(i => { 79 uop(i).robIdx.needFlush(io.redirect) && allocated(i) 80 }))) 81 val lastNeedCancel = RegNext(needCancel) 82 val enqCancel = io.enq.req.map(_.bits.robIdx.needFlush(io.redirect)) 83 val lastEnqCancel = PopCount(RegNext(VecInit(canEnqueue.zip(enqCancel).map(x => x._1 && x._2)))) 84 val lastCycleCancelCount = PopCount(lastNeedCancel) 85 86 // update enqueue pointer 87 val enqCount = Mux(io.enq.canAccept && io.enq.sqCanAccept, PopCount(io.enq.req.map(_.valid)), 0.U) 88 val enqPtrExtNextVec = Wire(Vec(io.enq.req.length, new LqPtr)) 89 val enqPtrExtNext = Wire(Vec(io.enq.req.length, new LqPtr)) 90 when (lastCycleRedirect.valid) { 91 // we recover the pointers in the next cycle after redirect 92 enqPtrExtNextVec := VecInit(enqPtrExt.map(_ - (lastCycleCancelCount + lastEnqCancel))) 93 }.otherwise { 94 enqPtrExtNextVec := VecInit(enqPtrExt.map(_ + enqCount)) 95 } 96 97 when (isAfter(enqPtrExtNextVec(0), deqPtrNext)) { 98 enqPtrExtNext := enqPtrExtNextVec 99 } .otherwise { 100 enqPtrExtNext := VecInit((0 until io.enq.req.length).map(i => deqPtrNext + i.U)) 101 } 102 enqPtrExt := enqPtrExtNext 103 104 // update dequeue pointer 105 val DeqPtrMoveStride = CommitWidth 106 require(DeqPtrMoveStride == CommitWidth, "DeqPtrMoveStride must be equal to CommitWidth!") 107 val deqLookupVec = VecInit((0 until DeqPtrMoveStride).map(deqPtr + _.U)) 108 val deqLookup = VecInit(deqLookupVec.map(ptr => allocated(ptr.value) && datavalid(ptr.value) && addrvalid(ptr.value) && ptr =/= enqPtrExt(0))) 109 val deqInSameRedirectCycle = VecInit(deqLookupVec.map(ptr => needCancel(ptr.value))) 110 // make chisel happy 111 val deqCountMask = Wire(UInt(DeqPtrMoveStride.W)) 112 deqCountMask := deqLookup.asUInt & ~deqInSameRedirectCycle.asUInt 113 val commitCount = PopCount(PriorityEncoderOH(~deqCountMask) - 1.U) 114 val lastCommitCount = RegNext(commitCount) 115 116 // update deqPtr 117 // cycle 1: generate deqPtrNext 118 // cycle 2: update deqPtr 119 val deqPtrUpdateEna = lastCommitCount =/= 0.U 120 deqPtrNext := deqPtr + lastCommitCount 121 deqPtr := RegEnable(next = deqPtrNext, init = 0.U.asTypeOf(new LqPtr), enable = deqPtrUpdateEna) 122 123 io.lqDeq := RegNext(lastCommitCount) 124 io.lqCancelCnt := RegNext(lastCycleCancelCount + lastEnqCancel) 125 io.ldWbPtr := deqPtr 126 127 /** 128 * Enqueue at dispatch 129 * 130 * Currently, VirtualLoadQueue only allows enqueue when #emptyEntries > EnqWidth 131 */ 132 io.enq.canAccept := allowEnqueue 133 for (i <- 0 until io.enq.req.length) { 134 val offset = PopCount(io.enq.needAlloc.take(i)) 135 val lqIdx = enqPtrExt(offset) 136 val index = io.enq.req(i).bits.lqIdx.value 137 when (canEnqueue(i) && !enqCancel(i)) { 138 allocated(index) := true.B 139 uop(index) := io.enq.req(i).bits 140 uop(index).lqIdx := lqIdx 141 142 // init 143 addrvalid(index) := false.B 144 datavalid(index) := false.B 145 146 debug_mmio(index) := false.B 147 debug_paddr(index) := 0.U 148 149 XSError(!io.enq.canAccept || !io.enq.sqCanAccept, s"must accept $i\n") 150 XSError(index =/= lqIdx.value, s"must be the same entry $i\n") 151 } 152 io.enq.resp(i) := lqIdx 153 } 154 155 /** 156 * Load commits 157 * 158 * When load commited, mark it as !allocated and move deqPtr forward. 159 */ 160 (0 until DeqPtrMoveStride).map(i => { 161 when (commitCount > i.U) { 162 allocated((deqPtr+i.U).value) := false.B 163 XSError(!allocated((deqPtr+i.U).value), s"why commit invalid entry $i?\n") 164 } 165 }) 166 167 // misprediction recovery / exception redirect 168 // invalidate lq term using robIdx 169 for (i <- 0 until VirtualLoadQueueSize) { 170 when (needCancel(i)) { 171 allocated(i) := false.B 172 } 173 } 174 175 XSDebug(p"(ready, valid): ${io.enq.canAccept}, ${Binary(Cat(io.enq.req.map(_.valid)))}\n") 176 177 /** 178 * Writeback load from load units 179 * 180 * Most load instructions writeback to regfile at the same time. 181 * However, 182 * (1) For ready load instruction (no need replay), it writes back to ROB immediately. 183 */ 184 for(i <- 0 until LoadPipelineWidth) { 185 // most lq status need to be updated immediately after load writeback to lq 186 // flag bits in lq needs to be updated accurately 187 io.loadIn(i).ready := true.B 188 val loadWbIndex = io.loadIn(i).bits.uop.lqIdx.value 189 190 when (io.loadIn(i).valid) { 191 val hasExceptions = ExceptionNO.selectByFu(io.loadIn(i).bits.uop.cf.exceptionVec, lduCfg).asUInt.orR 192 val needReplay = io.loadIn(i).bits.replayInfo.needReplay() 193 194 when (!needReplay) { 195 // update control flag 196 addrvalid(loadWbIndex) := hasExceptions || !io.loadIn(i).bits.tlbMiss 197 datavalid(loadWbIndex) := 198 (if (EnableFastForward) { 199 hasExceptions || 200 io.loadIn(i).bits.mmio || 201 !io.loadIn(i).bits.miss && // dcache miss 202 !io.loadIn(i).bits.dcacheRequireReplay // do not writeback if that inst will be resend from rs 203 } else { 204 hasExceptions || 205 io.loadIn(i).bits.mmio || 206 !io.loadIn(i).bits.miss 207 }) 208 209 // 210 when (io.loadIn(i).bits.lqDataWenDup(1)) { 211 uop(loadWbIndex).pdest := io.loadIn(i).bits.uop.pdest 212 } 213 when (io.loadIn(i).bits.lqDataWenDup(2)) { 214 uop(loadWbIndex).cf := io.loadIn(i).bits.uop.cf 215 } 216 when (io.loadIn(i).bits.lqDataWenDup(3)) { 217 uop(loadWbIndex).ctrl := io.loadIn(i).bits.uop.ctrl 218 } 219 when (io.loadIn(i).bits.lqDataWenDup(4)) { 220 uop(loadWbIndex).debugInfo := io.loadIn(i).bits.uop.debugInfo 221 } 222 uop(loadWbIndex).debugInfo := io.loadIn(i).bits.replayInfo.debug 223 224 // Debug info 225 debug_mmio(loadWbIndex) := io.loadIn(i).bits.mmio 226 debug_paddr(loadWbIndex) := io.loadIn(i).bits.paddr 227 228 XSInfo(io.loadIn(i).valid, "load hit write to lq idx %d pc 0x%x vaddr %x paddr %x mask %x forwardData %x forwardMask: %x mmio %x\n", 229 io.loadIn(i).bits.uop.lqIdx.asUInt, 230 io.loadIn(i).bits.uop.cf.pc, 231 io.loadIn(i).bits.vaddr, 232 io.loadIn(i).bits.paddr, 233 io.loadIn(i).bits.mask, 234 io.loadIn(i).bits.forwardData.asUInt, 235 io.loadIn(i).bits.forwardMask.asUInt, 236 io.loadIn(i).bits.mmio 237 ) 238 } 239 } 240 } 241 242 // perf counter 243 QueuePerf(VirtualLoadQueueSize, validCount, !allowEnqueue) 244 io.lqFull := !allowEnqueue 245 val perfEvents: Seq[(String, UInt)] = Seq() 246 generatePerfEvent() 247 248 // debug info 249 XSDebug("enqPtrExt %d:%d deqPtrExt %d:%d\n", enqPtrExt(0).flag, enqPtr, deqPtr.flag, deqPtr.value) 250 251 def PrintFlag(flag: Bool, name: String): Unit = { 252 when(flag) { 253 XSDebug(false, true.B, name) 254 }.otherwise { 255 XSDebug(false, true.B, " ") 256 } 257 } 258 259 for (i <- 0 until VirtualLoadQueueSize) { 260 XSDebug(i + " pc %x pa %x ", uop(i).cf.pc, debug_paddr(i)) 261 PrintFlag(allocated(i), "v") 262 PrintFlag(allocated(i) && datavalid(i), "d") 263 PrintFlag(allocated(i) && addrvalid(i), "a") 264 PrintFlag(allocated(i) && addrvalid(i) && datavalid(i), "w") 265 XSDebug(false, true.B, "\n") 266 } 267 // end 268} 269