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