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