xref: /XiangShan/src/main/scala/xiangshan/cache/dcache/Uncache.scala (revision 45f43e6e5f88874a7573ff096d1e5c2855bd16c7)
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