| @@ -3,7 +3,8 @@ | ||||
| package uncore.tilelink2 | ||||
|  | ||||
| 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 | ||||
| { | ||||
| @@ -190,7 +191,7 @@ object TLBundle | ||||
|   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 valid = Bool() | ||||
| @@ -232,3 +233,53 @@ object TLBundleSnoop | ||||
|     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)) | ||||
| } | ||||
|   | ||||
| @@ -6,33 +6,63 @@ import Chisel._ | ||||
| import chisel3.internal.sourceinfo.SourceInfo | ||||
| 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) { | ||||
|     val io = new Bundle { | ||||
|       val in  = node.bundleIn | ||||
|       val in_clock  = Clock(INPUT) | ||||
|       val in_reset  = Bool(INPUT) | ||||
|       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)) => | ||||
|       out.a <> AsyncIrrevocableCrossing(io.in_clock, io.in_reset, in.a, io.out_clock, io.out_reset, depth, sync) | ||||
|       in.d <> AsyncIrrevocableCrossing(io.out_clock, io.out_reset, out.d, io.in_clock, io.in_reset, depth, sync) | ||||
|       val bce = edgeIn.manager.anySupportAcquire && edgeIn.client.anySupportProbe | ||||
|       val depth = edgeOut.manager.depth | ||||
|  | ||||
|       if (edgeOut.manager.anySupportAcquire && edgeOut.client.anySupportProbe) { | ||||
|         in.b <> AsyncIrrevocableCrossing(io.out_clock, io.out_reset, out.b, io.in_clock, io.in_reset, depth, 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) | ||||
|       out.a := ToAsyncBundle(in.a, depth, sync) | ||||
|       in.d := FromAsyncBundle(out.d, sync) | ||||
|  | ||||
|       if (bce) { | ||||
|         in.b := FromAsyncBundle(out.b, sync) | ||||
|         out.c := ToAsyncBundle(in.c, depth, sync) | ||||
|         out.e := ToAsyncBundle(in.e, depth, sync) | ||||
|       } else { | ||||
|         in.b.valid := Bool(false) | ||||
|         in.c.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.c.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 */ | ||||
| import unittest._ | ||||
|  | ||||
| @@ -51,8 +119,8 @@ class TLRAMCrossing extends LazyModule { | ||||
|   val cross = LazyModule(new TLAsyncCrossing) | ||||
|  | ||||
|   model.node := fuzz.node | ||||
|   cross.node := TLFragmenter(4, 256)(model.node) | ||||
|   val monitor = (ram.node := cross.node) | ||||
|   cross.nodeIn := TLFragmenter(4, 256)(model.node) | ||||
|   val monitor = (ram.node := cross.nodeOut) | ||||
|  | ||||
|   lazy val module = new LazyModuleImp(this) with HasUnitTestIO { | ||||
|     io.finished := fuzz.module.io.finished | ||||
|   | ||||
| @@ -224,8 +224,8 @@ class TLFuzzRAM extends LazyModule | ||||
|   xbar2.node := TLAtomicAutomata()(model.node) | ||||
|   ram2.node := TLFragmenter(16, 256)(xbar2.node) | ||||
|   xbar.node := TLWidthWidget(16)(TLHintHandler()(xbar2.node)) | ||||
|   cross.node := TLFragmenter(4, 256)(TLBuffer()(xbar.node)) | ||||
|   val monitor = (ram.node := cross.node) | ||||
|   cross.nodeIn := TLFragmenter(4, 256)(TLBuffer()(xbar.node)) | ||||
|   val monitor = (ram.node := cross.nodeOut) | ||||
|   gpio.node := TLFragmenter(4, 32)(TLBuffer()(xbar.node)) | ||||
|  | ||||
|   lazy val module = new LazyModuleImp(this) with HasUnitTestIO { | ||||
|   | ||||
| @@ -23,13 +23,13 @@ object IntRange | ||||
|  | ||||
| case class IntSourceParameters( | ||||
|   range:    IntRange, | ||||
|   nodePath: Seq[IntBaseNode] = Seq()) | ||||
|   nodePath: Seq[BaseNode] = Seq()) | ||||
| { | ||||
|   val name = nodePath.lastOption.map(_.lazyModule.name).getOrElse("disconnected") | ||||
| } | ||||
|  | ||||
| case class IntSinkParameters( | ||||
|   nodePath: Seq[IntBaseNode] = Seq()) | ||||
|   nodePath: Seq[BaseNode] = Seq()) | ||||
| { | ||||
|   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]] | ||||
| { | ||||
|   def edgeO(po: IntSourcePortParameters, pi: IntSinkPortParameters): IntEdge = IntEdge(po, pi) | ||||
|   def edgeI(po: IntSourcePortParameters, pi: IntSinkPortParameters): IntEdge = IntEdge(po, pi) | ||||
|   def edgeO(pd: IntSourcePortParameters, pu: IntSinkPortParameters): IntEdge = IntEdge(pd, pu) | ||||
|   def edgeI(pd: IntSourcePortParameters, pu: IntSinkPortParameters): IntEdge = IntEdge(pd, pu) | ||||
|   def bundleO(eo: Seq[IntEdge]): Vec[Vec[Bool]] = { | ||||
|     if (eo.isEmpty) Vec(0, Vec(0, Bool())) else | ||||
|     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 | ||||
|   } | ||||
|  | ||||
|   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, () => { | ||||
|       require (eo == ei) | ||||
|       // Cannot use bulk connect, because the widths could differ | ||||
|       (bo zip bi) foreach { case (o, i) => i := o } | ||||
|     }) | ||||
|   } | ||||
|  | ||||
|   override def mixO(po: IntSourcePortParameters, node: IntBaseNode): IntSourcePortParameters = | ||||
|    po.copy(sources = po.sources.map  { s => s.copy (nodePath = node +: s.nodePath) }) | ||||
|   override def mixI(pi: IntSinkPortParameters,  node: IntBaseNode): IntSinkPortParameters = | ||||
|    pi.copy(sinks   = pi.sinks.map    { s => s.copy (nodePath = node +: s.nodePath) }) | ||||
|   override def mixO(pd: IntSourcePortParameters, node: OutwardNode[IntSourcePortParameters, IntSinkPortParameters, Vec[Bool]]): IntSourcePortParameters = | ||||
|    pd.copy(sources = pd.sources.map  { s => s.copy (nodePath = node +: s.nodePath) }) | ||||
|   override def mixI(pu: IntSinkPortParameters, node: InwardNode[IntSourcePortParameters, IntSinkPortParameters, Vec[Bool]]): IntSinkPortParameters = | ||||
|    pu.copy(sinks   = pu.sinks.map    { s => s.copy (nodePath = node +: s.nodePath) }) | ||||
| } | ||||
|  | ||||
| case class IntIdentityNode() extends IdentityNode(IntImp) | ||||
|   | ||||
| @@ -9,18 +9,13 @@ abstract class LazyModule | ||||
| { | ||||
|   protected[tilelink2] var bindings = List[() => Unit]() | ||||
|   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] val parent = LazyModule.stack.headOption | ||||
|  | ||||
|   LazyModule.stack = this :: LazyModule.stack | ||||
|   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 line = sourceLine(info) | ||||
|  | ||||
|   | ||||
| @@ -6,23 +6,33 @@ import Chisel._ | ||||
| import scala.collection.mutable.ListBuffer | ||||
| import chisel3.internal.sourceinfo.SourceInfo | ||||
|  | ||||
| // PI = PortInputParameters | ||||
| // PO = PortOutputParameters | ||||
| // EI = EdgeInput | ||||
| // EO = EdgeOutput | ||||
| abstract class NodeImp[PO, PI, EO, EI, B <: Data] | ||||
| // 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 edgeO(po: PO, pi: PI): EO | ||||
|   def edgeI(po: PO, pi: PI): EI | ||||
|   def bundleO(eo: Seq[EO]): Vec[B] | ||||
|   def bundleI(ei: Seq[EI]): Vec[B] | ||||
|   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 | ||||
|   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 connect(bo: => BI, bi: => BI, e: => EI)(implicit sourceInfo: SourceInfo): (Option[LazyModule], () => Unit) | ||||
| } | ||||
|  | ||||
| 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! | ||||
|   require (!LazyModule.stack.isEmpty) | ||||
| @@ -35,108 +45,146 @@ abstract class RootNode | ||||
|   def colour = "blue" | ||||
|   def omitGraphML = outputs.isEmpty && inputs.isEmpty | ||||
|  | ||||
|   protected[tilelink2] def outputs: Seq[RootNode] | ||||
|   protected[tilelink2] def inputs:  Seq[RootNode] | ||||
|   protected[tilelink2] def outputs: Seq[BaseNode] | ||||
|   protected[tilelink2] def inputs:  Seq[BaseNode] | ||||
| } | ||||
|  | ||||
| class BaseNode[PO, PI, EO, EI, B <: Data](imp: NodeImp[PO, PI, EO, EI, B])( | ||||
|   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 | ||||
| trait InwardNode[DI, UI, BI <: Data] extends BaseNode | ||||
| { | ||||
|   // At least 0 ports must be supported | ||||
|   require (!numPO.isEmpty, s"No number of outputs would be acceptable to ${name}${lazyModule.line}") | ||||
|   protected[tilelink2] val numPI: Range.Inclusive | ||||
|   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}") | ||||
|  | ||||
|   val noOs = numPO.size == 1 && numPO.contains(0) | ||||
|   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 val accPI = ListBuffer[(Int, OutwardNode[DI, UI, BI])]() | ||||
|   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}") | ||||
|   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() } | ||||
|   private 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)) | ||||
|   } | ||||
|  | ||||
|   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 inputs  = iPorts.map(_._2) | ||||
|  | ||||
|   private lazy val oParams : Seq[PO] = { | ||||
|     val o = oFn(oPorts.size, iPorts.map{ case (i, n) => n.oParams(i) }) | ||||
|   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(imp.mixO(_, this)) | ||||
|     o.map(outer.mixO(_, this)) | ||||
|   } | ||||
|   private lazy val iParams : Seq[PI] = { | ||||
|     val i = iFn(iPorts.size, oPorts.map{ case (o, n) => n.iParams(o) }) | ||||
|   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(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 edgesIn  = (iPorts zip iParams).map { case ((o, n), i) => imp.edgeI(n.oParams(o), 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) => inner.edgeI(n.oParams(o), i) } | ||||
|  | ||||
|   lazy val bundleOut = imp.bundleO(edgesOut) | ||||
|   lazy val bundleIn  = imp.bundleI(edgesIn) | ||||
|   lazy val bundleOut = outer.bundleO(edgesOut) | ||||
|   lazy val bundleIn  = inner.bundleI(edgesIn) | ||||
|  | ||||
|   def connectOut = bundleOut | ||||
|   def connectIn = bundleIn | ||||
|   def oConnect = bundleOut | ||||
|   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 info = sourceLine(sourceInfo, " at ", "") | ||||
|     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) | ||||
|     require (!y.oRealized, s"${y.name}${y.lazyModule.line} was incorrectly connected as a source after it's .module was used" + info) | ||||
|     require (!x.noIs, s"${x.name}${x.lazyModule.line} was incorrectly connected as a sink" + info) | ||||
|     require (!x.iRealized, s"${x.name}${x.lazyModule.line} was incorrectly connected as a sink after it's .module was used" + info) | ||||
|     val i = x.accPI.size | ||||
|     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)) | ||||
|     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 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) | ||||
| { | ||||
|   override def connectOut = bundleOut | ||||
|   override def connectIn  = bundleOut | ||||
|   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 connectOut = bundleIn | ||||
|   override def connectIn  = bundleIn | ||||
|   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 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}") | ||||
| } | ||||
|  | ||||
| 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}") | ||||
| } | ||||
|  | ||||
| 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 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 (numPI.end >= 1, s"${name} is an adapter which does not accept inputs${lazyModule.line}") | ||||
|   | ||||
| @@ -149,7 +149,7 @@ case class TLManagerParameters( | ||||
|   sinkId:             IdRange       = IdRange(0, 1), | ||||
|   regionType:         RegionType.T  = RegionType.GET_EFFECTS, | ||||
|   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 | ||||
|   supportsAcquire:    TransferSizes = TransferSizes.none, | ||||
|   supportsArithmetic: TransferSizes = TransferSizes.none, | ||||
| @@ -293,7 +293,7 @@ case class TLManagerPortParameters( | ||||
|  | ||||
| case class TLClientParameters( | ||||
|   sourceId:            IdRange       = IdRange(0,1), | ||||
|   nodePath:            Seq[TLBaseNode] = Seq(), | ||||
|   nodePath:            Seq[BaseNode] = Seq(), | ||||
|   // Supports both Probe+Grant of these sizes | ||||
|   supportsProbe:       TransferSizes = TransferSizes.none, | ||||
|   supportsArithmetic:  TransferSizes = TransferSizes.none, | ||||
| @@ -401,6 +401,17 @@ case class TLBundleParameters( | ||||
|       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( | ||||
|   client:  TLClientPortParameters, | ||||
|   manager: TLManagerPortParameters) | ||||
| @@ -411,10 +422,13 @@ case class TLEdgeParameters( | ||||
|   // Sanity check the link... | ||||
|   require (maxTransfer >= manager.beatBytes) | ||||
|  | ||||
|   val bundle = TLBundleParameters( | ||||
|     addrHiBits  = log2Up(manager.maxAddress + 1) - log2Ceil(manager.beatBytes), | ||||
|     dataBits    = manager.beatBytes * 8, | ||||
|     sourceBits  = log2Up(client.endSourceId), | ||||
|     sinkBits    = log2Up(manager.endSinkId), | ||||
|     sizeBits    = log2Up(maxLgSize+1)) | ||||
|   val bundle = TLBundleParameters(client, manager) | ||||
| } | ||||
|  | ||||
| case class TLAsyncManagerPortParameters(depth: Int, base: TLManagerPortParameters) { require (isPow2(depth)) } | ||||
| case class TLAsyncClientPortParameters(base: TLClientPortParameters) | ||||
| case class TLAsyncBundleParameters(depth: Int, base: TLBundleParameters) { require (isPow2(depth)) } | ||||
| case class TLAsyncEdgeParameters(client: TLAsyncClientPortParameters, manager: TLAsyncManagerPortParameters) | ||||
| { | ||||
|   val bundle = TLAsyncBundleParameters(manager.depth, TLBundleParameters(client.base, manager.base)) | ||||
| } | ||||
|   | ||||
| @@ -8,8 +8,8 @@ import chisel3.internal.sourceinfo.SourceInfo | ||||
|  | ||||
| object TLImp extends NodeImp[TLClientPortParameters, TLManagerPortParameters, TLEdgeOut, TLEdgeIn, TLBundle] | ||||
| { | ||||
|   def edgeO(po: TLClientPortParameters, pi: TLManagerPortParameters): TLEdgeOut = new TLEdgeOut(po, pi) | ||||
|   def edgeI(po: TLClientPortParameters, pi: TLManagerPortParameters): TLEdgeIn  = new TLEdgeIn(po, pi) | ||||
|   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(_)))) | ||||
| @@ -19,19 +19,18 @@ object TLImp extends NodeImp[TLClientPortParameters, TLManagerPortParameters, TL | ||||
|     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) = { | ||||
|     val monitor = LazyModule(new TLMonitor(() => new TLBundleSnoop(bo.params), () => eo, sourceInfo)) | ||||
|   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), () => { | ||||
|       require (eo.asInstanceOf[TLEdgeParameters] == ei.asInstanceOf[TLEdgeParameters]) | ||||
|       bi <> bo | ||||
|       monitor.module.io.in := TLBundleSnoop(bo) | ||||
|     }) | ||||
|   } | ||||
|  | ||||
|   override def mixO(po: TLClientPortParameters,  node: TLBaseNode): TLClientPortParameters  = | ||||
|    po.copy(clients  = po.clients.map  { c => c.copy (nodePath = node +: c.nodePath) }) | ||||
|   override def mixI(pi: TLManagerPortParameters, node: TLBaseNode): TLManagerPortParameters = | ||||
|    pi.copy(managers = pi.managers.map { m => m.copy (nodePath = node +: m.nodePath) }) | ||||
|   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) | ||||
| @@ -72,3 +71,42 @@ class TLInputNodeTest extends UnitTest(500000) { | ||||
|  | ||||
|   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) | ||||
|   | ||||
| @@ -5,8 +5,8 @@ import chisel3.internal.sourceinfo.{SourceInfo, SourceLine, UnlocatableSourceInf | ||||
|  | ||||
| package object tilelink2 | ||||
| { | ||||
|   type TLBaseNode = BaseNode[TLClientPortParameters, TLManagerPortParameters, TLEdgeOut, TLEdgeIn, TLBundle] | ||||
|   type IntBaseNode = BaseNode[IntSourcePortParameters, IntSinkPortParameters, IntEdge, IntEdge, Vec[Bool]] | ||||
|   type TLBaseNode = SimpleNode[TLClientPortParameters, TLManagerPortParameters, TLEdgeOut, TLEdgeIn, TLBundle] | ||||
|   type IntBaseNode = SimpleNode[IntSourcePortParameters, IntSinkPortParameters, IntEdge, IntEdge, Vec[Bool]] | ||||
|   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 trailingZeros(x: Int) = if (x > 0) Some(log2Ceil(x & -x)) else None | ||||
|   | ||||
| @@ -25,8 +25,7 @@ object AsyncGrayCounter { | ||||
|   } | ||||
| } | ||||
|  | ||||
| class AsyncQueueSource[T <: Data](gen: T, depth: Int, sync: Int, clockIn: Clock, resetIn: Bool) | ||||
|     extends Module(_clock = clockIn, _reset = resetIn) { | ||||
| class AsyncQueueSource[T <: Data](gen: T, depth: Int, sync: Int) extends Module { | ||||
|   val bits = log2Ceil(depth) | ||||
|   val io = new Bundle { | ||||
|     // 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 | ||||
| } | ||||
|  | ||||
| class AsyncQueueSink[T <: Data](gen: T, depth: Int, sync: Int, clockIn: Clock, resetIn: Bool) | ||||
|     extends Module(_clock = clockIn, _reset = resetIn) { | ||||
| class AsyncQueueSink[T <: Data](gen: T, depth: Int, sync: Int) extends Module { | ||||
|   val bits = log2Ceil(depth) | ||||
|   val io = new Bundle { | ||||
|     // 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)) | ||||
|  | ||||
|   val io = new CrossingIO(gen) | ||||
|   val source = Module(new AsyncQueueSource(gen, depth, sync, io.enq_clock, io.enq_reset)) | ||||
|   val sink   = Module(new AsyncQueueSink  (gen, depth, sync, io.deq_clock, io.deq_reset)) | ||||
|   val source = Module(new AsyncQueueSource(gen, depth, sync)) | ||||
|   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 | ||||
|   io.deq <> sink.io.deq | ||||
|   | ||||
		Reference in New Issue
	
	Block a user