1
0

Merge pull request #364 from ucb-bar/tl2-async-nodes

Tl2 async nodes
This commit is contained in:
Wesley W. Terpstra 2016-09-29 18:18:31 -07:00 committed by GitHub
commit 8730887baa
10 changed files with 346 additions and 130 deletions

View File

@ -3,7 +3,8 @@
package uncore.tilelink2 package uncore.tilelink2
import Chisel._ import Chisel._
import chisel3.util.{Irrevocable, IrrevocableIO} import chisel3.util.{Irrevocable, IrrevocableIO, ReadyValidIO}
import util.{AsyncQueueSource, AsyncQueueSink}
abstract class GenericParameterizedBundle[T <: Object](val params: T) extends Bundle abstract class GenericParameterizedBundle[T <: Object](val params: T) extends Bundle
{ {
@ -190,7 +191,7 @@ object TLBundle
def apply(params: TLBundleParameters) = new TLBundle(params) def apply(params: TLBundleParameters) = new TLBundle(params)
} }
class IrrevocableSnoop[+T <: Data](gen: T) extends Bundle final class IrrevocableSnoop[+T <: Data](gen: T) extends Bundle
{ {
val ready = Bool() val ready = Bool()
val valid = Bool() val valid = Bool()
@ -232,3 +233,53 @@ object TLBundleSnoop
out out
} }
} }
final class AsyncBundle[T <: Data](val depth: Int, gen: T) extends Bundle
{
require (isPow2(depth))
val ridx = UInt(width = log2Up(depth)+1).flip
val widx = UInt(width = log2Up(depth)+1)
val mem = Vec(depth, gen)
override def cloneType: this.type = new AsyncBundle(depth, gen).asInstanceOf[this.type]
}
object FromAsyncBundle
{
def apply[T <: Data](x: AsyncBundle[T], sync: Int = 3): IrrevocableIO[T] = {
val sink = Module(new AsyncQueueSink(x.mem(0), x.depth, sync))
x.ridx := sink.io.ridx
sink.io.widx := x.widx
sink.io.mem := x.mem
val out = Wire(Irrevocable(x.mem(0)))
out.valid := sink.io.deq.valid
out.bits := sink.io.deq.bits
sink.io.deq.ready := out.ready
out
}
}
object ToAsyncBundle
{
def apply[T <: Data](x: ReadyValidIO[T], depth: Int = 8, sync: Int = 3): AsyncBundle[T] = {
val source = Module(new AsyncQueueSource(x.bits, depth, sync))
source.io.enq.valid := x.valid
source.io.enq.bits := x.bits
x.ready := source.io.enq.ready
val out = Wire(new AsyncBundle(depth, x.bits))
source.io.ridx := out.ridx
out.mem := source.io.mem
out.widx := source.io.widx
out
}
}
class TLAsyncBundleBase(params: TLAsyncBundleParameters) extends GenericParameterizedBundle(params)
class TLAsyncBundle(params: TLAsyncBundleParameters) extends TLAsyncBundleBase(params)
{
val a = new AsyncBundle(params.depth, new TLBundleA(params.base))
val b = new AsyncBundle(params.depth, new TLBundleB(params.base)).flip
val c = new AsyncBundle(params.depth, new TLBundleC(params.base))
val d = new AsyncBundle(params.depth, new TLBundleD(params.base)).flip
val e = new AsyncBundle(params.depth, new TLBundleE(params.base))
}

View File

