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 org.chipsalliance.cde.config._ 19import chisel3._ 20import chisel3.util._ 21import utils._ 22import utility._ 23import xiangshan._ 24import xiangshan.backend.rob.RobPtr 25import xiangshan.backend.Bundles.DynInst 26import xiangshan.mem.Bundles._ 27import xiangshan.cache._ 28 29class LoadQueueRAR(implicit p: Parameters) extends XSModule 30 with HasDCacheParameters 31 with HasCircularQueuePtrHelper 32 with HasLoadHelper 33 with HasPerfEvents 34{ 35 val io = IO(new Bundle() { 36 // control 37 val redirect = Flipped(Valid(new Redirect)) 38 39 // violation query 40 val query = Vec(LoadPipelineWidth, Flipped(new LoadNukeQueryIO)) 41 42 // release cacheline 43 val release = Flipped(Valid(new Release)) 44 45 // from VirtualLoadQueue 46 val ldWbPtr = Input(new LqPtr) 47 48 // global 49 val lqFull = Output(Bool()) 50 }) 51 52 private val PartialPAddrStride: Int = 6 53 private val PartialPAddrBits: Int = 16 54 private val PartialPAddrLowBits: Int = (PartialPAddrBits - PartialPAddrStride) / 2 // avoid overlap 55 private val PartialPAddrHighBits: Int = PartialPAddrBits - PartialPAddrLowBits 56 private def boundary(x: Int, h: Int) = if (x < h) Some(x) else None 57 private def lowMapping = (0 until PartialPAddrLowBits).map(i => Seq( 58 boundary(PartialPAddrStride + i , PartialPAddrBits), 59 boundary(PartialPAddrBits - i - 1, PartialPAddrBits) 60 ) 61 ) 62 private def highMapping = (0 until PartialPAddrHighBits).map(i => Seq( 63 boundary(i + PartialPAddrStride , PAddrBits), 64 boundary(i + PartialPAddrStride + 11, PAddrBits), 65 boundary(i + PartialPAddrStride + 22, PAddrBits), 66 boundary(i + PartialPAddrStride + 33, PAddrBits) 67 ) 68 ) 69 private def genPartialPAddr(paddr: UInt) = { 70 val ppaddr_low = Wire(Vec(PartialPAddrLowBits, Bool())) 71 ppaddr_low.zip(lowMapping).foreach { 72 case (bit, mapping) => 73 bit := mapping.filter(_.isDefined).map(x => paddr(x.get)).reduce(_^_) 74 } 75 76 val ppaddr_high = Wire(Vec(PartialPAddrHighBits, Bool())) 77 ppaddr_high.zip(highMapping).foreach { 78 case (bit, mapping) => 79 bit := mapping.filter(_.isDefined).map(x => paddr(x.get)).reduce(_^_) 80 } 81 Cat(ppaddr_high.asUInt, ppaddr_low.asUInt) 82 } 83 84 println("LoadQueueRAR: size: " + LoadQueueRARSize) 85 // LoadQueueRAR field 86 // +-------+-------+-------+----------+ 87 // | Valid | Uop | PAddr | Released | 88 // +-------+-------+-------+----------+ 89 // 90 // Field descriptions: 91 // Allocated : entry is valid. 92 // MicroOp : Micro-op 93 // PAddr : physical address. 94 // Released : DCache released. 95 val allocated = RegInit(VecInit(List.fill(LoadQueueRARSize)(false.B))) // The control signals need to explicitly indicate the initial value 96 val uop = Reg(Vec(LoadQueueRARSize, new DynInst)) 97 val paddrModule = Module(new LqPAddrModule( 98 gen = UInt(PartialPAddrBits.W), 99 numEntries = LoadQueueRARSize, 100 numRead = LoadPipelineWidth, 101 numWrite = LoadPipelineWidth, 102 numWBank = LoadQueueNWriteBanks, 103 numWDelay = 2, 104 numCamPort = LoadPipelineWidth, 105 enableCacheLineCheck = false, // Now `RARQueue` has no need to check cacheline. 106 paddrOffset = 0 // If you need to check cacheline, set the offset relative to the original paddr correctly. 107 )) 108 paddrModule.io := DontCare 109 val released = RegInit(VecInit(List.fill(LoadQueueRARSize)(false.B))) 110 111 // freeliset: store valid entries index. 112 // +---+---+--------------+-----+-----+ 113 // | 0 | 1 | ...... | n-2 | n-1 | 114 // +---+---+--------------+-----+-----+ 115 val freeList = Module(new FreeList( 116 size = LoadQueueRARSize, 117 allocWidth = LoadPipelineWidth, 118 freeWidth = 4, 119 enablePreAlloc = false, 120 moduleName = "LoadQueueRAR freelist" 121 )) 122 freeList.io := DontCare 123 124 // Real-allocation: load_s2 125 // PAddr write needs 2 cycles, release signal should delay 1 cycle so that 126 // load enqueue can catch release. 127 val release1Cycle = io.release 128 // val release2Cycle = RegNext(io.release) 129 // val release2Cycle_dup_lsu = RegNext(io.release) 130 val release2Cycle = RegEnable(io.release, io.release.valid) 131 release2Cycle.valid := RegNext(io.release.valid) 132 //val release2Cycle_dup_lsu = RegEnable(io.release, io.release.valid) 133 134 // LoadQueueRAR enqueue condition: 135 // There are still not completed load instructions before the current load instruction. 136 // (e.g. "not completed" means that load instruction get the data or exception). 137 val canEnqueue = io.query.map(_.req.valid) 138 val cancelEnqueue = io.query.map(_.req.bits.uop.robIdx.needFlush(io.redirect)) 139 val hasNotWritebackedLoad = io.query.map(_.req.bits.uop.lqIdx).map(lqIdx => isAfter(lqIdx, io.ldWbPtr)) 140 val needEnqueue = canEnqueue.zip(hasNotWritebackedLoad).zip(cancelEnqueue).map { case ((v, r), c) => v && r && !c } 141 142 // Allocate logic 143 val acceptedVec = Wire(Vec(LoadPipelineWidth, Bool())) 144 val enqIndexVec = Wire(Vec(LoadPipelineWidth, UInt(log2Up(LoadQueueRARSize).W))) 145 require(LoadQueueRARSize == VirtualLoadQueueSize, "LoadQueueRARSize should be equal to VirtualLoadQueueSize for timing!") 146 147 for ((enq, w) <- io.query.map(_.req).zipWithIndex) { 148 acceptedVec(w) := false.B 149 paddrModule.io.wen(w) := false.B 150 freeList.io.doAllocate(w) := false.B 151 152 freeList.io.allocateReq(w) := true.B 153 154 // Allocate ready 155 val offset = PopCount(needEnqueue.take(w)) 156 val canAccept = freeList.io.canAllocate(offset) 157 val enqIndex = freeList.io.allocateSlot(offset) 158 enq.ready := Mux(needEnqueue(w), canAccept, true.B) 159 160 enqIndexVec(w) := enqIndex 161 when (needEnqueue(w) && enq.ready) { 162 acceptedVec(w) := true.B 163 164 freeList.io.doAllocate(w) := true.B 165 // Allocate new entry 166 allocated(enqIndex) := true.B 167 168 // Write paddr 169 paddrModule.io.wen(w) := true.B 170 paddrModule.io.waddr(w) := enqIndex 171 paddrModule.io.wdata(w) := genPartialPAddr(enq.bits.paddr) 172 173 // Fill info 174 uop(enqIndex) := enq.bits.uop 175 // NC is uncachable and will not be explicitly released. 176 // So NC requests are not allowed to have RAR 177 released(enqIndex) := enq.bits.is_nc || ( 178 enq.bits.data_valid && 179 (release2Cycle.valid && 180 enq.bits.paddr(PAddrBits-1, DCacheLineOffset) === release2Cycle.bits.paddr(PAddrBits-1, DCacheLineOffset) || 181 release1Cycle.valid && 182 enq.bits.paddr(PAddrBits-1, DCacheLineOffset) === release1Cycle.bits.paddr(PAddrBits-1, DCacheLineOffset)) 183 ) 184 } 185 val debug_robIdx = enq.bits.uop.robIdx.asUInt 186 XSError( 187 needEnqueue(w) && enq.ready && allocated(enqIndex), 188 p"LoadQueueRAR: You can not write an valid entry! check: ldu $w, robIdx $debug_robIdx") 189 } 190 191 // LoadQueueRAR deallocate 192 val freeMaskVec = Wire(Vec(LoadQueueRARSize, Bool())) 193 194 // init 195 freeMaskVec.map(e => e := false.B) 196 197 // when the loads that "older than" current load were writebacked, 198 // current load will be released. 199 for (i <- 0 until LoadQueueRARSize) { 200 val deqNotBlock = !isBefore(io.ldWbPtr, uop(i).lqIdx) 201 val needFlush = uop(i).robIdx.needFlush(io.redirect) 202 203 when (allocated(i) && (deqNotBlock || needFlush)) { 204 allocated(i) := false.B 205 freeMaskVec(i) := true.B 206 } 207 } 208 209 // if need replay revoke entry 210 val lastCanAccept = GatedRegNext(acceptedVec) 211 val lastAllocIndex = GatedRegNext(enqIndexVec) 212 213 for ((revoke, w) <- io.query.map(_.revoke).zipWithIndex) { 214 val revokeValid = revoke && lastCanAccept(w) 215 val revokeIndex = lastAllocIndex(w) 216 217 when (allocated(revokeIndex) && revokeValid) { 218 allocated(revokeIndex) := false.B 219 freeMaskVec(revokeIndex) := true.B 220 } 221 } 222 223 freeList.io.free := freeMaskVec.asUInt 224 225 // LoadQueueRAR Query 226 // Load-to-Load violation check condition: 227 // 1. Physical address match by CAM port. 228 // 2. release or nc_with_data is set. 229 // 3. Younger than current load instruction. 230 val ldLdViolation = Wire(Vec(LoadPipelineWidth, Bool())) 231 //val allocatedUInt = RegNext(allocated.asUInt) 232 for ((query, w) <- io.query.zipWithIndex) { 233 ldLdViolation(w) := false.B 234 paddrModule.io.releaseViolationMdata(w) := genPartialPAddr(query.req.bits.paddr) 235 236 query.resp.valid := RegNext(query.req.valid) 237 // Generate real violation mask 238 val robIdxMask = VecInit(uop.map(_.robIdx).map(isAfter(_, query.req.bits.uop.robIdx))) 239 val matchMaskReg = Wire(Vec(LoadQueueRARSize, Bool())) 240 for(i <- 0 until LoadQueueRARSize) { 241 matchMaskReg(i) := (allocated(i) & 242 paddrModule.io.releaseViolationMmask(w)(i) & 243 robIdxMask(i) && 244 released(i)) 245 } 246 val matchMask = GatedValidRegNext(matchMaskReg) 247 // Load-to-Load violation check result 248 val ldLdViolationMask = matchMask 249 ldLdViolationMask.suggestName("ldLdViolationMask_" + w) 250 query.resp.bits.rep_frm_fetch := ParallelORR(ldLdViolationMask) 251 } 252 253 254 // When io.release.valid (release1cycle.valid), it uses the last ld-ld paddr cam port to 255 // update release flag in 1 cycle 256 val releaseVioMask = Reg(Vec(LoadQueueRARSize, Bool())) 257 when (release1Cycle.valid) { 258 paddrModule.io.releaseMdata.takeRight(1)(0) := genPartialPAddr(release1Cycle.bits.paddr) 259 } 260 261 (0 until LoadQueueRARSize).map(i => { 262 when (RegNext((paddrModule.io.releaseMmask.takeRight(1)(0)(i)) && allocated(i) && release1Cycle.valid)) { 263 // Note: if a load has missed in dcache and is waiting for refill in load queue, 264 // its released flag still needs to be set as true if addr matches. 265 released(i) := true.B 266 } 267 }) 268 269 io.lqFull := freeList.io.empty 270 271 // perf cnt 272 val canEnqCount = PopCount(io.query.map(_.req.fire)) 273 val validCount = freeList.io.validCount 274 val allowEnqueue = validCount <= (LoadQueueRARSize - LoadPipelineWidth).U 275 val ldLdViolationCount = PopCount(io.query.map(_.resp).map(resp => resp.valid && resp.bits.rep_frm_fetch)) 276 277 QueuePerf(LoadQueueRARSize, validCount, !allowEnqueue) 278 XSPerfAccumulate("enq", canEnqCount) 279 XSPerfAccumulate("ld_ld_violation", ldLdViolationCount) 280 val perfEvents: Seq[(String, UInt)] = Seq( 281 ("enq", canEnqCount), 282 ("ld_ld_violation", ldLdViolationCount) 283 ) 284 generatePerfEvent() 285 // End 286} 287