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