Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dynamic local and remote order #2461

Merged
merged 5 commits into from
May 15, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 20 additions & 24 deletions src/main/scala/diplomacy/Parameters.scala
Original file line number Diff line number Diff line change
Expand Up @@ -167,13 +167,13 @@ case class AddressSet(base: BigInt, mask: BigInt) extends Ordered[AddressSet]
}

def subtract(x: AddressSet): Seq[AddressSet] = {
if (!overlaps(x)) {
Seq(this)
} else {
val new_inflex = ~x.mask & mask
// !!! this fractures too much; find a better algorithm
val fracture = AddressSet.enumerateMask(new_inflex).flatMap(m => intersect(AddressSet(m, ~new_inflex)))
fracture.filter(!_.overlaps(x))
intersect(x) match {
case None => Seq(this)
case Some(remove) => AddressSet.enumerateBits(mask & ~remove.mask).map { bit =>
val nmask = (mask & (bit-1)) | remove.mask
val nbase = (remove.base ^ bit) & ~nmask
AddressSet(nbase, nmask)
}
}
}

Expand Down Expand Up @@ -219,24 +219,20 @@ object AddressSet
}
}

def unify(seq: Seq[AddressSet], bit: BigInt): Seq[AddressSet] = {
// Pair terms up by ignoring 'bit'
seq.groupBy(x => x.copy(base = x.base & ~bit)).map { case (key, seq) =>
if (seq.size == 1) {
seq.head // singleton -> unaffected
} else {
key.copy(mask = key.mask | bit) // pair - widen mask by bit
}
}.toList
}

def unify(seq: Seq[AddressSet]): Seq[AddressSet] = {
val n = seq.size
val array = Array(seq:_*)
var filter = Array.fill(n) { false }
for (i <- 0 until n-1) { if (!filter(i)) {
for (j <- i+1 until n) { if (!filter(j)) {
val a = array(i)
val b = array(j)
if (a.mask == b.mask && isPow2(a.base ^ b.base)) {
val c_base = a.base & ~(a.base ^ b.base)
val c_mask = a.mask | (a.base ^ b.base)
filter.update(j, true)
array.update(i, AddressSet(c_base, c_mask))
}
}}
}}
val out = (array zip filter) flatMap { case (a, f) => if (f) None else Some(a) }
if (out.size != n) unify(out) else out.toList
val bits = seq.map(_.base).foldLeft(BigInt(0))(_ | _)
AddressSet.enumerateBits(bits).foldLeft(seq) { case (acc, bit) => unify(acc, bit) }.sorted
}

