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

311 lines
11 KiB
Scala
Raw Normal View History

// See LICENSE for license details.
package uncore.tilelink2
import Chisel._
import scala.math.max
/** Options for memory regions */
object RegionType {
sealed trait T
case object CACHED extends T
case object TRACKED extends T
case object UNCACHED extends T
case object UNCACHEABLE extends T
val cases = Seq(CACHED, TRACKED, UNCACHED, UNCACHEABLE)
}
// A non-empty half-open range; [start, end)
case class IdRange(start: Int, end: Int)
{
require (start >= 0)
require (end >= 0)
require (start < end) // not empty
// This is a strict partial ordering
def <(x: IdRange) = end <= x.start
def >(x: IdRange) = x < this
def overlaps(x: IdRange) = start < x.end && x.start < end
def contains(x: IdRange) = start <= x.start && x.end <= end
// contains => overlaps (because empty is forbidden)
def contains(x: Int) = start <= x && x < end
def contains(x: UInt) = UInt(start) <= x && x < UInt(end) // !!! special-case =
def shift(x: Int) = IdRange(start+x, end+x)
}
// An potentially empty inclusive range of 2-powers [min, max] (in bytes)
case class TransferSizes(min: Int, max: Int)
{
def this(x: Int) = this(x, x)
require (min <= max)
require (min != 0 || max == 0)
require (max == 0 || isPow2(max))
require (min == 0 || isPow2(min))
def none = min == 0
def contains(x: Int) = isPow2(x) && min <= x && x <= max
def containsLg(x: Int) = contains(1 << x)
def containsLg(x: UInt) = if (none) Bool(false) else { UInt(log2Ceil(min)) <= x && x <= UInt(log2Ceil(max)) } // !!! special-case =
def contains(x: TransferSizes) = x.none || (min <= x.min && x.max <= max)
def intersect(x: TransferSizes) =
if (x.max < min || min < x.max) TransferSizes.none
else TransferSizes(scala.math.max(min, x.min), scala.math.min(max, x.max))
}
object TransferSizes {
def apply(x: Int) = new TransferSizes(x)
val none = new TransferSizes(0)
implicit def asBool(x: TransferSizes) = !x.none
}
// AddressSets specify the mask of bits consumed by the manager
// The base address used by the crossbar for routing
case class AddressSet(mask: BigInt, base: Option[BigInt] = None)
{
// Forbid empty sets
require (base == None || (base.get & mask) == 0)
def contains(x: BigInt) = ((x ^ base.get) & ~mask) == 0
def contains(x: UInt) = ((x ^ UInt(base.get)) & UInt(~mask)) === UInt(0)
// overlap iff bitwise: both care (~mask0 & ~mask1) => both equal (base0=base1)
// if base = None, it will be auto-assigned and thus not overlap anything
def overlaps(x: AddressSet) = (base, x.base) match {
case (Some(tbase), Some(xbase)) => (~(mask | x.mask) & (tbase ^ xbase)) == 0
case _ => false
}
// contains iff bitwise: x.mask => mask && contains(x.base)
def contains(x: AddressSet) = ((x.mask | (base.get ^ x.base.get)) & ~mask) == 0
// 1 less than the number of bytes to which the manager should be aligned
def alignment1 = ((mask + 1) & ~mask) - 1
def max = base.get | mask
}
case class TLManagerParameters(
address: Seq[AddressSet],
sinkId: IdRange = IdRange(0, 1),
regionType: RegionType.T = RegionType.UNCACHEABLE,
// Supports both Acquire+Release+Finish of these sizes
supportsAcquire: TransferSizes = TransferSizes.none,
supportsArithmetic: TransferSizes = TransferSizes.none,
supportsLogical: TransferSizes = TransferSizes.none,
supportsGet: TransferSizes = TransferSizes.none,
supportsPutFull: TransferSizes = TransferSizes.none,
supportsPutPartial: TransferSizes = TransferSizes.none,
supportsHint: Boolean = false,
// If fifoId=Some, all messages sent to the same fifoId are delivered in FIFO order
fifoId: Option[Int] = None)
{
address.combinations(2).foreach({ case Seq(x,y) =>
require (!x.overlaps(y))
})
address.foreach({ case a =>
require (supportsAcquire.none || a.alignment1 >= supportsAcquire.max-1)
})
// Largest support transfer of all types
val maxTransfer = List(
supportsAcquire.max,
supportsArithmetic.max,
supportsLogical.max,
supportsGet.max,
supportsPutFull.max,
supportsPutPartial.max).max
}
case class TLManagerPortParameters(managers: Seq[TLManagerParameters], beatBytes: Int)
{
require (isPow2(beatBytes))
// Require disjoint ranges for Ids and addresses
managers.combinations(2).foreach({ case Seq(x,y) =>
require (!x.sinkId.overlaps(y.sinkId))
x.address.foreach({ a => y.address.foreach({ b =>
require (!a.overlaps(b))
})})
})
// Bounds on required sizes
def endSinkId = managers.map(_.sinkId.end).max
def maxAddress = managers.map(_.address.map(_.max).max).max
def maxTransfer = managers.map(_.maxTransfer).max
// Operation sizes supported by all outward Managers
val allSupportAcquire = managers.map(_.supportsAcquire) .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(_ && _)
// These return Option[TLManagerParameters] for your convenience
def find(address: BigInt) = managers.find(_.address.exists(_.contains(address)))
def findById(id: Int) = managers.find(_.sinkId.contains(id))
// Synthesizable lookup methods
def find(address: UInt) = Vec(managers.map(_.address.map(_.contains(address)).reduce(_ || _)))
def findById(id: UInt) = Vec(managers.map(_.sinkId.contains(id)))
// Does this Port manage this ID/address?
def contains(address: UInt) = find(address).reduce(_ || _)
def containsById(id: UInt) = findById(id).reduce(_ || _)
private def safety_helper(member: TLManagerParameters => TransferSizes)(address: UInt, lgSize: UInt) = {
(find(address) zip managers.map(member(_).containsLg(lgSize)))
.map { case (m, s) => m && s } reduce (_ || _)
}
// Check for support of a given operation at a specific address
val supportsAcquire = safety_helper(_.supportsAcquire) _
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) _
def supportsHint(address: UInt) = {
(find(address) zip managers.map(_.supportsHint))
.map { case (m, b) => m && Bool(b) } reduce (_ || _)
}
}
case class TLClientParameters(
sourceId: IdRange = IdRange(0,1),
// 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: Boolean = false)
{
val maxTransfer = List(
supportsProbe.max,
supportsArithmetic.max,
supportsLogical.max,
supportsGet.max,
supportsPutFull.max,
supportsPutPartial.max).max
}
case class TLClientPortParameters(clients: Seq[TLClientParameters]) {
// Require disjoint ranges for Ids
clients.combinations(2).foreach({ case Seq(x,y) =>
require (!x.sourceId.overlaps(y.sourceId))
})
// 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(_ && _)
// 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(_ || _)
private def safety_helper(member: TLClientParameters => TransferSizes)(address: UInt, lgSize: UInt) = {
(find(address) zip clients.map(member(_).containsLg(lgSize)))
.map { case (m, s) => m && s } reduce (_ || _)
}
// 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) _
def supportsHint(address: UInt) = {
(find(address) zip clients.map(_.supportsHint))
.map { case (m, b) => m && Bool(b) } reduce (_ || _)
}
}
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 >= 1)
require (sourceBits >= 1)
require (sinkBits >= 1)
require (sizeBits >= 1)
require (isPow2(dataBits))
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))
}
case class TLEdgeParameters(
client: TLClientPortParameters,
manager: TLManagerPortParameters)
{
val maxTransfer = max(client.maxTransfer, manager.maxTransfer)
val maxLgSize = log2Up(maxTransfer)
// Sanity check the link...
require (maxTransfer >= manager.beatBytes)
val bundle = TLBundleParameters(
addressBits = log2Up(manager.maxAddress + 1),
dataBits = manager.beatBytes * 8,
sourceBits = log2Up(client.endSourceId),
sinkBits = log2Up(manager.endSinkId),
sizeBits = log2Up(maxLgSize+1))
def isAligned(address: UInt, lgSize: UInt) =
if (maxLgSize == 0) Bool(true) else {
val mask = Vec.tabulate(maxLgSize) { UInt(_) < lgSize }
address & mask.toBits.asUInt === UInt(0)
}
// This gets used everywhere, so make the smallest circuit possible ...
def fullMask(address: UInt, lgSize: UInt) = {
val lgBytes = log2Ceil(manager.beatBytes)
def helper(i: Int): Seq[(Bool, Bool)] = {
if (i == 0) {
Seq((lgSize >= UInt(lgBytes), Bool(true)))
} else {
val sub = helper(i-1)
val size = lgSize === UInt(lgBytes - i)
Seq.tabulate (1 << i) { j =>
val (sub_acc, sub_eq) = sub(j/2)
val eq = sub_eq && address(lgBytes - i) === Bool(j % 2 == 1)
val acc = sub_acc || (size && eq)
(acc, eq)
}
}
}
Vec(helper(lgBytes).map(_._1)).toBits.asUInt
}
}