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