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