xref: /XiangShan/src/main/scala/xiangshan/cache/mmu/TLBStorage.scala (revision 12e221b1295bd5e50822d86f9ccd637ebeaffc2f)
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.mmu
18
19import chipsalliance.rocketchip.config.Parameters
20import chisel3._
21import chisel3.experimental.chiselName
22import chisel3.util._
23import utils._
24
25@chiselName
26class TLBFA(
27  sameCycle: Boolean,
28  ports: Int,
29  nSets: Int,
30  nWays: Int,
31  sramSinglePort: Boolean,
32  saveLevel: Boolean = false,
33  normalPage: Boolean,
34  superPage: Boolean
35)(implicit p: Parameters) extends TlbModule with HasPerfEvents {
36  require(!(sameCycle && saveLevel))
37
38  val io = IO(new TlbStorageIO(nSets, nWays, ports))
39  io.r.req.map(_.ready := true.B)
40
41  val v = RegInit(VecInit(Seq.fill(nWays)(false.B)))
42  val entries = Reg(Vec(nWays, new TlbEntry(normalPage, superPage)))
43  val g = entries.map(_.perm.g)
44
45  for (i <- 0 until ports) {
46    val req = io.r.req(i)
47    val resp = io.r.resp(i)
48    val access = io.access(i)
49
50    val vpn = req.bits.vpn
51    val vpn_reg = if (sameCycle) vpn else RegEnable(vpn, req.fire())
52    val vpn_gen_ppn = if(sameCycle || saveLevel) vpn else vpn_reg
53
54    val refill_mask = if (sameCycle) 0.U(nWays.W) else Mux(io.w.valid, UIntToOH(io.w.bits.wayIdx), 0.U(nWays.W))
55    val hitVec = VecInit((entries.zipWithIndex).zip(v zip refill_mask.asBools).map{case (e, m) => e._1.hit(vpn, io.csr.satp.asid) && m._1 && !m._2 })
56
57    hitVec.suggestName("hitVec")
58
59    val hitVecReg = if (sameCycle) hitVec else RegEnable(hitVec, req.fire())
60
61    resp.valid := { if (sameCycle) req.valid else RegNext(req.valid) }
62    resp.bits.hit := Cat(hitVecReg).orR
63    if (nWays == 1) {
64      resp.bits.ppn := entries(0).genPPN(saveLevel, req.valid)(vpn_gen_ppn)
65      resp.bits.perm := entries(0).perm
66    } else {
67      resp.bits.ppn := ParallelMux(hitVecReg zip entries.map(_.genPPN(saveLevel, req.valid)(vpn_gen_ppn)))
68      resp.bits.perm := ParallelMux(hitVecReg zip entries.map(_.perm))
69    }
70    io.r.resp_hit_sameCycle(i) := Cat(hitVec).orR
71
72    access.sets := get_set_idx(vpn_reg, nSets) // no use
73    access.touch_ways.valid := resp.valid && Cat(hitVecReg).orR
74    access.touch_ways.bits := OHToUInt(hitVecReg)
75
76    resp.bits.hit.suggestName("hit")
77    resp.bits.ppn.suggestName("ppn")
78    resp.bits.perm.suggestName("perm")
79  }
80
81  when (io.w.valid) {
82    v(io.w.bits.wayIdx) := true.B
83    entries(io.w.bits.wayIdx).apply(io.w.bits.data, io.csr.satp.asid, io.w.bits.data_replenish)
84  }
85
86  val refill_vpn_reg = RegNext(io.w.bits.data.entry.tag)
87  val refill_wayIdx_reg = RegNext(io.w.bits.wayIdx)
88  when (RegNext(io.w.valid)) {
89    io.access.map { access =>
90      access.sets := get_set_idx(refill_vpn_reg, nSets)
91      access.touch_ways.valid := true.B
92      access.touch_ways.bits := refill_wayIdx_reg
93    }
94  }
95
96  val sfence = io.sfence
97  val sfence_vpn = sfence.bits.addr.asTypeOf(new VaBundle().cloneType).vpn
98  val sfenceHit = entries.map(_.hit(sfence_vpn, sfence.bits.asid))
99  val sfenceHit_noasid = entries.map(_.hit(sfence_vpn, sfence.bits.asid, ignoreAsid = true))
100  when (io.sfence.valid) {
101    when (sfence.bits.rs1) { // virtual address *.rs1 <- (rs1===0.U)
102      when (sfence.bits.rs2) { // asid, but i do not want to support asid, *.rs2 <- (rs2===0.U)
103        // all addr and all asid
104        v.map(_ := false.B)
105      }.otherwise {
106        // all addr but specific asid
107        v.zipWithIndex.map{ case (a,i) => a := a & (g(i) | !(entries(i).asid === sfence.bits.asid)) }
108      }
109    }.otherwise {
110      when (sfence.bits.rs2) {
111        // specific addr but all asid
112        v.zipWithIndex.map{ case (a,i) => a := a & !sfenceHit_noasid(i) }
113      }.otherwise {
114        // specific addr and specific asid
115        v.zipWithIndex.map{ case (a,i) => a := a & !(sfenceHit(i) && !g(i)) }
116      }
117    }
118  }
119
120  val victim_idx = io.w.bits.wayIdx
121  io.victim.out.valid := v(victim_idx) && io.w.valid && entries(victim_idx).level.getOrElse(3.U) === 2.U
122  io.victim.out.bits.entry := ns_to_n(entries(victim_idx))
123
124  def ns_to_n(ns: TlbEntry): TlbEntry = {
125    val n = Wire(new TlbEntry(pageNormal = true, pageSuper = false))
126    n.perm := ns.perm
127    n.ppn := ns.ppn
128    n.tag := ns.tag
129    n.asid := ns.asid
130    n
131  }
132
133  XSPerfAccumulate(s"access", io.r.resp.map(_.valid.asUInt()).fold(0.U)(_ + _))
134  XSPerfAccumulate(s"hit", io.r.resp.map(a => a.valid && a.bits.hit).fold(0.U)(_.asUInt() + _.asUInt()))
135
136  for (i <- 0 until nWays) {
137    XSPerfAccumulate(s"access${i}", io.r.resp.zip(io.access.map(acc => UIntToOH(acc.touch_ways.bits))).map{ case (a, b) =>
138      a.valid && a.bits.hit && b(i)}.fold(0.U)(_.asUInt() + _.asUInt()))
139  }
140  for (i <- 0 until nWays) {
141    XSPerfAccumulate(s"refill${i}", io.w.valid && io.w.bits.wayIdx === i.U)
142  }
143
144  val perfEvents = Seq(
145    ("tlbstore_access", io.r.resp.map(_.valid.asUInt()).fold(0.U)(_ + _)                            ),
146    ("tlbstore_hit   ", io.r.resp.map(a => a.valid && a.bits.hit).fold(0.U)(_.asUInt() + _.asUInt())),
147  )
148  generatePerfEvent()
149
150  println(s"tlb_fa: nSets${nSets} nWays:${nWays}")
151}
152
153@chiselName
154class TLBSA(
155  sameCycle: Boolean,
156  ports: Int,
157  nSets: Int,
158  nWays: Int,
159  sramSinglePort: Boolean,
160  normalPage: Boolean,
161  superPage: Boolean
162)(implicit p: Parameters) extends TlbModule {
163  require(!superPage, "super page should use reg/fa")
164  require(!sameCycle, "sram needs next cycle")
165
166  val io = IO(new TlbStorageIO(nSets, nWays, ports))
167
168  io.r.req.map(_.ready := { if (sramSinglePort) !io.w.valid else true.B })
169  val v = RegInit(VecInit(Seq.fill(nSets)(VecInit(Seq.fill(nWays)(false.B)))))
170
171  for (i <- 0 until ports) { // duplicate sram
172    val entries = Module(new SRAMTemplate(
173      new TlbEntry(normalPage, superPage),
174      set = nSets,
175      way = nWays,
176      singlePort = sramSinglePort
177    ))
178
179    val req = io.r.req(i)
180    val resp = io.r.resp(i)
181    val access = io.access(i)
182
183    val vpn = req.bits.vpn
184    val vpn_reg = RegEnable(vpn, req.fire())
185
186    val ridx = get_set_idx(vpn, nSets)
187    val vidx = RegNext(Mux(req.fire(), v(ridx), VecInit(Seq.fill(nWays)(false.B))))
188    entries.io.r.req.valid := req.valid
189    entries.io.r.req.bits.apply(setIdx = ridx)
190
191    val data = entries.io.r.resp.data
192    val hitVec = VecInit(data.zip(vidx).map { case (e, vi) => e.hit(vpn_reg, io.csr.satp.asid, nSets) && vi })
193    resp.bits.hit := Cat(hitVec).orR && RegNext(req.ready, init = false.B)
194    if (nWays == 1) {
195      resp.bits.ppn := data(0).genPPN()(vpn_reg)
196      resp.bits.perm := data(0).perm
197    } else {
198      resp.bits.ppn := ParallelMux(hitVec zip data.map(_.genPPN()(vpn_reg)))
199      resp.bits.perm := ParallelMux(hitVec zip data.map(_.perm))
200    }
201    io.r.resp_hit_sameCycle(i) := DontCare
202
203    resp.valid := {
204      if (sramSinglePort) RegNext(req.fire()) else RegNext(req.valid)
205    }
206    resp.bits.hit.suggestName("hit")
207    resp.bits.ppn.suggestName("ppn")
208    resp.bits.perm.suggestName("perm")
209
210    access.sets := get_set_idx(vpn_reg, nSets) // no use
211    access.touch_ways.valid := resp.valid && Cat(hitVec).orR
212    access.touch_ways.bits := OHToUInt(hitVec)
213
214    entries.io.w.apply(
215      valid = io.w.valid || io.victim.in.valid,
216      setIdx = Mux(io.w.valid,
217        get_set_idx(io.w.bits.data.entry.tag, nSets),
218        get_set_idx(io.victim.in.bits.entry.tag, nSets)),
219      data = Mux(io.w.valid,
220        (Wire(new TlbEntry(normalPage, superPage)).apply(io.w.bits.data, io.csr.satp.asid, io.w.bits.data_replenish)),
221        io.victim.in.bits.entry),
222      waymask = UIntToOH(io.w.bits.wayIdx)
223    )
224  }
225
226  when (io.victim.in.valid) {
227    v(get_set_idx(io.victim.in.bits.entry.tag, nSets))(io.w.bits.wayIdx) := true.B
228  }
229  // w has higher priority than victim
230  when (io.w.valid) {
231    v(get_set_idx(io.w.bits.data.entry.tag, nSets))(io.w.bits.wayIdx) := true.B
232  }
233
234  val refill_vpn_reg = RegNext(Mux(io.victim.in.valid, io.victim.in.bits.entry.tag, io.w.bits.data.entry.tag))
235  val refill_wayIdx_reg = RegNext(io.w.bits.wayIdx)
236  when (RegNext(io.w.valid || io.victim.in.valid)) {
237    io.access.map { access =>
238      access.sets := get_set_idx(refill_vpn_reg, nSets)
239      access.touch_ways.valid := true.B
240      access.touch_ways.bits := refill_wayIdx_reg
241    }
242  }
243
244  val sfence = io.sfence
245  val sfence_vpn = sfence.bits.addr.asTypeOf(new VaBundle().cloneType).vpn
246  when (io.sfence.valid) {
247    when (sfence.bits.rs1) { // virtual address *.rs1 <- (rs1===0.U)
248        v.map(a => a.map(b => b := false.B))
249    }.otherwise {
250        // specific addr but all asid
251        v(get_set_idx(sfence_vpn, nSets)).map(_ := false.B)
252    }
253  }
254
255  io.victim.out := DontCare
256
257  XSPerfAccumulate(s"access", io.r.req.map(_.valid.asUInt()).fold(0.U)(_ + _))
258  XSPerfAccumulate(s"hit", io.r.resp.map(a => a.valid && a.bits.hit).fold(0.U)(_.asUInt() + _.asUInt()))
259
260  for (i <- 0 until nSets) {
261    for (j <- 0 until nWays) {
262      XSPerfAccumulate(s"refill${i}_${j}", (io.w.valid || io.victim.in.valid) &&
263        (Mux(io.w.valid, get_set_idx(io.w.bits.data.entry.tag, nSets), get_set_idx(io.victim.in.bits.entry.tag, nSets)) === i.U) &&
264        (j.U === io.w.bits.wayIdx)
265      )
266    }
267  }
268
269  for (i <- 0 until nSets) {
270    for (j <- 0 until nWays) {
271      XSPerfAccumulate(s"hit${i}_${j}", io.r.resp.map(_.valid)
272        .zip(io.access.map(a => UIntToOH(a.touch_ways.bits)(j)))
273        .map{case(vi, hi) => vi && hi }
274        .zip(io.r.req.map(a => RegNext(get_set_idx(a.bits.vpn, nSets)) === i.U))
275        .map{a => (a._1 && a._2).asUInt()}
276        .fold(0.U)(_ + _)
277      )
278    }
279  }
280
281  for (i <- 0 until nSets) {
282    XSPerfAccumulate(s"access${i}", io.r.resp.map(_.valid)
283      .zip(io.r.req.map(a => RegNext(get_set_idx(a.bits.vpn, nSets)) === i.U))
284      .map{a => (a._1 && a._2).asUInt()}
285      .fold(0.U)(_ + _)
286    )
287  }
288
289  println(s"tlb_sa: nSets:${nSets} nWays:${nWays}")
290}
291
292object TlbStorage {
293  def apply
294  (
295    name: String,
296    associative: String,
297    sameCycle: Boolean,
298    ports: Int,
299    nSets: Int,
300    nWays: Int,
301    sramSinglePort: Boolean,
302    saveLevel: Boolean = false,
303    normalPage: Boolean,
304    superPage: Boolean
305  )(implicit p: Parameters) = {
306    if (associative == "fa") {
307       val storage = Module(new TLBFA(sameCycle, ports, nSets, nWays, sramSinglePort, saveLevel, normalPage, superPage))
308       storage.suggestName(s"tlb_${name}_fa")
309       storage.io
310    } else {
311       val storage = Module(new TLBSA(sameCycle, ports, nSets, nWays, sramSinglePort, normalPage, superPage))
312       storage.suggestName(s"tlb_${name}_sa")
313       storage.io
314    }
315  }
316}
317