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