1
0
rocket-chip/src/main/scala/rocket/TLB.scala

210 lines
8.3 KiB
Scala
Raw Normal View History

// See LICENSE.SiFive for license details.
// See LICENSE.Berkeley for license details.
2014-09-13 03:06:41 +02:00
2012-10-10 06:35:03 +02:00
package rocket
import Chisel._
import Chisel.ImplicitConversions._
import config._
import diplomacy._
Heterogeneous Tiles (#550) Fundamental new features: * Added tile package: This package is intended to hold components re-usable across different types of tile. Will be the future location of TL2-RoCC accelerators and new diplomatic versions of intra-tile interfaces. * Adopted [ModuleName]Params convention: Code base was very inconsistent about what to name case classes that provide parameters to modules. Settled on calling them [ModuleName]Params to distinguish them from config.Parameters and config.Config. So far applied mostly only to case classes defined within rocket and tile. * Defined RocketTileParams: A nested case class containing case classes for all the components of a tile (L1 caches and core). Allows all such parameters to vary per-tile. * Defined RocketCoreParams: All the parameters that can be varied per-core. * Defined L1CacheParams: A trait defining the parameters common to L1 caches, made concrete in different derived case classes. * Defined RocketTilesKey: A sequence of RocketTileParams, one for every tile to be created. * Provided HeterogeneousDualCoreConfig: An example of making a heterogeneous chip with two cores, one big and one little. * Changes to legacy code: ReplacementPolicy moved to package util. L1Metadata moved to package tile. Legacy L2 cache agent removed because it can no longer share the metadata array implementation with the L1. Legacy GroundTests on life support. Additional changes that got rolled in along the way: * rocket: Fix critical path through BTB for I$ index bits > pgIdxBits * coreplex: tiles connected via :=* * groundtest: updated to use TileParams * tilelink: cache cork requirements are relaxed to allow more cacheless masters
2017-02-09 22:59:09 +01:00
import coreplex.CacheBlockBytes
import tile.{XLen, CoreModule, CoreBundle}
import uncore.tilelink2._
Heterogeneous Tiles (#550) Fundamental new features: * Added tile package: This package is intended to hold components re-usable across different types of tile. Will be the future location of TL2-RoCC accelerators and new diplomatic versions of intra-tile interfaces. * Adopted [ModuleName]Params convention: Code base was very inconsistent about what to name case classes that provide parameters to modules. Settled on calling them [ModuleName]Params to distinguish them from config.Parameters and config.Config. So far applied mostly only to case classes defined within rocket and tile. * Defined RocketTileParams: A nested case class containing case classes for all the components of a tile (L1 caches and core). Allows all such parameters to vary per-tile. * Defined RocketCoreParams: All the parameters that can be varied per-core. * Defined L1CacheParams: A trait defining the parameters common to L1 caches, made concrete in different derived case classes. * Defined RocketTilesKey: A sequence of RocketTileParams, one for every tile to be created. * Provided HeterogeneousDualCoreConfig: An example of making a heterogeneous chip with two cores, one big and one little. * Changes to legacy code: ReplacementPolicy moved to package util. L1Metadata moved to package tile. Legacy L2 cache agent removed because it can no longer share the metadata array implementation with the L1. Legacy GroundTests on life support. Additional changes that got rolled in along the way: * rocket: Fix critical path through BTB for I$ index bits > pgIdxBits * coreplex: tiles connected via :=* * groundtest: updated to use TileParams * tilelink: cache cork requirements are relaxed to allow more cacheless masters
2017-02-09 22:59:09 +01:00
import util._
2012-10-10 06:35:03 +02:00
case object PAddrBits extends Field[Int]
case object PgLevels extends Field[Int]
case object ASIdBits extends Field[Int]
class SFenceReq(implicit p: Parameters) extends CoreBundle()(p) {
val rs1 = Bool()
val rs2 = Bool()
val asid = UInt(width = asIdBits max 1) // TODO zero-width
}
2015-10-06 06:48:05 +02:00
class TLBReq(implicit p: Parameters) extends CoreBundle()(p) {
2017-03-13 04:42:51 +01:00
val vaddr = UInt(width = vaddrBitsExtended)
val passthrough = Bool()
2012-10-10 06:35:03 +02:00
val instruction = Bool()
2015-03-14 10:49:07 +01:00
val store = Bool()
val sfence = Valid(new SFenceReq)
2012-10-10 06:35:03 +02:00
}
2016-07-02 23:26:05 +02:00
class TLBResp(implicit p: Parameters) extends CoreBundle()(p) {
2012-10-10 06:35:03 +02:00
// lookup responses
val miss = Bool(OUTPUT)
2017-03-13 04:42:51 +01:00
val paddr = UInt(OUTPUT, paddrBits)
2012-10-10 06:35:03 +02:00
val xcpt_ld = Bool(OUTPUT)
val xcpt_st = Bool(OUTPUT)
val xcpt_if = Bool(OUTPUT)
val cacheable = Bool(OUTPUT)
2012-10-10 06:35:03 +02:00
}
Heterogeneous Tiles (#550) Fundamental new features: * Added tile package: This package is intended to hold components re-usable across different types of tile. Will be the future location of TL2-RoCC accelerators and new diplomatic versions of intra-tile interfaces. * Adopted [ModuleName]Params convention: Code base was very inconsistent about what to name case classes that provide parameters to modules. Settled on calling them [ModuleName]Params to distinguish them from config.Parameters and config.Config. So far applied mostly only to case classes defined within rocket and tile. * Defined RocketTileParams: A nested case class containing case classes for all the components of a tile (L1 caches and core). Allows all such parameters to vary per-tile. * Defined RocketCoreParams: All the parameters that can be varied per-core. * Defined L1CacheParams: A trait defining the parameters common to L1 caches, made concrete in different derived case classes. * Defined RocketTilesKey: A sequence of RocketTileParams, one for every tile to be created. * Provided HeterogeneousDualCoreConfig: An example of making a heterogeneous chip with two cores, one big and one little. * Changes to legacy code: ReplacementPolicy moved to package util. L1Metadata moved to package tile. Legacy L2 cache agent removed because it can no longer share the metadata array implementation with the L1. Legacy GroundTests on life support. Additional changes that got rolled in along the way: * rocket: Fix critical path through BTB for I$ index bits > pgIdxBits * coreplex: tiles connected via :=* * groundtest: updated to use TileParams * tilelink: cache cork requirements are relaxed to allow more cacheless masters
2017-02-09 22:59:09 +01:00
class TLB(entries: Int)(implicit edge: TLEdgeOut, p: Parameters) extends CoreModule()(p) {
2012-10-10 06:35:03 +02:00
val io = new Bundle {
2013-08-12 19:39:11 +02:00
val req = Decoupled(new TLBReq).flip
val resp = new TLBResp
2013-01-07 22:38:59 +01:00
val ptw = new TLBPTWIO
2012-10-10 06:35:03 +02:00
}
2017-03-13 04:42:51 +01:00
val totalEntries = entries + 1
val normalEntries = entries
val specialEntry = entries
val valid = Reg(init = UInt(0, totalEntries))
val ppns = Reg(Vec(totalEntries, UInt(width = ppnBits)))
val tags = Reg(Vec(totalEntries, UInt(width = asIdBits + vpnBits)))
val levels = Reg(Vec(totalEntries, UInt(width = log2Ceil(pgLevels))))
2016-03-31 07:48:31 +02:00
2013-09-10 19:51:35 +02:00
val s_ready :: s_request :: s_wait :: s_wait_invalidate :: Nil = Enum(UInt(), 4)
2013-08-16 00:28:15 +02:00
val state = Reg(init=s_ready)
2016-07-02 23:26:05 +02:00
val r_refill_tag = Reg(UInt(width = asIdBits + vpnBits))
2017-03-13 04:42:51 +01:00
val r_refill_waddr = Reg(UInt(width = log2Ceil(normalEntries)))
2015-03-14 10:49:07 +01:00
val r_req = Reg(new TLBReq)
val do_mprv = io.ptw.status.mprv && !io.req.bits.instruction
val priv = Mux(do_mprv, io.ptw.status.mpp, io.ptw.status.prv)
val priv_s = priv(0)
val priv_uses_vm = priv <= PRV.S
2017-03-13 04:42:51 +01:00
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)
2017-03-13 04:42:51 +01:00
val (vpn, pgOffset) = Split(io.req.bits.vaddr, 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)
2017-03-13 04:42:51 +01:00
val mpu_ppn = Mux(do_refill, refill_ppn,
Mux(vm_enabled, ppns.last, vpn(ppnBits-1, 0)))
val mpu_physaddr = Cat(mpu_ppn, io.req.bits.vaddr(pgIdxBits-1, 0))
2017-03-15 09:18:39 +01:00
val pmp = Module(new PMPChecker(8))
pmp.io.addr := mpu_physaddr
pmp.io.size := 2
pmp.io.pmp := io.ptw.pmp
pmp.io.prv := Mux(io.req.bits.passthrough /* PTW */, PRV.S, priv)
val legal_address = edge.manager.findSafe(mpu_physaddr).reduce(_||_)
def fastCheck(member: TLManagerParameters => Boolean) =
legal_address && Mux1H(edge.manager.findFast(mpu_physaddr), edge.manager.managers.map(m => Bool(member(m))))
2017-03-15 09:18:39 +01:00
val prot_r = fastCheck(_.supportsGet) && !pmp.io.xcpt_ld
val prot_w = fastCheck(_.supportsPutFull) && !pmp.io.xcpt_st
val prot_x = fastCheck(_.executable) && !pmp.io.xcpt_if
val cacheable = fastCheck(_.supportsAcquireB)
2017-03-13 04:42:51 +01:00
val isSpecial = {
val homogeneous = Wire(init = false.B)
for (i <- 0 until pgLevels) {
when (io.ptw.resp.bits.level === i) { homogeneous := TLBPageLookup(edge.manager.managers, xLen, p(CacheBlockBytes), BigInt(1) << (pgIdxBits + ((pgLevels - 1 - i) * pgLevelBits)))(mpu_physaddr).homogeneous }
}
!homogeneous
2016-08-10 07:14:32 +02:00
}
2017-03-13 04:42:51 +01:00
val lookup_tag = Cat(io.ptw.ptbr.asid, vpn(vpnBits-1,0))
val hitsVec = (0 until totalEntries).map { i => vm_enabled && {
var tagMatch = valid(i)
for (j <- 0 until pgLevels) {
val base = vpnBits - (j + 1) * pgLevelBits
tagMatch = tagMatch && (levels(i) < j || tags(i)(base + pgLevelBits - 1, base) === vpn(base + pgLevelBits - 1, base))
}
tagMatch
}} :+ !vm_enabled
val hits = hitsVec.asUInt
2017-03-13 04:42:51 +01:00
val level = Mux1H(hitsVec.init, levels)
val partialPPN = Mux1H(hitsVec.init, ppns)
val ppn = {
var ppn = Mux(vm_enabled, partialPPN, vpn)(pgLevelBits*pgLevels - 1, pgLevelBits*(pgLevels - 1))
for (i <- 1 until pgLevels)
ppn = Cat(ppn, (Mux(level < i, vpn, 0.U) | partialPPN)(vpnBits - i*pgLevelBits - 1, vpnBits - (i + 1)*pgLevelBits))
ppn
}
2012-10-10 06:35:03 +02:00
// permission bit arrays
2017-03-13 04:42:51 +01:00
val u_array = Reg(UInt(width = totalEntries)) // user permission
val g_array = Reg(UInt(width = totalEntries)) // global mapping
2017-03-13 04:42:51 +01:00
val sw_array = Reg(UInt(width = totalEntries)) // write permission
val sx_array = Reg(UInt(width = totalEntries)) // execute permission
val sr_array = Reg(UInt(width = totalEntries)) // read permission
val xr_array = Reg(UInt(width = totalEntries)) // read permission to executable page
val cash_array = Reg(UInt(width = normalEntries)) // cacheable
when (do_refill && !invalidate_refill) {
2017-03-13 04:42:51 +01:00
val waddr = Mux(isSpecial, specialEntry.U, r_refill_waddr)
val pte = io.ptw.resp.bits.pte
2017-03-13 04:42:51 +01:00
ppns(waddr) := pte.ppn
tags(waddr) := r_refill_tag
levels(waddr) := io.ptw.resp.bits.level
2016-07-02 23:26:05 +02:00
2017-03-13 04:42:51 +01:00
val mask = UIntToOH(waddr)
2016-07-02 23:26:05 +02:00
valid := valid | mask
2016-07-06 04:19:49 +02:00
u_array := Mux(pte.u, u_array | mask, u_array & ~mask)
g_array := Mux(pte.g, g_array | mask, g_array & ~mask)
2017-03-13 04:42:51 +01:00
sw_array := Mux(pte.sw() && (isSpecial || prot_w), sw_array | mask, sw_array & ~mask)
sx_array := Mux(pte.sx() && (isSpecial || prot_x), sx_array | mask, sx_array & ~mask)
sr_array := Mux(pte.sr() && (isSpecial || prot_r), sr_array | mask, sr_array & ~mask)
xr_array := Mux(pte.sx() && (isSpecial || prot_r), xr_array | mask, xr_array & ~mask)
cash_array := Mux(cacheable, cash_array | mask, cash_array & ~mask)
2012-10-10 06:35:03 +02:00
}
2017-03-13 04:42:51 +01:00
val plru = new PseudoLRU(normalEntries)
2016-07-02 23:26:05 +02:00
val repl_waddr = Mux(!valid.andR, PriorityEncoder(~valid), plru.replace)
2016-03-03 08:29:58 +01:00
2016-07-06 04:19:49 +02:00
val priv_ok = Mux(priv_s, ~Mux(io.ptw.status.pum, u_array, UInt(0)), u_array)
2017-03-13 04:42:51 +01:00
val w_array = Cat(prot_w, priv_ok & ~(~prot_w << specialEntry) & sw_array)
val x_array = Cat(prot_x, priv_ok & ~(~prot_x << specialEntry) & sx_array)
val r_array = Cat(prot_r, priv_ok & ~(~prot_r << specialEntry) & (sr_array | Mux(io.ptw.status.mxr, xr_array, UInt(0))))
val c_array = Cat(cacheable, cacheable, cash_array)
2015-03-14 10:49:07 +01:00
2016-03-11 02:32:00 +01:00
val bad_va =
if (vpnBits == vpnBitsExtended) Bool(false)
2017-03-13 04:42:51 +01:00
else vpn(vpnBits) =/= vpn(vpnBits-1)
val tlb_hit = hits(totalEntries-1, 0).orR
val tlb_miss = vm_enabled && !bad_va && !tlb_hit && !io.req.bits.sfence.valid
2017-03-13 04:42:51 +01:00
when (io.req.valid && !tlb_miss && !hits(specialEntry)) {
plru.access(OHToUInt(hits(normalEntries-1, 0)))
2012-10-10 06:35:03 +02:00
}
// Superpages create the possibility that two entries in the TLB may match.
// This corresponds to a software bug, but we can't return complete garbage;
// we must return either the old translation or the new translation. This
// isn't compatible with the Mux1H approach. So, flush the TLB and report
// a miss on duplicate entries.
2017-03-13 04:42:51 +01:00
val multipleHits = PopCountAtLeast(hits(totalEntries-1, 0), 2)
2012-10-10 06:35:03 +02:00
io.req.ready := state === s_ready
io.resp.xcpt_ld := bad_va || (~r_array & hits).orR
io.resp.xcpt_st := bad_va || (~w_array & hits).orR
io.resp.xcpt_if := bad_va || (~x_array & hits).orR
io.resp.cacheable := (c_array & hits).orR
io.resp.miss := do_refill || tlb_miss || multipleHits
2017-03-13 04:42:51 +01:00
io.resp.paddr := Cat(ppn, pgOffset)
2015-03-14 10:49:07 +01:00
2012-10-10 06:35:03 +02:00
io.ptw.req.valid := state === s_request
io.ptw.req.bits <> io.ptw.status
2015-03-14 10:49:07 +01:00
io.ptw.req.bits.addr := r_refill_tag
io.ptw.req.bits.store := r_req.store
io.ptw.req.bits.fetch := r_req.instruction
2012-10-10 06:35:03 +02:00
2016-03-25 22:17:25 +01:00
if (usingVM) {
val sfence = io.req.valid && io.req.bits.sfence.valid
2016-03-25 22:17:25 +01:00
when (io.req.fire() && tlb_miss) {
state := s_request
r_refill_tag := lookup_tag
r_refill_waddr := repl_waddr
r_req := io.req.bits
2012-10-10 06:35:03 +02:00
}
2016-03-25 22:17:25 +01:00
when (state === s_request) {
when (sfence) { state := s_ready }
when (io.ptw.req.ready) { state := Mux(sfence, s_wait_invalidate, s_wait) }
2016-03-25 22:17:25 +01:00
}
when (state === s_wait && sfence) {
2016-03-25 22:17:25 +01:00
state := s_wait_invalidate
}
when (io.ptw.resp.valid) {
state := s_ready
2012-10-10 06:35:03 +02:00
}
2016-07-02 23:26:05 +02:00
when (sfence) {
valid := Mux(io.req.bits.sfence.bits.rs1, valid & ~hits(totalEntries-1, 0),
Mux(io.req.bits.sfence.bits.rs2, valid & g_array, 0))
}
when (multipleHits) {
2016-07-02 23:26:05 +02:00
valid := 0
}
2012-10-10 06:35:03 +02:00
}
}