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