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