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