diff --git a/src/main/scala/coreplex/BaseCoreplex.scala b/src/main/scala/coreplex/BaseCoreplex.scala index 6c885148..f635d248 100644 --- a/src/main/scala/coreplex/BaseCoreplex.scala +++ b/src/main/scala/coreplex/BaseCoreplex.scala @@ -3,6 +3,7 @@ package coreplex import Chisel._ import cde.{Parameters, Field} import junctions._ +import diplomacy._ import uncore.tilelink._ import uncore.tilelink2._ import uncore.coherence._ diff --git a/src/main/scala/uncore/tilelink2/AddressDecoder.scala b/src/main/scala/diplomacy/AddressDecoder.scala similarity index 99% rename from src/main/scala/uncore/tilelink2/AddressDecoder.scala rename to src/main/scala/diplomacy/AddressDecoder.scala index ea7cacac..461de161 100644 --- a/src/main/scala/uncore/tilelink2/AddressDecoder.scala +++ b/src/main/scala/diplomacy/AddressDecoder.scala @@ -1,6 +1,6 @@ // See LICENSE for license details. -package uncore.tilelink2 +package diplomacy import Chisel._ import scala.math.{max,min} diff --git a/src/main/scala/uncore/tilelink2/LazyModule.scala b/src/main/scala/diplomacy/LazyModule.scala similarity index 89% rename from src/main/scala/uncore/tilelink2/LazyModule.scala rename to src/main/scala/diplomacy/LazyModule.scala index d2bb370b..ba9d9660 100644 --- a/src/main/scala/uncore/tilelink2/LazyModule.scala +++ b/src/main/scala/diplomacy/LazyModule.scala @@ -1,17 +1,17 @@ // See LICENSE for license details. -package uncore.tilelink2 +package diplomacy import Chisel._ import chisel3.internal.sourceinfo.{SourceInfo, SourceLine, UnlocatableSourceInfo} abstract class LazyModule { - protected[tilelink2] var bindings = List[() => Unit]() - protected[tilelink2] var children = List[LazyModule]() - protected[tilelink2] var nodes = List[BaseNode]() - protected[tilelink2] var info: SourceInfo = UnlocatableSourceInfo - protected[tilelink2] val parent = LazyModule.stack.headOption + protected[diplomacy] var bindings = List[() => Unit]() + protected[diplomacy] var children = List[LazyModule]() + protected[diplomacy] var nodes = List[BaseNode]() + protected[diplomacy] var info: SourceInfo = UnlocatableSourceInfo + protected[diplomacy] val parent = LazyModule.stack.headOption LazyModule.stack = this :: LazyModule.stack parent.foreach(p => p.children = this :: p.children) @@ -21,7 +21,7 @@ abstract class LazyModule def module: LazyModuleImp - protected[tilelink2] def instantiate() = { + protected[diplomacy] def instantiate() = { children.reverse.foreach { c => // !!! fix chisel3 so we can pass the desired sourceInfo // implicit val sourceInfo = c.module.outer.info @@ -71,7 +71,7 @@ abstract class LazyModule object LazyModule { - protected[tilelink2] var stack = List[LazyModule]() + protected[diplomacy] var stack = List[LazyModule]() private var index = 0 def apply[T <: LazyModule](bc: T)(implicit sourceInfo: SourceInfo): T = { diff --git a/src/main/scala/diplomacy/Nodes.scala b/src/main/scala/diplomacy/Nodes.scala new file mode 100644 index 00000000..3796f5ec --- /dev/null +++ b/src/main/scala/diplomacy/Nodes.scala @@ -0,0 +1,193 @@ +// See LICENSE for license details. + +package diplomacy + +import Chisel._ +import scala.collection.mutable.ListBuffer +import chisel3.internal.sourceinfo.SourceInfo + +// DI = Downwards flowing Parameters received on the inner side of the node +// UI = Upwards flowing Parameters generated by the inner side of the node +// EI = Edge Parameters describing a connection on the inner side of the node +// BI = Bundle type used when connecting to the inner side of the node +trait InwardNodeImp[DI, UI, EI, BI <: Data] +{ + def edgeI(pd: DI, pu: UI): EI + def bundleI(ei: Seq[EI]): Vec[BI] + def mixI(pu: UI, node: InwardNode[DI, UI, BI]): UI = pu + def colour: String + def connect(bo: => BI, bi: => BI, e: => EI)(implicit sourceInfo: SourceInfo): (Option[LazyModule], () => Unit) +} + +// DO = Downwards flowing Parameters generated by the outner side of the node +// UO = Upwards flowing Parameters received on the outner side of the node +// EO = Edge Parameters describing a connection on the outer side of the node +// BO = Bundle type used when connecting to the outer side of the node +trait OutwardNodeImp[DO, UO, EO, BO <: Data] +{ + def edgeO(pd: DO, pu: UO): EO + def bundleO(eo: Seq[EO]): Vec[BO] + def mixO(pd: DO, node: OutwardNode[DO, UO, BO]): DO = pd +} + +abstract class NodeImp[D, U, EO, EI, B <: Data] + extends Object with InwardNodeImp[D, U, EI, B] with OutwardNodeImp[D, U, EO, B] + +abstract class BaseNode +{ + // You cannot create a Node outside a LazyModule! + require (!LazyModule.stack.isEmpty) + + val lazyModule = LazyModule.stack.head + val index = lazyModule.nodes.size + lazyModule.nodes = this :: lazyModule.nodes + + def name = lazyModule.name + "." + getClass.getName.split('.').last + def omitGraphML = outputs.isEmpty && inputs.isEmpty + + protected[diplomacy] def outputs: Seq[BaseNode] + protected[diplomacy] def inputs: Seq[BaseNode] + protected[diplomacy] def colour: String +} + +trait InwardNode[DI, UI, BI <: Data] extends BaseNode +{ + protected[diplomacy] val numPI: Range.Inclusive + require (!numPI.isEmpty, s"No number of inputs would be acceptable to ${name}${lazyModule.line}") + require (numPI.start >= 0, s"${name} accepts a negative number of inputs${lazyModule.line}") + + private val accPI = ListBuffer[(Int, OutwardNode[DI, UI, BI])]() + private var iRealized = false + + protected[diplomacy] def iPushed = accPI.size + protected[diplomacy] def iPush(index: Int, node: OutwardNode[DI, UI, BI])(implicit sourceInfo: SourceInfo) { + val info = sourceLine(sourceInfo, " at ", "") + val noIs = numPI.size == 1 && numPI.contains(0) + require (!noIs, s"${name}${lazyModule.line} was incorrectly connected as a sink" + info) + require (!iRealized, s"${name}${lazyModule.line} was incorrectly connected as a sink after it's .module was used" + info) + accPI += ((index, node)) + } + + private def reqI() = require(numPI.contains(accPI.size), s"${name} has ${accPI.size} inputs, expected ${numPI}${lazyModule.line}") + protected[diplomacy] lazy val iPorts = { iRealized = true; reqI(); accPI.result() } + + protected[diplomacy] val iParams: Seq[UI] + protected[diplomacy] def iConnect: Vec[BI] +} + +trait OutwardNode[DO, UO, BO <: Data] extends BaseNode +{ + protected[diplomacy] val numPO: Range.Inclusive + require (!numPO.isEmpty, s"No number of outputs would be acceptable to ${name}${lazyModule.line}") + require (numPO.start >= 0, s"${name} accepts a negative number of outputs${lazyModule.line}") + + private val accPO = ListBuffer[(Int, InwardNode [DO, UO, BO])]() + private var oRealized = false + + protected[diplomacy] def oPushed = accPO.size + protected[diplomacy] def oPush(index: Int, node: InwardNode [DO, UO, BO])(implicit sourceInfo: SourceInfo) { + val info = sourceLine(sourceInfo, " at ", "") + val noOs = numPO.size == 1 && numPO.contains(0) + require (!noOs, s"${name}${lazyModule.line} was incorrectly connected as a source" + info) + require (!oRealized, s"${name}${lazyModule.line} was incorrectly connected as a source after it's .module was used" + info) + accPO += ((index, node)) + } + + private def reqO() = require(numPO.contains(accPO.size), s"${name} has ${accPO.size} outputs, expected ${numPO}${lazyModule.line}") + protected[diplomacy] lazy val oPorts = { oRealized = true; reqO(); accPO.result() } + + protected[diplomacy] val oParams: Seq[DO] + protected[diplomacy] def oConnect: Vec[BO] +} + +class MixedNode[DI, UI, EI, BI <: Data, DO, UO, EO, BO <: Data]( + inner: InwardNodeImp [DI, UI, EI, BI], + outer: OutwardNodeImp[DO, UO, EO, BO])( + private val dFn: (Int, Seq[DI]) => Seq[DO], + private val uFn: (Int, Seq[UO]) => Seq[UI], + protected[diplomacy] val numPO: Range.Inclusive, + protected[diplomacy] val numPI: Range.Inclusive) + extends BaseNode with InwardNode[DI, UI, BI] with OutwardNode[DO, UO, BO] +{ + // meta-data for printing the node graph + protected[diplomacy] def colour = inner.colour + protected[diplomacy] def outputs = oPorts.map(_._2) + protected[diplomacy] def inputs = iPorts.map(_._2) + + private def reqE(o: Int, i: Int) = require(i == o, s"${name} has ${i} inputs and ${o} outputs; they must match${lazyModule.line}") + protected[diplomacy] lazy val oParams: Seq[DO] = { + val o = dFn(oPorts.size, iPorts.map { case (i, n) => n.oParams(i) }) + reqE(oPorts.size, o.size) + o.map(outer.mixO(_, this)) + } + protected[diplomacy] lazy val iParams: Seq[UI] = { + val i = uFn(iPorts.size, oPorts.map { case (o, n) => n.iParams(o) }) + reqE(i.size, iPorts.size) + i.map(inner.mixI(_, this)) + } + + lazy val edgesOut = (oPorts zip oParams).map { case ((i, n), o) => outer.edgeO(o, n.iParams(i)) } + lazy val edgesIn = (iPorts zip iParams).map { case ((o, n), i) => inner.edgeI(n.oParams(o), i) } + + lazy val bundleOut = outer.bundleO(edgesOut) + lazy val bundleIn = inner.bundleI(edgesIn) + + def oConnect = bundleOut + def iConnect = bundleIn + + // connects the outward part of a node with the inward part of this node + def := (y: OutwardNode[DI, UI, BI])(implicit sourceInfo: SourceInfo): Option[LazyModule] = { + val x = this // x := y + val info = sourceLine(sourceInfo, " at ", "") + require (!LazyModule.stack.isEmpty, s"${y.name} cannot be connected to ${x.name} outside of LazyModule scope" + info) + val i = x.iPushed + val o = y.oPushed + y.oPush(i, x) + x.iPush(o, y) + val (out, binding) = inner.connect(y.oConnect(o), x.iConnect(i), x.edgesIn(i)) + LazyModule.stack.head.bindings = binding :: LazyModule.stack.head.bindings + out + } +} + +class SimpleNode[D, U, EO, EI, B <: Data](imp: NodeImp[D, U, EO, EI, B])( + oFn: (Int, Seq[D]) => Seq[D], + iFn: (Int, Seq[U]) => Seq[U], + numPO: Range.Inclusive, + numPI: Range.Inclusive) + extends MixedNode[D, U, EI, B, D, U, EO, B](imp, imp)(oFn, iFn, numPO, numPI) + +class IdentityNode[PO, PI, EO, EI, B <: Data](imp: NodeImp[PO, PI, EO, EI, B]) + extends SimpleNode(imp)({case (_, s) => s}, {case (_, s) => s}, 0 to 999, 0 to 999) + +class OutputNode[PO, PI, EO, EI, B <: Data](imp: NodeImp[PO, PI, EO, EI, B]) extends IdentityNode(imp) +{ + override def oConnect = bundleOut + override def iConnect = bundleOut +} + +class InputNode[PO, PI, EO, EI, B <: Data](imp: NodeImp[PO, PI, EO, EI, B]) extends IdentityNode(imp) +{ + override def oConnect = bundleIn + override def iConnect = bundleIn +} + +class SourceNode[PO, PI, EO, EI, B <: Data](imp: NodeImp[PO, PI, EO, EI, B])(po: PO, num: Range.Inclusive = 1 to 1) + extends SimpleNode(imp)({case (n, Seq()) => Seq.fill(n)(po)}, {case (0, _) => Seq()}, num, 0 to 0) +{ + require (num.end >= 1, s"${name} is a source which does not accept outputs${lazyModule.line}") +} + +class SinkNode[PO, PI, EO, EI, B <: Data](imp: NodeImp[PO, PI, EO, EI, B])(pi: PI, num: Range.Inclusive = 1 to 1) + extends SimpleNode(imp)({case (0, _) => Seq()}, {case (n, Seq()) => Seq.fill(n)(pi)}, 0 to 0, num) +{ + require (num.end >= 1, s"${name} is a sink which does not accept inputs${lazyModule.line}") +} + +class InteriorNode[PO, PI, EO, EI, B <: Data](imp: NodeImp[PO, PI, EO, EI, B]) + (oFn: Seq[PO] => PO, iFn: Seq[PI] => PI, numPO: Range.Inclusive, numPI: Range.Inclusive) + extends SimpleNode(imp)({case (n,s) => Seq.fill(n)(oFn(s))}, {case (n,s) => Seq.fill(n)(iFn(s))}, numPO, numPI) +{ + require (numPO.end >= 1, s"${name} is an adapter which does not accept outputs${lazyModule.line}") + require (numPI.end >= 1, s"${name} is an adapter which does not accept inputs${lazyModule.line}") +} diff --git a/src/main/scala/diplomacy/Parameters.scala b/src/main/scala/diplomacy/Parameters.scala new file mode 100644 index 00000000..2ce88b64 --- /dev/null +++ b/src/main/scala/diplomacy/Parameters.scala @@ -0,0 +1,146 @@ +// See LICENSE for license details. + +package diplomacy + +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 PUT_EFFECTS extends T + case object GET_EFFECTS extends T // GET_EFFECTS => PUT_EFFECTS + val cases = Seq(CACHED, TRACKED, UNCACHED, PUT_EFFECTS, GET_EFFECTS) +} + +// A non-empty half-open range; [start, end) +case class IdRange(start: Int, end: Int) +{ + require (start >= 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) = + if (start+1 == end) { UInt(start) === x } + else if (isPow2(end-start) && ((end | start) & (end-start-1)) == 0) + { ~(~(UInt(start) ^ x) | UInt(end-start-1)) === UInt(0) } + else { UInt(start) <= x && x < UInt(end) } + + def shift(x: Int) = IdRange(start+x, end+x) + def size = end - start +} + +// 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)) + require (max == 0 || min != 0) // 0 is forbidden unless (0,0) + + 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 if (min == max) { UInt(log2Ceil(min)) === x } + else { UInt(log2Ceil(min)) <= x && x <= UInt(log2Ceil(max)) } + + def contains(x: TransferSizes) = x.none || (min <= x.min && x.max <= max) + + def intersect(x: TransferSizes) = + if (x.max < min || max < x.min) 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 address space managed by the manager +// Base is the base address, and mask are the bits consumed by the manager +// e.g: base=0x200, mask=0xff describes a device managing 0x200-0x2ff +// e.g: base=0x1000, mask=0xf0f decribes a device managing 0x1000-0x100f, 0x1100-0x110f, ... +case class AddressSet(base: BigInt, mask: BigInt) extends Ordered[AddressSet] +{ + // Forbid misaligned base address (and empty sets) + require ((base & mask) == 0) + require (base >= 0) // TL2 address widths are not fixed => negative is ambiguous + // We do allow negative mask (=> ignore all high bits) + + def contains(x: BigInt) = ((x ^ base) & ~mask) == 0 + def contains(x: UInt) = ((x ^ UInt(base)).zext() & SInt(~mask)) === SInt(0) + + // overlap iff bitwise: both care (~mask0 & ~mask1) => both equal (base0=base1) + def overlaps(x: AddressSet) = (~(mask | x.mask) & (base ^ x.base)) == 0 + // contains iff bitwise: x.mask => mask && contains(x.base) + def contains(x: AddressSet) = ((x.mask | (base ^ x.base)) & ~mask) == 0 + + // The number of bytes to which the manager must be aligned + def alignment = ((mask + 1) & ~mask) + // Is this a contiguous memory range + def contiguous = alignment == mask+1 + + def finite = mask >= 0 + def max = { require (finite); base | mask } + + // Widen the match function to ignore all bits in imask + def widen(imask: BigInt) = AddressSet(base & ~imask, mask | imask) + + // AddressSets have one natural Ordering (the containment order, if contiguous) + def compare(x: AddressSet) = { + val primary = (this.base - x.base).signum // smallest address first + val secondary = (x.mask - this.mask).signum // largest mask first + if (primary != 0) primary else secondary + } + + // We always want to see things in hex + override def toString() = { + if (mask >= 0) { + "AddressSet(0x%x, 0x%x)".format(base, mask) + } else { + "AddressSet(0x%x, ~0x%x)".format(base, ~mask) + } + } +} + +object AddressSet +{ + def misaligned(base: BigInt, size: BigInt): Seq[AddressSet] = { + val largestPow2 = BigInt(1) << log2Floor(size) + val mostZeros = (base + size - 1) & ~(largestPow2 - 1) + def splitLo(low: BigInt, high: BigInt, tail: Seq[AddressSet]): Seq[AddressSet] = { + if (low == high) tail else { + val toggleBits = low ^ high + val misalignment = toggleBits & (-toggleBits) + splitLo(low+misalignment, high, AddressSet(low, misalignment-1) +: tail) + } + } + def splitHi(low: BigInt, high: BigInt, tail: Seq[AddressSet]): Seq[AddressSet] = { + if (low == high) tail else { + val toggleBits = low ^ high + val misalignment = toggleBits & (-toggleBits) + splitHi(low, high-misalignment, AddressSet(high-misalignment, misalignment-1) +: tail) + } + } + splitLo(base, mostZeros, splitHi(mostZeros, base+size, Seq())).sorted + } +} + diff --git a/src/main/scala/diplomacy/package.scala b/src/main/scala/diplomacy/package.scala new file mode 100644 index 00000000..f3a6397f --- /dev/null +++ b/src/main/scala/diplomacy/package.scala @@ -0,0 +1,10 @@ +import Chisel._ +import chisel3.internal.sourceinfo.{SourceInfo, SourceLine, UnlocatableSourceInfo} + +package object diplomacy +{ + def sourceLine(sourceInfo: SourceInfo, prefix: String = " (", suffix: String = ")") = sourceInfo match { + case SourceLine(filename, line, col) => s"$prefix$filename:$line:$col$suffix" + case _ => "" + } +} diff --git a/src/main/scala/groundtest/Configs.scala b/src/main/scala/groundtest/Configs.scala index 9480e1b2..7f5bc0eb 100644 --- a/src/main/scala/groundtest/Configs.scala +++ b/src/main/scala/groundtest/Configs.scala @@ -2,6 +2,7 @@ package groundtest import Chisel._ import rocket._ +import diplomacy._ import uncore.tilelink._ import uncore.coherence._ import uncore.agents._ @@ -72,7 +73,7 @@ class Edge32BitMemtestConfig extends Config( class WithGroundTest extends Config( (pname, site, here) => pname match { case BuildCoreplex => - (c: CoreplexConfig, p: Parameters) => uncore.tilelink2.LazyModule(new GroundTestCoreplex(c)(p)).module + (c: CoreplexConfig, p: Parameters) => LazyModule(new GroundTestCoreplex(c)(p)).module case TLKey("L1toL2") => { val useMEI = site(NTiles) <= 1 && site(NCachedTileLinkPorts) <= 1 val dataBeats = (8 * site(CacheBlockBytes)) / site(XLen) @@ -105,7 +106,7 @@ class WithGroundTest extends Config( } } case BuildExampleTop => - (p: Parameters) => uncore.tilelink2.LazyModule(new ExampleTopWithTestRAM(p)) + (p: Parameters) => LazyModule(new ExampleTopWithTestRAM(p)) case FPUKey => None case UseAtomics => false case UseCompressed => false diff --git a/src/main/scala/uncore/tilelink2/RegField.scala b/src/main/scala/regmapper/RegField.scala similarity index 99% rename from src/main/scala/uncore/tilelink2/RegField.scala rename to src/main/scala/regmapper/RegField.scala index 61fc45a4..ba17d6ae 100644 --- a/src/main/scala/uncore/tilelink2/RegField.scala +++ b/src/main/scala/regmapper/RegField.scala @@ -1,6 +1,6 @@ // See LICENSE for license details. -package uncore.tilelink2 +package regmapper import Chisel._ import chisel3.util.{ReadyValidIO} diff --git a/src/main/scala/uncore/tilelink2/RegMapper.scala b/src/main/scala/regmapper/RegMapper.scala similarity index 99% rename from src/main/scala/uncore/tilelink2/RegMapper.scala rename to src/main/scala/regmapper/RegMapper.scala index 0c3b4f9c..f456b899 100644 --- a/src/main/scala/uncore/tilelink2/RegMapper.scala +++ b/src/main/scala/regmapper/RegMapper.scala @@ -1,8 +1,10 @@ // See LICENSE for license details. -package uncore.tilelink2 +package regmapper import Chisel._ +import diplomacy._ +import util.{GenericParameterizedBundle} // A bus agnostic register interface to a register-based device diff --git a/src/main/scala/uncore/tilelink2/RegisterCrossing.scala b/src/main/scala/regmapper/RegisterCrossing.scala similarity index 98% rename from src/main/scala/uncore/tilelink2/RegisterCrossing.scala rename to src/main/scala/regmapper/RegisterCrossing.scala index 85ec94f8..c9b504ed 100644 --- a/src/main/scala/uncore/tilelink2/RegisterCrossing.scala +++ b/src/main/scala/regmapper/RegisterCrossing.scala @@ -1,10 +1,10 @@ // See LICENSE for license details. -package uncore.tilelink2 +package regmapper import Chisel._ import chisel3.util.{Irrevocable} -import util.{AsyncResetRegVec, AsyncQueue, AsyncScope} +import util.{AsyncQueue,AsyncScope,AsyncResetRegVec} // A very simple flow control state machine, run in the specified clock domain class BusyRegisterCrossing(clock: Clock, reset: Bool) diff --git a/src/main/scala/rocketchip/BaseTop.scala b/src/main/scala/rocketchip/BaseTop.scala index 4bbdc974..4638be12 100644 --- a/src/main/scala/rocketchip/BaseTop.scala +++ b/src/main/scala/rocketchip/BaseTop.scala @@ -5,6 +5,7 @@ package rocketchip import Chisel._ import cde.{Parameters, Field} import junctions._ +import diplomacy._ import uncore.tilelink._ import uncore.tilelink2._ import uncore.devices._ diff --git a/src/main/scala/rocketchip/Configs.scala b/src/main/scala/rocketchip/Configs.scala index 6127e1c4..97d9594f 100644 --- a/src/main/scala/rocketchip/Configs.scala +++ b/src/main/scala/rocketchip/Configs.scala @@ -5,9 +5,9 @@ package rocketchip import Chisel._ import junctions._ import rocket._ +import diplomacy._ import uncore.agents._ import uncore.tilelink._ -import uncore.tilelink2.{LazyModule} import uncore.devices._ import uncore.converters._ import util._ @@ -40,7 +40,7 @@ class BasePlatformConfig extends Config( case TLKey("MMIOtoEdge") => site(TLKey("L2toMMIO")).copy(dataBeats = edgeDataBeats) case BuildCoreplex => - (c: CoreplexConfig, p: Parameters) => uncore.tilelink2.LazyModule(new DefaultCoreplex(c)(p)).module + (c: CoreplexConfig, p: Parameters) => LazyModule(new DefaultCoreplex(c)(p)).module case NExtTopInterrupts => 2 case PeripheryBusKey => PeripheryBusConfig(arithAMO = true, beatBytes = 4) // Note that PLIC asserts that this is > 0. @@ -68,7 +68,7 @@ class BasePlatformConfig extends Config( case ExtMemSize => Dump("MEM_SIZE", 0x10000000L) case RTCPeriod => 100 // gives 10 MHz RTC assuming 1 GHz uncore clock case BuildExampleTop => - (p: Parameters) => uncore.tilelink2.LazyModule(new ExampleTop(p)) + (p: Parameters) => LazyModule(new ExampleTop(p)) case SimMemLatency => 0 case _ => throw new CDEMatchError } diff --git a/src/main/scala/rocketchip/Periphery.scala b/src/main/scala/rocketchip/Periphery.scala index 908ad453..023f9e5e 100644 --- a/src/main/scala/rocketchip/Periphery.scala +++ b/src/main/scala/rocketchip/Periphery.scala @@ -6,6 +6,7 @@ import Chisel._ import cde.{Parameters, Field} import junctions._ import junctions.NastiConstants._ +import diplomacy._ import uncore.tilelink._ import uncore.tilelink2._ import uncore.converters._ diff --git a/src/main/scala/rocketchip/Utils.scala b/src/main/scala/rocketchip/Utils.scala index 88fb8436..69a7d338 100644 --- a/src/main/scala/rocketchip/Utils.scala +++ b/src/main/scala/rocketchip/Utils.scala @@ -4,6 +4,7 @@ package rocketchip import cde.{Parameters, Dump} import junctions._ +import diplomacy._ import uncore.devices._ import rocket._ import coreplex._ diff --git a/src/main/scala/uncore/devices/Prci.scala b/src/main/scala/uncore/devices/Prci.scala index 519f9f43..a16351fe 100644 --- a/src/main/scala/uncore/devices/Prci.scala +++ b/src/main/scala/uncore/devices/Prci.scala @@ -5,6 +5,7 @@ package uncore.devices import Chisel._ import junctions._ import junctions.NastiConstants._ +import regmapper._ import uncore.tilelink2._ import uncore.util._ import util._ diff --git a/src/main/scala/uncore/devices/Rom.scala b/src/main/scala/uncore/devices/Rom.scala index 937c6a89..3385fd64 100644 --- a/src/main/scala/uncore/devices/Rom.scala +++ b/src/main/scala/uncore/devices/Rom.scala @@ -3,6 +3,7 @@ package uncore.devices import Chisel._ import unittest.UnitTest import junctions._ +import diplomacy._ import uncore.tilelink._ import uncore.tilelink2._ import uncore.util._ diff --git a/src/main/scala/uncore/tilelink2/Arbiter.scala b/src/main/scala/uncore/tilelink2/Arbiter.scala index 7c74a040..8d4a0bb2 100644 --- a/src/main/scala/uncore/tilelink2/Arbiter.scala +++ b/src/main/scala/uncore/tilelink2/Arbiter.scala @@ -4,6 +4,7 @@ package uncore.tilelink2 import Chisel._ import chisel3.util.IrrevocableIO +import diplomacy._ object TLArbiter { diff --git a/src/main/scala/uncore/tilelink2/AtomicAutomata.scala b/src/main/scala/uncore/tilelink2/AtomicAutomata.scala index 36e1f714..dd74f6e6 100644 --- a/src/main/scala/uncore/tilelink2/AtomicAutomata.scala +++ b/src/main/scala/uncore/tilelink2/AtomicAutomata.scala @@ -4,6 +4,7 @@ package uncore.tilelink2 import Chisel._ import chisel3.internal.sourceinfo.SourceInfo +import diplomacy._ import scala.math.{min,max} // Ensures that all downstream RW managers support Atomic operationss. diff --git a/src/main/scala/uncore/tilelink2/Buffer.scala b/src/main/scala/uncore/tilelink2/Buffer.scala index 84211a09..659cf6dd 100644 --- a/src/main/scala/uncore/tilelink2/Buffer.scala +++ b/src/main/scala/uncore/tilelink2/Buffer.scala @@ -4,6 +4,7 @@ package uncore.tilelink2 import Chisel._ import chisel3.internal.sourceinfo.SourceInfo +import diplomacy._ import scala.math.max // pipe is only used if a queue has depth = 1 diff --git a/src/main/scala/uncore/tilelink2/Bundles.scala b/src/main/scala/uncore/tilelink2/Bundles.scala index 27071966..45fdd960 100644 --- a/src/main/scala/uncore/tilelink2/Bundles.scala +++ b/src/main/scala/uncore/tilelink2/Bundles.scala @@ -4,22 +4,8 @@ package uncore.tilelink2 import Chisel._ import chisel3.util.{Irrevocable, IrrevocableIO, ReadyValidIO} -import util.{AsyncQueueSource, AsyncQueueSink} - -abstract class GenericParameterizedBundle[T <: Object](val params: T) extends Bundle -{ - override def cloneType = { - try { - this.getClass.getConstructors.head.newInstance(params).asInstanceOf[this.type] - } catch { - case e: java.lang.IllegalArgumentException => - throw new Exception("Unable to use GenericParameterizedBundle.cloneType on " + - this.getClass + ", probably because " + this.getClass + - "() takes more than one argument. Consider overriding " + - "cloneType() on " + this.getClass, e) - } - } -} +import diplomacy._ +import util.{AsyncQueueSource, AsyncQueueSink, GenericParameterizedBundle} abstract class TLBundleBase(params: TLBundleParameters) extends GenericParameterizedBundle(params) diff --git a/src/main/scala/uncore/tilelink2/Crossing.scala b/src/main/scala/uncore/tilelink2/Crossing.scala index c5de8d9e..92b0795c 100644 --- a/src/main/scala/uncore/tilelink2/Crossing.scala +++ b/src/main/scala/uncore/tilelink2/Crossing.scala @@ -4,6 +4,7 @@ package uncore.tilelink2 import Chisel._ import chisel3.internal.sourceinfo.SourceInfo +import diplomacy._ import util._ class TLAsyncCrossingSource(sync: Int = 3) extends LazyModule diff --git a/src/main/scala/uncore/tilelink2/Edges.scala b/src/main/scala/uncore/tilelink2/Edges.scala index 95b538d5..4e9c3284 100644 --- a/src/main/scala/uncore/tilelink2/Edges.scala +++ b/src/main/scala/uncore/tilelink2/Edges.scala @@ -4,6 +4,7 @@ package uncore.tilelink2 import Chisel._ import chisel3.internal.sourceinfo.SourceInfo +import diplomacy._ class TLEdge( client: TLClientPortParameters, diff --git a/src/main/scala/uncore/tilelink2/Example.scala b/src/main/scala/uncore/tilelink2/Example.scala index 84fa377e..c4095042 100644 --- a/src/main/scala/uncore/tilelink2/Example.scala +++ b/src/main/scala/uncore/tilelink2/Example.scala @@ -3,6 +3,7 @@ package uncore.tilelink2 import Chisel._ +import regmapper._ case class ExampleParams(num: Int, address: BigInt) diff --git a/src/main/scala/uncore/tilelink2/Fragmenter.scala b/src/main/scala/uncore/tilelink2/Fragmenter.scala index 2bd83d4f..4de68af9 100644 --- a/src/main/scala/uncore/tilelink2/Fragmenter.scala +++ b/src/main/scala/uncore/tilelink2/Fragmenter.scala @@ -4,6 +4,7 @@ package uncore.tilelink2 import Chisel._ import chisel3.internal.sourceinfo.SourceInfo +import diplomacy._ import scala.math.{min,max} // minSize: minimum size of transfers supported by all outward managers diff --git a/src/main/scala/uncore/tilelink2/Fuzzer.scala b/src/main/scala/uncore/tilelink2/Fuzzer.scala index 8b396f50..b0a4fab6 100644 --- a/src/main/scala/uncore/tilelink2/Fuzzer.scala +++ b/src/main/scala/uncore/tilelink2/Fuzzer.scala @@ -2,6 +2,7 @@ package uncore.tilelink2 import Chisel._ +import diplomacy._ class IDMapGenerator(numIds: Int) extends Module { val w = log2Up(numIds) diff --git a/src/main/scala/uncore/tilelink2/HintHandler.scala b/src/main/scala/uncore/tilelink2/HintHandler.scala index f97cd48a..bda7f2e4 100644 --- a/src/main/scala/uncore/tilelink2/HintHandler.scala +++ b/src/main/scala/uncore/tilelink2/HintHandler.scala @@ -4,6 +4,7 @@ package uncore.tilelink2 import Chisel._ import chisel3.internal.sourceinfo.SourceInfo +import diplomacy._ // Acks Hints for managers that don't support them or Acks all Hints if !passthrough class TLHintHandler(supportManagers: Boolean = true, supportClients: Boolean = false, passthrough: Boolean = true) extends LazyModule diff --git a/src/main/scala/uncore/tilelink2/IntNodes.scala b/src/main/scala/uncore/tilelink2/IntNodes.scala index 21ddd87f..639d03ed 100644 --- a/src/main/scala/uncore/tilelink2/IntNodes.scala +++ b/src/main/scala/uncore/tilelink2/IntNodes.scala @@ -3,9 +3,10 @@ package uncore.tilelink2 import Chisel._ +import chisel3.internal.sourceinfo.SourceInfo +import diplomacy._ import scala.collection.mutable.ListBuffer import scala.math.max -import chisel3.internal.sourceinfo.SourceInfo // A potentially empty half-open range; [start, end) case class IntRange(start: Int, end: Int) diff --git a/src/main/scala/uncore/tilelink2/Isolation.scala b/src/main/scala/uncore/tilelink2/Isolation.scala index c38cc10d..6f7fc5fe 100644 --- a/src/main/scala/uncore/tilelink2/Isolation.scala +++ b/src/main/scala/uncore/tilelink2/Isolation.scala @@ -4,6 +4,7 @@ package uncore.tilelink2 import Chisel._ import chisel3.internal.sourceinfo.SourceInfo +import diplomacy._ class TLIsolation(f: (Bool, UInt) => UInt) extends LazyModule { diff --git a/src/main/scala/uncore/tilelink2/Legacy.scala b/src/main/scala/uncore/tilelink2/Legacy.scala index e4338ad1..9d5ab238 100644 --- a/src/main/scala/uncore/tilelink2/Legacy.scala +++ b/src/main/scala/uncore/tilelink2/Legacy.scala @@ -3,6 +3,7 @@ package uncore.tilelink2 import Chisel._ +import diplomacy._ import cde.Parameters import uncore.tilelink._ import uncore.constants._ diff --git a/src/main/scala/uncore/tilelink2/Monitor.scala b/src/main/scala/uncore/tilelink2/Monitor.scala index ae686e08..c0fc51a8 100644 --- a/src/main/scala/uncore/tilelink2/Monitor.scala +++ b/src/main/scala/uncore/tilelink2/Monitor.scala @@ -4,6 +4,7 @@ package uncore.tilelink2 import Chisel._ import chisel3.internal.sourceinfo.{SourceInfo, SourceLine} +import diplomacy._ class TLMonitor(gen: () => TLBundleSnoop, edge: () => TLEdge, sourceInfo: SourceInfo) extends LazyModule { diff --git a/src/main/scala/uncore/tilelink2/Nodes.scala b/src/main/scala/uncore/tilelink2/Nodes.scala index 34d2be29..e92e7445 100644 --- a/src/main/scala/uncore/tilelink2/Nodes.scala +++ b/src/main/scala/uncore/tilelink2/Nodes.scala @@ -3,191 +3,124 @@ package uncore.tilelink2 import Chisel._ -import scala.collection.mutable.ListBuffer import chisel3.internal.sourceinfo.SourceInfo +import diplomacy._ +import scala.collection.mutable.ListBuffer -// DI = Downwards flowing Parameters received on the inner side of the node -// UI = Upwards flowing Parameters generated by the inner side of the node -// EI = Edge Parameters describing a connection on the inner side of the node -// BI = Bundle type used when connecting to the inner side of the node -trait InwardNodeImp[DI, UI, EI, BI <: Data] +object TLImp extends NodeImp[TLClientPortParameters, TLManagerPortParameters, TLEdgeOut, TLEdgeIn, TLBundle] { - def edgeI(pd: DI, pu: UI): EI - def bundleI(ei: Seq[EI]): Vec[BI] - def mixI(pu: UI, node: InwardNode[DI, UI, BI]): UI = pu - def colour: String - def connect(bo: => BI, bi: => BI, e: => EI)(implicit sourceInfo: SourceInfo): (Option[LazyModule], () => Unit) -} - -// DO = Downwards flowing Parameters generated by the outner side of the node -// UO = Upwards flowing Parameters received on the outner side of the node -// EO = Edge Parameters describing a connection on the outer side of the node -// BO = Bundle type used when connecting to the outer side of the node -trait OutwardNodeImp[DO, UO, EO, BO <: Data] -{ - def edgeO(pd: DO, pu: UO): EO - def bundleO(eo: Seq[EO]): Vec[BO] - def mixO(pd: DO, node: OutwardNode[DO, UO, BO]): DO = pd -} - -abstract class NodeImp[D, U, EO, EI, B <: Data] - extends Object with InwardNodeImp[D, U, EI, B] with OutwardNodeImp[D, U, EO, B] - -abstract class BaseNode -{ - // You cannot create a Node outside a LazyModule! - require (!LazyModule.stack.isEmpty) - - val lazyModule = LazyModule.stack.head - val index = lazyModule.nodes.size - lazyModule.nodes = this :: lazyModule.nodes - - def name = lazyModule.name + "." + getClass.getName.split('.').last - def omitGraphML = outputs.isEmpty && inputs.isEmpty - - protected[tilelink2] def outputs: Seq[BaseNode] - protected[tilelink2] def inputs: Seq[BaseNode] - protected[tilelink2] def colour: String -} - -trait InwardNode[DI, UI, BI <: Data] extends BaseNode -{ - protected[tilelink2] val numPI: Range.Inclusive - require (!numPI.isEmpty, s"No number of inputs would be acceptable to ${name}${lazyModule.line}") - require (numPI.start >= 0, s"${name} accepts a negative number of inputs${lazyModule.line}") - - private val accPI = ListBuffer[(Int, OutwardNode[DI, UI, BI])]() - private var iRealized = false - - protected[tilelink2] def iPushed = accPI.size - protected[tilelink2] def iPush(index: Int, node: OutwardNode[DI, UI, BI])(implicit sourceInfo: SourceInfo) { - val info = sourceLine(sourceInfo, " at ", "") - val noIs = numPI.size == 1 && numPI.contains(0) - require (!noIs, s"${name}${lazyModule.line} was incorrectly connected as a sink" + info) - require (!iRealized, s"${name}${lazyModule.line} was incorrectly connected as a sink after it's .module was used" + info) - accPI += ((index, node)) + def edgeO(pd: TLClientPortParameters, pu: TLManagerPortParameters): TLEdgeOut = new TLEdgeOut(pd, pu) + def edgeI(pd: TLClientPortParameters, pu: TLManagerPortParameters): TLEdgeIn = new TLEdgeIn(pd, pu) + def bundleO(eo: Seq[TLEdgeOut]): Vec[TLBundle] = { + require (!eo.isEmpty) + Vec(eo.size, TLBundle(eo.map(_.bundle).reduce(_.union(_)))) + } + def bundleI(ei: Seq[TLEdgeIn]): Vec[TLBundle] = { + require (!ei.isEmpty) + Vec(ei.size, TLBundle(ei.map(_.bundle).reduce(_.union(_)))).flip } - private def reqI() = require(numPI.contains(accPI.size), s"${name} has ${accPI.size} inputs, expected ${numPI}${lazyModule.line}") - protected[tilelink2] lazy val iPorts = { iRealized = true; reqI(); accPI.result() } - - protected[tilelink2] val iParams: Seq[UI] - protected[tilelink2] def iConnect: Vec[BI] -} - -trait OutwardNode[DO, UO, BO <: Data] extends BaseNode -{ - protected[tilelink2] val numPO: Range.Inclusive - require (!numPO.isEmpty, s"No number of outputs would be acceptable to ${name}${lazyModule.line}") - require (numPO.start >= 0, s"${name} accepts a negative number of outputs${lazyModule.line}") - - private val accPO = ListBuffer[(Int, InwardNode [DO, UO, BO])]() - private var oRealized = false - - protected[tilelink2] def oPushed = accPO.size - protected[tilelink2] def oPush(index: Int, node: InwardNode [DO, UO, BO])(implicit sourceInfo: SourceInfo) { - val info = sourceLine(sourceInfo, " at ", "") - val noOs = numPO.size == 1 && numPO.contains(0) - require (!noOs, s"${name}${lazyModule.line} was incorrectly connected as a source" + info) - require (!oRealized, s"${name}${lazyModule.line} was incorrectly connected as a source after it's .module was used" + info) - accPO += ((index, node)) + def colour = "#000000" // black + def connect(bo: => TLBundle, bi: => TLBundle, ei: => TLEdgeIn)(implicit sourceInfo: SourceInfo): (Option[LazyModule], () => Unit) = { + val monitor = LazyModule(new TLMonitor(() => new TLBundleSnoop(bo.params), () => ei, sourceInfo)) + (Some(monitor), () => { + bi <> bo + monitor.module.io.in := TLBundleSnoop(bo) + }) } - private def reqO() = require(numPO.contains(accPO.size), s"${name} has ${accPO.size} outputs, expected ${numPO}${lazyModule.line}") - protected[tilelink2] lazy val oPorts = { oRealized = true; reqO(); accPO.result() } - - protected[tilelink2] val oParams: Seq[DO] - protected[tilelink2] def oConnect: Vec[BO] + override def mixO(pd: TLClientPortParameters, node: OutwardNode[TLClientPortParameters, TLManagerPortParameters, TLBundle]): TLClientPortParameters = + pd.copy(clients = pd.clients.map { c => c.copy (nodePath = node +: c.nodePath) }) + override def mixI(pu: TLManagerPortParameters, node: InwardNode[TLClientPortParameters, TLManagerPortParameters, TLBundle]): TLManagerPortParameters = + pu.copy(managers = pu.managers.map { m => m.copy (nodePath = node +: m.nodePath) }) } -class MixedNode[DI, UI, EI, BI <: Data, DO, UO, EO, BO <: Data]( - inner: InwardNodeImp [DI, UI, EI, BI], - outer: OutwardNodeImp[DO, UO, EO, BO])( - private val dFn: (Int, Seq[DI]) => Seq[DO], - private val uFn: (Int, Seq[UO]) => Seq[UI], - protected[tilelink2] val numPO: Range.Inclusive, - protected[tilelink2] val numPI: Range.Inclusive) - extends BaseNode with InwardNode[DI, UI, BI] with OutwardNode[DO, UO, BO] -{ - // meta-data for printing the node graph - protected[tilelink2] def colour = inner.colour - protected[tilelink2] def outputs = oPorts.map(_._2) - protected[tilelink2] def inputs = iPorts.map(_._2) +case class TLIdentityNode() extends IdentityNode(TLImp) +case class TLOutputNode() extends OutputNode(TLImp) +case class TLInputNode() extends InputNode(TLImp) - private def reqE(o: Int, i: Int) = require(i == o, s"${name} has ${i} inputs and ${o} outputs; they must match${lazyModule.line}") - protected[tilelink2] lazy val oParams: Seq[DO] = { - val o = dFn(oPorts.size, iPorts.map { case (i, n) => n.oParams(i) }) - reqE(oPorts.size, o.size) - o.map(outer.mixO(_, this)) - } - protected[tilelink2] lazy val iParams: Seq[UI] = { - val i = uFn(iPorts.size, oPorts.map { case (o, n) => n.iParams(o) }) - reqE(i.size, iPorts.size) - i.map(inner.mixI(_, this)) +case class TLClientNode(portParams: TLClientPortParameters, numPorts: Range.Inclusive = 1 to 1) + extends SourceNode(TLImp)(portParams, numPorts) +case class TLManagerNode(portParams: TLManagerPortParameters, numPorts: Range.Inclusive = 1 to 1) + extends SinkNode(TLImp)(portParams, numPorts) + +object TLClientNode +{ + def apply(params: TLClientParameters) = + new TLClientNode(TLClientPortParameters(Seq(params)), 1 to 1) +} + +object TLManagerNode +{ + def apply(beatBytes: Int, params: TLManagerParameters) = + new TLManagerNode(TLManagerPortParameters(Seq(params), beatBytes, 0), 1 to 1) +} + +case class TLAdapterNode( + clientFn: Seq[TLClientPortParameters] => TLClientPortParameters, + managerFn: Seq[TLManagerPortParameters] => TLManagerPortParameters, + numClientPorts: Range.Inclusive = 1 to 1, + numManagerPorts: Range.Inclusive = 1 to 1) + extends InteriorNode(TLImp)(clientFn, managerFn, numClientPorts, numManagerPorts) + +/** Synthesizeable unit tests */ +import unittest._ + +class TLInputNodeTest extends UnitTest(500000) { + class Acceptor extends LazyModule { + val node = TLInputNode() + val tlram = LazyModule(new TLRAM(AddressSet(0x54321000, 0xfff))) + tlram.node := node + + lazy val module = new LazyModuleImp(this) { + val io = new Bundle { + val in = node.bundleIn + } + } } - lazy val edgesOut = (oPorts zip oParams).map { case ((i, n), o) => outer.edgeO(o, n.iParams(i)) } - lazy val edgesIn = (iPorts zip iParams).map { case ((o, n), i) => inner.edgeI(n.oParams(o), i) } + val fuzzer = LazyModule(new TLFuzzer(5000)) + LazyModule(new Acceptor).node := TLFragmenter(4, 64)(fuzzer.node) - lazy val bundleOut = outer.bundleO(edgesOut) - lazy val bundleIn = inner.bundleI(edgesIn) + io.finished := Module(fuzzer.module).io.finished +} - def oConnect = bundleOut - def iConnect = bundleIn - - // connects the outward part of a node with the inward part of this node - def := (y: OutwardNode[DI, UI, BI])(implicit sourceInfo: SourceInfo): Option[LazyModule] = { - val x = this // x := y - val info = sourceLine(sourceInfo, " at ", "") - require (!LazyModule.stack.isEmpty, s"${y.name} cannot be connected to ${x.name} outside of LazyModule scope" + info) - val i = x.iPushed - val o = y.oPushed - y.oPush(i, x) - x.iPush(o, y) - val (out, binding) = inner.connect(y.oConnect(o), x.iConnect(i), x.edgesIn(i)) - LazyModule.stack.head.bindings = binding :: LazyModule.stack.head.bindings - out +object TLAsyncImp extends NodeImp[TLAsyncClientPortParameters, TLAsyncManagerPortParameters, TLAsyncEdgeParameters, TLAsyncEdgeParameters, TLAsyncBundle] +{ + def edgeO(pd: TLAsyncClientPortParameters, pu: TLAsyncManagerPortParameters): TLAsyncEdgeParameters = TLAsyncEdgeParameters(pd, pu) + def edgeI(pd: TLAsyncClientPortParameters, pu: TLAsyncManagerPortParameters): TLAsyncEdgeParameters = TLAsyncEdgeParameters(pd, pu) + def bundleO(eo: Seq[TLAsyncEdgeParameters]): Vec[TLAsyncBundle] = { + require (eo.size == 1) + Vec(eo.size, new TLAsyncBundle(eo(0).bundle)) } + def bundleI(ei: Seq[TLAsyncEdgeParameters]): Vec[TLAsyncBundle] = { + require (ei.size == 1) + Vec(ei.size, new TLAsyncBundle(ei(0).bundle)).flip + } + + def colour = "#ff0000" // red + def connect(bo: => TLAsyncBundle, bi: => TLAsyncBundle, ei: => TLAsyncEdgeParameters)(implicit sourceInfo: SourceInfo): (Option[LazyModule], () => Unit) = { + (None, () => { bi <> bo }) + } + + override def mixO(pd: TLAsyncClientPortParameters, node: OutwardNode[TLAsyncClientPortParameters, TLAsyncManagerPortParameters, TLAsyncBundle]): TLAsyncClientPortParameters = + pd.copy(base = pd.base.copy(clients = pd.base.clients.map { c => c.copy (nodePath = node +: c.nodePath) })) + override def mixI(pu: TLAsyncManagerPortParameters, node: InwardNode[TLAsyncClientPortParameters, TLAsyncManagerPortParameters, TLAsyncBundle]): TLAsyncManagerPortParameters = + pu.copy(base = pu.base.copy(managers = pu.base.managers.map { m => m.copy (nodePath = node +: m.nodePath) })) } -class SimpleNode[D, U, EO, EI, B <: Data](imp: NodeImp[D, U, EO, EI, B])( - oFn: (Int, Seq[D]) => Seq[D], - iFn: (Int, Seq[U]) => Seq[U], - numPO: Range.Inclusive, - numPI: Range.Inclusive) - extends MixedNode[D, U, EI, B, D, U, EO, B](imp, imp)(oFn, iFn, numPO, numPI) +case class TLAsyncIdentityNode() extends IdentityNode(TLAsyncImp) +case class TLAsyncOutputNode() extends OutputNode(TLAsyncImp) +case class TLAsyncInputNode() extends InputNode(TLAsyncImp) -class IdentityNode[PO, PI, EO, EI, B <: Data](imp: NodeImp[PO, PI, EO, EI, B]) - extends SimpleNode(imp)({case (_, s) => s}, {case (_, s) => s}, 0 to 999, 0 to 999) +case class TLAsyncSourceNode() extends MixedNode(TLImp, TLAsyncImp)( + dFn = { case (1, s) => s.map(TLAsyncClientPortParameters(_)) }, + uFn = { case (1, s) => s.map(_.base) }, + numPO = 1 to 1, + numPI = 1 to 1) -class OutputNode[PO, PI, EO, EI, B <: Data](imp: NodeImp[PO, PI, EO, EI, B]) extends IdentityNode(imp) -{ - override def oConnect = bundleOut - override def iConnect = bundleOut -} - -class InputNode[PO, PI, EO, EI, B <: Data](imp: NodeImp[PO, PI, EO, EI, B]) extends IdentityNode(imp) -{ - override def oConnect = bundleIn - override def iConnect = bundleIn -} - -class SourceNode[PO, PI, EO, EI, B <: Data](imp: NodeImp[PO, PI, EO, EI, B])(po: PO, num: Range.Inclusive = 1 to 1) - extends SimpleNode(imp)({case (n, Seq()) => Seq.fill(n)(po)}, {case (0, _) => Seq()}, num, 0 to 0) -{ - require (num.end >= 1, s"${name} is a source which does not accept outputs${lazyModule.line}") -} - -class SinkNode[PO, PI, EO, EI, B <: Data](imp: NodeImp[PO, PI, EO, EI, B])(pi: PI, num: Range.Inclusive = 1 to 1) - extends SimpleNode(imp)({case (0, _) => Seq()}, {case (n, Seq()) => Seq.fill(n)(pi)}, 0 to 0, num) -{ - require (num.end >= 1, s"${name} is a sink which does not accept inputs${lazyModule.line}") -} - -class InteriorNode[PO, PI, EO, EI, B <: Data](imp: NodeImp[PO, PI, EO, EI, B]) - (oFn: Seq[PO] => PO, iFn: Seq[PI] => PI, numPO: Range.Inclusive, numPI: Range.Inclusive) - extends SimpleNode(imp)({case (n,s) => Seq.fill(n)(oFn(s))}, {case (n,s) => Seq.fill(n)(iFn(s))}, numPO, numPI) -{ - require (numPO.end >= 1, s"${name} is an adapter which does not accept outputs${lazyModule.line}") - require (numPI.end >= 1, s"${name} is an adapter which does not accept inputs${lazyModule.line}") -} +case class TLAsyncSinkNode(depth: Int) extends MixedNode(TLAsyncImp, TLImp)( + dFn = { case (1, s) => s.map(_.base) }, + uFn = { case (1, s) => s.map(TLAsyncManagerPortParameters(depth, _)) }, + numPO = 1 to 1, + numPI = 1 to 1) diff --git a/src/main/scala/uncore/tilelink2/Parameters.scala b/src/main/scala/uncore/tilelink2/Parameters.scala index 1b0b8ef4..45b95c13 100644 --- a/src/main/scala/uncore/tilelink2/Parameters.scala +++ b/src/main/scala/uncore/tilelink2/Parameters.scala @@ -3,147 +3,9 @@ package uncore.tilelink2 import Chisel._ +import diplomacy._ 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 PUT_EFFECTS extends T - case object GET_EFFECTS extends T // GET_EFFECTS => PUT_EFFECTS - val cases = Seq(CACHED, TRACKED, UNCACHED, PUT_EFFECTS, GET_EFFECTS) -} - -// A non-empty half-open range; [start, end) -case class IdRange(start: Int, end: Int) -{ - require (start >= 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) = - if (start+1 == end) { UInt(start) === x } - else if (isPow2(end-start) && ((end | start) & (end-start-1)) == 0) - { ~(~(UInt(start) ^ x) | UInt(end-start-1)) === UInt(0) } - else { UInt(start) <= x && x < UInt(end) } - - def shift(x: Int) = IdRange(start+x, end+x) - def size = end - start -} - -// 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)) - require (max == 0 || min != 0) // 0 is forbidden unless (0,0) - - 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 if (min == max) { UInt(log2Ceil(min)) === x } - else { UInt(log2Ceil(min)) <= x && x <= UInt(log2Ceil(max)) } - - def contains(x: TransferSizes) = x.none || (min <= x.min && x.max <= max) - - def intersect(x: TransferSizes) = - if (x.max < min || max < x.min) 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 address space managed by the manager -// Base is the base address, and mask are the bits consumed by the manager -// e.g: base=0x200, mask=0xff describes a device managing 0x200-0x2ff -// e.g: base=0x1000, mask=0xf0f decribes a device managing 0x1000-0x100f, 0x1100-0x110f, ... -case class AddressSet(base: BigInt, mask: BigInt) extends Ordered[AddressSet] -{ - // Forbid misaligned base address (and empty sets) - require ((base & mask) == 0) - require (base >= 0) // TL2 address widths are not fixed => negative is ambiguous - // We do allow negative mask (=> ignore all high bits) - - def contains(x: BigInt) = ((x ^ base) & ~mask) == 0 - def contains(x: UInt) = ((x ^ UInt(base)).zext() & SInt(~mask)) === SInt(0) - - // overlap iff bitwise: both care (~mask0 & ~mask1) => both equal (base0=base1) - def overlaps(x: AddressSet) = (~(mask | x.mask) & (base ^ x.base)) == 0 - // contains iff bitwise: x.mask => mask && contains(x.base) - def contains(x: AddressSet) = ((x.mask | (base ^ x.base)) & ~mask) == 0 - - // The number of bytes to which the manager must be aligned - def alignment = ((mask + 1) & ~mask) - // Is this a contiguous memory range - def contiguous = alignment == mask+1 - - def finite = mask >= 0 - def max = { require (finite); base | mask } - - // Widen the match function to ignore all bits in imask - def widen(imask: BigInt) = AddressSet(base & ~imask, mask | imask) - - // AddressSets have one natural Ordering (the containment order, if contiguous) - def compare(x: AddressSet) = { - val primary = (this.base - x.base).signum // smallest address first - val secondary = (x.mask - this.mask).signum // largest mask first - if (primary != 0) primary else secondary - } - - // We always want to see things in hex - override def toString() = { - if (mask >= 0) { - "AddressSet(0x%x, 0x%x)".format(base, mask) - } else { - "AddressSet(0x%x, ~0x%x)".format(base, ~mask) - } - } -} - -object AddressSet -{ - def misaligned(base: BigInt, size: BigInt): Seq[AddressSet] = { - val largestPow2 = BigInt(1) << log2Floor(size) - val mostZeros = (base + size - 1) & ~(largestPow2 - 1) - def splitLo(low: BigInt, high: BigInt, tail: Seq[AddressSet]): Seq[AddressSet] = { - if (low == high) tail else { - val toggleBits = low ^ high - val misalignment = toggleBits & (-toggleBits) - splitLo(low+misalignment, high, AddressSet(low, misalignment-1) +: tail) - } - } - def splitHi(low: BigInt, high: BigInt, tail: Seq[AddressSet]): Seq[AddressSet] = { - if (low == high) tail else { - val toggleBits = low ^ high - val misalignment = toggleBits & (-toggleBits) - splitHi(low, high-misalignment, AddressSet(high-misalignment, misalignment-1) +: tail) - } - } - splitLo(base, mostZeros, splitHi(mostZeros, base+size, Seq())).sorted - } -} - case class TLManagerParameters( address: Seq[AddressSet], sinkId: IdRange = IdRange(0, 1), diff --git a/src/main/scala/uncore/tilelink2/RAMModel.scala b/src/main/scala/uncore/tilelink2/RAMModel.scala index 533f6639..b8d46c61 100644 --- a/src/main/scala/uncore/tilelink2/RAMModel.scala +++ b/src/main/scala/uncore/tilelink2/RAMModel.scala @@ -3,6 +3,7 @@ package uncore.tilelink2 import Chisel._ +import diplomacy._ // We detect concurrent puts that put memory into an undefined state. // put0, put0Ack, put1, put1Ack => ok: defined diff --git a/src/main/scala/uncore/tilelink2/RegisterRouter.scala b/src/main/scala/uncore/tilelink2/RegisterRouter.scala index 7dc921b5..dddd0a60 100644 --- a/src/main/scala/uncore/tilelink2/RegisterRouter.scala +++ b/src/main/scala/uncore/tilelink2/RegisterRouter.scala @@ -3,6 +3,8 @@ package uncore.tilelink2 import Chisel._ +import diplomacy._ +import regmapper._ import scala.math.{min,max} class TLRegisterNode(address: AddressSet, concurrency: Int = 0, beatBytes: Int = 4, undefZero: Boolean = true) diff --git a/src/main/scala/uncore/tilelink2/RegisterRouterTest.scala b/src/main/scala/uncore/tilelink2/RegisterRouterTest.scala index 1bfa37a3..a7823ea3 100644 --- a/src/main/scala/uncore/tilelink2/RegisterRouterTest.scala +++ b/src/main/scala/uncore/tilelink2/RegisterRouterTest.scala @@ -3,8 +3,10 @@ package uncore.tilelink2 import Chisel._ +import diplomacy._ +import regmapper._ import unittest._ -import util.Pow2ClockDivider +import util.{Pow2ClockDivider} object LFSR16Seed { diff --git a/src/main/scala/uncore/tilelink2/SRAM.scala b/src/main/scala/uncore/tilelink2/SRAM.scala index f2191766..dd2d6872 100644 --- a/src/main/scala/uncore/tilelink2/SRAM.scala +++ b/src/main/scala/uncore/tilelink2/SRAM.scala @@ -3,6 +3,7 @@ package uncore.tilelink2 import Chisel._ +import diplomacy._ class TLRAM(address: AddressSet, executable: Boolean = true, beatBytes: Int = 4) extends LazyModule { diff --git a/src/main/scala/uncore/tilelink2/TLNodes.scala b/src/main/scala/uncore/tilelink2/TLNodes.scala deleted file mode 100644 index f908334c..00000000 --- a/src/main/scala/uncore/tilelink2/TLNodes.scala +++ /dev/null @@ -1,125 +0,0 @@ -// See LICENSE for license details. - -package uncore.tilelink2 - -import Chisel._ -import scala.collection.mutable.ListBuffer -import chisel3.internal.sourceinfo.SourceInfo - -object TLImp extends NodeImp[TLClientPortParameters, TLManagerPortParameters, TLEdgeOut, TLEdgeIn, TLBundle] -{ - def edgeO(pd: TLClientPortParameters, pu: TLManagerPortParameters): TLEdgeOut = new TLEdgeOut(pd, pu) - def edgeI(pd: TLClientPortParameters, pu: TLManagerPortParameters): TLEdgeIn = new TLEdgeIn(pd, pu) - def bundleO(eo: Seq[TLEdgeOut]): Vec[TLBundle] = { - require (!eo.isEmpty) - Vec(eo.size, TLBundle(eo.map(_.bundle).reduce(_.union(_)))) - } - def bundleI(ei: Seq[TLEdgeIn]): Vec[TLBundle] = { - require (!ei.isEmpty) - Vec(ei.size, TLBundle(ei.map(_.bundle).reduce(_.union(_)))).flip - } - - def colour = "#000000" // black - def connect(bo: => TLBundle, bi: => TLBundle, ei: => TLEdgeIn)(implicit sourceInfo: SourceInfo): (Option[LazyModule], () => Unit) = { - val monitor = LazyModule(new TLMonitor(() => new TLBundleSnoop(bo.params), () => ei, sourceInfo)) - (Some(monitor), () => { - bi <> bo - monitor.module.io.in := TLBundleSnoop(bo) - }) - } - - override def mixO(pd: TLClientPortParameters, node: OutwardNode[TLClientPortParameters, TLManagerPortParameters, TLBundle]): TLClientPortParameters = - pd.copy(clients = pd.clients.map { c => c.copy (nodePath = node +: c.nodePath) }) - override def mixI(pu: TLManagerPortParameters, node: InwardNode[TLClientPortParameters, TLManagerPortParameters, TLBundle]): TLManagerPortParameters = - pu.copy(managers = pu.managers.map { m => m.copy (nodePath = node +: m.nodePath) }) -} - -case class TLIdentityNode() extends IdentityNode(TLImp) -case class TLOutputNode() extends OutputNode(TLImp) -case class TLInputNode() extends InputNode(TLImp) - -case class TLClientNode(portParams: TLClientPortParameters, numPorts: Range.Inclusive = 1 to 1) - extends SourceNode(TLImp)(portParams, numPorts) -case class TLManagerNode(portParams: TLManagerPortParameters, numPorts: Range.Inclusive = 1 to 1) - extends SinkNode(TLImp)(portParams, numPorts) - -object TLClientNode -{ - def apply(params: TLClientParameters) = - new TLClientNode(TLClientPortParameters(Seq(params)), 1 to 1) -} - -object TLManagerNode -{ - def apply(beatBytes: Int, params: TLManagerParameters) = - new TLManagerNode(TLManagerPortParameters(Seq(params), beatBytes, 0), 1 to 1) -} - -case class TLAdapterNode( - clientFn: Seq[TLClientPortParameters] => TLClientPortParameters, - managerFn: Seq[TLManagerPortParameters] => TLManagerPortParameters, - numClientPorts: Range.Inclusive = 1 to 1, - numManagerPorts: Range.Inclusive = 1 to 1) - extends InteriorNode(TLImp)(clientFn, managerFn, numClientPorts, numManagerPorts) - -/** Synthesizeable unit tests */ -import unittest._ - -class TLInputNodeTest extends UnitTest(500000) { - class Acceptor extends LazyModule { - val node = TLInputNode() - val tlram = LazyModule(new TLRAM(AddressSet(0x54321000, 0xfff))) - tlram.node := node - - lazy val module = new LazyModuleImp(this) { - val io = new Bundle { - val in = node.bundleIn - } - } - } - - val fuzzer = LazyModule(new TLFuzzer(5000)) - LazyModule(new Acceptor).node := TLFragmenter(4, 64)(fuzzer.node) - - io.finished := Module(fuzzer.module).io.finished -} - -object TLAsyncImp extends NodeImp[TLAsyncClientPortParameters, TLAsyncManagerPortParameters, TLAsyncEdgeParameters, TLAsyncEdgeParameters, TLAsyncBundle] -{ - def edgeO(pd: TLAsyncClientPortParameters, pu: TLAsyncManagerPortParameters): TLAsyncEdgeParameters = TLAsyncEdgeParameters(pd, pu) - def edgeI(pd: TLAsyncClientPortParameters, pu: TLAsyncManagerPortParameters): TLAsyncEdgeParameters = TLAsyncEdgeParameters(pd, pu) - def bundleO(eo: Seq[TLAsyncEdgeParameters]): Vec[TLAsyncBundle] = { - require (eo.size == 1) - Vec(eo.size, new TLAsyncBundle(eo(0).bundle)) - } - def bundleI(ei: Seq[TLAsyncEdgeParameters]): Vec[TLAsyncBundle] = { - require (ei.size == 1) - Vec(ei.size, new TLAsyncBundle(ei(0).bundle)).flip - } - - def colour = "#ff0000" // red - def connect(bo: => TLAsyncBundle, bi: => TLAsyncBundle, ei: => TLAsyncEdgeParameters)(implicit sourceInfo: SourceInfo): (Option[LazyModule], () => Unit) = { - (None, () => { bi <> bo }) - } - - override def mixO(pd: TLAsyncClientPortParameters, node: OutwardNode[TLAsyncClientPortParameters, TLAsyncManagerPortParameters, TLAsyncBundle]): TLAsyncClientPortParameters = - pd.copy(base = pd.base.copy(clients = pd.base.clients.map { c => c.copy (nodePath = node +: c.nodePath) })) - override def mixI(pu: TLAsyncManagerPortParameters, node: InwardNode[TLAsyncClientPortParameters, TLAsyncManagerPortParameters, TLAsyncBundle]): TLAsyncManagerPortParameters = - pu.copy(base = pu.base.copy(managers = pu.base.managers.map { m => m.copy (nodePath = node +: m.nodePath) })) -} - -case class TLAsyncIdentityNode() extends IdentityNode(TLAsyncImp) -case class TLAsyncOutputNode() extends OutputNode(TLAsyncImp) -case class TLAsyncInputNode() extends InputNode(TLAsyncImp) - -case class TLAsyncSourceNode() extends MixedNode(TLImp, TLAsyncImp)( - dFn = { case (1, s) => s.map(TLAsyncClientPortParameters(_)) }, - uFn = { case (1, s) => s.map(_.base) }, - numPO = 1 to 1, - numPI = 1 to 1) - -case class TLAsyncSinkNode(depth: Int) extends MixedNode(TLAsyncImp, TLImp)( - dFn = { case (1, s) => s.map(_.base) }, - uFn = { case (1, s) => s.map(TLAsyncManagerPortParameters(depth, _)) }, - numPO = 1 to 1, - numPI = 1 to 1) diff --git a/src/main/scala/uncore/tilelink2/WidthWidget.scala b/src/main/scala/uncore/tilelink2/WidthWidget.scala index 2d819f32..f4699122 100644 --- a/src/main/scala/uncore/tilelink2/WidthWidget.scala +++ b/src/main/scala/uncore/tilelink2/WidthWidget.scala @@ -5,6 +5,7 @@ package uncore.tilelink2 import Chisel._ import chisel3.internal.sourceinfo.SourceInfo import chisel3.util.{Irrevocable, IrrevocableIO} +import diplomacy._ import scala.math.{min,max} // innBeatBytes => the new client-facing bus width diff --git a/src/main/scala/uncore/tilelink2/Xbar.scala b/src/main/scala/uncore/tilelink2/Xbar.scala index 3fb8c36f..6a6c8fa3 100644 --- a/src/main/scala/uncore/tilelink2/Xbar.scala +++ b/src/main/scala/uncore/tilelink2/Xbar.scala @@ -4,6 +4,7 @@ package uncore.tilelink2 import Chisel._ import chisel3.util.IrrevocableIO +import diplomacy._ class TLXbar(policy: TLArbiter.Policy = TLArbiter.lowestIndexFirst) extends LazyModule { diff --git a/src/main/scala/uncore/tilelink2/package.scala b/src/main/scala/uncore/tilelink2/package.scala index 303e9f74..afcb77a4 100644 --- a/src/main/scala/uncore/tilelink2/package.scala +++ b/src/main/scala/uncore/tilelink2/package.scala @@ -1,7 +1,7 @@ package uncore import Chisel._ -import chisel3.internal.sourceinfo.{SourceInfo, SourceLine, UnlocatableSourceInfo} +import diplomacy._ package object tilelink2 { @@ -17,9 +17,4 @@ package object tilelink2 if (s >= w) x else helper(s+s, x | (x << s)(w-1,0)) helper(1, x) } - - def sourceLine(sourceInfo: SourceInfo, prefix: String = " (", suffix: String = ")") = sourceInfo match { - case SourceLine(filename, line, col) => s"$prefix$filename:$line:$col$suffix" - case _ => "" - } } diff --git a/src/main/scala/util/GeneratorUtils.scala b/src/main/scala/util/GeneratorUtils.scala index cca9842a..5ae69265 100644 --- a/src/main/scala/util/GeneratorUtils.scala +++ b/src/main/scala/util/GeneratorUtils.scala @@ -4,8 +4,8 @@ package util import Chisel._ import cde._ +import diplomacy.LazyModule import java.io.{File, FileWriter} -import uncore.tilelink2.LazyModule /** Representation of the information this Generator needs to collect from external sources. */ case class ParsedInputNames( diff --git a/src/main/scala/util/GenericParameterizedBundle.scala b/src/main/scala/util/GenericParameterizedBundle.scala new file mode 100644 index 00000000..57298703 --- /dev/null +++ b/src/main/scala/util/GenericParameterizedBundle.scala @@ -0,0 +1,21 @@ +// See LICENSE for license details. + +package util + +import Chisel._ + +abstract class GenericParameterizedBundle[T <: Object](val params: T) extends Bundle +{ + override def cloneType = { + try { + this.getClass.getConstructors.head.newInstance(params).asInstanceOf[this.type] + } catch { + case e: java.lang.IllegalArgumentException => + throw new Exception("Unable to use GenericParameterizedBundle.cloneType on " + + this.getClass + ", probably because " + this.getClass + + "() takes more than one argument. Consider overriding " + + "cloneType() on " + this.getClass, e) + } + } +} +