xref: /XiangShan/src/main/scala/xiangshan/cache/dcache/Uncache.scala (revision 708ceed4afe43fb0ea3a52407e46b2794c573634)
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 utils.{HasTLDump, PriorityMuxWithFlag, XSDebug}
22import chipsalliance.rocketchip.config.Parameters
23import freechips.rocketchip.diplomacy.{IdRange, LazyModule, LazyModuleImp, TransferSizes}
24import freechips.rocketchip.tilelink.{TLArbiter, TLBundleA, TLBundleD, TLClientNode, TLEdgeOut, TLMasterParameters, TLMasterPortParameters}
25import xiangshan.{MicroOp, Redirect}
26
27// One miss entry deals with one mmio request
28class MMIOEntry(edge: TLEdgeOut)(implicit p: Parameters) extends DCacheModule
29{
30  val io = IO(new Bundle {
31    // MSHR ID
32    val id = Input(UInt())
33
34    // client requests
35    val req = Flipped(DecoupledIO(new DCacheWordReq ))
36    val resp = DecoupledIO(new DCacheWordResp)
37
38    val mem_acquire = DecoupledIO(new TLBundleA(edge.bundle))
39    val mem_grant   = Flipped(DecoupledIO(new TLBundleD(edge.bundle)))
40  })
41
42
43  val s_invalid :: s_refill_req :: s_refill_resp :: s_send_resp :: Nil = Enum(4)
44
45  val state = RegInit(s_invalid)
46
47  val req       = Reg(new DCacheWordReq )
48  val resp_data = Reg(UInt(DataBits.W))
49
50
51  // assign default values to output signals
52  io.req.ready           := false.B
53  io.resp.valid          := false.B
54  io.resp.bits           := DontCare
55
56  io.mem_acquire.valid   := false.B
57  io.mem_acquire.bits    := DontCare
58
59  io.mem_grant.ready     := false.B
60
61
62  XSDebug("entry: %d state: %d\n", io.id, state)
63  // --------------------------------------------
64  // s_invalid: receive requests
65  when (state === s_invalid) {
66    io.req.ready := true.B
67
68    when (io.req.fire()) {
69      req   := io.req.bits
70      req.addr := io.req.bits.addr
71      state := s_refill_req
72    }
73  }
74
75  // --------------------------------------------
76  // refill
77  // TODO: determine 'lgSize' in memend
78  val size = PopCount(req.mask)
79  val (lgSize, legal) = PriorityMuxWithFlag(Seq(
80    1.U -> 0.U,
81    2.U -> 1.U,
82    4.U -> 2.U,
83    8.U -> 3.U
84  ).map(m => (size===m._1) -> m._2))
85  assert(!(io.mem_acquire.valid && !legal))
86
87  val load = edge.Get(
88    fromSource      = io.id,
89    toAddress       = req.addr,
90    lgSize          = lgSize
91  )._2
92
93  val store = edge.Put(
94    fromSource      = io.id,
95    toAddress = req.addr,
96    lgSize  = lgSize,
97    data  = req.data,
98    mask = req.mask
99  )._2
100
101  when (state === s_refill_req) {
102    io.mem_acquire.valid := true.B
103    io.mem_acquire.bits  := Mux(req.cmd === MemoryOpConstants.M_XWR, store, load)
104
105    when (io.mem_acquire.fire()) {
106      state := s_refill_resp
107    }
108  }
109
110  val (_, _, refill_done, _) = edge.addr_inc(io.mem_grant)
111
112  when (state === s_refill_resp) {
113    io.mem_grant.ready := true.B
114
115    when (io.mem_grant.fire()) {
116      resp_data := io.mem_grant.bits.data
117      assert(refill_done, "MMIO response should be one beat only!")
118      state := s_send_resp
119    }
120  }
121
122  // --------------------------------------------
123  when (state === s_send_resp) {
124    io.resp.valid := true.B
125    io.resp.bits.data   := resp_data
126    // meta data should go with the response
127    io.resp.bits.id     := req.id
128    io.resp.bits.miss   := false.B
129    io.resp.bits.replay := false.B
130
131    when (io.resp.fire()) {
132      state := s_invalid
133    }
134  }
135}
136
137class UncacheIO(implicit p: Parameters) extends DCacheBundle {
138  val lsq = Flipped(new DCacheWordIO)
139}
140
141// convert DCacheIO to TileLink
142// for Now, we only deal with TL-UL
143
144class Uncache()(implicit p: Parameters) extends LazyModule with HasDCacheParameters {
145
146  val clientParameters = TLMasterPortParameters.v1(
147    clients = Seq(TLMasterParameters.v1(
148      "uncache",
149      sourceId = IdRange(0, cfg.nMMIOEntries)
150    ))
151  )
152  val clientNode = TLClientNode(Seq(clientParameters))
153
154  lazy val module = new UncacheImp(this)
155
156}
157
158class UncacheImp(outer: Uncache)
159  extends LazyModuleImp(outer)
160    with HasDCacheParameters
161    with HasTLDump
162{
163  val io = IO(new UncacheIO)
164
165  val (bus, edge) = outer.clientNode.out.head
166  require(bus.d.bits.data.getWidth == wordBits, "Uncache: tilelink width does not match")
167
168  val resp_arb = Module(new Arbiter(new DCacheWordResp, cfg.nMMIOEntries))
169
170  val req  = io.lsq.req
171  val resp = io.lsq.resp
172  val mem_acquire = bus.a
173  val mem_grant   = bus.d
174
175  val entry_alloc_idx = Wire(UInt())
176  val req_ready = WireInit(false.B)
177
178  // assign default values to output signals
179  bus.b.ready := false.B
180  bus.c.valid := false.B
181  bus.c.bits  := DontCare
182  bus.d.ready := false.B
183  bus.e.valid := false.B
184  bus.e.bits  := DontCare
185
186  val entries = (0 until cfg.nMMIOEntries) map { i =>
187    val entry = Module(new MMIOEntry(edge))
188
189    entry.io.id := i.U(log2Up(cfg.nMMIOEntries).W)
190
191    // entry req
192    entry.io.req.valid := (i.U === entry_alloc_idx) && req.valid
193    entry.io.req.bits  := req.bits
194    when (i.U === entry_alloc_idx) {
195      req_ready := entry.io.req.ready
196    }
197
198    // entry resp
199    resp_arb.io.in(i) <> entry.io.resp
200
201    entry.io.mem_grant.valid := false.B
202    entry.io.mem_grant.bits  := DontCare
203    when (mem_grant.bits.source === i.U) {
204      entry.io.mem_grant <> mem_grant
205    }
206    entry
207  }
208
209  entry_alloc_idx    := PriorityEncoder(entries.map(m=>m.io.req.ready))
210
211  req.ready  := req_ready
212  resp          <> resp_arb.io.out
213  TLArbiter.lowestFromSeq(edge, mem_acquire, entries.map(_.io.mem_acquire))
214
215
216  // print all input/output requests for debug purpose
217
218  // print req/resp
219  XSDebug(req.fire(), "req cmd: %x addr: %x data: %x mask: %x\n",
220    req.bits.cmd, req.bits.addr, req.bits.data, req.bits.mask)
221  XSDebug(resp.fire(), "data: %x\n", req.bits.data)
222
223  // print tilelink messages
224  when(mem_acquire.valid){
225    XSDebug("mem_acquire valid, ready=%d ", mem_acquire.ready)
226    mem_acquire.bits.dump
227  }
228  when (mem_grant.fire()) {
229    XSDebug("mem_grant fire ")
230    mem_grant.bits.dump
231  }
232}
233