@ -6,33 +6,63 @@ import Chisel._
import chisel3.internal.sourceinfo.SourceInfo import chisel3.internal.sourceinfo.SourceInfo
import util._ import util._
class TLAsyncCrossing(depth: Int = 8, sync: Int = 3) extends LazyModule class TLAsyncCrossingSource(sync: Int = 3) extends LazyModule
{ {
val node = TLIdentityNode() val node = TLAsyncSourceNode()
lazy val module = new LazyModuleImp(this) { lazy val module = new LazyModuleImp(this) {
val io = new Bundle { val io = new Bundle {
val in = node.bundleIn val in = node.bundleIn
val in_clock = Clock(INPUT)
val in_reset = Bool(INPUT)
val out = node.bundleOut val out = node.bundleOut
val out_clock = Clock(INPUT)
val out_reset = Bool(INPUT)
} }
// Transfer all TL2 bundles from/to the same domains
((io.in zip io.out) zip (node.edgesIn zip node.edgesOut)) foreach { case ((in, out), (edgeIn, edgeOut)) => ((io.in zip io.out) zip (node.edgesIn zip node.edgesOut)) foreach { case ((in, out), (edgeIn, edgeOut)) =>
out.a <> AsyncIrrevocableCrossing(io.in_clock, io.in_reset, in.a, io.out_clock, io.out_reset, depth, sync) val bce = edgeIn.manager.anySupportAcquire && edgeIn.client.anySupportProbe
in.d <> AsyncIrrevocableCrossing(io.out_clock, io.out_reset, out.d, io.in_clock, io.in_reset, depth, sync) val depth = edgeOut.manager.depth
if (edgeOut.manager.anySupportAcquire && edgeOut.client.anySupportProbe) { out.a := ToAsyncBundle(in.a, depth, sync)
in.b <> AsyncIrrevocableCrossing(io.out_clock, io.out_reset, out.b, io.in_clock, io.in_reset, depth, sync) in.d := FromAsyncBundle(out.d, sync)
out.c <> AsyncIrrevocableCrossing(io.in_clock, io.in_reset, in.c, io.out_clock, io.out_reset, depth, sync)
out.e <> AsyncIrrevocableCrossing(io.in_clock, io.in_reset, in.e, io.out_clock, io.out_reset, depth, sync) if (bce) {
in.b := FromAsyncBundle(out.b, sync)
out.c := ToAsyncBundle(in.c, depth, sync)
out.e := ToAsyncBundle(in.e, depth, sync)
} else { } else {
in.b.valid := Bool(false) in.b.valid := Bool(false)
in.c.ready := Bool(true) in.c.ready := Bool(true)
in.e.ready := Bool(true) in.e.ready := Bool(true)
out.b.ridx := UInt(0)
out.c.widx := UInt(0)
out.e.widx := UInt(0)
}
}
}
}
class TLAsyncCrossingSink(depth: Int = 8, sync: Int = 3) extends LazyModule
{
val node = TLAsyncSinkNode(depth)
lazy val module = new LazyModuleImp(this) {
val io = new Bundle {
val in = node.bundleIn
val out = node.bundleOut
}
((io.in zip io.out) zip (node.edgesIn zip node.edgesOut)) foreach { case ((in, out), (edgeIn, edgeOut)) =>
val bce = edgeOut.manager.anySupportAcquire && edgeOut.client.anySupportProbe
out.a := FromAsyncBundle(in.a, sync)
in.d := ToAsyncBundle(out.d, depth, sync)
if (bce) {
in.b := ToAsyncBundle(out.b, depth, sync)
out.c := FromAsyncBundle(in.c, sync)
out.e := FromAsyncBundle(in.e, sync)
} else {
in.b.widx := UInt(0)
in.c.ridx := UInt(0)
in.e.ridx := UInt(0)
out.b.ready := Bool(true) out.b.ready := Bool(true)
out.c.valid := Bool(false) out.c.valid := Bool(false)
out.e.valid := Bool(false) out.e.valid := Bool(false)
@ -41,6 +71,44 @@ class TLAsyncCrossing(depth: Int = 8, sync: Int = 3) extends LazyModule
} }
} }
class TLAsyncCrossing(depth: Int = 8, sync: Int = 3) extends LazyModule
{
val nodeIn = TLInputNode()
val nodeOut = TLOutputNode()
val source = LazyModule(new TLAsyncCrossingSource(sync))
val sink = LazyModule(new TLAsyncCrossingSink(depth, sync))
val _ = (sink.node := source.node) // no monitor
val in = (source.node := nodeIn)
val out = (nodeOut := sink.node)
lazy val module = new LazyModuleImp(this) {
val io = new Bundle {
val in = nodeIn.bundleIn
val in_clock = Clock(INPUT)
val in_reset = Bool(INPUT)
val out = nodeOut.bundleOut
val out_clock = Clock(INPUT)
val out_reset = Bool(INPUT)
}
source.module.clock := io.in_clock
source.module.reset := io.in_reset
in.foreach { lm =>
lm.module.clock := io.in_clock
lm.module.reset := io.in_reset
}
sink.module.clock := io.out_clock
sink.module.reset := io.out_reset
out.foreach { lm =>
lm.module.clock := io.out_clock
lm.module.reset := io.out_reset
}
}
}
/** Synthesizeable unit tests */ /** Synthesizeable unit tests */
import unittest._ import unittest._
@ -51,8 +119,8 @@ class TLRAMCrossing extends LazyModule {
val cross = LazyModule(new TLAsyncCrossing) val cross = LazyModule(new TLAsyncCrossing)
model.node := fuzz.node model.node := fuzz.node
cross.node := TLFragmenter(4, 256)(model.node) cross.nodeIn := TLFragmenter(4, 256)(model.node)
val monitor = (ram.node := cross.node) val monitor = (ram.node := cross.nodeOut)
lazy val module = new LazyModuleImp(this) with HasUnitTestIO { lazy val module = new LazyModuleImp(this) with HasUnitTestIO {
io.finished := fuzz.module.io.finished io.finished := fuzz.module.io.finished

View File

@ -224,8 +224,8 @@ class TLFuzzRAM extends LazyModule
xbar2.node := TLAtomicAutomata()(model.node) xbar2.node := TLAtomicAutomata()(model.node)
ram2.node := TLFragmenter(16, 256)(xbar2.node) ram2.node := TLFragmenter(16, 256)(xbar2.node)
xbar.node := TLWidthWidget(16)(TLHintHandler()(xbar2.node)) xbar.node := TLWidthWidget(16)(TLHintHandler()(xbar2.node))
cross.node := TLFragmenter(4, 256)(TLBuffer()(xbar.node)) cross.nodeIn := TLFragmenter(4, 256)(TLBuffer()(xbar.node))
val monitor = (ram.node := cross.node) val monitor = (ram.node := cross.nodeOut)
gpio.node := TLFragmenter(4, 32)(TLBuffer()(xbar.node)) gpio.node := TLFragmenter(4, 32)(TLBuffer()(xbar.node))
lazy val module = new LazyModuleImp(this) with HasUnitTestIO { lazy val module = new LazyModuleImp(this) with HasUnitTestIO {

View File

@ -23,13 +23,13 @@ object IntRange
case class IntSourceParameters( case class IntSourceParameters(
range: IntRange, range: IntRange,
nodePath: Seq[IntBaseNode] = Seq()) nodePath: Seq[BaseNode] = Seq())
{ {
val name = nodePath.lastOption.map(_.lazyModule.name).getOrElse("disconnected") val name = nodePath.lastOption.map(_.lazyModule.name).getOrElse("disconnected")
} }
case class IntSinkParameters( case class IntSinkParameters(
nodePath: Seq[IntBaseNode] = Seq()) nodePath: Seq[BaseNode] = Seq())
{ {
val name = nodePath.lastOption.map(_.lazyModule.name).getOrElse("disconnected") val name = nodePath.lastOption.map(_.lazyModule.name).getOrElse("disconnected")
} }
@ -49,8 +49,8 @@ case class IntEdge(source: IntSourcePortParameters, sink: IntSinkPortParameters)
object IntImp extends NodeImp[IntSourcePortParameters, IntSinkPortParameters, IntEdge, IntEdge, Vec[Bool]] object IntImp extends NodeImp[IntSourcePortParameters, IntSinkPortParameters, IntEdge, IntEdge, Vec[Bool]]
{ {
def edgeO(po: IntSourcePortParameters, pi: IntSinkPortParameters): IntEdge = IntEdge(po, pi) def edgeO(pd: IntSourcePortParameters, pu: IntSinkPortParameters): IntEdge = IntEdge(pd, pu)
def edgeI(po: IntSourcePortParameters, pi: IntSinkPortParameters): IntEdge = IntEdge(po, pi) def edgeI(pd: IntSourcePortParameters, pu: IntSinkPortParameters): IntEdge = IntEdge(pd, pu)
def bundleO(eo: Seq[IntEdge]): Vec[Vec[Bool]] = { def bundleO(eo: Seq[IntEdge]): Vec[Vec[Bool]] = {
if (eo.isEmpty) Vec(0, Vec(0, Bool())) else if (eo.isEmpty) Vec(0, Vec(0, Bool())) else
Vec(eo.size, Vec(eo.map(_.source.num).max, Bool())) Vec(eo.size, Vec(eo.map(_.source.num).max, Bool()))
@ -60,18 +60,17 @@ object IntImp extends NodeImp[IntSourcePortParameters, IntSinkPortParameters, In
Vec(ei.size, Vec(ei.map(_.source.num).max, Bool())).flip Vec(ei.size, Vec(ei.map(_.source.num).max, Bool())).flip
} }
def connect(bo: => Vec[Bool], eo: => IntEdge, bi: => Vec[Bool], ei: => IntEdge)(implicit sourceInfo: SourceInfo): (Option[LazyModule], () => Unit) = { def connect(bo: => Vec[Bool], bi: => Vec[Bool], ei: => IntEdge)(implicit sourceInfo: SourceInfo): (Option[LazyModule], () => Unit) = {
(None, () => { (None, () => {
require (eo == ei)
// Cannot use bulk connect, because the widths could differ // Cannot use bulk connect, because the widths could differ
(bo zip bi) foreach { case (o, i) => i := o } (bo zip bi) foreach { case (o, i) => i := o }
}) })
} }
override def mixO(po: IntSourcePortParameters, node: IntBaseNode): IntSourcePortParameters = override def mixO(pd: IntSourcePortParameters, node: OutwardNode[IntSourcePortParameters, IntSinkPortParameters, Vec[Bool]]): IntSourcePortParameters =
po.copy(sources = po.sources.map { s => s.copy (nodePath = node +: s.nodePath) }) pd.copy(sources = pd.sources.map { s => s.copy (nodePath = node +: s.nodePath) })
override def mixI(pi: IntSinkPortParameters, node: IntBaseNode): IntSinkPortParameters = override def mixI(pu: IntSinkPortParameters, node: InwardNode[IntSourcePortParameters, IntSinkPortParameters, Vec[Bool]]): IntSinkPortParameters =
pi.copy(sinks = pi.sinks.map { s => s.copy (nodePath = node +: s.nodePath) }) pu.copy(sinks = pu.sinks.map { s => s.copy (nodePath = node +: s.nodePath) })
} }
case class IntIdentityNode() extends IdentityNode(IntImp) case class IntIdentityNode() extends IdentityNode(IntImp)

View File

@ -9,18 +9,13 @@ abstract class LazyModule
{ {
protected[tilelink2] var bindings = List[() => Unit]() protected[tilelink2] var bindings = List[() => Unit]()
protected[tilelink2] var children = List[LazyModule]() protected[tilelink2] var children = List[LazyModule]()
protected[tilelink2] var nodes = List[RootNode]() protected[tilelink2] var nodes = List[BaseNode]()
protected[tilelink2] var info: SourceInfo = UnlocatableSourceInfo protected[tilelink2] var info: SourceInfo = UnlocatableSourceInfo
protected[tilelink2] val parent = LazyModule.stack.headOption protected[tilelink2] val parent = LazyModule.stack.headOption
LazyModule.stack = this :: LazyModule.stack LazyModule.stack = this :: LazyModule.stack
parent.foreach(p => p.children = this :: p.children) parent.foreach(p => p.children = this :: p.children)
// Use as: connect(source -> sink, source2 -> sink2, ...)
def connect[PO, PI, EO, EI, B <: Data](edges: (BaseNode[PO, PI, EO, EI, B], BaseNode[PO, PI, EO, EI, B])*)(implicit sourceInfo: SourceInfo) = {
edges.foreach { case (source, sink) => sink := source }
}
def name = getClass.getName.split('.').last def name = getClass.getName.split('.').last
def line = sourceLine(info) def line = sourceLine(info)

View File

@ -6,23 +6,33 @@ import Chisel._
import scala.collection.mutable.ListBuffer import scala.collection.mutable.ListBuffer
import chisel3.internal.sourceinfo.SourceInfo import chisel3.internal.sourceinfo.SourceInfo
// PI = PortInputParameters // DI = Downwards flowing Parameters received on the inner side of the node
// PO = PortOutputParameters // UI = Upwards flowing Parameters generated by the inner side of the node
// EI = EdgeInput // EI = Edge Parameters describing a connection on the inner side of the node
// EO = EdgeOutput // BI = Bundle type used when connecting to the inner side of the node
abstract class NodeImp[PO, PI, EO, EI, B <: Data] trait InwardNodeImp[DI, UI, EI, BI <: Data]
{ {
def edgeO(po: PO, pi: PI): EO def edgeI(pd: DI, pu: UI): EI
def edgeI(po: PO, pi: PI): EI def bundleI(ei: Seq[EI]): Vec[BI]
def bundleO(eo: Seq[EO]): Vec[B] def mixI(pu: UI, node: InwardNode[DI, UI, BI]): UI = pu
def bundleI(ei: Seq[EI]): Vec[B] def connect(bo: => BI, bi: => BI, e: => EI)(implicit sourceInfo: SourceInfo): (Option[LazyModule], () => Unit)
def connect(bo: => B, eo: => EO, bi: => B, ei: => EI)(implicit sourceInfo: SourceInfo): (Option[LazyModule], () => Unit)
// If you want to track parameters as they flow through nodes, overload these:
def mixO(po: PO, node: BaseNode[PO, PI, EO, EI, B]): PO = po
def mixI(pi: PI, node: BaseNode[PO, PI, EO, EI, B]): PI = pi
} }
abstract class RootNode // 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! // You cannot create a Node outside a LazyModule!
require (!LazyModule.stack.isEmpty) require (!LazyModule.stack.isEmpty)
@ -35,108 +45,146 @@ abstract class RootNode
def colour = "blue" def colour = "blue"
def omitGraphML = outputs.isEmpty && inputs.isEmpty def omitGraphML = outputs.isEmpty && inputs.isEmpty
protected[tilelink2] def outputs: Seq[RootNode] protected[tilelink2] def outputs: Seq[BaseNode]
protected[tilelink2] def inputs: Seq[RootNode] protected[tilelink2] def inputs: Seq[BaseNode]
} }
class BaseNode[PO, PI, EO, EI, B <: Data](imp: NodeImp[PO, PI, EO, EI, B])( trait InwardNode[DI, UI, BI <: Data] extends BaseNode
private val oFn: (Int, Seq[PO]) => Seq[PO],
private val iFn: (Int, Seq[PI]) => Seq[PI],
private val numPO: Range.Inclusive,
private val numPI: Range.Inclusive) extends RootNode
{ {
// At least 0 ports must be supported protected[tilelink2] val numPI: Range.Inclusive
require (!numPO.isEmpty, s"No number of outputs would be acceptable to ${name}${lazyModule.line}")
require (!numPI.isEmpty, s"No number of inputs would be acceptable to ${name}${lazyModule.line}") require (!numPI.isEmpty, s"No number of inputs would be acceptable to ${name}${lazyModule.line}")
require (numPO.start >= 0, s"${name} accepts a negative number of outputs${lazyModule.line}")
require (numPI.start >= 0, s"${name} accepts a negative number of inputs${lazyModule.line}") require (numPI.start >= 0, s"${name} accepts a negative number of inputs${lazyModule.line}")
val noOs = numPO.size == 1 && numPO.contains(0) private val accPI = ListBuffer[(Int, OutwardNode[DI, UI, BI])]()
val noIs = numPI.size == 1 && numPI.contains(0)
private val accPO = ListBuffer[(Int, BaseNode[PO, PI, EO, EI, B])]()
private val accPI = ListBuffer[(Int, BaseNode[PO, PI, EO, EI, B])]()
private var oRealized = false
private var iRealized = false private var iRealized = false
private def reqO() = require(numPO.contains(accPO.size), s"${name} has ${accPO.size} outputs, expected ${numPO}${lazyModule.line}") 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))
}
private def reqI() = require(numPI.contains(accPI.size), s"${name} has ${accPI.size} inputs, expected ${numPI}${lazyModule.line}") private def reqI() = require(numPI.contains(accPI.size), s"${name} has ${accPI.size} inputs, expected ${numPI}${lazyModule.line}")
protected 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 iPorts = { iRealized = true; reqI(); accPI.result() }
private lazy val oPorts = { oRealized = true; reqO(); accPO.result() } protected[tilelink2] val iParams: Seq[UI]
private lazy val iPorts = { iRealized = true; reqI(); accPI.result() } 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))
}
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]
}
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 outputs = oPorts.map(_._2) protected[tilelink2] def outputs = oPorts.map(_._2)
protected[tilelink2] def inputs = iPorts.map(_._2) protected[tilelink2] def inputs = iPorts.map(_._2)
private lazy val oParams : Seq[PO] = { private def reqE(o: Int, i: Int) = require(i == o, s"${name} has ${i} inputs and ${o} outputs; they must match${lazyModule.line}")
val o = oFn(oPorts.size, iPorts.map{ case (i, n) => n.oParams(i) }) 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) reqE(oPorts.size, o.size)
o.map(imp.mixO(_, this)) o.map(outer.mixO(_, this))
} }
private lazy val iParams : Seq[PI] = { protected[tilelink2] lazy val iParams: Seq[UI] = {
val i = iFn(iPorts.size, oPorts.map{ case (o, n) => n.iParams(o) }) val i = uFn(iPorts.size, oPorts.map { case (o, n) => n.iParams(o) })
reqE(i.size, iPorts.size) reqE(i.size, iPorts.size)
i.map(imp.mixI(_, this)) i.map(inner.mixI(_, this))
} }
lazy val edgesOut = (oPorts zip oParams).map { case ((i, n), o) => imp.edgeO(o, n.iParams(i)) } 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) => imp.edgeI(n.oParams(o), i) } lazy val edgesIn = (iPorts zip iParams).map { case ((o, n), i) => inner.edgeI(n.oParams(o), i) }
lazy val bundleOut = imp.bundleO(edgesOut) lazy val bundleOut = outer.bundleO(edgesOut)
lazy val bundleIn = imp.bundleI(edgesIn) lazy val bundleIn = inner.bundleI(edgesIn)
def connectOut = bundleOut def oConnect = bundleOut
def connectIn = bundleIn def iConnect = bundleIn
def := (y: BaseNode[PO, PI, EO, EI, B])(implicit sourceInfo: SourceInfo): Option[LazyModule] = { // 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 x = this // x := y
val info = sourceLine(sourceInfo, " at ", "") val info = sourceLine(sourceInfo, " at ", "")
require (!LazyModule.stack.isEmpty, s"${y.name} cannot be connected to ${x.name} outside of LazyModule scope" + info) require (!LazyModule.stack.isEmpty, s"${y.name} cannot be connected to ${x.name} outside of LazyModule scope" + info)
require (!y.noOs, s"${y.name}${y.lazyModule.line} was incorrectly connected as a source" + info) val i = x.iPushed
require (!y.oRealized, s"${y.name}${y.lazyModule.line} was incorrectly connected as a source after it's .module was used" + info) val o = y.oPushed
require (!x.noIs, s"${x.name}${x.lazyModule.line} was incorrectly connected as a sink" + info) y.oPush(i, x)
require (!x.iRealized, s"${x.name}${x.lazyModule.line} was incorrectly connected as a sink after it's .module was used" + info) x.iPush(o, y)
val i = x.accPI.size val (out, binding) = inner.connect(y.oConnect(o), x.iConnect(i), x.edgesIn(i))
val o = y.accPO.size
y.accPO += ((i, x))
x.accPI += ((o, y))
val (out, binding) = imp.connect(y.connectOut(o), y.edgesOut(o), x.connectIn(i), x.edgesIn(i))
LazyModule.stack.head.bindings = binding :: LazyModule.stack.head.bindings LazyModule.stack.head.bindings = binding :: LazyModule.stack.head.bindings
out 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]) class IdentityNode[PO, PI, EO, EI, B <: Data](imp: NodeImp[PO, PI, EO, EI, B])
extends BaseNode(imp)({case (_, s) => s}, {case (_, s) => s}, 0 to 999, 0 to 999) 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) class OutputNode[PO, PI, EO, EI, B <: Data](imp: NodeImp[PO, PI, EO, EI, B]) extends IdentityNode(imp)
{ {
override def connectOut = bundleOut override def oConnect = bundleOut
override def connectIn = bundleOut override def iConnect = bundleOut
} }
class InputNode[PO, PI, EO, EI, B <: Data](imp: NodeImp[PO, PI, EO, EI, B]) extends IdentityNode(imp) class InputNode[PO, PI, EO, EI, B <: Data](imp: NodeImp[PO, PI, EO, EI, B]) extends IdentityNode(imp)
{ {
override def connectOut = bundleIn override def oConnect = bundleIn
override def connectIn = 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) class SourceNode[PO, PI, EO, EI, B <: Data](imp: NodeImp[PO, PI, EO, EI, B])(po: PO, num: Range.Inclusive = 1 to 1)
extends BaseNode(imp)({case (n, Seq()) => Seq.fill(n)(po)}, {case (0, _) => Seq()}, num, 0 to 0) 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}") 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) class SinkNode[PO, PI, EO, EI, B <: Data](imp: NodeImp[PO, PI, EO, EI, B])(pi: PI, num: Range.Inclusive = 1 to 1)
extends BaseNode(imp)({case (0, _) => Seq()}, {case (n, Seq()) => Seq.fill(n)(pi)}, 0 to 0, num) 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}") 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]) 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) (oFn: Seq[PO] => PO, iFn: Seq[PI] => PI, numPO: Range.Inclusive, numPI: Range.Inclusive)
extends BaseNode(imp)({case (n,s) => Seq.fill(n)(oFn(s))}, {case (n,s) => Seq.fill(n)(iFn(s))}, numPO, numPI) 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 (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}") require (numPI.end >= 1, s"${name} is an adapter which does not accept inputs${lazyModule.line}")

