diff --git a/src/main/scala/diplomaticobjectmodel/logicaltree/RocketLogicalTreeNode.scala b/src/main/scala/diplomaticobjectmodel/logicaltree/RocketLogicalTreeNode.scala index 314474e59c2..6ece366a09e 100644 --- a/src/main/scala/diplomaticobjectmodel/logicaltree/RocketLogicalTreeNode.scala +++ b/src/main/scala/diplomaticobjectmodel/logicaltree/RocketLogicalTreeNode.scala @@ -30,6 +30,7 @@ class DCacheLogicalTreeNode(dcache: HellaCache, deviceOpt: Option[SimpleDevice], dataMemorySizeBytes = params.nSets * params.nWays * params.blockBytes, dataECC = params.dataECC.map(OMECC.fromString), tagECC = params.tagECC.map(OMECC.fromString), + nTLBSets = params.nTLBSets, nTLBEntries = params.nTLBEntries, memories = dcache.getOMSRAMs(), ) @@ -49,6 +50,7 @@ class ICacheLogicalTreeNode(icache: ICache, deviceOpt: Option[SimpleDevice], par dataMemorySizeBytes = params.nSets * params.nWays * params.blockBytes, dataECC = params.dataECC.map(OMECC.fromString), tagECC = params.tagECC.map(OMECC.fromString), + nTLBSets = params.nTLBSets, nTLBEntries = params.nTLBEntries, maxTimSize = params.nSets * (params.nWays-1) * params.blockBytes, memories = icache.module.data_arrays.map(_._2), diff --git a/src/main/scala/diplomaticobjectmodel/model/OMCaches.scala b/src/main/scala/diplomaticobjectmodel/model/OMCaches.scala index 9435ff011ea..4fc6b58090d 100644 --- a/src/main/scala/diplomaticobjectmodel/model/OMCaches.scala +++ b/src/main/scala/diplomaticobjectmodel/model/OMCaches.scala @@ -25,6 +25,7 @@ case class OMICache( dataMemorySizeBytes: Int, dataECC: Option[OMECC], tagECC: Option[OMECC], + nTLBSets: Int, nTLBEntries: Int, maxTimSize: Int, memories: Seq[OMSRAM], @@ -40,6 +41,7 @@ case class OMDCache( dataMemorySizeBytes: Int, dataECC: Option[OMECC], tagECC: Option[OMECC], + nTLBSets: Int, nTLBEntries: Int, memories: Seq[OMSRAM], _types: Seq[String] = Seq("OMDCache", "OMCache", "OMDevice", "OMComponent", "OMCompoundType") diff --git a/src/main/scala/rocket/DCache.scala b/src/main/scala/rocket/DCache.scala index 5828fb0b450..d5cb047cf23 100644 --- a/src/main/scala/rocket/DCache.scala +++ b/src/main/scala/rocket/DCache.scala @@ -112,8 +112,8 @@ class DCacheModule(outer: DCache) extends HellaCacheModule(outer) { else ClockGate(clock, clock_en_reg, "dcache_clock_gate") @chiselName class DCacheModuleImpl extends NoChiselNamePrefix { // entering gated-clock domain - val tlb = Module(new TLB(false, log2Ceil(coreDataBytes), TLBConfig(nTLBEntries, cacheParams.nTLBBasePageSectors, cacheParams.nTLBSuperpages))) - val pma_checker = Module(new TLB(false, log2Ceil(coreDataBytes), TLBConfig(nTLBEntries, cacheParams.nTLBBasePageSectors, cacheParams.nTLBSuperpages)) with InlineInstance) + val tlb = Module(new TLB(false, log2Ceil(coreDataBytes), TLBConfig(nTLBSets, nTLBEntries, cacheParams.nTLBBasePageSectors, cacheParams.nTLBSuperpages))) + val pma_checker = Module(new TLB(false, log2Ceil(coreDataBytes), TLBConfig(nTLBSets, nTLBEntries, cacheParams.nTLBBasePageSectors, cacheParams.nTLBSuperpages)) with InlineInstance) // tags val replacer = cacheParams.replacement diff --git a/src/main/scala/rocket/Frontend.scala b/src/main/scala/rocket/Frontend.scala index 024d8345ed0..7886bab209c 100644 --- a/src/main/scala/rocket/Frontend.scala +++ b/src/main/scala/rocket/Frontend.scala @@ -97,7 +97,7 @@ class FrontendModule(outer: Frontend) extends LazyModuleImp(outer) icache.io.clock_enabled := clock_en withClock (gated_clock) { // entering gated-clock domain - val tlb = Module(new TLB(true, log2Ceil(fetchBytes), TLBConfig(nTLBEntries, outer.icacheParams.nTLBBasePageSectors, outer.icacheParams.nTLBSuperpages))) + val tlb = Module(new TLB(true, log2Ceil(fetchBytes), TLBConfig(nTLBSets, nTLBEntries, outer.icacheParams.nTLBBasePageSectors, outer.icacheParams.nTLBSuperpages))) val s1_valid = Reg(Bool()) val s2_valid = RegInit(false.B) diff --git a/src/main/scala/rocket/HellaCache.scala b/src/main/scala/rocket/HellaCache.scala index 312e15cca1b..0e461345c34 100644 --- a/src/main/scala/rocket/HellaCache.scala +++ b/src/main/scala/rocket/HellaCache.scala @@ -19,6 +19,7 @@ case class DCacheParams( nSets: Int = 64, nWays: Int = 4, rowBits: Int = 64, + nTLBSets: Int = 1, nTLBEntries: Int = 32, nTLBBasePageSectors: Int = 4, nTLBSuperpages: Int = 4, diff --git a/src/main/scala/rocket/ICache.scala b/src/main/scala/rocket/ICache.scala index 28be75b51e9..1ccb0dbd59d 100644 --- a/src/main/scala/rocket/ICache.scala +++ b/src/main/scala/rocket/ICache.scala @@ -22,6 +22,7 @@ case class ICacheParams( nSets: Int = 64, nWays: Int = 4, rowBits: Int = 128, + nTLBSets: Int = 1, nTLBEntries: Int = 32, nTLBBasePageSectors: Int = 4, nTLBSuperpages: Int = 4, diff --git a/src/main/scala/rocket/NBDcache.scala b/src/main/scala/rocket/NBDcache.scala index 6adce7cde1d..a0e9116c194 100644 --- a/src/main/scala/rocket/NBDcache.scala +++ b/src/main/scala/rocket/NBDcache.scala @@ -718,7 +718,7 @@ class NonBlockingDCacheModule(outer: NonBlockingDCache) extends HellaCacheModule // check for unsupported operations assert(!s1_valid || !s1_req.cmd.isOneOf(M_PWR)) - val dtlb = Module(new TLB(false, log2Ceil(coreDataBytes), TLBConfig(nTLBEntries))) + val dtlb = Module(new TLB(false, log2Ceil(coreDataBytes), TLBConfig(nTLBSets, nTLBEntries))) io.ptw <> dtlb.io.ptw dtlb.io.kill := io.cpu.s2_kill dtlb.io.req.valid := s1_valid && !io.cpu.s1_kill && s1_readwrite diff --git a/src/main/scala/rocket/TLB.scala b/src/main/scala/rocket/TLB.scala index 35a396dd1cd..7a96e50fdcd 100644 --- a/src/main/scala/rocket/TLB.scala +++ b/src/main/scala/rocket/TLB.scala @@ -145,6 +145,7 @@ class TLBEntry(val nSectors: Int, val superpage: Boolean, val superpageOnly: Boo } case class TLBConfig( + nSets: Int, nEntries: Int, nSectors: Int = 4, nSuperpageEntries: Int = 4) @@ -159,18 +160,21 @@ class TLB(instruction: Boolean, lgMaxSize: Int, cfg: TLBConfig)(implicit edge: T } val pageGranularityPMPs = pmpGranularity >= (1 << pgIdxBits) - val sectored_entries = Reg(Vec(cfg.nEntries / cfg.nSectors, new TLBEntry(cfg.nSectors, false, false))) + val vpn = io.req.bits.vaddr(vaddrBits-1, pgIdxBits) + val memIdx = vpn.extract(cfg.nSectors.log2 + cfg.nSets.log2 - 1, cfg.nSectors.log2) + val sectored_entries = Reg(Vec(cfg.nSets, Vec(cfg.nEntries / cfg.nSectors, new TLBEntry(cfg.nSectors, false, false)))) val superpage_entries = Reg(Vec(cfg.nSuperpageEntries, new TLBEntry(1, true, true))) val special_entry = (!pageGranularityPMPs).option(Reg(new TLBEntry(1, true, false))) - def ordinary_entries = sectored_entries ++ superpage_entries + def ordinary_entries = sectored_entries(memIdx) ++ superpage_entries def all_entries = ordinary_entries ++ special_entry + def all_real_entries = sectored_entries.flatten ++ superpage_entries ++ special_entry val s_ready :: s_request :: s_wait :: s_wait_invalidate :: Nil = Enum(UInt(), 4) val state = Reg(init=s_ready) val r_refill_tag = Reg(UInt(width = vpnBits)) val r_superpage_repl_addr = Reg(UInt(log2Ceil(superpage_entries.size).W)) - val r_sectored_repl_addr = Reg(UInt(log2Ceil(sectored_entries.size).W)) - val r_sectored_hit_addr = Reg(UInt(log2Ceil(sectored_entries.size).W)) + val r_sectored_repl_addr = Reg(UInt(log2Ceil(sectored_entries(0).size).W)) + val r_sectored_hit_addr = Reg(UInt(log2Ceil(sectored_entries(0).size).W)) val r_sectored_hit = Reg(Bool()) val priv = if (instruction) io.ptw.status.prv else io.ptw.status.dprv @@ -179,7 +183,6 @@ class TLB(instruction: Boolean, lgMaxSize: Int, cfg: TLBConfig)(implicit edge: T val vm_enabled = Bool(usingVM) && io.ptw.ptbr.mode(io.ptw.ptbr.mode.getWidth-1) && priv_uses_vm && !io.req.bits.passthrough // share a single physical memory attribute checker (unshare if critical path) - val vpn = io.req.bits.vaddr(vaddrBits-1, pgIdxBits) val refill_ppn = io.ptw.resp.bits.pte.ppn(ppnBits-1, 0) val do_refill = Bool(usingVM) && io.ptw.resp.valid val invalidate_refill = state.isOneOf(s_request /* don't care */, s_wait_invalidate) || io.sfence.valid @@ -206,7 +209,7 @@ class TLB(instruction: Boolean, lgMaxSize: Int, cfg: TLBConfig)(implicit edge: T val prot_x = fastCheck(_.executable) && !deny_access_to_debug && pmp.io.x val prot_eff = fastCheck(Seq(RegionType.PUT_EFFECTS, RegionType.GET_EFFECTS) contains _.regionType) - val sector_hits = sectored_entries.map(_.sectorHit(vpn)) + val sector_hits = sectored_entries(memIdx).map(_.sectorHit(vpn)) val superpage_hits = superpage_entries.map(_.hit(vpn)) val hitsVec = all_entries.map(vm_enabled && _.hit(vpn)) val real_hits = hitsVec.asUInt @@ -245,8 +248,9 @@ class TLB(instruction: Boolean, lgMaxSize: Int, cfg: TLBConfig)(implicit edge: T when (invalidate_refill) { e.invalidate() } } }.otherwise { + val r_memIdx = r_refill_tag.extract(cfg.nSectors.log2 + cfg.nSets.log2 - 1, cfg.nSectors.log2) val waddr = Mux(r_sectored_hit, r_sectored_hit_addr, r_sectored_repl_addr) - for ((e, i) <- sectored_entries.zipWithIndex) when (waddr === i) { + for ((e, i) <- sectored_entries(r_memIdx).zipWithIndex) when (waddr === i) { when (!r_sectored_hit) { e.invalidate() } e.insert(r_refill_tag, 0.U, newEntry) when (invalidate_refill) { e.invalidate() } @@ -320,10 +324,10 @@ class TLB(instruction: Boolean, lgMaxSize: Int, cfg: TLBConfig)(implicit edge: T val tlb_hit = real_hits.orR val tlb_miss = vm_enabled && !bad_va && !tlb_hit - val sectored_plru = new PseudoLRU(sectored_entries.size) + val sectored_plru = new SetAssocLRU(cfg.nSets, cfg.nEntries, "plru") val superpage_plru = new PseudoLRU(superpage_entries.size) when (io.req.valid && vm_enabled) { - when (sector_hits.orR) { sectored_plru.access(OHToUInt(sector_hits)) } + when (sector_hits.orR) { sectored_plru.access(memIdx, OHToUInt(sector_hits)) } when (superpage_hits.orR) { superpage_plru.access(OHToUInt(superpage_hits)) } } @@ -361,7 +365,7 @@ class TLB(instruction: Boolean, lgMaxSize: Int, cfg: TLBConfig)(implicit edge: T r_refill_tag := vpn r_superpage_repl_addr := replacementEntry(superpage_entries, superpage_plru.way) - r_sectored_repl_addr := replacementEntry(sectored_entries, sectored_plru.way) + r_sectored_repl_addr := replacementEntry(sectored_entries(memIdx), sectored_plru.way(memIdx)) r_sectored_hit_addr := OHToUInt(sector_hits) r_sectored_hit := sector_hits.orR } @@ -379,14 +383,14 @@ class TLB(instruction: Boolean, lgMaxSize: Int, cfg: TLBConfig)(implicit edge: T when (sfence) { assert(!io.sfence.bits.rs1 || (io.sfence.bits.addr >> pgIdxBits) === vpn) - for (e <- all_entries) { + for (e <- all_real_entries) { when (io.sfence.bits.rs1) { e.invalidateVPN(vpn) } .elsewhen (io.sfence.bits.rs2) { e.invalidateNonGlobal() } .otherwise { e.invalidate() } } } when (multipleHits || reset) { - all_entries.foreach(_.invalidate()) + all_real_entries.foreach(_.invalidate()) } ccover(io.ptw.req.fire(), "MISS", "TLB miss") diff --git a/src/main/scala/subsystem/Configs.scala b/src/main/scala/subsystem/Configs.scala index 20fd3dd455d..c0ba6cfef24 100644 --- a/src/main/scala/subsystem/Configs.scala +++ b/src/main/scala/subsystem/Configs.scala @@ -109,6 +109,7 @@ class WithNMedCores(n: Int, overrideIdOffset: Option[Int] = None) extends Config rowBits = site(SystemBusKey).beatBits, nSets = 64, nWays = 1, + nTLBSets = 1, nTLBEntries = 4, nMSHRs = 0, blockBytes = site(CacheBlockBytes))), @@ -116,6 +117,7 @@ class WithNMedCores(n: Int, overrideIdOffset: Option[Int] = None) extends Config rowBits = site(SystemBusKey).beatBits, nSets = 64, nWays = 1, + nTLBSets = 1, nTLBEntries = 4, blockBytes = site(CacheBlockBytes)))) List.tabulate(n)(i => med.copy(hartId = i + idOffset)) ++ prev @@ -133,6 +135,7 @@ class WithNSmallCores(n: Int, overrideIdOffset: Option[Int] = None) extends Conf rowBits = site(SystemBusKey).beatBits, nSets = 64, nWays = 1, + nTLBSets = 1, nTLBEntries = 4, nMSHRs = 0, blockBytes = site(CacheBlockBytes))), @@ -140,6 +143,7 @@ class WithNSmallCores(n: Int, overrideIdOffset: Option[Int] = None) extends Conf rowBits = site(SystemBusKey).beatBits, nSets = 64, nWays = 1, + nTLBSets = 1, nTLBEntries = 4, blockBytes = site(CacheBlockBytes)))) List.tabulate(n)(i => small.copy(hartId = i + idOffset)) ++ prev @@ -158,6 +162,7 @@ class With1TinyCore extends Config((site, here, up) => { rowBits = site(SystemBusKey).beatBits, nSets = 256, // 16Kb scratchpad nWays = 1, + nTLBSets = 1, nTLBEntries = 4, nMSHRs = 0, blockBytes = site(CacheBlockBytes), @@ -166,6 +171,7 @@ class With1TinyCore extends Config((site, here, up) => { rowBits = site(SystemBusKey).beatBits, nSets = 64, nWays = 1, + nTLBSets = 1, nTLBEntries = 4, blockBytes = site(CacheBlockBytes))))) case RocketCrossingKey => List(RocketCrossingParams( diff --git a/src/main/scala/tile/BaseTile.scala b/src/main/scala/tile/BaseTile.scala index b9d35f21f20..0c6761832f0 100644 --- a/src/main/scala/tile/BaseTile.scala +++ b/src/main/scala/tile/BaseTile.scala @@ -117,12 +117,12 @@ trait HasNonDiplomaticTileParameters { ).getOrElse(Nil) val dtlb = tileParams.dcache.filter(_ => tileParams.core.useVM).map(d => Map( - "d-tlb-size" -> d.nTLBEntries.asProperty, - "d-tlb-sets" -> 1.asProperty)).getOrElse(Nil) + "d-tlb-size" -> (d.nTLBEntries * d.nTLBSets).asProperty, + "d-tlb-sets" -> d.nTLBSets.asProperty)).getOrElse(Nil) val itlb = tileParams.icache.filter(_ => tileParams.core.useVM).map(i => Map( - "i-tlb-size" -> i.nTLBEntries.asProperty, - "i-tlb-sets" -> 1.asProperty)).getOrElse(Nil) + "i-tlb-size" -> (i.nTLBEntries * i.nTLBSets).asProperty, + "i-tlb-sets" -> i.nTLBSets.asProperty)).getOrElse(Nil) val mmu = if (!tileParams.core.useVM) Nil else Map( "tlb-split" -> Nil, diff --git a/src/main/scala/tile/L1Cache.scala b/src/main/scala/tile/L1Cache.scala index dab775448b4..2990e9741d1 100644 --- a/src/main/scala/tile/L1Cache.scala +++ b/src/main/scala/tile/L1Cache.scala @@ -12,6 +12,7 @@ trait L1CacheParams { def nSets: Int def nWays: Int def rowBits: Int + def nTLBSets: Int def nTLBEntries: Int def blockBytes: Int // TODO this is ignored in favor of p(CacheBlockBytes) in BaseTile } @@ -31,6 +32,7 @@ trait HasL1CacheParameters extends HasTileParameters { def rowBits = cacheParams.rowBits def rowBytes = rowBits/8 def rowOffBits = log2Up(rowBytes) + def nTLBSets = cacheParams.nTLBSets def nTLBEntries = cacheParams.nTLBEntries def cacheDataBits = tlBundleParams.dataBits diff --git a/src/main/scala/util/Replacement.scala b/src/main/scala/util/Replacement.scala index f9817627a8d..fa6c95a593b 100644 --- a/src/main/scala/util/Replacement.scala +++ b/src/main/scala/util/Replacement.scala @@ -54,6 +54,11 @@ abstract class SeqReplacementPolicy { def way: UInt } +abstract class SetAssocReplacementPolicy { + def access(set: UInt, touch_way: UInt): Unit + def way(set: UInt): UInt +} + class SeqRandom(n_ways: Int) extends SeqReplacementPolicy { val logic = new RandomReplacement(n_ways) def access(set: UInt) = { } @@ -283,6 +288,23 @@ class SeqPLRU(n_sets: Int, n_ways: Int) extends SeqReplacementPolicy { def way = plru_way } + +class SetAssocLRU(n_sets: Int, n_ways: Int, policy: String) extends SetAssocReplacementPolicy { + val logic = policy.toLowerCase match { + case "plru" => new PseudoLRU(n_ways) + case "lru" => new TrueLRU(n_ways) + case t => throw new IllegalArgumentException(s"unknown Replacement Policy type $t") + } + val state_vec = Reg(Vec(n_sets, UInt(logic.nBits.W))) + + def access(set: UInt, touch_way: UInt) = { + state_vec(set) := logic.get_next_state(state_vec(set), touch_way) + } + + def way(set: UInt) = logic.get_replace_way(state_vec(set)) + +} + /** Synthesizeable unit tests */ import freechips.rocketchip.unittest._