diff options
| author | Adam Izraelevitz | 2016-10-27 13:00:02 -0700 |
|---|---|---|
| committer | GitHub | 2016-10-27 13:00:02 -0700 |
| commit | 5b35f2d2722f72c81d2d6c507cd379be2a1476d8 (patch) | |
| tree | 78dc2db9e12c6db52fcbf222e339a37b6ebc0b72 /src/main/scala/firrtl/passes/wiring | |
| parent | 1c61a0e7102983891d99d8e9c49e331c8a2178a6 (diff) | |
Wiring (#348)
Added wiring pass and simple test
Diffstat (limited to 'src/main/scala/firrtl/passes/wiring')
| -rw-r--r-- | src/main/scala/firrtl/passes/wiring/Wiring.scala | 162 | ||||
| -rw-r--r-- | src/main/scala/firrtl/passes/wiring/WiringTransform.scala | 80 | ||||
| -rw-r--r-- | src/main/scala/firrtl/passes/wiring/WiringUtils.scala | 164 |
3 files changed, 406 insertions, 0 deletions
diff --git a/src/main/scala/firrtl/passes/wiring/Wiring.scala b/src/main/scala/firrtl/passes/wiring/Wiring.scala new file mode 100644 index 00000000..d3f6f3dd --- /dev/null +++ b/src/main/scala/firrtl/passes/wiring/Wiring.scala @@ -0,0 +1,162 @@ +package firrtl.passes +package wiring + +import firrtl._ +import firrtl.ir._ +import firrtl.Utils._ +import firrtl.Mappers._ +import scala.collection.mutable +import firrtl.Annotations._ +import WiringUtils._ + +case class WiringException(msg: String) extends PassException(msg) + +case class WiringInfo(source: String, comp: String, sinks: Map[String, String], top: String) + +class Wiring(wi: WiringInfo) extends Pass { + def name = this.getClass.getSimpleName + + /** Add pins to modules and wires a signal to them, under the scope of a specified top module + * Description: + * Adds a pin to each sink module + * Punches ports up from the source signal to the specified top module + * Punches ports down to each sink module + * Wires the source up and down, connecting to all sink modules + * Restrictions: + * - Can only have one source module instance under scope of the specified top + * - All instances of each sink module must be under the scope of the specified top + * Notes: + * - No module uniquification occurs (due to imposed restrictions) + */ + def run(c: Circuit): Circuit = { + // Split out WiringInfo + val source = wi.source + val sinks = wi.sinks.keys.toSet + val compName = wi.comp + + // Maps modules to children instances, i.e. (instance, module) + val childrenMap = getChildrenMap(c) + + // Check restrictions + val nSources = countInstances(childrenMap, wi.top, source) + if(nSources != 1) + throw new WiringException(s"Cannot have $nSources instance of $source under ${wi.top}") + sinks.foreach { m => + val total = countInstances(childrenMap, c.main, m) + val nTop = countInstances(childrenMap, c.main, wi.top) + val perTop = countInstances(childrenMap, wi.top, m) + if(total != nTop * perTop) + throw new WiringException(s"Module ${wi.top} does not contain all instances of $m.") + } + + // Create valid port names for wiring that have no name conflicts + val portNames = c.modules.foldLeft(Map.empty[String, String]) { (map, m) => + map + (m.name -> { + val ns = Namespace(m) + wi.sinks.get(m.name) match { + case Some(pin) => ns.newName(pin) + case None => ns.newName(tokenize(compName) filterNot ("[]." contains _) mkString "_") + } + }) + } + + // Create a lineage tree from children map + val lineages = getLineage(childrenMap, wi.top) + + // Populate lineage tree with relationship information, i.e. who is source, + // sink, parent of source, etc. + val withFields = setSharedParent(wi.top)(setFields(sinks, source)(lineages)) + + // Populate lineage tree with what to instantiate, connect to/from, etc. + val withThings = setThings(portNames, compName)(withFields) + + // Create a map from module name to lineage information + val map = pointToLineage(withThings) + + // Obtain the source component type + val sourceComponentType = getType(c, source, compName) + + // Return new circuit with correct wiring + val cx = c.copy(modules = c.modules map onModule(map, sourceComponentType)) + + // Replace inserted IR nodes with WIR nodes + ToWorkingIR.run(cx) + } + + /** Return new module with correct wiring + */ + private def onModule(map: Map[String, Lineage], t: Type)(m: DefModule) = { + map.get(m.name) match { + case None => m + case Some(l) => + val stmts = mutable.ArrayBuffer[Statement]() + val ports = mutable.ArrayBuffer[Port]() + l.addPort match { + case None => + case Some((s, dt)) => dt match { + case DecInput => ports += Port(NoInfo, s, Input, t) + case DecOutput => ports += Port(NoInfo, s, Output, t) + case DecWire => + stmts += DefWire(NoInfo, s, t) + } + } + stmts ++= (l.cons map { case ((l, r)) => + Connect(NoInfo, toExp(l), toExp(r)) + }) + def onStmt(s: Statement): Statement = Block(Seq(s) ++ stmts) + m match { + case Module(i, n, ps, s) => Module(i, n, ps ++ ports, Block(Seq(s) ++ stmts)) + case ExtModule(i, n, ps, dn, p) => ExtModule(i, n, ps ++ ports, dn, p) + } + } + } + + /** Returns the type of the component specified + */ + private def getType(c: Circuit, module: String, comp: String) = { + def getRoot(e: Expression): String = e match { + case r: Reference => r.name + case i: SubIndex => getRoot(i.expr) + case a: SubAccess => getRoot(a.expr) + case f: SubField => getRoot(f.expr) + } + val eComp = toExp(comp) + val root = getRoot(eComp) + var tpe: Option[Type] = None + def getType(s: Statement): Statement = s match { + case DefRegister(_, n, t, _, _, _) if n == root => + tpe = Some(t) + s + case DefWire(_, n, t) if n == root => + tpe = Some(t) + s + case WDefInstance(_, n, m, t) if n == root => + tpe = Some(t) + s + case DefNode(_, n, e) if n == root => + tpe = Some(e.tpe) + s + case sx: DefMemory if sx.name == root => + tpe = Some(MemPortUtils.memType(sx)) + sx + case sx => sx map getType + } + val m = c.modules find (_.name == module) getOrElse error(s"Must have a module named $module") + tpe = m.ports find (_.name == root) map (_.tpe) + m match { + case Module(i, n, ps, b) => getType(b) + case e: ExtModule => + } + tpe match { + case None => error(s"Didn't find $comp in $module!") + case Some(t) => + def setType(e: Expression): Expression = e map setType match { + case ex: Reference => ex.copy(tpe = t) + case ex: SubField => ex.copy(tpe = field_type(ex.expr.tpe, ex.name)) + case ex: SubIndex => ex.copy(tpe = sub_type(ex.expr.tpe)) + case ex: SubAccess => ex.copy(tpe = sub_type(ex.expr.tpe)) + } + setType(eComp).tpe + } + } +} diff --git a/src/main/scala/firrtl/passes/wiring/WiringTransform.scala b/src/main/scala/firrtl/passes/wiring/WiringTransform.scala new file mode 100644 index 00000000..919948b6 --- /dev/null +++ b/src/main/scala/firrtl/passes/wiring/WiringTransform.scala @@ -0,0 +1,80 @@ +package firrtl.passes +package wiring + +import firrtl._ +import firrtl.ir._ +import firrtl.Utils._ +import firrtl.Mappers._ +import scala.collection.mutable +import firrtl.Annotations._ +import WiringUtils._ + +/** A component, e.g. register etc. Must be declared only once under the TopAnnotation + */ +case class SourceAnnotation(target: ComponentName, tID: TransID) extends Annotation with Loose with Unstable { + def duplicate(n: Named) = n match { + case n: ComponentName => this.copy(target = n) + case _ => throwInternalError + } +} + +/** A module, e.g. ExtModule etc., that should add the input pin + */ +case class SinkAnnotation(target: ModuleName, tID: TransID, pin: String) extends Annotation with Loose with Unstable { + def duplicate(n: Named) = n match { + case n: ModuleName => this.copy(target = n) + case _ => throwInternalError + } +} + +/** A module under which all sink module must be declared, and there is only + * one source component + */ +case class TopAnnotation(target: ModuleName, tID: TransID) extends Annotation with Loose with Unstable { + def duplicate(n: Named) = n match { + case n: ModuleName => this.copy(target = n) + case _ => throwInternalError + } +} + +/** Add pins to modules and wires a signal to them, under the scope of a specified top module + * Description: + * Adds a pin to each sink module + * Punches ports up from the source signal to the specified top module + * Punches ports down to each sink module + * Wires the source up and down, connecting to all sink modules + * Restrictions: + * - Can only have one source module instance under scope of the specified top + * - All instances of each sink module must be under the scope of the specified top + * Notes: + * - No module uniquification occurs (due to imposed restrictions) + */ +class WiringTransform(transID: TransID) extends Transform with SimpleRun { + def passSeq(wi: WiringInfo) = + Seq(new Wiring(wi), + InferTypes, + ResolveKinds, + ResolveGenders) + def execute(c: Circuit, map: AnnotationMap) = map get transID match { + case Some(p) => + val sinks = mutable.HashMap[String, String]() + val sources = mutable.Set[String]() + val tops = mutable.Set[String]() + val comp = mutable.Set[String]() + p.values.foreach { a => + a match { + case SinkAnnotation(m, _, pin) => sinks(m.name) = pin + case SourceAnnotation(c, _) => + sources += c.module.name + comp += c.name + case TopAnnotation(m, _) => tops += m.name + } + } + (sources.size, tops.size, sinks.size, comp.size) match { + case (0, 0, p, 0) => TransformResult(c) + case (1, 1, p, 1) if p > 0 => run(c, passSeq(WiringInfo(sources.head, comp.head, sinks.toMap, tops.head))) + case _ => error("Wrong number of sources, tops, or sinks!") + } + case None => TransformResult(c) + } +} diff --git a/src/main/scala/firrtl/passes/wiring/WiringUtils.scala b/src/main/scala/firrtl/passes/wiring/WiringUtils.scala new file mode 100644 index 00000000..bfa94a81 --- /dev/null +++ b/src/main/scala/firrtl/passes/wiring/WiringUtils.scala @@ -0,0 +1,164 @@ +package firrtl.passes +package wiring + +import firrtl._ +import firrtl.ir._ +import firrtl.Utils._ +import firrtl.Mappers._ +import scala.collection.mutable +import firrtl.Annotations._ +import WiringUtils._ + +/** Declaration kind in lineage (e.g. input port, output port, wire) + */ +sealed trait DecKind +case object DecInput extends DecKind +case object DecOutput extends DecKind +case object DecWire extends DecKind + +/** A lineage tree representing the instance hierarchy in a design + */ +case class Lineage( + name: String, + children: Seq[(String, Lineage)] = Seq.empty, + source: Boolean = false, + sink: Boolean = false, + sourceParent: Boolean = false, + sinkParent: Boolean = false, + sharedParent: Boolean = false, + addPort: Option[(String, DecKind)] = None, + cons: Seq[(String, String)] = Seq.empty) { + + def map(f: Lineage => Lineage): Lineage = + this.copy(children = children.map{ case (i, m) => (i, f(m)) }) + + override def toString: String = shortSerialize("") + + def shortSerialize(tab: String): String = s""" + |$tab name: $name, + |$tab children: ${children.map(c => tab + " " + c._2.shortSerialize(tab + " "))} + |""".stripMargin + + def serialize(tab: String): String = s""" + |$tab name: $name, + |$tab source: $source, + |$tab sink: $sink, + |$tab sourceParent: $sourceParent, + |$tab sinkParent: $sinkParent, + |$tab sharedParent: $sharedParent, + |$tab addPort: $addPort + |$tab cons: $cons + |$tab children: ${children.map(c => tab + " " + c._2.serialize(tab + " "))} + |""".stripMargin +} + + + + +object WiringUtils { + type ChildrenMap = mutable.HashMap[String, Seq[(String, String)]] + + /** Given a circuit, returns a map from module name to children + * instance/module names + */ + def getChildrenMap(c: Circuit): ChildrenMap = { + val childrenMap = new ChildrenMap() + def getChildren(mname: String)(s: Statement): Statement = s match { + case s: WDefInstance => + childrenMap(mname) = childrenMap(mname) :+ (s.name, s.module) + s + case s: DefInstance => + childrenMap(mname) = childrenMap(mname) :+ (s.name, s.module) + s + case s => s map getChildren(mname) + } + c.modules.foreach{ m => + childrenMap(m.name) = Nil + m map getChildren(m.name) + } + childrenMap + } + + /** Counts the number of instances of a module declared under a top module + */ + def countInstances(childrenMap: ChildrenMap, top: String, module: String): Int = { + if(top == module) 1 + else childrenMap(top).foldLeft(0) { case (count, (i, child)) => + count + countInstances(childrenMap, child, module) + } + } + + /** Returns a module's lineage, containing all children lineages as well + */ + def getLineage(childrenMap: ChildrenMap, module: String): Lineage = + Lineage(module, childrenMap(module) map { case (i, m) => (i, getLineage(childrenMap, m)) } ) + + /** Sets the sink, sinkParent, source, and sourceParent fields of every + * Lineage in tree + */ + def setFields(sinks: Set[String], source: String)(lin: Lineage): Lineage = lin map setFields(sinks, source) match { + case l if sinks.contains(l.name) => l.copy(sink = true) + case l => + val src = l.name == source + val sinkParent = l.children.foldLeft(false) { case (b, (i, m)) => b || m.sink || m.sinkParent } + val sourceParent = if(src) true else l.children.foldLeft(false) { case (b, (i, m)) => b || m.source || m.sourceParent } + l.copy(sinkParent=sinkParent, sourceParent=sourceParent, source=src) + } + + /** Sets the sharedParent of lineage top + */ + def setSharedParent(top: String)(lin: Lineage): Lineage = lin map setSharedParent(top) match { + case l if l.name == top => l.copy(sharedParent = true) + case l => l + } + + /** Sets the addPort and cons fields of the lineage tree + */ + def setThings(portNames:Map[String, String], compName: String)(lin: Lineage): Lineage = { + val funs = Seq( + ((l: Lineage) => l map setThings(portNames, compName)), + ((l: Lineage) => l match { + case Lineage(name, _, _, _, _, _, true, _, _) => //SharedParent + l.copy(addPort=Some((portNames(name), DecWire))) + case Lineage(name, _, _, _, true, _, _, _, _) => //SourceParent + l.copy(addPort=Some((portNames(name), DecOutput))) + case Lineage(name, _, _, _, _, true, _, _, _) => //SinkParent + l.copy(addPort=Some((portNames(name), DecInput))) + case Lineage(name, _, _, true, _, _, _, _, _) => //Sink + l.copy(addPort=Some((portNames(name), DecInput))) + case l => l + }), + ((l: Lineage) => l match { + case Lineage(name, _, true, _, _, _, _, _, _) => //Source + val tos = Seq(s"${portNames(name)}") + val from = compName + l.copy(cons = l.cons ++ tos.map(t => (t, from))) + case Lineage(name, _, _, _, true, _, _, _, _) => //SourceParent + val tos = Seq(s"${portNames(name)}") + val from = l.children.filter { case (i, c) => c.sourceParent }.map { case (i, c) => s"$i.${portNames(c.name)}" }.head + l.copy(cons = l.cons ++ tos.map(t => (t, from))) + case l => l + }), + ((l: Lineage) => l match { + case Lineage(name, _, _, _, _, true, _, _, _) => //SinkParent + val tos = l.children.filter { case (i, c) => (c.sinkParent || c.sink) && !c.sourceParent } map { case (i, c) => s"$i.${portNames(c.name)}" } + val from = s"${portNames(name)}" + l.copy(cons = l.cons ++ tos.map(t => (t, from))) + case l => l + }) + ) + funs.foldLeft(lin)((l, fun) => fun(l)) + } + + /** Return a map from module to its lineage in the tree + */ + def pointToLineage(lin: Lineage): Map[String, Lineage] = { + val map = mutable.HashMap[String, Lineage]() + def onLineage(l: Lineage): Lineage = { + map(l.name) = l + l map onLineage + } + onLineage(lin) + map.toMap + } +} |