View File

@ -149,7 +149,7 @@ case class TLManagerParameters(
sinkId: IdRange = IdRange(0, 1), sinkId: IdRange = IdRange(0, 1),
regionType: RegionType.T = RegionType.GET_EFFECTS, regionType: RegionType.T = RegionType.GET_EFFECTS,
executable: Boolean = false, // processor can execute from this memory executable: Boolean = false, // processor can execute from this memory
nodePath: Seq[TLBaseNode] = Seq(), nodePath: Seq[BaseNode] = Seq(),
// Supports both Acquire+Release+Finish of these sizes // Supports both Acquire+Release+Finish of these sizes
supportsAcquire: TransferSizes = TransferSizes.none, supportsAcquire: TransferSizes = TransferSizes.none,
supportsArithmetic: TransferSizes = TransferSizes.none, supportsArithmetic: TransferSizes = TransferSizes.none,
@ -293,7 +293,7 @@ case class TLManagerPortParameters(
case class TLClientParameters( case class TLClientParameters(
sourceId: IdRange = IdRange(0,1), sourceId: IdRange = IdRange(0,1),
nodePath: Seq[TLBaseNode] = Seq(), nodePath: Seq[BaseNode] = Seq(),
// Supports both Probe+Grant of these sizes // Supports both Probe+Grant of these sizes
supportsProbe: TransferSizes = TransferSizes.none, supportsProbe: TransferSizes = TransferSizes.none,
supportsArithmetic: TransferSizes = TransferSizes.none, supportsArithmetic: TransferSizes = TransferSizes.none,
@ -401,6 +401,17 @@ case class TLBundleParameters(
max(sizeBits, x.sizeBits)) max(sizeBits, x.sizeBits))
} }
object TLBundleParameters
{
def apply(client: TLClientPortParameters, manager: TLManagerPortParameters) =
new TLBundleParameters(
addrHiBits = log2Up(manager.maxAddress + 1) - log2Ceil(manager.beatBytes),
dataBits = manager.beatBytes * 8,
sourceBits = log2Up(client.endSourceId),
sinkBits = log2Up(manager.endSinkId),
sizeBits = log2Up(log2Ceil(max(client.maxTransfer, manager.maxTransfer))+1))
}
case class TLEdgeParameters( case class TLEdgeParameters(
client: TLClientPortParameters, client: TLClientPortParameters,
manager: TLManagerPortParameters) manager: TLManagerPortParameters)
@ -411,10 +422,13 @@ case class TLEdgeParameters(
// Sanity check the link... // Sanity check the link...
require (maxTransfer >= manager.beatBytes) require (maxTransfer >= manager.beatBytes)
val bundle = TLBundleParameters( val bundle = TLBundleParameters(client, manager)
addrHiBits = log2Up(manager.maxAddress + 1) - log2Ceil(manager.beatBytes), }
dataBits = manager.beatBytes * 8,
sourceBits = log2Up(client.endSourceId), case class TLAsyncManagerPortParameters(depth: Int, base: TLManagerPortParameters) { require (isPow2(depth)) }
sinkBits = log2Up(manager.endSinkId), case class TLAsyncClientPortParameters(base: TLClientPortParameters)
sizeBits = log2Up(maxLgSize+1)) case class TLAsyncBundleParameters(depth: Int, base: TLBundleParameters) { require (isPow2(depth)) }
case class TLAsyncEdgeParameters(client: TLAsyncClientPortParameters, manager: TLAsyncManagerPortParameters)
{
val bundle = TLAsyncBundleParameters(manager.depth, TLBundleParameters(client.base, manager.base))
} }

