From 7328b55abdc2f3c07cb48290de1ee9db8568b4da Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" Date: Fri, 19 Aug 2016 11:08:35 -0700 Subject: [PATCH] tilelink2: first cut at parameterization --- uncore/src/main/scala/tilelink2/Bases.scala | 41 ++++ uncore/src/main/scala/tilelink2/Bundles.scala | 77 +++++++ uncore/src/main/scala/tilelink2/Nodes.scala | 121 +++++++++++ .../src/main/scala/tilelink2/Operations.scala | 51 +++++ .../src/main/scala/tilelink2/Parameters.scala | 205 ++++++++++++++++++ 5 files changed, 495 insertions(+) create mode 100644 uncore/src/main/scala/tilelink2/Bases.scala create mode 100644 uncore/src/main/scala/tilelink2/Bundles.scala create mode 100644 uncore/src/main/scala/tilelink2/Nodes.scala create mode 100644 uncore/src/main/scala/tilelink2/Operations.scala create mode 100644 uncore/src/main/scala/tilelink2/Parameters.scala diff --git a/uncore/src/main/scala/tilelink2/Bases.scala b/uncore/src/main/scala/tilelink2/Bases.scala new file mode 100644 index 00000000..64e30679 --- /dev/null +++ b/uncore/src/main/scala/tilelink2/Bases.scala @@ -0,0 +1,41 @@ +// See LICENSE for license details. + +package uncore.tilelink2 + +import Chisel._ +import scala.collection.mutable.ListBuffer +import chisel3.internal.sourceinfo.SourceInfo + +abstract class TLFactory +{ + private val bindings = ListBuffer[(TLBaseNode, Int, TLBaseNode, Int, SourceInfo)]() + + def tl(manager: TLBaseNode, client: TLBaseNode)(implicit sourceInfo: SourceInfo) = { + val (i, j) = manager.edge(client) + bindings += ((manager, i, client, j, sourceInfo)) + } + + def module: TLModule + + protected[tilelink2] def instantiate() = { + // Find all TLFactory members of self + for (m <- getClass.getMethods) { + if (m.getParameterTypes.isEmpty && + !java.lang.reflect.Modifier.isStatic(m.getModifiers) && + !(m.getName contains '$') && + classOf[TLFactory].isAssignableFrom(m.getReturnType)) { + // ... and force their lazy module members to exist + m.invoke(this).asInstanceOf[TLFactory].module + } + } + bindings.foreach { case (x, i, y, j, s) => + x.bundleIn(i).<>(y.bundleOut(j))(s) + } + } +} + +abstract class TLModule(factory: TLFactory) extends Module +{ + override def desiredName = factory.getClass.getName.split('.').last + factory.instantiate() +} diff --git a/uncore/src/main/scala/tilelink2/Bundles.scala b/uncore/src/main/scala/tilelink2/Bundles.scala new file mode 100644 index 00000000..6225ae06 --- /dev/null +++ b/uncore/src/main/scala/tilelink2/Bundles.scala @@ -0,0 +1,77 @@ +// See LICENSE for license details. + +package uncore.tilelink2 + +import Chisel._ + +abstract class TLBundleBase(val params: TLBundleParameters) extends Bundle +{ + override def cloneType = { + try { + this.getClass.getConstructors.head.newInstance(params).asInstanceOf[this.type] + } catch { + case e: java.lang.IllegalArgumentException => + throwException("Unable to use TLBundleBase.cloneType on " + + this.getClass + ", probably because " + this.getClass + + "() takes more than one argument. Consider overriding " + + "cloneType() on " + this.getClass, e) + } + } +} + +class TLBundleA(params: TLBundleParameters) extends TLBundleBase(params) +{ + val opcode = UInt(width = 3) + val param = UInt(width = 3) // amo_opcode || perms(req) + val size = UInt(width = params.sizeBits) + val source = UInt(width = params.sourceBits) // from + val address = UInt(width = params.addressBits) // to + val wmask = UInt(width = params.dataBits/8) + val data = UInt(width = params.dataBits) +} + +class TLBundleB(params: TLBundleParameters) extends TLBundleBase(params) +{ + val opcode = UInt(width = 3) + val param = UInt(width = 3) // amo_opcode || perms(req) + val size = UInt(width = params.sizeBits) + val source = UInt(width = params.sourceBits) // to + val address = UInt(width = params.addressBits) // from + val wmask = UInt(width = params.dataBits/8) + val data = UInt(width = params.dataBits) +} + +class TLBundleC(params: TLBundleParameters) extends TLBundleBase(params) +{ + val opcode = UInt(width = 3) + val param = UInt(width = 3) // perms(from=>to) + val size = UInt(width = params.sizeBits) + val address = UInt(width = params.addressBits) // to + val data = UInt(width = params.dataBits) + val error = Bool() +} + +class TLBundleD(params: TLBundleParameters) extends TLBundleBase(params) +{ + val opcode = UInt(width = 3) + val param = UInt(width = 3) // perms(to) + val size = UInt(width = params.sizeBits) + val source = UInt(width = params.sourceBits) // to + val sink = UInt(width = params.sinkBits) // from + val data = UInt(width = params.dataBits) + val error = Bool() +} + +class TLBundleE(params: TLBundleParameters) extends TLBundleBase(params) +{ + val sink = UInt(width = params.sourceBits) // to +} + +class TLBundle(params: TLBundleParameters) extends TLBundleBase(params) +{ + val a = Decoupled(new TLBundleA(params)) + val b = Decoupled(new TLBundleB(params)).flip + val c = Decoupled(new TLBundleC(params)) + val d = Decoupled(new TLBundleD(params)).flip + val e = Decoupled(new TLBundleE(params)) +} diff --git a/uncore/src/main/scala/tilelink2/Nodes.scala b/uncore/src/main/scala/tilelink2/Nodes.scala new file mode 100644 index 00000000..1e4f0d4e --- /dev/null +++ b/uncore/src/main/scala/tilelink2/Nodes.scala @@ -0,0 +1,121 @@ +// See LICENSE for license details. + +package uncore.tilelink2 + +import Chisel._ +import scala.collection.mutable.ListBuffer + +class TLBaseNode( + private val clientFn: Option[Seq[TLClientPortParameters] => TLClientPortParameters], + private val managerFn: Option[Seq[TLManagerPortParameters] => TLManagerPortParameters], + private val numClientPorts: Range.Inclusive, + private val numManagerPorts: Range.Inclusive) +{ + // At least 0 ports must be supported + require (!numClientPorts.isEmpty) + require (!numManagerPorts.isEmpty) + require (numClientPorts.start >= 0) + require (numManagerPorts.start >= 0) + + val noClients = numClientPorts.size == 1 && numClientPorts.contains(0) + val noManagers = numManagerPorts.size == 1 && numManagerPorts.contains(0) + + require (noClients || clientFn.isDefined) + require (noManagers || managerFn.isDefined) + + private val accClientPorts = ListBuffer[TLBaseNode]() + private val accManagerPorts = ListBuffer[TLBaseNode]() + private var clientRealized = false + private var managerRealized = false + + protected[tilelink2] def edge(x: TLBaseNode) = { + require (!noManagers) + require (!managerRealized) + require (!x.noClients) + require (!x.clientRealized) + val i = accManagerPorts.size + val j = x.accClientPorts.size + accManagerPorts += x + x.accClientPorts += this + (i, j) + } + + private lazy val clientPorts = { clientRealized = true; require (numClientPorts.contains(accClientPorts.size)); accClientPorts.result() } + private lazy val managerPorts = { managerRealized = true; require (numManagerPorts.contains(accManagerPorts.size)); accManagerPorts.result() } + private lazy val clientParams : Option[TLClientPortParameters] = clientFn.map(_(managerPorts.map(_.clientParams.get))) + private lazy val managerParams : Option[TLManagerPortParameters] = managerFn.map(_(clientPorts.map(_.managerParams.get))) + + lazy val edgesOut = clientPorts.map { n => new TLEdgeOut(clientParams.get, n.managerParams.get) } + lazy val edgesIn = managerPorts.map { n => new TLEdgeIn (n.clientParams.get, managerParams.get) } + + lazy val bundleOut = Vec(edgesOut.size, new TLBundle(edgesOut.map(_.bundle).reduce(_.union(_)))) + lazy val bundleIn = Vec(edgesIn .size, new TLBundle(edgesIn .map(_.bundle).reduce(_.union(_)))).flip +} + +class TLClientNode( + params: TLClientParameters, + numPorts: Range.Inclusive = 1 to 1) extends TLBaseNode( + clientFn = Some {case Seq() => TLClientPortParameters(Seq(params))}, + managerFn = None, + numClientPorts = numPorts, + numManagerPorts = 0 to 0) +{ + require(numPorts.end >= 1) +} + +object TLClientNode +{ + def apply( + params: TLClientParameters, + numPorts: Range.Inclusive = 1 to 1) = new TLClientNode(params, numPorts) +} + +class TLManagerNode( + beatBytes: Int, + params: TLManagerParameters, + numPorts: Range.Inclusive = 1 to 1) extends TLBaseNode( + clientFn = None, + managerFn = Some {case Seq() => TLManagerPortParameters(Seq(params), beatBytes)}, + numClientPorts = 0 to 0, + numManagerPorts = numPorts) +{ + require(numPorts.end >= 1) +} + +object TLManagerNode +{ + def apply( + beatBytes: Int, + params: TLManagerParameters, + numPorts: Range.Inclusive = 1 to 1) = new TLManagerNode(beatBytes, params, numPorts) +} + +class TLAdapterNode( + clientFn: Seq[TLClientPortParameters] => TLClientPortParameters, + managerFn: Seq[TLManagerPortParameters] => TLManagerPortParameters, + numClientPorts: Range.Inclusive = 1 to 1, + numManagerPorts: Range.Inclusive = 1 to 1) extends TLBaseNode( + clientFn = Some(clientFn), + managerFn = Some(managerFn), + numClientPorts = numClientPorts, + numManagerPorts = numManagerPorts) + +object TLAdapterNode +{ + def apply( + clientFn: Seq[TLClientPortParameters] => TLClientPortParameters, + managerFn: Seq[TLManagerPortParameters] => TLManagerPortParameters, + numClientPorts: Range.Inclusive = 1 to 1, + numManagerPorts: Range.Inclusive = 1 to 1) = new TLAdapterNode(clientFn, managerFn, numClientPorts, numManagerPorts) +} + +class TLIDNode extends TLBaseNode( + clientFn = Some({case Seq(x) => x}), + managerFn = Some({case Seq(x) => x}), + numClientPorts = 1 to 1, + numManagerPorts = 1 to 1) + +object TLIDNode +{ + def apply() = new TLIDNode() +} diff --git a/uncore/src/main/scala/tilelink2/Operations.scala b/uncore/src/main/scala/tilelink2/Operations.scala new file mode 100644 index 00000000..33f5e6c6 --- /dev/null +++ b/uncore/src/main/scala/tilelink2/Operations.scala @@ -0,0 +1,51 @@ +// See LICENSE for license details. + +package uncore.tilelink2 + +import Chisel._ + +class TLEdgeOut( + client: TLClientPortParameters, + manager: TLManagerPortParameters) + extends TLEdgeParameters(client, manager) +{ + // Transfers + def Acquire(x: Int) = () // A + def Release(x: Int) = () // C + def ReleaseData(x: Int) = () // C + def ProbeAck(x: Int) = () // C + def ProbeDataAck(x: Int) = () // C + def GrantAck(x: Int) = () // E + + // Accessors + def Get(x: Int) = () // A + def Put(x: Int) = () // A + def Atomic(x: Int) = () // A + def AccessAck(x: Int) = () // C + def AccessDataAck(x: Int) = () // C + + def Hint(x: Int) = () // A + def HintAck(x: Int) = () // C +} + +class TLEdgeIn( + client: TLClientPortParameters, + manager: TLManagerPortParameters) + extends TLEdgeParameters(client, manager) +{ + // Transfers + def Probe(x: Int) = () // B + def Grant(x: Int) = () // D + def GrantData(x: Int) = () // D + def ReleaseAck(x: Int) = () // D + + // Accessors + def Get(x: Int) = () // B + def Put(x: Int) = () // B + def Atomic(x: Int) = () // B + def AccessAck(x: Int) = () // D + def AccessDataAck(x: Int) = () // D + + def Hint(x: Int) = () // B + def HintAck(x: Int) = () // D +} diff --git a/uncore/src/main/scala/tilelink2/Parameters.scala b/uncore/src/main/scala/tilelink2/Parameters.scala new file mode 100644 index 00000000..e894d8d9 --- /dev/null +++ b/uncore/src/main/scala/tilelink2/Parameters.scala @@ -0,0 +1,205 @@ +// 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] +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) +} + +// 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 of these sizes + supportsAcquire: TransferSizes = TransferSizes.none, + supportsAtomic: TransferSizes = TransferSizes.none, + supportsGet: TransferSizes = TransferSizes.none, + supportsPutFull: TransferSizes = TransferSizes.none, + supportsPutPartial: TransferSizes = TransferSizes.none, + supportsHints: 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) + }) + + val maxTransfer = List( + supportsAcquire.max, + supportsAtomic.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)) + })}) + }) + + def endSinkId = managers.map(_.sinkId.end).max + def maxAddress = managers.map(_.address.map(_.max).max).max + def maxGet = managers.map(_.supportsGet.max).max + def maxTransfer = managers.map(_.maxTransfer).max + + // These return Option[TLSinkParameters] for your convenience + def findById(x: Int) = managers.find(_.sinkId.contains(x)) + def findByAddress(x: BigInt) = managers.find(_.address.exists(_.contains(x))) + + //def buildCacheInfo(): UInt => Chilse(RegionType) // UInt = address, not sink_id + //def buildAtomicInfo(): UInt => Bool +} + +case class TLClientParameters( + sourceId: IdRange = IdRange(0,1), + // Supports both Probe+Grant of these sizes + supportsProbe: TransferSizes = TransferSizes.none, + supportsAtomics: TransferSizes = TransferSizes.none, + supportsGet: TransferSizes = TransferSizes.none, + supportsPutFull: TransferSizes = TransferSizes.none, + supportsPutPartial: TransferSizes = TransferSizes.none, + supportsHints: Boolean = false) +{ + val maxTransfer = List( + supportsProbe.max, + supportsAtomics.max, + supportsGet.max, + supportsPutFull.max, + supportsPutPartial.max).max +} + +case class TLClientPortParameters(clients: Seq[TLClientParameters]) { + def endSourceId = clients.map(_.sourceId.end).max + def maxTransfer = clients.map(_.maxTransfer).max +// def nSources: Int = sourceView.map(_.sourceIds.count).sum +// def nCaches: Int = sourceView.map(s => if(s.supportsProbe) 1 else 0).sum + //def makeSourceToCache() = ... + //def makeCacheToStartSource() = ... +} + +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 bundle = TLBundleParameters( + addressBits = log2Up(manager.maxAddress + 1) - log2Up(manager.beatBytes), + dataBits = manager.beatBytes * 8, + sourceBits = log2Up(client.endSourceId), + sinkBits = log2Up(manager.endSinkId), + sizeBits = log2Up(log2Up(max(client.maxTransfer, manager.maxTransfer))+1)) +}