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.cache 18 19import chisel3._ 20import chisel3.util._ 21import chipsalliance.rocketchip.config.Parameters 22import utils._ 23import utility._ 24import xiangshan._ 25import freechips.rocketchip.diplomacy.{IdRange, LazyModule, LazyModuleImp, TransferSizes} 26import freechips.rocketchip.tilelink.{TLArbiter, TLBundleA, TLBundleD, TLClientNode, TLEdgeOut, TLMasterParameters, TLMasterPortParameters} 27 28class UncachePtr(implicit p: Parameters) extends CircularQueuePtr[UncachePtr]( 29 p => p(XSCoreParamsKey).UncacheBufferSize 30){ 31 32} 33 34object UncachePtr { 35 def apply(f: Bool, v: UInt)(implicit p: Parameters): UncachePtr = { 36 val ptr = Wire(new UncachePtr) 37 ptr.flag := f 38 ptr.value := v 39 ptr 40 } 41} 42 43class UncacheFlushBundle extends Bundle { 44 val valid = Output(Bool()) 45 val empty = Input(Bool()) 46} 47 48// One miss entry deals with one mmio request 49class MMIOEntry(edge: TLEdgeOut)(implicit p: Parameters) extends DCacheModule 50{ 51 val io = IO(new Bundle { 52 // MSHR ID 53 val hartId = Input(UInt()) 54 // Control IO 55 val enableOutstanding = Input(Bool()) 56 57 // Client requests 58 val req = Flipped(DecoupledIO(new UncacheWordReq)) 59 val resp = DecoupledIO(new DCacheWordRespWithError) 60 61 // TileLink 62 val mem_acquire = DecoupledIO(new TLBundleA(edge.bundle)) 63 val mem_grant = Flipped(DecoupledIO(new TLBundleD(edge.bundle))) 64 65 // This entry is selected. 66 val select = Input(Bool()) 67 val atomic = Output(Bool()) 68 }) 69 // ================================================ 70 // FSM state description: 71 // s_invalid : Entry is invalid. 72 // s_refill_req : Send Acquire request. 73 // s_refill_resp : Wait for Grant response. 74 // s_send_resp : Send Uncache response. 75 val s_invalid :: s_refill_req :: s_refill_resp :: s_send_resp :: Nil = Enum(4) 76 val state = RegInit(s_invalid) 77 78 val req = Reg(new UncacheWordReq) 79 val resp_data = Reg(UInt(DataBits.W)) 80 def storeReq = req.cmd === MemoryOpConstants.M_XWR 81 82 // Assign default values to output signals. 83 io.req.ready := false.B 84 io.resp.valid := false.B 85 io.resp.bits := DontCare 86 87 io.mem_acquire.valid := false.B 88 io.mem_acquire.bits := DontCare 89 io.mem_grant.ready := false.B 90 91 io.atomic := req.atomic 92 // Receive request 93 when (state === s_invalid) { 94 io.req.ready := true.B 95 96 when (io.req.fire) { 97 req := io.req.bits 98 req.addr := io.req.bits.addr 99 state := s_refill_req 100 } 101 } 102 103 // Refill 104 // TODO: determine 'lgSize' in memend 105 val size = PopCount(req.mask) 106 val (lgSize, legal) = PriorityMuxWithFlag(Seq( 107 1.U -> 0.U, 108 2.U -> 1.U, 109 4.U -> 2.U, 110 8.U -> 3.U 111 ).map(m => (size===m._1) -> m._2)) 112 assert(!(io.mem_acquire.valid && !legal)) 113 114 val load = edge.Get( 115 fromSource = io.hartId, 116 toAddress = req.addr, 117 lgSize = lgSize 118 )._2 119 120 val store = edge.Put( 121 fromSource = io.hartId, 122 toAddress = req.addr, 123 lgSize = lgSize, 124 data = req.data, 125 mask = req.mask 126 )._2 127 128 XSDebug("entry: %d state: %d\n", io.hartId, state) 129 130 when (state === s_refill_req) { 131 io.mem_acquire.valid := true.B && io.select 132 io.mem_acquire.bits := Mux(storeReq, store, load) 133 134 when (io.mem_acquire.fire) { 135 state := s_refill_resp 136 } 137 } 138 139 val (_, _, refill_done, _) = edge.addr_inc(io.mem_grant) 140 when (state === s_refill_resp) { 141 io.mem_grant.ready := true.B 142 143 when (io.mem_grant.fire) { 144 resp_data := io.mem_grant.bits.data 145 assert(refill_done, "Uncache response should be one beat only!") 146 state := Mux(storeReq && io.enableOutstanding, s_invalid, s_send_resp) 147 } 148 } 149 150 // Response 151 when (state === s_send_resp) { 152 io.resp.valid := true.B 153 io.resp.bits.data := resp_data 154 // meta data should go with the response 155 io.resp.bits.id := req.id 156 io.resp.bits.miss := false.B 157 io.resp.bits.replay := false.B 158 io.resp.bits.tag_error := false.B 159 io.resp.bits.error := false.B 160 161 when (io.resp.fire) { 162 state := s_invalid 163 } 164 } 165 166 // End 167} 168 169class UncacheIO(implicit p: Parameters) extends DCacheBundle { 170 val hartId = Input(UInt()) 171 val enableOutstanding = Input(Bool()) 172 val flush = Flipped(new UncacheFlushBundle) 173 val lsq = Flipped(new UncacheWordIO) 174} 175 176// convert DCacheIO to TileLink 177// for Now, we only deal with TL-UL 178 179class Uncache()(implicit p: Parameters) extends LazyModule with HasXSParameter { 180 override def shouldBeInlined: Boolean = false 181 def idRange: Int = UncacheBufferSize 182 183 val clientParameters = TLMasterPortParameters.v1( 184 clients = Seq(TLMasterParameters.v1( 185 "uncache", 186 sourceId = IdRange(0, idRange) 187 )) 188 ) 189 val clientNode = TLClientNode(Seq(clientParameters)) 190 191 lazy val module = new UncacheImp(this) 192} 193 194class UncacheImp(outer: Uncache)extends LazyModuleImp(outer) 195 with HasTLDump 196 with HasXSParameter 197 with HasPerfEvents 198{ 199 val io = IO(new UncacheIO) 200 201 val (bus, edge) = outer.clientNode.out.head 202 203 val req = io.lsq.req 204 val resp = io.lsq.resp 205 val mem_acquire = bus.a 206 val mem_grant = bus.d 207 208 val req_ready = WireInit(false.B) 209 val need_fence = WireInit(false.B) 210 211 // assign default values to output signals 212 bus.b.ready := false.B 213 bus.c.valid := false.B 214 bus.c.bits := DontCare 215 bus.d.ready := false.B 216 bus.e.valid := false.B 217 bus.e.bits := DontCare 218 219 val enqPtr = RegInit(0.U.asTypeOf(new UncachePtr)) 220 val issPtr = RegInit(0.U.asTypeOf(new UncachePtr)) 221 val deqPtr = RegInit(0.U.asTypeOf(new UncachePtr)) 222 val fence = RegInit(Bool(), false.B) 223 224 io.lsq.resp.valid := false.B 225 io.lsq.resp.bits := DontCare 226 227 val entries = Seq.fill(UncacheBufferSize) { Module(new MMIOEntry(edge)) } 228 for ((entry, i) <- entries.zipWithIndex) { 229 entry.io.hartId := io.hartId 230 entry.io.enableOutstanding := io.enableOutstanding 231 232 // Enqueue 233 entry.io.req.valid := (i.U === enqPtr.value) && req.valid 234 entry.io.req.bits := req.bits 235 236 when (i.U === enqPtr.value) { 237 req_ready := entry.io.req.ready 238 } 239 240 // Acquire 241 entry.io.select := (i.U === issPtr.value) && Mux(entry.io.atomic, issPtr.value === deqPtr.value, !fence) 242 243 when (i.U === issPtr.value) { 244 need_fence := entry.io.atomic 245 } 246 247 // Grant 248 entry.io.mem_grant.valid := false.B 249 entry.io.mem_grant.bits := DontCare 250 when (i.U === deqPtr.value) { 251 entry.io.mem_grant <> mem_grant 252 } 253 254 entry.io.resp.ready := false.B 255 when (i.U === deqPtr.value) { 256 io.lsq.resp <> entry.io.resp 257 } 258 259 } 260 261 io.lsq.req.ready := req_ready 262 when (io.enableOutstanding) { 263 // Uncache Buffer is a circular queue, which contains UncacheBufferSize entries. 264 // Description: 265 // enqPtr: Point to an invalid (means that the entry is free) entry. 266 // issPtr: Point to a ready entry, the entry is ready to issue. 267 // deqPtr: Point to the oldest entry, which was issued but has not accepted response (used to keep order with the program order). 268 // 269 // When outstanding disabled, only one read/write request can be accepted at a time. 270 // 271 // Example (Enable outstanding): 272 // 1. enqPtr: 273 // 1) Before enqueue 274 // enqPtr -- 275 // | 276 // | 277 // V 278 // +--+--+--+--+ 279 // | | | | | 280 // | | | | | 281 // | | | | | 282 // +--+--+--+--+ 283 // 284 // 2) After 285 // enqPtr+1 --- 286 // | 287 // | 288 // V 289 // +--+--+--+--+ 290 // | | | | | 291 // | | | | | 292 // | | | | | 293 // +--+--+--+--+ 294 // 295 // 2. issPtr: 296 // 1) Before issue 297 // issPtr -- 298 // | 299 // | 300 // V 301 // +--+--+--+--+ 302 // | | | | | 303 // | | | | | 304 // | | | | | 305 // +--+--+--+--+ 306 // 307 // 2) After issue 308 // issPtr+1 -- 309 // | 310 // | 311 // V 312 // +--+--+--+--+ 313 // | | | | | 314 // | | | | | 315 // | | | | | 316 // +--+--+--+--+ 317 // 318 // 3. deqPtr: 319 // 1) Before dequeue 320 // deqPtr -- 321 // | 322 // | 323 // V 324 // +--+--+--+--+ 325 // | | | | | 326 // | | | | | 327 // | | | | | 328 // +--+--+--+--+ 329 // 330 // 2) After dequeue 331 // deqPtr -- deqPtr+1 -- 332 // | | 333 // | | 334 // V V 335 // +--+--+--+--+ or +--+--+--+--+ 336 // | | | | | | | | | | 337 // | | | | | | | | | | 338 // | | | | | | | | | | 339 // +--+--+--+--+ +--+--+--+--+ 340 // (load) (store) 341 // 342 // 3) After response 343 // deqPtr+1 --- deqPtr-- 344 // | | 345 // | | 346 // V V 347 // +--+--+--+--+ or +--+--+--+--+ 348 // | | | | | | | | | | 349 // | | | | | | | | | | 350 // | | | | | | | | | | 351 // +--+--+--+--+ +--+--+--+--+ 352 // (load) (store) 353 // 354 355 // Enqueue 356 when (req.fire) { 357 enqPtr := enqPtr + 1.U 358 } 359 360 // Issue 361 when (mem_acquire.fire) { 362 issPtr := issPtr + 1.U 363 } 364 365 when (mem_acquire.fire) { 366 fence := need_fence 367 } 368 369 // Dequeue 370 when (mem_grant.fire) { 371 deqPtr := Mux(edge.hasData(mem_grant.bits), deqPtr /* Load */, deqPtr + 1.U /* Store */) 372 } .elsewhen (io.lsq.resp.fire /* Load */) { 373 deqPtr := deqPtr + 1.U 374 } 375 376 when (mem_grant.fire && fence) { 377 fence := false.B 378 } 379 } .otherwise { 380 when (io.lsq.resp.fire) { 381 enqPtr := enqPtr + 1.U 382 issPtr := issPtr + 1.U 383 deqPtr := deqPtr + 1.U 384 } 385 } 386 387 TLArbiter.lowestFromSeq(edge, mem_acquire, entries.map(_.io.mem_acquire)) 388 io.flush.empty := deqPtr === enqPtr 389 390 println(s"Uncahe Buffer Size: $UncacheBufferSize entries") 391 392 // print all input/output requests for debug purpose 393 // print req/resp 394 XSDebug(req.fire, "req cmd: %x addr: %x data: %x mask: %x\n", 395 req.bits.cmd, req.bits.addr, req.bits.data, req.bits.mask) 396 XSDebug(resp.fire, "data: %x\n", req.bits.data) 397 398 // print tilelink messages 399 when(mem_acquire.valid){ 400 XSDebug("mem_acquire valid, ready=%d ", mem_acquire.ready) 401 mem_acquire.bits.dump 402 } 403 when (mem_grant.fire) { 404 XSDebug("mem_grant fire ") 405 mem_grant.bits.dump 406 } 407 408 // Performance Counters 409 def isStore: Bool = io.lsq.req.bits.cmd === MemoryOpConstants.M_XWR 410 XSPerfAccumulate("mmio_store", io.lsq.req.fire && isStore) 411 XSPerfAccumulate("mmio_load", io.lsq.req.fire && !isStore) 412 XSPerfAccumulate("mmio_outstanding", mem_acquire.fire && (deqPtr =/= issPtr)) 413 val perfEvents = Seq( 414 ("mmio_store", io.lsq.req.fire && isStore), 415 ("mmio_load", io.lsq.req.fire && !isStore), 416 ("mmio_outstanding", mem_acquire.fire && (deqPtr =/= issPtr)) 417 ) 418 419 generatePerfEvent() 420 // End 421} 422