def enumerateMask(mask: BigInt): Seq[BigInt] = {
Expand Down
3 changes: 3 additions & 0 deletions src/main/scala/diplomaticobjectmodel/model/OMISA.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ case object Bare extends OMAddressTranslationMode
case object Sv32 extends OMAddressTranslationMode
case object Sv39 extends OMAddressTranslationMode
case object Sv48 extends OMAddressTranslationMode
// unratified/subject-to-change in the RISC-V priviledged ISA specification:
case object Sv57 extends OMAddressTranslationMode

trait OMBaseInstructionSet extends OMEnum
case object RV32E extends OMBaseInstructionSet
Expand Down Expand Up @@ -86,6 +88,7 @@ object OMISA {
case 32 if (pgLevels == 2) => Sv32
case 64 if (pgLevels == 3) => Sv39
case 64 if (pgLevels == 4) => Sv48
case 64 if (pgLevels == 5) => Sv57
case _ => throw new IllegalArgumentException(s"ERROR: Invalid Xlen/PgLevels combination: $xLen/$pgLevels")
}

Expand Down
77 changes: 46 additions & 31 deletions src/main/scala/tilelink/AddressAdjuster.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,21 @@ class AddressAdjuster(val params: ReplicatedRegion, val forceLocal: Seq[AddressS
val mask = params.replicationMask
// Which bits are in the mask?
val bits = AddressSet.enumerateBits(mask)
// Which ids must we route within that mask?
val ids = AddressSet.enumerateMask(mask)
// Find the intersection of the mask with some region
private def masked(region: Seq[AddressSet], offset: BigInt = 0): Seq[AddressSet] = {
region.flatMap { _.intersect(AddressSet(offset, ~mask)) }
// Find the portion of the addresses which correspond to prefix0
private def prefix0(region: Seq[AddressSet]): Seq[AddressSet] = {
region.flatMap { _.intersect(params.local) }
}
private def prefixNot0(region: Seq[AddressSet]): Seq[AddressSet] = {
region.flatMap { _.subtract(params.local) }
}

// forceLocal better only go one place (the low index)
forceLocal.foreach { as => require((as.max & mask) == 0) }

// Address Adjustment requires many things about the downstream devices, captured here as helper functions:

// Report whether a region of addresses fully contains a particular manager
def isDeviceContainedBy(region: Seq[AddressSet], m: TLSlaveParameters): Boolean = {
val addr = masked(m.address)
val addr = prefix0(m.address)
val any_in = region.exists { f => addr.exists { a => f.overlaps(a) } }
val any_out = region.exists { f => addr.exists { a => !f.contains(a) } }
// Ensure device is either completely inside or outside this region
Expand Down Expand Up @@ -57,8 +57,8 @@ class AddressAdjuster(val params: ReplicatedRegion, val forceLocal: Seq[AddressS
}

def sameSupport(local: Seq[TLSlaveParameters], remote: Seq[TLSlaveParameters]): (Boolean, Seq[AddressSet]) = {
val ra = masked(remote.flatMap(_.address))
val la = masked(local .flatMap(_.address))
val ra = prefix0(remote.flatMap(_.address))
val la = prefix0(local .flatMap(_.address))
val holes = la.foldLeft(ra) { case (holes, la) => holes.flatMap(_.subtract(la)) }
val covered = remote.forall { r =>
r.address.forall { ra =>
Expand Down Expand Up @@ -119,6 +119,10 @@ class AddressAdjuster(val params: ReplicatedRegion, val forceLocal: Seq[AddressS
val local = mp(0)
val remote = mp(1)

// Confirm that the two manager paths have homogeneous FIFO ids
requireFifoHomogeneity(local.managers)
requireFifoHomogeneity(remote.managers)

// Subdivide the managers into four cases: (adjustable vs fixed) x (local vs remote)
val adjustableLocalManagers = local.managers.filter(m => isDeviceContainedBy(Seq(params.region), m))
val fixedLocalManagers = local.managers.filter(m => !isDeviceContainedBy(Seq(params.region), m))
Expand Down Expand Up @@ -155,10 +159,6 @@ class AddressAdjuster(val params: ReplicatedRegion, val forceLocal: Seq[AddressS
// Confirm that the error device can supply all the same capabilities as the remote path
errorDev.foreach { e => requireErrorSupport(e, adjustableRemoteManagers) }

// Confirm that each subset of adjustable managers have homogeneous FIFO ids
requireFifoHomogeneity(adjustableLocalManagers)
requireFifoHomogeneity(adjustableRemoteManagers)

// Actually rewrite the PMAs for the adjustable local devices
val newLocals = adjustableLocalManagers.map { l =>
// Ensure that every local device has a matching remote device
Expand All @@ -172,7 +172,7 @@ class AddressAdjuster(val params: ReplicatedRegion, val forceLocal: Seq[AddressS
// All other PMAs are replaced with the capabilities of the remote path, since that's all we can know statically.
// Capabilities supported by the remote but not the local will result in dynamic re-reouting to the error device.
l.v1copy(
address = AddressSet.unify(masked(l.address) ++ (if (Some(l) == errorDev) holes else Nil)),
address = AddressSet.unify(prefix0(l.address) ++ (if (Some(l) == errorDev) holes else Nil)),
regionType = r.regionType,
executable = r.executable,
supportsAcquireT = r.supportsAcquireT,
Expand All @@ -186,27 +186,21 @@ class AddressAdjuster(val params: ReplicatedRegion, val forceLocal: Seq[AddressS
mayDenyGet = r.mayDenyGet,
mayDenyPut = r.mayDenyPut,
alwaysGrantsT = r.alwaysGrantsT,
fifoId = Some(if (isDeviceContainedBy(forceLocal, l)) ids.size else 0))
fifoId = Some(0))
}

// Actually rewrite the PMAs for the adjustable remote region too, to account for the differing FIFO domains under the mask
val newRemotes = ids.tail.zipWithIndex.flatMap { case (id, i) => adjustableRemoteManagers.map { r =>
val newRemotes = adjustableRemoteManagers.map { r =>
r.v1copy(
address = AddressSet.unify(masked(r.address, offset = id)),
fifoId = Some(i+1))
} }

// Relable the FIFO domains for certain manager subsets
val fifoIdFactory = TLXbar.relabeler()
def relabelFifo(managers: Seq[TLSlaveParameters]): Seq[TLSlaveParameters] = {
val fifoIdMapper = fifoIdFactory()
managers.map(m => m.v1copy(fifoId = m.fifoId.map(fifoIdMapper(_))))
address = prefixNot0(r.address),
fifoId = Some(0))
}

val newManagerList =
relabelFifo(newLocals ++ newRemotes) ++
relabelFifo(fixedLocalManagers) ++
relabelFifo(fixedRemoteManagers)
newLocals ++
newRemotes ++
fixedLocalManagers ++
fixedRemoteManagers

Seq(local.v1copy(
managers = newManagerList,
Expand Down Expand Up @@ -249,13 +243,34 @@ class AddressAdjuster(val params: ReplicatedRegion, val forceLocal: Seq[AddressS
def routeLocal(addr: UInt): Bool = Mux(isAdjustable(addr), isDynamicallyLocal(addr), isStaticallyLocal(addr))

// Route A by address, but reroute unsupported operations
val a_stall = Wire(Bool())
val a_local = routeLocal(parent.a.bits.address)
parent.a.ready := Mux(a_local, local.a.ready, remote.a.ready)
local .a.valid := parent.a.valid && a_local
remote.a.valid := parent.a.valid && !a_local
parent.a.ready := Mux(a_local, local.a.ready, remote.a.ready) && !a_stall
local .a.valid := parent.a.valid && a_local && !a_stall
remote.a.valid := parent.a.valid && !a_local && !a_stall
local .a.bits := parent.a.bits
remote.a.bits := parent.a.bits

// Count beats
val a_first = parentEdge.first(parent.a)
val d_first = parentEdge.first(parent.d) && parent.d.bits.opcode =/= TLMessages.ReleaseAck

// Keep one bit for each source recording if there is an outstanding request that must be made FIFO
// Sources unused in the stall signal calculation should be pruned by DCE
val flight = RegInit(VecInit(Seq.fill(parentEdge.client.endSourceId) { false.B }))
when (a_first && parent.a.fire()) { flight(parent.a.bits.source) := true.B }
when (d_first && parent.d.fire()) { flight(parent.d.bits.source) := false.B }

val stalls = parentEdge.client.clients.filter(c => c.requestFifo && c.sourceId.size > 1).map { c =>
val a_sel = c.sourceId.contains(parent.a.bits.source)
val local = RegEnable(a_local, parent.a.fire() && a_sel)
val track = flight.slice(c.sourceId.start, c.sourceId.end)

a_sel && a_first && track.reduce(_ || _) && (local =/= a_local)
}

a_stall := stalls.foldLeft(false.B)(_||_)

val (allSame, holes) = sameSupport(adjustableLocalManagers, adjustableRemoteManagers)

val dynamicLocal = AddressSet.unify(adjustableLocalManagers.flatMap(_.address))
Expand Down
5 changes: 2 additions & 3 deletions src/main/scala/tilelink/BusWrapper.scala
Original file line number Diff line number Diff line change
Expand Up @@ -356,8 +356,7 @@ case class AddressAdjusterWrapperParams(
blockBytes: Int,
beatBytes: Int,
replication: Option[ReplicatedRegion],
forceLocal: Seq[AddressSet] = Nil,
policy: TLFIFOFixer.Policy = TLFIFOFixer.allVolatile
forceLocal: Seq[AddressSet] = Nil
)
extends HasTLBusParams
with TLBusWrapperInstantiationLike
Expand All @@ -374,7 +373,7 @@ case class AddressAdjusterWrapperParams(
class AddressAdjusterWrapper(params: AddressAdjusterWrapperParams, name: String)(implicit p: Parameters) extends TLBusWrapper(params, name) {
private val address_adjuster = params.replication.map { r => LazyModule(new AddressAdjuster(r, params.forceLocal)) }
private val viewNode = TLIdentityNode()
val inwardNode: TLInwardNode = address_adjuster.map(_.node :*=* TLFIFOFixer(params.policy) :*=* viewNode).getOrElse(viewNode)
hcook marked this conversation as resolved.
Show resolved Hide resolved
val inwardNode: TLInwardNode = address_adjuster.map(_.node :*=* viewNode).getOrElse(viewNode)
def outwardNode: TLOutwardNode = address_adjuster.map(_.node).getOrElse(viewNode)
def busView: TLEdge = viewNode.edges.in.head
val prefixNode = address_adjuster.map(_.prefix)
Expand Down
74 changes: 36 additions & 38 deletions src/main/scala/tilelink/Parameters.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1113,47 +1113,45 @@ case class TLRationalEdgeParameters(client: TLRationalClientPortParameters, mana
def formatEdge = client.infoString + "\n" + manager.infoString
}

// To be unified, devices must agree on all of these terms
case class ManagerUnificationKey(
resources: Seq[Resource],
regionType: RegionType.T,
executable: Boolean,
supportsAcquireT: TransferSizes,
supportsAcquireB: TransferSizes,
supportsArithmetic: TransferSizes,
supportsLogical: TransferSizes,
supportsGet: TransferSizes,
supportsPutFull: TransferSizes,
supportsPutPartial: TransferSizes,
supportsHint: TransferSizes)

object ManagerUnificationKey
{
def apply(x: TLManagerParameters): ManagerUnificationKey = ManagerUnificationKey(
resources = x.resources,
regionType = x.regionType,
executable = x.executable,
supportsAcquireT = x.supportsAcquireT,
supportsAcquireB = x.supportsAcquireB,
supportsArithmetic = x.supportsArithmetic,
supportsLogical = x.supportsLogical,
supportsGet = x.supportsGet,
supportsPutFull = x.supportsPutFull,
supportsPutPartial = x.supportsPutPartial,
supportsHint = x.supportsHint)
}

object ManagerUnification
{
def apply(managers: Seq[TLManagerParameters]): List[TLManagerParameters] = {
// To be unified, devices must agree on all of these terms
case class TLManagerKey(
resources: Seq[Resource],
regionType: RegionType.T,
executable: Boolean,
supportsAcquireT: TransferSizes,
supportsAcquireB: TransferSizes,
supportsArithmetic: TransferSizes,
supportsLogical: TransferSizes,
supportsGet: TransferSizes,
supportsPutFull: TransferSizes,
supportsPutPartial: TransferSizes,
supportsHint: TransferSizes)
def key(x: TLManagerParameters) = TLManagerKey(
resources = x.resources,
regionType = x.regionType,
executable = x.executable,
supportsAcquireT = x.supportsAcquireT,
supportsAcquireB = x.supportsAcquireB,
supportsArithmetic = x.supportsArithmetic,
supportsLogical = x.supportsLogical,
supportsGet = x.supportsGet,
supportsPutFull = x.supportsPutFull,
supportsPutPartial = x.supportsPutPartial,
supportsHint = x.supportsHint)
val map = scala.collection.mutable.HashMap[TLManagerKey, TLManagerParameters]()
managers.foreach { m =>
val k = key(m)
map.get(k) match {
case None => map.update(k, m)
case Some(n) => {
map.update(k, m.v1copy(
address = m.address ++ n.address,
fifoId = None)) // Merging means it's not FIFO anymore!
}
}
}
map.values.map(m => m.v1copy(address = AddressSet.unify(m.address))).toList
managers.groupBy(ManagerUnificationKey.apply).values.map { seq =>
val agree = seq.forall(_.fifoId == seq.head.fifoId)
seq(0).v1copy(
address = AddressSet.unify(seq.flatMap(_.address)),
fifoId = if (agree) seq(0).fifoId else None)
}.toList
}
}

Expand Down