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

218 lines
8.4 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
}
class TLBReq(lgMaxSize: Int)(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)
val size = UInt(width = log2Ceil(lgMaxSize + 1))
override def cloneType = new TLBReq(lgMaxSize).asInstanceOf[this.type]
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
}
class TLB(lgMaxSize: Int, nEntries: Int)(implicit edge: TLEdgeOut, p: Parameters) extends CoreModule()(p) {
2012-10-10 06:35:03 +02:00
val io = new Bundle {
val req = Decoupled(new TLBReq(lgMaxSize)).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
}
class Entry extends Bundle {
val ppn = UInt(width = ppnBits)
val tag = UInt(width = asIdBits + vpnBits)
val level = UInt(width = log2Ceil(pgLevels))
val u = Bool()
val g = Bool()
val sw = Bool()
val sx = Bool()
val sr = Bool()
val xr = Bool()
val cacheable = Bool()
}
val totalEntries = nEntries + 1
val normalEntries = nEntries
val specialEntry = nEntries
2017-03-13 04:42:51 +01:00
val valid = Reg(init = UInt(0, totalEntries))
val reg_entries = Reg(Vec(totalEntries, UInt(width = new Entry().getWidth)))
val entries = reg_entries.map(_.asTypeOf(new Entry))
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)))
val r_req = Reg(new TLBReq(lgMaxSize))
val priv = Mux(io.req.bits.instruction, io.ptw.status.prv, io.ptw.status.dprv)
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)
val mpu_ppn = Mux(do_refill, refill_ppn,
Mux(vm_enabled, entries.last.ppn, vpn(ppnBits-1, 0)))
val mpu_physaddr = Cat(mpu_ppn, io.req.bits.vaddr(pgIdxBits-1, 0))
val pmp = Module(new PMPChecker(lgMaxSize))
2017-03-15 09:18:39 +01:00
pmp.io.addr := mpu_physaddr
pmp.io.size := io.req.bits.size
2017-03-15 09:18:39 +01:00
pmp.io.pmp := io.ptw.pmp
pmp.io.prv := Mux(Bool(usingVM) && (do_refill || 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-16 02:00:32 +01:00
val prot_r = fastCheck(_.supportsGet) && pmp.io.r
val prot_w = fastCheck(_.supportsPutFull) && pmp.io.w
val prot_x = fastCheck(_.executable) && pmp.io.x
val cacheable = fastCheck(_.supportsAcquireB)
val isSpecial = !io.ptw.resp.bits.homogeneous
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 && (entries(i).level < j || entries(i).tag(base + pgLevelBits - 1, base) === vpn(base + pgLevelBits - 1, base))
2017-03-13 04:42:51 +01:00
}
tagMatch
}} :+ !vm_enabled
val hits = hitsVec.asUInt
val level = Mux1H(hitsVec.init, entries.map(_.level))
val partialPPN = Mux1H(hitsVec.init, entries.map(_.ppn))
2017-03-13 04:42:51 +01:00
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
}
2017-03-20 09:34:47 +01:00
2012-10-10 06:35:03 +02:00
// permission bit arrays
val u_array = Reg(Vec(totalEntries, Bool())) // user permission
val g_array = Reg(Vec(totalEntries, Bool())) // global mapping
val sw_array = Reg(Vec(totalEntries, Bool())) // write permission
val sx_array = Reg(Vec(totalEntries, Bool())) // execute permission
val sr_array = Reg(Vec(totalEntries, Bool())) // read permission
val xr_array = Reg(Vec(totalEntries, Bool())) // read permission to executable page
val cash_array = Reg(Vec(normalEntries, Bool())) // 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
val newEntry = Wire(new Entry)
newEntry.ppn := pte.ppn
newEntry.tag := r_refill_tag
newEntry.level := io.ptw.resp.bits.level
newEntry.u := pte.u
newEntry.g := pte.g
newEntry.sw := pte.sw() && (isSpecial || prot_w)
newEntry.sx := pte.sx() && (isSpecial || prot_x)
newEntry.sr := pte.sr() && (isSpecial || prot_r)
newEntry.xr := pte.sx() && (isSpecial || prot_r)
newEntry.cacheable := isSpecial || cacheable
valid := valid | UIntToOH(waddr)
reg_entries(waddr) := newEntry.asUInt
2012-10-10 06:35:03 +02:00
}
2017-03-20 09:34:47 +01:00
2017-03-13 04:42:51 +01:00
val plru = new PseudoLRU(normalEntries)
2017-03-20 09:34:47 +01:00
val repl_waddr = Mux(!valid(normalEntries-1, 0).andR, PriorityEncoder(~valid(normalEntries-1, 0)), plru.replace)
2016-03-03 08:29:58 +01:00
val priv_ok = Mux(priv_s, ~Mux(io.ptw.status.sum, UInt(0), entries.map(_.u).asUInt), entries.map(_.u).asUInt)
val w_array = Cat(prot_w, priv_ok & ~(~prot_w << specialEntry) & entries.map(_.sw).asUInt)
val x_array = Cat(prot_x, priv_ok & ~(~prot_x << specialEntry) & entries.map(_.sx).asUInt)
val r_array = Cat(prot_r, priv_ok & ~(~prot_r << specialEntry) & (entries.map(_.sr).asUInt | Mux(io.ptw.status.mxr, entries.map(_.xr).asUInt, UInt(0))))
val c_array = Cat(cacheable, ~(~cacheable << specialEntry) & entries.map(_.cacheable).asUInt)
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
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.asUInt, 0))
}
when (multipleHits) {
2016-07-02 23:26:05 +02:00
valid := 0
}
2012-10-10 06:35:03 +02:00
}
}