View File

@ -8,8 +8,8 @@ import chisel3.internal.sourceinfo.SourceInfo
object TLImp extends NodeImp[TLClientPortParameters, TLManagerPortParameters, TLEdgeOut, TLEdgeIn, TLBundle] object TLImp extends NodeImp[TLClientPortParameters, TLManagerPortParameters, TLEdgeOut, TLEdgeIn, TLBundle]
{ {
def edgeO(po: TLClientPortParameters, pi: TLManagerPortParameters): TLEdgeOut = new TLEdgeOut(po, pi) def edgeO(pd: TLClientPortParameters, pu: TLManagerPortParameters): TLEdgeOut = new TLEdgeOut(pd, pu)
def edgeI(po: TLClientPortParameters, pi: TLManagerPortParameters): TLEdgeIn = new TLEdgeIn(po, pi) def edgeI(pd: TLClientPortParameters, pu: TLManagerPortParameters): TLEdgeIn = new TLEdgeIn(pd, pu)
def bundleO(eo: Seq[TLEdgeOut]): Vec[TLBundle] = { def bundleO(eo: Seq[TLEdgeOut]): Vec[TLBundle] = {
require (!eo.isEmpty) require (!eo.isEmpty)
Vec(eo.size, TLBundle(eo.map(_.bundle).reduce(_.union(_)))) Vec(eo.size, TLBundle(eo.map(_.bundle).reduce(_.union(_))))
@ -19,19 +19,18 @@ object TLImp extends NodeImp[TLClientPortParameters, TLManagerPortParameters, TL
Vec(ei.size, TLBundle(ei.map(_.bundle).reduce(_.union(_)))).flip Vec(ei.size, TLBundle(ei.map(_.bundle).reduce(_.union(_)))).flip
} }
def connect(bo: => TLBundle, eo: => TLEdgeOut, bi: => TLBundle, ei: => TLEdgeIn)(implicit sourceInfo: SourceInfo): (Option[LazyModule], () => Unit) = { def connect(bo: => TLBundle, bi: => TLBundle, ei: => TLEdgeIn)(implicit sourceInfo: SourceInfo): (Option[LazyModule], () => Unit) = {
val monitor = LazyModule(new TLMonitor(() => new TLBundleSnoop(bo.params), () => eo, sourceInfo)) val monitor = LazyModule(new TLMonitor(() => new TLBundleSnoop(bo.params), () => ei, sourceInfo))
(Some(monitor), () => { (Some(monitor), () => {
require (eo.asInstanceOf[TLEdgeParameters] == ei.asInstanceOf[TLEdgeParameters])
bi <> bo bi <> bo
monitor.module.io.in := TLBundleSnoop(bo) monitor.module.io.in := TLBundleSnoop(bo)
}) })
} }
override def mixO(po: TLClientPortParameters, node: TLBaseNode): TLClientPortParameters = override def mixO(pd: TLClientPortParameters, node: OutwardNode[TLClientPortParameters, TLManagerPortParameters, TLBundle]): TLClientPortParameters =
po.copy(clients = po.clients.map { c => c.copy (nodePath = node +: c.nodePath) }) pd.copy(clients = pd.clients.map { c => c.copy (nodePath = node +: c.nodePath) })
override def mixI(pi: TLManagerPortParameters, node: TLBaseNode): TLManagerPortParameters = override def mixI(pu: TLManagerPortParameters, node: InwardNode[TLClientPortParameters, TLManagerPortParameters, TLBundle]): TLManagerPortParameters =
pi.copy(managers = pi.managers.map { m => m.copy (nodePath = node +: m.nodePath) }) pu.copy(managers = pu.managers.map { m => m.copy (nodePath = node +: m.nodePath) })
} }
case class TLIdentityNode() extends IdentityNode(TLImp) case class TLIdentityNode() extends IdentityNode(TLImp)
@ -72,3 +71,42 @@ class TLInputNodeTest extends UnitTest(500000) {
io.finished := Module(fuzzer.module).io.finished 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 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)

View File

@ -5,8 +5,8 @@ import chisel3.internal.sourceinfo.{SourceInfo, SourceLine, UnlocatableSourceInf
package object tilelink2 package object tilelink2
{ {
type TLBaseNode = BaseNode[TLClientPortParameters, TLManagerPortParameters, TLEdgeOut, TLEdgeIn, TLBundle] type TLBaseNode = SimpleNode[TLClientPortParameters, TLManagerPortParameters, TLEdgeOut, TLEdgeIn, TLBundle]
type IntBaseNode = BaseNode[IntSourcePortParameters, IntSinkPortParameters, IntEdge, IntEdge, Vec[Bool]] type IntBaseNode = SimpleNode[IntSourcePortParameters, IntSinkPortParameters, IntEdge, IntEdge, Vec[Bool]]
def OH1ToUInt(x: UInt) = OHToUInt((x << 1 | UInt(1)) ^ x) def OH1ToUInt(x: UInt) = OHToUInt((x << 1 | UInt(1)) ^ x)
def UIntToOH1(x: UInt, width: Int) = ~(SInt(-1, width=width).asUInt << x)(width-1, 0) def UIntToOH1(x: UInt, width: Int) = ~(SInt(-1, width=width).asUInt << x)(width-1, 0)
def trailingZeros(x: Int) = if (x > 0) Some(log2Ceil(x & -x)) else None def trailingZeros(x: Int) = if (x > 0) Some(log2Ceil(x & -x)) else None

View File

@ -25,8 +25,7 @@ object AsyncGrayCounter {
} }
} }
class AsyncQueueSource[T <: Data](gen: T, depth: Int, sync: Int, clockIn: Clock, resetIn: Bool) class AsyncQueueSource[T <: Data](gen: T, depth: Int, sync: Int) extends Module {
extends Module(_clock = clockIn, _reset = resetIn) {
val bits = log2Ceil(depth) val bits = log2Ceil(depth)
val io = new Bundle { val io = new Bundle {
// These come from the source domain // These come from the source domain
@ -53,8 +52,7 @@ class AsyncQueueSource[T <: Data](gen: T, depth: Int, sync: Int, clockIn: Clock,
io.mem := mem io.mem := mem
} }
class AsyncQueueSink[T <: Data](gen: T, depth: Int, sync: Int, clockIn: Clock, resetIn: Bool) class AsyncQueueSink[T <: Data](gen: T, depth: Int, sync: Int) extends Module {
extends Module(_clock = clockIn, _reset = resetIn) {
val bits = log2Ceil(depth) val bits = log2Ceil(depth)
val io = new Bundle { val io = new Bundle {
// These come from the sink domain // These come from the sink domain
@ -88,8 +86,13 @@ class AsyncQueue[T <: Data](gen: T, depth: Int = 8, sync: Int = 3) extends Cross
require (depth > 0 && isPow2(depth)) require (depth > 0 && isPow2(depth))
val io = new CrossingIO(gen) val io = new CrossingIO(gen)
val source = Module(new AsyncQueueSource(gen, depth, sync, io.enq_clock, io.enq_reset)) val source = Module(new AsyncQueueSource(gen, depth, sync))
val sink = Module(new AsyncQueueSink (gen, depth, sync, io.deq_clock, io.deq_reset)) val sink = Module(new AsyncQueueSink (gen, depth, sync))
source.clock := io.enq_clock
source.reset := io.enq_reset
sink.clock := io.deq_clock
sink.reset := io.deq_reset
source.io.enq <> io.enq source.io.enq <> io.enq
io.deq <> sink.io.deq io.deq <> sink.io.deq