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