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