1
0
Fork 0
rocket-chip/src/main/scala/rocket/PTW.scala

312 lines
10 KiB
Scala
Raw Normal View History

// See LICENSE.Berkeley for license details.
// See LICENSE.SiFive for license details.
2014-09-13 03:06:41 +02:00
package freechips.rocketchip.rocket
2011-11-09 23:52:17 +01:00
2012-10-08 05:15:54 +02:00
import Chisel._
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 Chisel.ImplicitConversions._
import freechips.rocketchip.config.Parameters
2018-01-12 21:29:27 +01:00
import freechips.rocketchip.subsystem.CacheBlockBytes
import freechips.rocketchip.tile._
import freechips.rocketchip.tilelink._
import freechips.rocketchip.util._
2017-10-10 03:33:36 +02:00
import freechips.rocketchip.util.property._
import chisel3.internal.sourceinfo.SourceInfo
import scala.collection.mutable.ListBuffer
2011-11-09 23:52:17 +01:00
2015-10-06 06:48:05 +02:00
class PTWReq(implicit p: Parameters) extends CoreBundle()(p) {
2016-07-06 04:19:49 +02:00
val addr = UInt(width = vpnBits)
2015-03-14 10:49:07 +01:00
}
2015-10-06 06:48:05 +02:00
class PTWResp(implicit p: Parameters) extends CoreBundle()(p) {
val ae = Bool()
val pte = new PTE
2017-03-13 04:42:51 +01:00
val level = UInt(width = log2Ceil(pgLevels))
val homogeneous = Bool()
2013-08-12 19:39:11 +02:00
}
2017-03-15 09:18:39 +01:00
class TLBPTWIO(implicit p: Parameters) extends CoreBundle()(p)
with HasCoreParameters {
2015-03-14 10:49:07 +01:00
val req = Decoupled(new PTWReq)
2013-08-12 19:39:11 +02:00
val resp = Valid(new PTWResp).flip
2016-06-18 03:29:05 +02:00
val ptbr = new PTBR().asInput
val status = new MStatus().asInput
2017-03-15 09:18:39 +01:00
val pmp = Vec(nPMPs, new PMP).asInput
}
2017-07-25 20:59:53 +02:00
class PTWPerfEvents extends Bundle {
val l2miss = Bool()
}
2017-03-15 09:18:39 +01:00
class DatapathPTWIO(implicit p: Parameters) extends CoreBundle()(p)
with HasCoreParameters {
2016-06-18 03:29:05 +02:00
val ptbr = new PTBR().asInput
2017-07-06 08:53:52 +02:00
val sfence = Valid(new SFenceReq).flip
2015-03-14 10:49:07 +01:00
val status = new MStatus().asInput
2017-03-15 09:18:39 +01:00
val pmp = Vec(nPMPs, new PMP).asInput
2017-07-25 20:59:53 +02:00
val perf = new PTWPerfEvents().asOutput
2011-11-09 23:52:17 +01:00
}
2015-10-06 06:48:05 +02:00
class PTE(implicit p: Parameters) extends CoreBundle()(p) {
2017-02-27 23:27:19 +01:00
val ppn = UInt(width = 54)
2016-07-06 04:19:49 +02:00
val reserved_for_software = Bits(width = 2)
val d = Bool()
2016-07-06 04:19:49 +02:00
val a = Bool()
val g = Bool()
val u = Bool()
val x = Bool()
val w = Bool()
val r = Bool()
2015-05-19 03:23:58 +02:00
val v = Bool()
2016-07-06 04:19:49 +02:00
def table(dummy: Int = 0) = v && !r && !w && !x
2017-02-27 23:27:19 +01:00
def leaf(dummy: Int = 0) = v && (r || (x && !w)) && a
2016-07-06 04:19:49 +02:00
def ur(dummy: Int = 0) = sr() && u
def uw(dummy: Int = 0) = sw() && u
def ux(dummy: Int = 0) = sx() && u
def sr(dummy: Int = 0) = leaf() && r
2017-02-27 23:27:19 +01:00
def sw(dummy: Int = 0) = leaf() && w && d
2016-07-06 04:19:49 +02:00
def sx(dummy: Int = 0) = leaf() && x
}
class PTW(n: Int)(implicit edge: TLEdgeOut, p: Parameters) extends CoreModule()(p) {
val io = new Bundle {
2016-01-14 22:57:45 +01:00
val requestor = Vec(n, new TLBPTWIO).flip
val mem = new HellaCacheIO
2013-01-07 22:38:59 +01:00
val dpath = new DatapathPTWIO
}
2017-02-27 23:27:19 +01:00
val s_ready :: s_req :: s_wait1 :: s_wait2 :: Nil = Enum(UInt(), 4)
2013-08-16 00:28:15 +02:00
val state = Reg(init=s_ready)
val count = Reg(UInt(width = log2Up(pgLevels)))
val s1_kill = Reg(next = Bool(false))
val resp_valid = Reg(next = Vec.fill(io.requestor.size)(Bool(false)))
2017-03-29 18:48:32 +02:00
val resp_ae = Reg(Bool())
2015-03-14 10:49:07 +01:00
val r_req = Reg(new PTWReq)
2013-08-12 19:39:11 +02:00
val r_req_dest = Reg(Bits())
val r_pte = Reg(new PTE)
2012-03-18 07:00:51 +01:00
2015-03-14 10:49:07 +01:00
val arb = Module(new RRArbiter(new PTWReq, n))
2012-10-10 06:35:03 +02:00
arb.io.in <> io.requestor.map(_.req)
arb.io.out.ready := state === s_ready
val (pte, invalid_paddr) = {
2016-07-30 02:52:56 +02:00
val tmp = new PTE().fromBits(io.mem.resp.bits.data)
val res = Wire(init = new PTE().fromBits(io.mem.resp.bits.data))
res.ppn := tmp.ppn(ppnBits-1, 0)
when (tmp.r || tmp.w || tmp.x) {
// for superpage mappings, make sure PPN LSBs are zero
for (i <- 0 until pgLevels-1)
when (count <= i && tmp.ppn((pgLevels-1-i)*pgLevelBits-1, (pgLevels-2-i)*pgLevelBits) =/= 0) { res.v := false }
}
(res, (tmp.ppn >> ppnBits) =/= 0)
2016-07-30 02:52:56 +02:00
}
val traverse = pte.table() && !invalid_paddr && count < pgLevels-1
val pte_addr = if (!usingVM) 0.U else {
val vpn_idxs = (0 until pgLevels).map(i => (r_req.addr >> (pgLevels-i-1)*pgLevelBits)(pgLevelBits-1,0))
val vpn_idx = vpn_idxs(count)
Cat(r_pte.ppn, vpn_idx) << log2Ceil(xLen/8)
}
2012-10-10 06:35:03 +02:00
when (arb.io.out.fire()) {
2015-03-14 10:49:07 +01:00
r_req := arb.io.out.bits
2012-10-10 06:35:03 +02:00
r_req_dest := arb.io.chosen
2016-06-18 03:29:05 +02:00
r_pte.ppn := io.dpath.ptbr.ppn
2011-11-10 09:23:29 +01:00
}
val (pte_cache_hit, pte_cache_data) = {
val size = 1 << log2Up(pgLevels * 2)
val plru = new PseudoLRU(size)
2016-07-02 23:34:18 +02:00
val valid = Reg(init = UInt(0, size))
val tags = Reg(Vec(size, UInt(width = paddrBits)))
val data = Reg(Vec(size, UInt(width = ppnBits)))
val hits = tags.map(_ === pte_addr).asUInt & valid
val hit = hits.orR
when (io.mem.resp.valid && traverse && !hit) {
2016-07-02 23:34:18 +02:00
val r = Mux(valid.andR, plru.replace, PriorityEncoder(~valid))
valid := valid | UIntToOH(r)
tags(r) := pte_addr
data(r) := pte.ppn
}
when (hit && state === s_req) { plru.access(OHToUInt(hits)) }
2017-07-06 08:53:52 +02:00
when (io.dpath.sfence.valid && !io.dpath.sfence.bits.rs1) { valid := 0 }
2017-10-10 03:33:36 +02:00
for (i <- 0 until pgLevels-1)
ccover(hit && state === s_req && count === i, s"PTE_CACHE_HIT_L$i", s"PTE cache hit, level $i")
(hit && count < pgLevels-1, Mux1H(hits, data))
2011-11-09 23:52:17 +01:00
}
2017-07-06 08:53:52 +02:00
val l2_refill = RegNext(false.B)
2017-07-25 20:59:53 +02:00
io.dpath.perf.l2miss := false
2017-09-19 22:41:11 +02:00
val (l2_hit, l2_valid, l2_pte, l2_tlb_ram) = if (coreParams.nL2TLBEntries == 0) (false.B, false.B, Wire(new PTE), None) else {
2017-07-26 11:22:43 +02:00
val code = new ParityCode
require(isPow2(coreParams.nL2TLBEntries))
val idxBits = log2Ceil(coreParams.nL2TLBEntries)
val tagBits = vpnBits - idxBits
2017-07-06 08:53:52 +02:00
class Entry extends Bundle {
2017-07-26 11:22:43 +02:00
val tag = UInt(width = tagBits)
2017-07-06 08:53:52 +02:00
val ppn = UInt(width = ppnBits)
val d = Bool()
val a = Bool()
val u = Bool()
val x = Bool()
val w = Bool()
val r = Bool()
2017-07-26 11:22:43 +02:00
override def cloneType = new Entry().asInstanceOf[this.type]
2017-07-06 08:53:52 +02:00
}
2017-07-26 11:22:43 +02:00
val ram = SeqMem(coreParams.nL2TLBEntries, UInt(width = code.width(new Entry().getWidth)))
2017-07-06 08:53:52 +02:00
val g = Reg(UInt(width = coreParams.nL2TLBEntries))
val valid = RegInit(UInt(0, coreParams.nL2TLBEntries))
val (r_tag, r_idx) = Split(r_req.addr, idxBits)
when (l2_refill) {
val entry = Wire(new Entry)
entry := r_pte
2017-07-26 11:22:43 +02:00
entry.tag := r_tag
ram.write(r_idx, code.encode(entry.asUInt))
2017-07-06 08:53:52 +02:00
val mask = UIntToOH(r_idx)
valid := valid | mask
g := Mux(r_pte.g, g | mask, g & ~mask)
}
when (io.dpath.sfence.valid) {
valid :=
Mux(io.dpath.sfence.bits.rs1, valid & ~UIntToOH(io.dpath.sfence.bits.addr(idxBits+pgIdxBits-1, pgIdxBits)),
Mux(io.dpath.sfence.bits.rs2, valid & g, 0.U))
}
val s0_valid = !l2_refill && arb.io.out.fire()
val s1_valid = RegNext(s0_valid)
val s2_valid = RegNext(s1_valid)
2017-07-06 08:53:52 +02:00
val s1_rdata = ram.read(arb.io.out.bits.addr(idxBits-1, 0), s0_valid)
val s2_rdata = code.decode(RegEnable(s1_rdata, s1_valid))
val s2_valid_bit = RegEnable(valid(r_idx), s1_valid)
val s2_g = RegEnable(g(r_idx), s1_valid)
when (s2_valid && s2_valid_bit && s2_rdata.error) { valid := 0.U }
2017-07-06 08:53:52 +02:00
2017-07-26 11:22:43 +02:00
val s2_entry = s2_rdata.uncorrected.asTypeOf(new Entry)
val s2_hit = s2_valid && s2_valid_bit && !s2_rdata.error && r_tag === s2_entry.tag
io.dpath.perf.l2miss := s2_valid && !(s2_valid_bit && r_tag === s2_entry.tag)
2017-07-06 08:53:52 +02:00
val s2_pte = Wire(new PTE)
2017-07-26 11:22:43 +02:00
s2_pte := s2_entry
s2_pte.g := s2_g
2017-07-06 08:53:52 +02:00
s2_pte.v := true
2017-10-10 03:33:36 +02:00
ccover(s2_hit, "L2_TLB_HIT", "L2 TLB hit")
2017-09-19 22:41:11 +02:00
(s2_hit, s2_valid && s2_valid_bit, s2_pte, Some(ram))
2017-07-06 08:53:52 +02:00
}
2011-11-09 23:52:17 +01:00
io.mem.req.valid := state === s_req && !l2_valid
io.mem.req.bits.phys := Bool(true)
2017-02-27 23:27:19 +01:00
io.mem.req.bits.cmd := M_XRD
io.mem.req.bits.typ := log2Ceil(xLen/8)
io.mem.req.bits.addr := pte_addr
2017-07-06 08:53:52 +02:00
io.mem.s1_kill := s1_kill || l2_hit
io.mem.invalidate_lr := Bool(false)
val pmaPgLevelHomogeneous = (0 until pgLevels) map { i =>
TLBPageLookup(edge.manager.managers, xLen, p(CacheBlockBytes), BigInt(1) << (pgIdxBits + ((pgLevels - 1 - i) * pgLevelBits)))(pte_addr >> pgIdxBits << pgIdxBits).homogeneous
}
val pmaHomogeneous = pmaPgLevelHomogeneous(count)
val pmpHomogeneous = new PMPHomogeneityChecker(io.dpath.pmp).apply(pte_addr >> pgIdxBits << pgIdxBits, count)
for (i <- 0 until io.requestor.size) {
io.requestor(i).resp.valid := resp_valid(i)
io.requestor(i).resp.bits.ae := resp_ae
io.requestor(i).resp.bits.pte := r_pte
2017-03-13 04:42:51 +01:00
io.requestor(i).resp.bits.level := count
io.requestor(i).resp.bits.pte.ppn := pte_addr >> pgIdxBits
io.requestor(i).resp.bits.homogeneous := pmpHomogeneous && pmaHomogeneous
2016-06-18 03:29:05 +02:00
io.requestor(i).ptbr := io.dpath.ptbr
io.requestor(i).status := io.dpath.status
2017-03-15 09:18:39 +01:00
io.requestor(i).pmp := io.dpath.pmp
}
2011-11-09 23:52:17 +01:00
// control state machine
switch (state) {
is (s_ready) {
2017-07-06 08:53:52 +02:00
when (arb.io.out.fire()) {
state := s_req
}
2013-08-12 19:39:11 +02:00
count := UInt(0)
2011-11-09 23:52:17 +01:00
}
is (s_req) {
when (pte_cache_hit) {
s1_kill := true
count := count + 1
r_pte.ppn := pte_cache_data
}.elsewhen (io.mem.req.fire()) {
state := s_wait1
}
}
is (s_wait1) {
state := s_wait2
2011-11-09 23:52:17 +01:00
}
is (s_wait2) {
when (io.mem.s2_nack) {
state := s_req
}
when (io.mem.resp.valid) {
2017-02-27 23:27:19 +01:00
r_pte := pte
when (traverse) {
state := s_req
count := count + 1
2017-02-27 23:27:19 +01:00
}.otherwise {
2017-07-06 08:53:52 +02:00
l2_refill := pte.v && !invalid_paddr && count === pgLevels-1
resp_ae := pte.v && invalid_paddr
2017-02-27 23:27:19 +01:00
state := s_ready
resp_valid(r_req_dest) := true
}
2011-11-09 23:52:17 +01:00
}
2017-04-15 08:57:32 +02:00
when (io.mem.s2_xcpt.ae.ld) {
resp_ae := true
2017-03-16 02:00:32 +01:00
state := s_ready
resp_valid(r_req_dest) := true
2017-03-16 02:00:32 +01:00
}
}
2011-11-09 23:52:17 +01:00
}
2017-07-06 08:53:52 +02:00
when (l2_hit) {
state := s_ready
resp_valid(r_req_dest) := true
resp_ae := false
r_pte := l2_pte
count := pgLevels-1
2017-07-06 08:53:52 +02:00
}
2017-10-10 03:33:36 +02:00
2018-01-24 01:13:35 +01:00
for (i <- 0 until pgLevels) {
val leaf = io.mem.resp.valid && !traverse && count === i
ccover(leaf && pte.v && !invalid_paddr, s"L$i", s"successful page-table access, level $i")
ccover(leaf && pte.v && invalid_paddr, s"L${i}_BAD_PPN_MSB", s"PPN too large, level $i")
ccover(leaf && !io.mem.resp.bits.data(0), s"L${i}_INVALID_PTE", s"page not present, level $i")
if (i != pgLevels-1)
ccover(leaf && !pte.v && io.mem.resp.bits.data(0), s"L${i}_BAD_PPN_LSB", s"PPN LSBs not zero, level $i")
}
ccover(io.mem.resp.valid && count === pgLevels-1 && pte.table(), s"TOO_DEEP", s"page table too deep")
2017-11-03 23:03:27 +01:00
ccover(io.mem.s2_nack, "NACK", "D$ nacked page-table access")
ccover(state === s_wait2 && io.mem.s2_xcpt.ae.ld, "AE", "access exception while walking page table")
2017-10-10 03:33:36 +02:00
def ccover(cond: Bool, label: String, desc: String)(implicit sourceInfo: SourceInfo) =
2017-11-03 23:03:27 +01:00
if (usingVM) cover(cond, s"PTW_$label", "MemorySystem;;" + desc)
2011-11-09 23:52:17 +01:00
}
/** Mix-ins for constructing tiles that might have a PTW */
trait CanHavePTW extends HasTileParameters with HasHellaCache { this: BaseTile =>
val module: CanHavePTWModule
var nPTWPorts = 1
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
nDCachePorts += usingPTW.toInt
}
trait CanHavePTWModule extends HasHellaCacheModule {
val outer: CanHavePTW
val ptwPorts = ListBuffer(outer.dcache.module.io.ptw)
2017-09-15 23:44:07 +02:00
val ptw = Module(new PTW(outer.nPTWPorts)(outer.dcache.node.edges.out(0), outer.p))
if (outer.usingPTW)
dcachePorts += ptw.io.mem
}