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 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 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 169 when (io.resp.fire) { 170 state := s_invalid 171 } 172 } 173 174 // End 175} 176 177class UncacheIO(implicit p: Parameters) extends DCacheBundle { 178 val hartId = Input(UInt()) 179 val enableOutstanding = Input(Bool()) 180 val flush = Flipped(new UncacheFlushBundle) 181 val lsq = Flipped(new UncacheWordIO) 182} 183 184// convert DCacheIO to TileLink 185// for Now, we only deal with TL-UL 186 187class Uncache()(implicit p: Parameters) extends LazyModule with HasXSParameter { 188 override def shouldBeInlined: Boolean = false 189 def idRange: Int = UncacheBufferSize 190 191 val clientParameters = TLMasterPortParameters.v1( 192 clients = Seq(TLMasterParameters.v1( 193 "uncache", 194 sourceId = IdRange(0, idRange) 195 )) 196 ) 197 val clientNode = TLClientNode(Seq(clientParameters)) 198 199 lazy val module = new UncacheImp(this) 200} 201 202class UncacheImp(outer: Uncache)extends LazyModuleImp(outer) 203 with HasTLDump 204 with HasXSParameter 205 with HasPerfEvents 206{ 207 val io = IO(new UncacheIO) 208 209 val (bus, edge) = outer.clientNode.out.head 210 211 val req = io.lsq.req 212 val resp = io.lsq.resp 213 val mem_acquire = bus.a 214 val mem_grant = bus.d 215 216 val req_ready = WireInit(false.B) 217 val need_fence = WireInit(false.B) 218 219 // assign default values to output signals 220 bus.b.ready := false.B 221 bus.c.valid := false.B 222 bus.c.bits := DontCare 223 bus.d.ready := false.B 224 bus.e.valid := false.B 225 bus.e.bits := DontCare 226 227 val enqPtr = RegInit(0.U.asTypeOf(new UncachePtr)) 228 val issPtr = RegInit(0.U.asTypeOf(new UncachePtr)) 229 val deqPtr = RegInit(0.U.asTypeOf(new UncachePtr)) 230 val fence = RegInit(Bool(), false.B) 231 232 io.lsq.resp.valid := false.B 233 io.lsq.resp.bits := DontCare 234 235 val entries = Seq.fill(UncacheBufferSize) { Module(new MMIOEntry(edge)) } 236 for ((entry, i) <- entries.zipWithIndex) { 237 entry.io.hartId := io.hartId 238 entry.io.enableOutstanding := io.enableOutstanding 239 240 // Enqueue 241 entry.io.req.valid := (i.U === enqPtr.value) && req.valid 242 entry.io.req.bits := req.bits 243 244 when (i.U === enqPtr.value) { 245 req_ready := entry.io.req.ready 246 } 247 248 // Acquire 249 entry.io.select := (i.U === issPtr.value) && Mux(entry.io.atomic, issPtr.value === deqPtr.value, !fence) 250 251 when (i.U === issPtr.value) { 252 need_fence := entry.io.atomic 253 } 254 255 // Grant 256 entry.io.mem_grant.valid := false.B 257 entry.io.mem_grant.bits := DontCare 258 when (i.U === deqPtr.value) { 259 entry.io.mem_grant <> mem_grant 260 } 261 262 entry.io.resp.ready := false.B 263 when (i.U === deqPtr.value) { 264 io.lsq.resp <> entry.io.resp 265 } 266 267 } 268 269 io.lsq.req.ready := req_ready 270 when (io.enableOutstanding) { 271 // Uncache Buffer is a circular queue, which contains UncacheBufferSize entries. 272 // Description: 273 // enqPtr: Point to an invalid (means that the entry is free) entry. 274 // issPtr: Point to a ready entry, the entry is ready to issue. 275 // deqPtr: Point to the oldest entry, which was issued but has not accepted response (used to keep order with the program order). 276 // 277 // When outstanding disabled, only one read/write request can be accepted at a time. 278 // 279 // Example (Enable outstanding): 280 // 1. enqPtr: 281 // 1) Before enqueue 282 // enqPtr -- 283 // | 284 // | 285 // V 286 // +--+--+--+--+ 287 // | | | | | 288 // | | | | | 289 // | | | | | 290 // +--+--+--+--+ 291 // 292 // 2) After 293 // enqPtr+1 --- 294 // | 295 // | 296 // V 297 // +--+--+--+--+ 298 // | | | | | 299 // | | | | | 300 // | | | | | 301 // +--+--+--+--+ 302 // 303 // 2. issPtr: 304 // 1) Before issue 305 // issPtr -- 306 // | 307 // | 308 // V 309 // +--+--+--+--+ 310 // | | | | | 311 // | | | | | 312 // | | | | | 313 // +--+--+--+--+ 314 // 315 // 2) After issue 316 // issPtr+1 -- 317 // | 318 // | 319 // V 320 // +--+--+--+--+ 321 // | | | | | 322 // | | | | | 323 // | | | | | 324 // +--+--+--+--+ 325 // 326 // 3. deqPtr: 327 // 1) Before dequeue 328 // deqPtr -- 329 // | 330 // | 331 // V 332 // +--+--+--+--+ 333 // | | | | | 334 // | | | | | 335 // | | | | | 336 // +--+--+--+--+ 337 // 338 // 2) After dequeue 339 // deqPtr -- deqPtr+1 -- 340 // | | 341 // | | 342 // V V 343 // +--+--+--+--+ or +--+--+--+--+ 344 // | | | | | | | | | | 345 // | | | | | | | | | | 346 // | | | | | | | | | | 347 // +--+--+--+--+ +--+--+--+--+ 348 // (load) (store) 349 // 350 // 3) After response 351 // deqPtr+1 --- deqPtr-- 352 // | | 353 // | | 354 // V V 355 // +--+--+--+--+ or +--+--+--+--+ 356 // | | | | | | | | | | 357 // | | | | | | | | | | 358 // | | | | | | | | | | 359 // +--+--+--+--+ +--+--+--+--+ 360 // (load) (store) 361 // 362 363 // Enqueue 364 when (req.fire) { 365 enqPtr := enqPtr + 1.U 366 } 367 368 // Issue 369 when (mem_acquire.fire) { 370 issPtr := issPtr + 1.U 371 } 372 373 when (mem_acquire.fire) { 374 fence := need_fence 375 } 376 377 // Dequeue 378 when (mem_grant.fire) { 379 deqPtr := Mux(edge.hasData(mem_grant.bits), deqPtr /* Load */, deqPtr + 1.U /* Store */) 380 } .elsewhen (io.lsq.resp.fire /* Load */) { 381 deqPtr := deqPtr + 1.U 382 } 383 384 when (mem_grant.fire && fence) { 385 fence := false.B 386 } 387 } .otherwise { 388 when (io.lsq.resp.fire) { 389 enqPtr := enqPtr + 1.U 390 issPtr := issPtr + 1.U 391 deqPtr := deqPtr + 1.U 392 } 393 } 394 395 TLArbiter.lowestFromSeq(edge, mem_acquire, entries.map(_.io.mem_acquire)) 396 val invalid_entries = PopCount(entries.map(_.io.invalid)) 397 io.flush.empty := invalid_entries === UncacheBufferSize.U 398 399 println(s"Uncahe Buffer Size: $UncacheBufferSize entries") 400 401 // print all input/output requests for debug purpose 402 // print req/resp 403 XSDebug(req.fire, "req cmd: %x addr: %x data: %x mask: %x\n", 404 req.bits.cmd, req.bits.addr, req.bits.data, req.bits.mask) 405 XSDebug(resp.fire, "data: %x\n", req.bits.data) 406 407 // print tilelink messages 408 when(mem_acquire.valid){ 409 XSDebug("mem_acquire valid, ready=%d ", mem_acquire.ready) 410 mem_acquire.bits.dump 411 } 412 when (mem_grant.fire) { 413 XSDebug("mem_grant fire ") 414 mem_grant.bits.dump 415 } 416 417 // Performance Counters 418 def isStore: Bool = io.lsq.req.bits.cmd === MemoryOpConstants.M_XWR 419 XSPerfAccumulate("mmio_store", io.lsq.req.fire && isStore) 420 XSPerfAccumulate("mmio_load", io.lsq.req.fire && !isStore) 421 XSPerfAccumulate("mmio_outstanding", mem_acquire.fire && (deqPtr =/= issPtr)) 422 val perfEvents = Seq( 423 ("mmio_store", io.lsq.req.fire && isStore), 424 ("mmio_load", io.lsq.req.fire && !isStore), 425 ("mmio_outstanding", mem_acquire.fire && (deqPtr =/= issPtr)) 426 ) 427 428 generatePerfEvent() 429 // End 430} 431