xref: /XiangShan/src/main/scala/xiangshan/cache/mmu/TLBStorage.scala (revision d0de7e4a4bcd4633260dda99dfedc2a5e543b8b4)
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 org.chipsalliance.cde.config.Parameters
20import chisel3._
21import chisel3.util._
22import utils._
23import utility._
24import freechips.rocketchip.formal.PropertyClass
25import xiangshan.backend.fu.util.HasCSRConst
26
27import scala.math.min
28
29// For Direct-map TLBs, we do not use it now
30class BankedAsyncDataModuleTemplateWithDup[T <: Data](
31  gen: T,
32  numEntries: Int,
33  numRead: Int,
34  numDup: Int,
35  numBanks: Int
36) extends Module {
37  val io = IO(new Bundle {
38    val raddr = Vec(numRead, Input(UInt(log2Ceil(numEntries).W)))
39    val rdata = Vec(numRead, Vec(numDup, Output(gen)))
40    val wen   = Input(Bool())
41    val waddr = Input(UInt(log2Ceil(numEntries).W))
42    val wdata = Input(gen)
43  })
44  require(numBanks > 1)
45  require(numEntries > numBanks)
46
47  val numBankEntries = numEntries / numBanks
48  def bankOffset(address: UInt): UInt = {
49    address(log2Ceil(numBankEntries) - 1, 0)
50  }
51
52  def bankIndex(address: UInt): UInt = {
53    address(log2Ceil(numEntries) - 1, log2Ceil(numBankEntries))
54  }
55
56  val dataBanks = Seq.tabulate(numBanks)(i => {
57    val bankEntries = if (i < numBanks - 1) numBankEntries else (numEntries - (i * numBankEntries))
58    Mem(bankEntries, gen)
59  })
60
61  // async read, but regnext
62  for (i <- 0 until numRead) {
63    val data_read = Reg(Vec(numDup, Vec(numBanks, gen)))
64    val bank_index = Reg(Vec(numDup, UInt(numBanks.W)))
65    for (j <- 0 until numDup) {
66      bank_index(j) := UIntToOH(bankIndex(io.raddr(i)))
67      for (k <- 0 until numBanks) {
68        data_read(j)(k) := Mux(io.wen && (io.waddr === io.raddr(i)),
69          io.wdata, dataBanks(k)(bankOffset(io.raddr(i))))
70      }
71    }
72    // next cycle
73    for (j <- 0 until numDup) {
74      io.rdata(i)(j) := Mux1H(bank_index(j), data_read(j))
75    }
76  }
77
78  // write
79  for (i <- 0 until numBanks) {
80    when (io.wen && (bankIndex(io.waddr) === i.U)) {
81      dataBanks(i)(bankOffset(io.waddr)) := io.wdata
82    }
83  }
84}
85
86class TLBFA(
87  parentName: String,
88  ports: Int,
89  nDups: Int,
90  nSets: Int,
91  nWays: Int,
92  saveLevel: Boolean = false,
93  normalPage: Boolean,
94  superPage: Boolean
95)(implicit p: Parameters) extends TlbModule with HasPerfEvents {
96
97  val io = IO(new TlbStorageIO(nSets, nWays, ports, nDups))
98  io.r.req.map(_.ready := true.B)
99
100  val v = RegInit(VecInit(Seq.fill(nWays)(false.B)))
101  val entries = Reg(Vec(nWays, new TlbSectorEntry(normalPage, superPage)))
102  val g = entries.map(_.perm.g)
103
104  for (i <- 0 until ports) {
105    val req = io.r.req(i)
106    val resp = io.r.resp(i)
107    val access = io.access(i)
108
109    val vpn = req.bits.vpn
110    val vpn_extend = req.bits.vpn_extend
111    val gvpn = Cat(vpn_extend, vpn)
112    val vpn_reg = RegEnable(vpn, req.fire())
113    val gvpn_reg = RegEnable(gvpn, req.fire())
114    val vpn_gen_ppn = if(saveLevel) vpn else vpn_reg
115    val gvpn_gen_ppn = if(saveLevel) gvpn else gvpn_reg
116    val hasS2xlate = req.bits.s2xlate =/= noS2xlate
117    val OnlyS2 = req.bits.s2xlate === onlyStage2
118    val refill_mask = Mux(io.w.valid, UIntToOH(io.w.bits.wayIdx), 0.U(nWays.W))
119    val hitVec = VecInit((entries.zipWithIndex).zip(v zip refill_mask.asBools).map{
120      case (e, m) => {
121        val s2xlate_hit = e._1.s2xlate === req.bits.s2xlate
122        val normal_hit = e._1.hit(vpn, Mux(req.bits.s2xlate(0), io.csr.vsatp.asid, io.csr.satp.asid), vmid = io.csr.hgatp.asid, s2xlate = req.bits.s2xlate(0).asBool())
123        val OnlyS2_hit = e._1.hit_S2(gvpn, io.csr.hgatp.asid)
124        s2xlate_hit && Mux(OnlyS2, OnlyS2_hit, normal_hit) && m._1 && !m._2
125      }
126    })
127
128    hitVec.suggestName("hitVec")
129
130    val hitVecReg = RegEnable(hitVec, req.fire)
131    // Sector tlb may trigger multi-hit, see def "wbhit"
132    XSPerfAccumulate(s"port${i}_multi_hit", !(!resp.valid || (PopCount(hitVecReg) === 0.U || PopCount(hitVecReg) === 1.U)))
133
134    resp.valid := RegNext(req.valid)
135    resp.bits.hit := Cat(hitVecReg).orR
136    if (nWays == 1) {
137      for (d <- 0 until nDups) {
138        resp.bits.ppn(d) := RegEnable(entries(0).genPPN(saveLevel, req.valid)(vpn), req.fire)
139        resp.bits.perm(d) := RegEnable(entries(0).perm, req.fire)
140      }
141    } else {
142      for (d <- 0 until nDups) {
143        resp.bits.ppn(d) := RegEnable(ParallelMux(hitVec zip entries.map(_.genPPN(saveLevel, req.valid)(vpn))), req.fire)
144        resp.bits.perm(d) := RegEnable(ParallelMux(hitVec zip entries.map(_.perm)), req.fire)
145      }
146    }
147
148    access.sets := get_set_idx(vpn_reg(vpn_reg.getWidth - 1, sectortlbwidth), nSets) // no use
149    access.touch_ways.valid := resp.valid && Cat(hitVecReg).orR
150    access.touch_ways.bits := OHToUInt(hitVecReg)
151
152    resp.bits.hit.suggestName("hit")
153    resp.bits.ppn.suggestName("ppn")
154    resp.bits.perm.suggestName("perm")
155    resp.bits.gvpn.suggestName("gvpn")
156    resp.bits.g_perm.suggestName("g_perm")
157  }
158
159  when (io.w.valid) {
160    v(io.w.bits.wayIdx) := true.B
161    entries(io.w.bits.wayIdx).apply(io.w.bits.data, io.csr.satp.asid)
162  }
163  // write assert, should not duplicate with the existing entries
164  val w_hit_vec = VecInit(entries.zip(v).map{case (e, vi) => e.wbhit(io.w.bits.data, io.csr.satp.asid) && vi })
165  XSError(io.w.valid && Cat(w_hit_vec).orR, s"${parentName} refill, duplicate with existing entries")
166
167  val refill_vpn_reg = RegNext(io.w.bits.data.s1.entry.tag)
168  val refill_wayIdx_reg = RegNext(io.w.bits.wayIdx)
169  when (RegNext(io.w.valid)) {
170    io.access.map { access =>
171      access.sets := get_set_idx(refill_vpn_reg, nSets)
172      access.touch_ways.valid := true.B
173      access.touch_ways.bits := refill_wayIdx_reg
174    }
175  }
176
177  val sfence = io.sfence
178  val sfence_valid = sfence.valid && !sfence.bits.hg && !sfence.bits.hv
179  val sfence_vpn = sfence.bits.addr(VAddrBits - 1, offLen)
180  val sfenceHit = entries.map(_.hit(sfence_vpn, sfence.bits.id, vmid = io.csr.hgatp.asid, s2xlate = io.csr.priv.virt))
181  val sfenceHit_noasid = entries.map(_.hit(sfence_vpn, sfence.bits.id, ignoreAsid = true, vmid = io.csr.hgatp.asid, s2xlate = io.csr.priv.virt))
182  // Sfence will flush all sectors of an entry when hit
183  when (sfence_valid) {
184    when (sfence.bits.rs1) { // virtual address *.rs1 <- (rs1===0.U)
185      when (sfence.bits.rs2) { // asid, but i do not want to support asid, *.rs2 <- (rs2===0.U)
186        // all addr and all asid
187        v.zipWithIndex.map{ case(a, i) => a := a && !((io.csr.priv.virt === false.B && entries(i).s2xlate(0) === 0.U) ||
188          (io.csr.priv.virt && entries(i).s2xlate(0) === 1.U && entries(i).vmid === io.csr.hgatp.asid))}
189      }.otherwise {
190        // all addr but specific asid
191        v.zipWithIndex.map{ case (a, i) => a := a && !(!g(i) && ((!io.csr.priv.virt && entries(i).s2xlate(0) === 0.U && entries(i).asid === sfence.bits.id) ||
192          (io.csr.priv.virt && entries(i).s2xlate(0) === 1.U && entries(i).asid === sfence.bits.id && entries(i).vmid === io.csr.hgatp.asid)))}
193      }
194    }.otherwise {
195      when (sfence.bits.rs2) {
196        // specific addr but all asid
197        v.zipWithIndex.map{ case (a, i) => a := a & !sfenceHit_noasid(i) }
198      }.otherwise {
199        // specific addr and specific asid
200        v.zipWithIndex.map{ case (a, i) => a := a & !(sfenceHit(i) && !g(i)) }
201      }
202    }
203  }
204
205  val hfencev_valid = sfence.valid && sfence.bits.hv
206  val hfenceg_valid = sfence.valid && sfence.bits.hg
207  val hfencev = io.sfence
208  val hfencev_vpn = sfence_vpn
209  val hfencevHit = entries.map(_.hit(hfencev_vpn, hfencev.bits.id, vmid = io.csr.hgatp.asid, s2xlate = true.B))
210  val hfencevHit_noasid = entries.map(_.hit(hfencev_vpn, 0.U, ignoreAsid = true, vmid = io.csr.hgatp.asid, s2xlate = true.B))
211  when (hfencev_valid) {
212    when (hfencev.bits.rs1) {
213      when (hfencev.bits.rs2) {
214        v.zipWithIndex.map { case (a, i) => a := a && !(entries(i).s2xlate(0) === 1.U && entries(i).vmid === io.csr.hgatp.asid)}
215      }.otherwise {
216        v.zipWithIndex.map { case (a, i) => a := a && !(!g(i) && (entries(i).s2xlate(0) === 1.U && entries(i).asid === sfence.bits.id && entries(i).vmid === io.csr.hgatp.asid))
217        }
218      }
219    }.otherwise {
220      when (hfencev.bits.rs2) {
221        v.zipWithIndex.map{ case (a, i) => a := a && !hfencevHit_noasid(i) }
222      }.otherwise {
223        v.zipWithIndex.map{ case (a, i) => a := a && !(hfencevHit(i) && !g(i)) }
224      }
225    }
226  }
227
228
229  val hfenceg = io.sfence
230  val hfenceg_gvpn = sfence_vpn
231  val hfencegHit = entries.map(_.hit_S2(hfenceg_gvpn, io.csr.hgatp.asid))
232  val hfencegHit_novmid = entries.map(_.hit_S2(hfenceg_gvpn, 0.U, ignoreVmid = true.B))
233  when (hfenceg_valid) {
234    when (hfenceg.bits.rs1) {
235      when(hfenceg.bits.rs2) {
236        v.zipWithIndex.map { case (a, i) => a := a && !(entries(i).s2xlate(0) === 1.U) }
237      }.otherwise {
238        v.zipWithIndex.map { case (a, i) => a := a && !(entries(i).s2xlate(0) === 1.U && entries(i).vmid === sfence.bits.id) }
239      }
240    }.otherwise {
241      when(hfenceg.bits.rs2) {
242        v.zipWithIndex.map { case (a, i) => a := a && !hfencegHit_novmid(i) }
243      }.otherwise {
244        v.zipWithIndex.map { case (a, i) => a := a && !hfencegHit(i) }
245      }
246    }
247  }
248
249  XSPerfAccumulate(s"access", io.r.resp.map(_.valid.asUInt).fold(0.U)(_ + _))
250  XSPerfAccumulate(s"hit", io.r.resp.map(a => a.valid && a.bits.hit).fold(0.U)(_.asUInt + _.asUInt))
251
252  for (i <- 0 until nWays) {
253    XSPerfAccumulate(s"access${i}", io.r.resp.zip(io.access.map(acc => UIntToOH(acc.touch_ways.bits))).map{ case (a, b) =>
254      a.valid && a.bits.hit && b(i)}.fold(0.U)(_.asUInt + _.asUInt))
255  }
256  for (i <- 0 until nWays) {
257    XSPerfAccumulate(s"refill${i}", io.w.valid && io.w.bits.wayIdx === i.U)
258  }
259
260  val perfEvents = Seq(
261    ("tlbstore_access", io.r.resp.map(_.valid.asUInt).fold(0.U)(_ + _)                            ),
262    ("tlbstore_hit   ", io.r.resp.map(a => a.valid && a.bits.hit).fold(0.U)(_.asUInt + _.asUInt)),
263  )
264  generatePerfEvent()
265
266  println(s"${parentName} tlb_fa: nSets${nSets} nWays:${nWays}")
267}
268
269class TLBFakeFA(
270             ports: Int,
271             nDups: Int,
272             nSets: Int,
273             nWays: Int,
274             useDmode: Boolean = false
275           )(implicit p: Parameters) extends TlbModule with HasCSRConst{
276
277  val io = IO(new TlbStorageIO(nSets, nWays, ports, nDups))
278  io.r.req.map(_.ready := true.B)
279  val mode = if (useDmode) io.csr.priv.dmode else io.csr.priv.imode
280  val vmEnable = if (EnbaleTlbDebug) (io.csr.satp.mode === 8.U)
281    else (io.csr.satp.mode === 8.U && (mode < ModeM))
282
283  for (i <- 0 until ports) {
284    val req = io.r.req(i)
285    val resp = io.r.resp(i)
286
287    val helper = Module(new PTEHelper())
288    helper.clock := clock
289    helper.satp := io.csr.satp.ppn
290    helper.enable := req.fire && vmEnable
291    helper.vpn := req.bits.vpn
292
293    val pte = helper.pte.asTypeOf(new PteBundle)
294    val ppn = pte.ppn
295    val vpn_reg = RegNext(req.bits.vpn)
296    val pf = helper.pf
297    val level = helper.level
298
299    resp.valid := RegNext(req.valid)
300    resp.bits.hit := true.B
301    for (d <- 0 until nDups) {
302      resp.bits.perm(d).pf := pf
303      resp.bits.perm(d).af := false.B
304      resp.bits.perm(d).d := pte.perm.d
305      resp.bits.perm(d).a := pte.perm.a
306      resp.bits.perm(d).g := pte.perm.g
307      resp.bits.perm(d).u := pte.perm.u
308      resp.bits.perm(d).x := pte.perm.x
309      resp.bits.perm(d).w := pte.perm.w
310      resp.bits.perm(d).r := pte.perm.r
311
312      resp.bits.ppn(d) := MuxLookup(level, 0.U)(Seq(
313        0.U -> Cat(ppn(ppn.getWidth-1, vpnnLen*2), vpn_reg(vpnnLen*2-1, 0)),
314        1.U -> Cat(ppn(ppn.getWidth-1, vpnnLen), vpn_reg(vpnnLen-1, 0)),
315        2.U -> ppn)
316      )
317    }
318  }
319
320  io.access := DontCare
321}
322
323object TlbStorage {
324  def apply
325  (
326    parentName: String,
327    associative: String,
328    ports: Int,
329    nDups: Int = 1,
330    nSets: Int,
331    nWays: Int,
332    saveLevel: Boolean = false,
333    normalPage: Boolean,
334    superPage: Boolean,
335    useDmode: Boolean,
336    SoftTLB: Boolean
337  )(implicit p: Parameters) = {
338    if (SoftTLB) {
339      val storage = Module(new TLBFakeFA(ports, nDups, nSets, nWays, useDmode))
340      storage.suggestName(s"${parentName}_fake_fa")
341      storage.io
342    } else {
343       val storage = Module(new TLBFA(parentName, ports, nDups, nSets, nWays, saveLevel, normalPage, superPage))
344       storage.suggestName(s"${parentName}_fa")
345       storage.io
346    }
347  }
348}
349
350class TlbStorageWrapper(ports: Int, q: TLBParameters, nDups: Int = 1)(implicit p: Parameters) extends TlbModule {
351  val io = IO(new TlbStorageWrapperIO(ports, q, nDups))
352
353  val page = TlbStorage(
354    parentName = q.name + "_storage",
355    associative = q.Associative,
356    ports = ports,
357    nDups = nDups,
358    nSets = q.NSets,
359    nWays = q.NWays,
360    normalPage = true,
361    superPage = true,
362    useDmode = q.useDmode,
363    SoftTLB = coreParams.softTLB
364  )
365
366  for (i <- 0 until ports) {
367    page.r_req_apply(
368      valid = io.r.req(i).valid,
369      vpn = io.r.req(i).bits.vpn,
370      i = i,
371      vpn_extend = io.r.req(i).bits.vpn_extend,
372      s2xlate = io.r.req(i).bits.s2xlate
373    )
374  }
375
376  for (i <- 0 until ports) {
377    val q = page.r.req(i)
378    val p = page.r.resp(i)
379    val rq = io.r.req(i)
380    val rp = io.r.resp(i)
381    rq.ready := q.ready // actually, not used
382    rp.valid := p.valid // actually, not used
383    rp.bits.hit := p.bits.hit
384    for (d <- 0 until nDups) {
385      rp.bits.ppn(d) := p.bits.ppn(d)
386      rp.bits.perm(d).pf := p.bits.perm(d).pf
387      rp.bits.perm(d).af := p.bits.perm(d).af
388      rp.bits.perm(d).d := p.bits.perm(d).d
389      rp.bits.perm(d).a := p.bits.perm(d).a
390      rp.bits.perm(d).g := p.bits.perm(d).g
391      rp.bits.perm(d).u := p.bits.perm(d).u
392      rp.bits.perm(d).x := p.bits.perm(d).x
393      rp.bits.perm(d).w := p.bits.perm(d).w
394      rp.bits.perm(d).r := p.bits.perm(d).r
395    }
396  }
397
398  page.sfence <> io.sfence
399  page.csr <> io.csr
400
401  val refill_idx = if (q.outReplace) {
402    io.replace.page.access <> page.access
403    io.replace.page.chosen_set := DontCare
404    io.replace.page.refillIdx
405  } else {
406    val re = ReplacementPolicy.fromString(q.Replacer, q.NWays)
407    re.access(page.access.map(_.touch_ways))
408    re.way
409  }
410
411  page.w_apply(
412    valid = io.w.valid,
413    wayIdx = refill_idx,
414    data = io.w.bits.data
415  )
416
417    // replacement
418  def get_access(one_hot: UInt, valid: Bool): Valid[UInt] = {
419    val res = Wire(Valid(UInt(log2Up(one_hot.getWidth).W)))
420    res.valid := Cat(one_hot).orR && valid
421    res.bits := OHToUInt(one_hot)
422    res
423  }
424}
425