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