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