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