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