1
0
Fork 0
rocket-chip/src/main/scala/tilelink/Parameters.scala

411 lines
20 KiB
Scala

// See LICENSE.SiFive for license details.
package freechips.rocketchip.tilelink
import Chisel._
import chisel3.internal.sourceinfo.SourceInfo
import freechips.rocketchip.config.Parameters
import freechips.rocketchip.diplomacy._
import freechips.rocketchip.util.RationalDirection
import scala.math.max
case class TLManagerParameters(
address: Seq[AddressSet],
resources: Seq[Resource] = Seq(),
regionType: RegionType.T = RegionType.GET_EFFECTS,
executable: Boolean = false, // processor can execute from this memory
nodePath: Seq[BaseNode] = Seq(),
// Supports both Acquire+Release+Finish of these sizes
supportsAcquireT: TransferSizes = TransferSizes.none,
supportsAcquireB: TransferSizes = TransferSizes.none,
supportsArithmetic: TransferSizes = TransferSizes.none,
supportsLogical: TransferSizes = TransferSizes.none,
supportsGet: TransferSizes = TransferSizes.none,
supportsPutFull: TransferSizes = TransferSizes.none,
supportsPutPartial: TransferSizes = TransferSizes.none,
supportsHint: TransferSizes = TransferSizes.none,
// If fifoId=Some, all accesses sent to the same fifoId are executed and ACK'd in FIFO order
// Note: you can only rely on this FIFO behaviour if your TLClientParameters include requestFifo
fifoId: Option[Int] = None)
{
require (!address.isEmpty)
address.foreach { a => require (a.finite) }
address.combinations(2).foreach { case Seq(x,y) => require (!x.overlaps(y), s"$x and $y overlap.") }
require (supportsPutFull.contains(supportsPutPartial), s"PutFull($supportsPutFull) < PutPartial($supportsPutPartial)")
require (supportsPutFull.contains(supportsArithmetic), s"PutFull($supportsPutFull) < Arithmetic($supportsArithmetic)")
require (supportsPutFull.contains(supportsLogical), s"PutFull($supportsPutFull) < Logical($supportsLogical)")
require (supportsGet.contains(supportsArithmetic), s"Get($supportsGet) < Arithmetic($supportsArithmetic)")
require (supportsGet.contains(supportsLogical), s"Get($supportsGet) < Logical($supportsLogical)")
require (supportsAcquireB.contains(supportsAcquireT), s"AcquireB($supportsAcquireB) < AcquireT($supportsAcquireT)")
// Make sure that the regionType agrees with the capabilities
require (!supportsAcquireB || regionType >= RegionType.UNCACHED) // acquire -> uncached, tracked, cached
require (regionType <= RegionType.UNCACHED || supportsAcquireB) // tracked, cached -> acquire
require (regionType != RegionType.UNCACHED || supportsGet) // uncached -> supportsGet
val name = nodePath.lastOption.map(_.lazyModule.name).getOrElse("disconnected")
val maxTransfer = List( // Largest supported transfer of all types
supportsAcquireT.max,
supportsAcquireB.max,
supportsArithmetic.max,
supportsLogical.max,
supportsGet.max,
supportsPutFull.max,
supportsPutPartial.max).max
val maxAddress = address.map(_.max).max
val minAlignment = address.map(_.alignment).min
// The device had better not support a transfer larger than its alignment
require (minAlignment >= maxTransfer, s"minAlignment ($minAlignment) must be >= maxTransfer ($maxTransfer)")
def toResource: ResourceAddress = {
ResourceAddress(address, ResourcePermissions(
r = supportsAcquireB || supportsGet,
w = supportsAcquireT || supportsPutFull,
x = executable,
c = regionType >= RegionType.UNCACHED,
a = supportsArithmetic && supportsLogical))
}
def findTreeViolation() = nodePath.find {
case _: MixedAdapterNode[_, _, _, _, _, _, _, _] => false
case _: SinkNode[_, _, _, _, _] => false
case node => node.inputs.size != 1
}
def isTree = findTreeViolation() == None
}
case class TLManagerPortParameters(
managers: Seq[TLManagerParameters],
beatBytes: Int,
endSinkId: Int = 0, // 0 = no sink ids, 1 = a reusable sink id, >1 = unique sink ids
minLatency: Int = 0)
{
require (!managers.isEmpty)
require (isPow2(beatBytes))
require (endSinkId >= 0)
require (minLatency >= 0)
def requireFifo() = managers.foreach { m =>
require(m.fifoId == Some(0), s"${m.name} had fifoId ${m.fifoId}, which was not 0 (${managers.map(s => (s.name, s.fifoId))}) ")
}
// Bounds on required sizes
def maxAddress = managers.map(_.maxAddress).max
def maxTransfer = managers.map(_.maxTransfer).max
// Operation sizes supported by all outward Managers
val allSupportAcquireT = managers.map(_.supportsAcquireT) .reduce(_ intersect _)
val allSupportAcquireB = managers.map(_.supportsAcquireB) .reduce(_ intersect _)
val allSupportArithmetic = managers.map(_.supportsArithmetic).reduce(_ intersect _)
val allSupportLogical = managers.map(_.supportsLogical) .reduce(_ intersect _)
val allSupportGet = managers.map(_.supportsGet) .reduce(_ intersect _)
val allSupportPutFull = managers.map(_.supportsPutFull) .reduce(_ intersect _)
val allSupportPutPartial = managers.map(_.supportsPutPartial).reduce(_ intersect _)
val allSupportHint = managers.map(_.supportsHint) .reduce(_ intersect _)
// Operation supported by at least one outward Managers
val anySupportAcquireT = managers.map(!_.supportsAcquireT.none) .reduce(_ || _)
val anySupportAcquireB = managers.map(!_.supportsAcquireB.none) .reduce(_ || _)
val anySupportArithmetic = managers.map(!_.supportsArithmetic.none).reduce(_ || _)
val anySupportLogical = managers.map(!_.supportsLogical.none) .reduce(_ || _)
val anySupportGet = managers.map(!_.supportsGet.none) .reduce(_ || _)
val anySupportPutFull = managers.map(!_.supportsPutFull.none) .reduce(_ || _)
val anySupportPutPartial = managers.map(!_.supportsPutPartial.none).reduce(_ || _)
val anySupportHint = managers.map(!_.supportsHint.none) .reduce(_ || _)
// These return Option[TLManagerParameters] for your convenience
def find(address: BigInt) = managers.find(_.address.exists(_.contains(address)))
// The safe version will check the entire address
def findSafe(address: UInt) = Vec(managers.map(_.address.map(_.contains(address)).reduce(_ || _)))
// The fast version assumes the address is valid (you probably want fastProperty instead of this function)
def findFast(address: UInt) = {
val routingMask = AddressDecoder(managers.map(_.address))
Vec(managers.map(_.address.map(_.widen(~routingMask)).distinct.map(_.contains(address)).reduce(_ || _)))
}
// Compute the simplest AddressSets that decide a key
def fastPropertyGroup[K](p: TLManagerParameters => K): Map[K, Seq[AddressSet]] = {
val groups = managers.map(m => (p(m), m.address)).groupBy(_._1).mapValues(_.flatMap(_._2))
val reductionMask = AddressDecoder(groups.values.toList)
groups.mapValues(seq => AddressSet.unify(seq.map(_.widen(~reductionMask)).distinct))
}
// Select a property
def fastProperty[K, D <: Data](address: UInt, p: TLManagerParameters => K, d: K => D): D =
Mux1H(fastPropertyGroup(p).map { case (v, a) => (a.map(_.contains(address)).reduce(_||_), d(v)) })
// Note: returns the actual fifoId + 1 or 0 if None
def findFifoIdFast(address: UInt) = fastProperty(address, _.fifoId.map(_+1).getOrElse(0), (i:Int) => UInt(i))
def hasFifoIdFast(address: UInt) = fastProperty(address, _.fifoId.isDefined, (b:Boolean) => Bool(b))
// Does this Port manage this ID/address?
def containsSafe(address: UInt) = findSafe(address).reduce(_ || _)
private def supportHelper(
safe: Boolean,
member: TLManagerParameters => TransferSizes,
address: UInt,
lgSize: UInt,
range: Option[TransferSizes]): Bool = {
def trim(x: TransferSizes) = range.map(_.intersect(x)).getOrElse(x)
val supportCases = managers.groupBy(m => trim(member(m))).mapValues(_.flatMap(_.address))
val mask = if (safe) ~BigInt(0) else AddressDecoder(supportCases.values.toList)
val simplified = supportCases.mapValues(seq => AddressSet.unify(seq.map(_.widen(~mask)).distinct))
simplified.map { case (s, a) =>
(Bool(Some(s) == range) || s.containsLg(lgSize)) &&
a.map(_.contains(address)).reduce(_||_)
}.foldLeft(Bool(false))(_||_)
}
// Check for support of a given operation at a specific address
def supportsAcquireTSafe (address: UInt, lgSize: UInt, range: Option[TransferSizes] = None) = supportHelper(true, _.supportsAcquireT, address, lgSize, range)
def supportsAcquireBSafe (address: UInt, lgSize: UInt, range: Option[TransferSizes] = None) = supportHelper(true, _.supportsAcquireB, address, lgSize, range)
def supportsArithmeticSafe(address: UInt, lgSize: UInt, range: Option[TransferSizes] = None) = supportHelper(true, _.supportsArithmetic, address, lgSize, range)
def supportsLogicalSafe (address: UInt, lgSize: UInt, range: Option[TransferSizes] = None) = supportHelper(true, _.supportsLogical, address, lgSize, range)
def supportsGetSafe (address: UInt, lgSize: UInt, range: Option[TransferSizes] = None) = supportHelper(true, _.supportsGet, address, lgSize, range)
def supportsPutFullSafe (address: UInt, lgSize: UInt, range: Option[TransferSizes] = None) = supportHelper(true, _.supportsPutFull, address, lgSize, range)
def supportsPutPartialSafe(address: UInt, lgSize: UInt, range: Option[TransferSizes] = None) = supportHelper(true, _.supportsPutPartial, address, lgSize, range)
def supportsHintSafe (address: UInt, lgSize: UInt, range: Option[TransferSizes] = None) = supportHelper(true, _.supportsHint, address, lgSize, range)
def supportsAcquireTFast (address: UInt, lgSize: UInt, range: Option[TransferSizes] = None) = supportHelper(false, _.supportsAcquireT, address, lgSize, range)
def supportsAcquireBFast (address: UInt, lgSize: UInt, range: Option[TransferSizes] = None) = supportHelper(false, _.supportsAcquireB, address, lgSize, range)
def supportsArithmeticFast(address: UInt, lgSize: UInt, range: Option[TransferSizes] = None) = supportHelper(false, _.supportsArithmetic, address, lgSize, range)
def supportsLogicalFast (address: UInt, lgSize: UInt, range: Option[TransferSizes] = None) = supportHelper(false, _.supportsLogical, address, lgSize, range)
def supportsGetFast (address: UInt, lgSize: UInt, range: Option[TransferSizes] = None) = supportHelper(false, _.supportsGet, address, lgSize, range)
def supportsPutFullFast (address: UInt, lgSize: UInt, range: Option[TransferSizes] = None) = supportHelper(false, _.supportsPutFull, address, lgSize, range)
def supportsPutPartialFast(address: UInt, lgSize: UInt, range: Option[TransferSizes] = None) = supportHelper(false, _.supportsPutPartial, address, lgSize, range)
def supportsHintFast (address: UInt, lgSize: UInt, range: Option[TransferSizes] = None) = supportHelper(false, _.supportsHint, address, lgSize, range)
def findTreeViolation() = managers.flatMap(_.findTreeViolation()).headOption
def isTree = !managers.exists(!_.isTree)
}
case class TLClientParameters(
name: String,
sourceId: IdRange = IdRange(0,1),
nodePath: Seq[BaseNode] = Seq(),
requestFifo: Boolean = false, // only a request, not a requirement. applies to A, not C.
// Supports both Probe+Grant of these sizes
supportsProbe: TransferSizes = TransferSizes.none,
supportsArithmetic: TransferSizes = TransferSizes.none,
supportsLogical: TransferSizes = TransferSizes.none,
supportsGet: TransferSizes = TransferSizes.none,
supportsPutFull: TransferSizes = TransferSizes.none,
supportsPutPartial: TransferSizes = TransferSizes.none,
supportsHint: TransferSizes = TransferSizes.none)
{
require (supportsPutFull.contains(supportsPutPartial))
// We only support these operations if we support Probe (ie: we're a cache)
require (supportsProbe.contains(supportsArithmetic))
require (supportsProbe.contains(supportsLogical))
require (supportsProbe.contains(supportsGet))
require (supportsProbe.contains(supportsPutFull))
require (supportsProbe.contains(supportsPutPartial))
require (supportsProbe.contains(supportsHint))
val maxTransfer = List(
supportsProbe.max,
supportsArithmetic.max,
supportsLogical.max,
supportsGet.max,
supportsPutFull.max,
supportsPutPartial.max).max
}
case class TLClientPortParameters(
clients: Seq[TLClientParameters],
minLatency: Int = 0) // Only applies to B=>C
{
require (!clients.isEmpty)
require (minLatency >= 0)
// Require disjoint ranges for Ids
IdRange.overlaps(clients.map(_.sourceId)).foreach { case (x, y) =>
require (!x.overlaps(y), s"TLClientParameters.sourceId ${x} overlaps ${y}")
}
// Bounds on required sizes
def endSourceId = clients.map(_.sourceId.end).max
def maxTransfer = clients.map(_.maxTransfer).max
// Operation sizes supported by all inward Clients
val allSupportProbe = clients.map(_.supportsProbe) .reduce(_ intersect _)
val allSupportArithmetic = clients.map(_.supportsArithmetic).reduce(_ intersect _)
val allSupportLogical = clients.map(_.supportsLogical) .reduce(_ intersect _)
val allSupportGet = clients.map(_.supportsGet) .reduce(_ intersect _)
val allSupportPutFull = clients.map(_.supportsPutFull) .reduce(_ intersect _)
val allSupportPutPartial = clients.map(_.supportsPutPartial).reduce(_ intersect _)
val allSupportHint = clients.map(_.supportsHint) .reduce(_ intersect _)
// Operation is supported by at least one client
val anySupportProbe = clients.map(!_.supportsProbe.none) .reduce(_ || _)
val anySupportArithmetic = clients.map(!_.supportsArithmetic.none).reduce(_ || _)
val anySupportLogical = clients.map(!_.supportsLogical.none) .reduce(_ || _)
val anySupportGet = clients.map(!_.supportsGet.none) .reduce(_ || _)
val anySupportPutFull = clients.map(!_.supportsPutFull.none) .reduce(_ || _)
val anySupportPutPartial = clients.map(!_.supportsPutPartial.none).reduce(_ || _)
val anySupportHint = clients.map(!_.supportsHint.none) .reduce(_ || _)
// These return Option[TLClientParameters] for your convenience
def find(id: Int) = clients.find(_.sourceId.contains(id))
// Synthesizable lookup methods
def find(id: UInt) = Vec(clients.map(_.sourceId.contains(id)))
def contains(id: UInt) = find(id).reduce(_ || _)
def requestFifo(id: UInt) = Mux1H(find(id), clients.map(c => Bool(c.requestFifo)))
private def safety_helper(member: TLClientParameters => TransferSizes)(id: UInt, lgSize: UInt) = {
val allSame = clients.map(member(_) == member(clients(0))).reduce(_ && _)
if (allSame) member(clients(0)).containsLg(lgSize) else {
Mux1H(find(id), clients.map(member(_).containsLg(lgSize)))
}
}
// Check for support of a given operation at a specific id
val supportsProbe = safety_helper(_.supportsProbe) _
val supportsArithmetic = safety_helper(_.supportsArithmetic) _
val supportsLogical = safety_helper(_.supportsLogical) _
val supportsGet = safety_helper(_.supportsGet) _
val supportsPutFull = safety_helper(_.supportsPutFull) _
val supportsPutPartial = safety_helper(_.supportsPutPartial) _
val supportsHint = safety_helper(_.supportsHint) _
}
case class TLBundleParameters(
addressBits: Int,
dataBits: Int,
sourceBits: Int,
sinkBits: Int,
sizeBits: Int)
{
// Chisel has issues with 0-width wires
require (addressBits >= 1)
require (dataBits >= 8)
require (sourceBits >= 1)
require (sinkBits >= 1)
require (sizeBits >= 1)
require (isPow2(dataBits))
val addrLoBits = log2Up(dataBits/8)
def union(x: TLBundleParameters) =
TLBundleParameters(
max(addressBits, x.addressBits),
max(dataBits, x.dataBits),
max(sourceBits, x.sourceBits),
max(sinkBits, x.sinkBits),
max(sizeBits, x.sizeBits))
}
object TLBundleParameters
{
val emptyBundleParams = TLBundleParameters(
addressBits = 1,
dataBits = 8,
sourceBits = 1,
sinkBits = 1,
sizeBits = 1)
def union(x: Seq[TLBundleParameters]) = x.foldLeft(emptyBundleParams)((x,y) => x.union(y))
def apply(client: TLClientPortParameters, manager: TLManagerPortParameters) =
new TLBundleParameters(
addressBits = log2Up(manager.maxAddress + 1),
dataBits = manager.beatBytes * 8,
sourceBits = log2Up(client.endSourceId),
sinkBits = log2Up(manager.endSinkId),
sizeBits = log2Up(log2Ceil(max(client.maxTransfer, manager.maxTransfer))+1))
}
case class TLEdgeParameters(
client: TLClientPortParameters,
manager: TLManagerPortParameters,
params: Parameters,
sourceInfo: SourceInfo)
{
val maxTransfer = max(client.maxTransfer, manager.maxTransfer)
val maxLgSize = log2Ceil(maxTransfer)
// Sanity check the link...
require (maxTransfer >= manager.beatBytes, s"Link's max transfer (${maxTransfer}) < ${manager.managers.map(_.name)}'s beatBytes (${manager.beatBytes})")
val bundle = TLBundleParameters(client, manager)
}
case class TLAsyncManagerPortParameters(depth: Int, base: TLManagerPortParameters) { require (isPow2(depth)) }
case class TLAsyncClientPortParameters(base: TLClientPortParameters)
case class TLAsyncBundleParameters(depth: Int, base: TLBundleParameters)
{
require (isPow2(depth))
def union(x: TLAsyncBundleParameters) = TLAsyncBundleParameters(
depth = max(depth, x.depth),
base = base.union(x.base))
}
object TLAsyncBundleParameters
{
val emptyBundleParams = TLAsyncBundleParameters(depth = 1, base = TLBundleParameters.emptyBundleParams)
def union(x: Seq[TLAsyncBundleParameters]) = x.foldLeft(emptyBundleParams)((x,y) => x.union(y))
}
case class TLAsyncEdgeParameters(client: TLAsyncClientPortParameters, manager: TLAsyncManagerPortParameters, params: Parameters, sourceInfo: SourceInfo)
{
val bundle = TLAsyncBundleParameters(manager.depth, TLBundleParameters(client.base, manager.base))
}
case class TLRationalManagerPortParameters(direction: RationalDirection, base: TLManagerPortParameters)
case class TLRationalClientPortParameters(base: TLClientPortParameters)
case class TLRationalEdgeParameters(client: TLRationalClientPortParameters, manager: TLRationalManagerPortParameters, params: Parameters, sourceInfo: SourceInfo)
{
val bundle = TLBundleParameters(client.base, manager.base)
}
object ManagerUnification
{
def apply(managers: Seq[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.copy(
address = m.address ++ n.address,
fifoId = None)) // Merging means it's not FIFO anymore!
}
}
}
map.values.map(m => m.copy(address = AddressSet.unify(m.address))).toList
}
}