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