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