aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJack Koenig2019-11-04 19:02:59 -0800
committerGitHub2019-11-04 19:02:59 -0800
commitcae20ae9ff51e7ebc2151b4f88853d3ac3859f65 (patch)
treeec0c3eba1789733087a2020fd17ea083957e5d34 /src
parentcd433e7cd54f53066b7c1f338e828d8e1d0b9d8a (diff)
parent0d7defc81b02c41e416237ad226adc5f1ab0f8f2 (diff)
Merge branch 'master' into serialization-utils
Diffstat (limited to 'src')
-rw-r--r--src/main/antlr4/FIRRTL.g42
-rw-r--r--src/main/scala/firrtl/Emitter.scala4
-rw-r--r--src/main/scala/firrtl/analyses/InstanceGraph.scala27
-rw-r--r--src/main/scala/firrtl/graph/EdgeData.scala126
-rw-r--r--src/main/scala/firrtl/transforms/CheckCombLoops.scala168
-rw-r--r--src/main/scala/firrtl/transforms/Flatten.scala56
-rw-r--r--src/test/scala/firrtlTests/FlattenTests.scala78
-rw-r--r--src/test/scala/firrtlTests/ParserSpec.scala23
-rw-r--r--src/test/scala/firrtlTests/VerilogEmitterTests.scala18
-rw-r--r--src/test/scala/firrtlTests/analyses/InstanceGraphTests.scala41
10 files changed, 404 insertions, 139 deletions
diff --git a/src/main/antlr4/FIRRTL.g4 b/src/main/antlr4/FIRRTL.g4
index 518cb698..0035423b 100644
--- a/src/main/antlr4/FIRRTL.g4
+++ b/src/main/antlr4/FIRRTL.g4
@@ -29,7 +29,7 @@ import firrtl.LexerHelper;
// Does there have to be at least one module?
circuit
- : 'circuit' id ':' info? INDENT module* DEDENT
+ : 'circuit' id ':' info? INDENT module* DEDENT EOF
;
module
diff --git a/src/main/scala/firrtl/Emitter.scala b/src/main/scala/firrtl/Emitter.scala
index c427e0fc..32027f67 100644
--- a/src/main/scala/firrtl/Emitter.scala
+++ b/src/main/scala/firrtl/Emitter.scala
@@ -878,6 +878,7 @@ class VerilogEmitter extends SeqTransform with Emitter {
emit(Seq(s" reg [$width:0] initvar;"))
}
emit(Seq("`endif"))
+ emit(Seq("`ifndef SYNTHESIS"))
emit(Seq("initial begin"))
emit(Seq(" `ifdef RANDOMIZE"))
emit(Seq(" `ifdef INIT_RANDOM"))
@@ -897,7 +898,8 @@ class VerilogEmitter extends SeqTransform with Emitter {
for (x <- initials) emit(Seq(tab, x))
emit(Seq(" `endif // RANDOMIZE"))
for (x <- asyncInitials) emit(Seq(tab, x))
- emit(Seq("end"))
+ emit(Seq("end // initial"))
+ emit(Seq("`endif // SYNTHESIS"))
}
for ((clk, content) <- noResetAlwaysBlocks if content.nonEmpty) {
diff --git a/src/main/scala/firrtl/analyses/InstanceGraph.scala b/src/main/scala/firrtl/analyses/InstanceGraph.scala
index 59eae09b..1a453a42 100644
--- a/src/main/scala/firrtl/analyses/InstanceGraph.scala
+++ b/src/main/scala/firrtl/analyses/InstanceGraph.scala
@@ -62,13 +62,12 @@ class InstanceGraph(c: Circuit) {
*/
lazy val fullHierarchy: mutable.LinkedHashMap[WDefInstance,Seq[Seq[WDefInstance]]] = graph.pathsInDAG(trueTopInstance)
- /** A count of the *static* number of instances of each module. For
- * any module other than the top module, this is equivalent to the
- * number of inst statements in the circuit instantiating each
- * module, irrespective of the number of times (if any) the
- * enclosing module appears in the hierarchy. Note that top module
- * of the circuit has an associated count of 1, even though it is
- * never directly instantiated.
+ /** A count of the *static* number of instances of each module. For any module
+ * other than the top (main) module, this is equivalent to the number of inst
+ * statements in the circuit instantiating each module, irrespective of the
+ * number of times (if any) the enclosing module appears in the hierarchy.
+ * Note that top module of the circuit has an associated count of 1, even
+ * though it is never directly instantiated.
*/
lazy val staticInstanceCount: Map[OfModule, Int] = {
val instModules = childInstances.flatMap(_._2.view.map(_.OfModule).toSeq)
@@ -76,19 +75,17 @@ class InstanceGraph(c: Circuit) {
}
/** Finds the absolute paths (each represented by a Seq of instances
- * representing the chain of hierarchy) of all instances of a
- * particular module.
+ * representing the chain of hierarchy) of all instances of a particular
+ * module. Note that this includes one implicit instance of the top (main)
+ * module of the circuit. If the module is not instantiated within the
+ * hierarchy of the top module of the circuit, it will return Nil.
*
* @param module the name of the selected module
* @return a Seq[ Seq[WDefInstance] ] of absolute instance paths
*/
def findInstancesInHierarchy(module: String): Seq[Seq[WDefInstance]] = {
- if (instantiated(module)) {
- val instances = graph.getVertices.filter(_.module == module).toSeq
- instances flatMap { i => fullHierarchy(i) }
- } else {
- Nil
- }
+ val instances = graph.getVertices.filter(_.module == module).toSeq
+ instances flatMap { i => fullHierarchy.getOrElse(i, Nil) }
}
/** An [[firrtl.graph.EulerTour EulerTour]] representation of the [[firrtl.graph.DiGraph DiGraph]] */
diff --git a/src/main/scala/firrtl/graph/EdgeData.scala b/src/main/scala/firrtl/graph/EdgeData.scala
new file mode 100644
index 00000000..4c1109ed
--- /dev/null
+++ b/src/main/scala/firrtl/graph/EdgeData.scala
@@ -0,0 +1,126 @@
+// See LICENSE for license details.
+
+package firrtl.graph
+
+import scala.collection.mutable
+
+/**
+ * An exception that indicates that an edge cannot be found in a graph with edge data.
+ *
+ * @note the vertex type is not captured as a type parameter, as it would be erased.
+ */
+class EdgeNotFoundException(u: Any, v: Any)
+ extends IllegalArgumentException(s"Edge (${u}, ${v}) does not exist!")
+
+/**
+ * Mixing this trait into a DiGraph indicates that each edge may be associated with an optional
+ * data value. The EdgeData trait provides a minimal API for viewing edge data without mutation.
+ *
+ * @tparam V the vertex type (datatype) of the underlying DiGraph
+ * @tparam E the type of each edge data value
+ */
+trait EdgeData[V, E] {
+ this: DiGraph[V] =>
+ protected val edgeDataMap: collection.Map[(V, V), E]
+
+ protected def assertEdgeExists(u: V, v: V): Unit = {
+ if (!contains(u) || !getEdges(u).contains(v)) {
+ throw new EdgeNotFoundException(u, v)
+ }
+ }
+
+ /**
+ * @return the edge data associated with a given edge
+ * @param u the source of the edge
+ * @param v the destination of the edge
+ * @throws EdgeNotFoundException if the edge does not exist
+ * @throws NoSuchElementException if the edge has no data
+ */
+ def edgeData(u: V, v: V): E = {
+ assertEdgeExists(u, v)
+ edgeDataMap((u, v))
+ }
+
+ /**
+ * Optionally return the edge data associated with a given edge.
+ *
+ * @return an option containing the edge data, if any, or None
+ * @param u the source of the edge
+ * @param v the destination of the edge
+ */
+ def getEdgeData(u: V, v: V): Option[E] = edgeDataMap.get((u, v))
+}
+
+/**
+ * Mixing this trait into a DiGraph indicates that each edge may be associated with an optional
+ * data value. The MutableEdgeData trait provides an API for viewing and mutating edge data.
+ *
+ * @tparam V the vertex type (datatype) of the underlying DiGraph
+ * @tparam E the type of each edge data value
+ */
+trait MutableEdgeData[V, E] extends EdgeData[V, E] {
+ this: MutableDiGraph[V] =>
+
+ protected val edgeDataMap: mutable.Map[(V, V), E] = new mutable.LinkedHashMap[(V, V), E]
+
+ /**
+ * Associate an edge data value with a graph edge.
+ *
+ * @param u the source of the edge
+ * @param v the destination of the edge
+ * @param data the edge data to associate with the edge
+ * @throws EdgeNotFoundException if the edge does not exist in the graph
+ */
+ def setEdgeData(u: V, v: V, data: E): Unit = {
+ assertEdgeExists(u, v)
+ edgeDataMap((u, v)) = data
+ }
+
+ /**
+ * Add an edge (u,v) to the graph with associated edge data.
+ *
+ * @see [[DiGraph.addEdge]]
+ * @param u the source of the edge
+ * @param v the destination of the edge
+ * @param data the edge data to associate with the edge
+ * @throws IllegalArgumentException if u or v is not part of the graph
+ */
+ def addEdge(u: V, v: V, data: E): Unit = {
+ addEdge(u, v)
+ setEdgeData(u, v, data)
+ }
+
+ /**
+ * Safely add an edge (u,v) to the graph with associated edge data. If on or more of the two
+ * vertices is not present in the graph, add them before creating the edge.
+ *
+ * @see [[DiGraph.addPairWithEdge]]
+ * @param u the source of the edge
+ * @param v the destination of the edge
+ * @param data the edge data to associate with the edge
+ */
+ def addPairWithEdge(u: V, v: V, data: E): Unit = {
+ addPairWithEdge(u, v)
+ setEdgeData(u, v, data)
+ }
+
+ /**
+ * Safely add an edge (u,v) to the graph with associated edge data if and only if both vertices
+ * are present in the graph. This is useful for preventing spurious edge creating when examining
+ * a subset of possible nodes.
+ *
+ * @see [[DiGraph.addEdgeIfValid]]
+ * @return a Boolean indicating whether the edge was added
+ * @param u the source of the edge
+ * @param v the destination of the edge
+ * @param data the edge data to associate with the edge
+ */
+ def addEdgeIfValid(u: V, v: V, data: E): Boolean = {
+ if (addEdgeIfValid(u, v)) {
+ setEdgeData(u, v, data)
+ true
+ } else {
+ false
+ }
+ }
+}
diff --git a/src/main/scala/firrtl/transforms/CheckCombLoops.scala b/src/main/scala/firrtl/transforms/CheckCombLoops.scala
index d1d98872..bb5d88e7 100644
--- a/src/main/scala/firrtl/transforms/CheckCombLoops.scala
+++ b/src/main/scala/firrtl/transforms/CheckCombLoops.scala
@@ -11,14 +11,51 @@ import firrtl.passes.{Errors, PassException}
import firrtl.traversals.Foreachers._
import firrtl.annotations._
import firrtl.Utils.throwInternalError
-import firrtl.graph.{MutableDiGraph,DiGraph}
+import firrtl.graph._
import firrtl.analyses.InstanceGraph
import firrtl.options.{RegisteredTransform, ShellOption}
+/*
+ * A case class that represents a net in the circuit. This is
+ * necessary since combinational loop checking is an analysis on the
+ * netlist of the circuit; the fields are specialized for low
+ * FIRRTL. Since all wires are ground types, a given ground type net
+ * may only be a subfield of an instance or a memory
+ * port. Therefore, it is uniquely specified within its module
+ * context by its name, its optional parent instance (a WDefInstance
+ * or WDefMemory), and its optional memory port name.
+ */
+case class LogicNode(name: String, inst: Option[String] = None, memport: Option[String] = None)
+
+object LogicNode {
+ def apply(e: Expression): LogicNode = e match {
+ case idx: WSubIndex =>
+ LogicNode(idx.expr)
+ case r: WRef =>
+ LogicNode(r.name)
+ case s: WSubField =>
+ s.expr match {
+ case modref: WRef =>
+ LogicNode(s.name,Some(modref.name))
+ case memport: WSubField =>
+ memport.expr match {
+ case memref: WRef =>
+ LogicNode(s.name,Some(memref.name),Some(memport.name))
+ case _ => throwInternalError(s"LogicNode: unrecognized subsubfield expression - $memport")
+ }
+ case _ => throwInternalError(s"LogicNode: unrecognized subfield expression - $s")
+ }
+ }
+}
+
object CheckCombLoops {
+ type AbstractConnMap = DiGraph[LogicNode]
+ type ConnMap = DiGraph[LogicNode] with EdgeData[LogicNode, Info]
+ type MutableConnMap = MutableDiGraph[LogicNode] with MutableEdgeData[LogicNode, Info]
+
+
class CombLoopException(info: Info, mname: String, cycle: Seq[String]) extends PassException(
s"$info: [module $mname] Combinational loop detected:\n" + cycle.mkString("\n"))
-
}
case object DontCheckCombLoopsAnnotation extends NoTargetAnnotation
@@ -70,63 +107,33 @@ class CheckCombLoops extends Transform with RegisteredTransform {
toAnnotationSeq = (_: Unit) => Seq(DontCheckCombLoopsAnnotation),
helpText = "Disable combinational loop checking" ) )
- /*
- * A case class that represents a net in the circuit. This is
- * necessary since combinational loop checking is an analysis on the
- * netlist of the circuit; the fields are specialized for low
- * FIRRTL. Since all wires are ground types, a given ground type net
- * may only be a subfield of an instance or a memory
- * port. Therefore, it is uniquely specified within its module
- * context by its name, its optional parent instance (a WDefInstance
- * or WDefMemory), and its optional memory port name.
- */
- private case class LogicNode(name: String, inst: Option[String] = None, memport: Option[String] = None)
-
- private def toLogicNode(e: Expression): LogicNode = e match {
- case idx: WSubIndex =>
- toLogicNode(idx.expr)
- case r: WRef =>
- LogicNode(r.name)
- case s: WSubField =>
- s.expr match {
- case modref: WRef =>
- LogicNode(s.name,Some(modref.name))
- case memport: WSubField =>
- memport.expr match {
- case memref: WRef =>
- LogicNode(s.name,Some(memref.name),Some(memport.name))
- case _ => throwInternalError(s"toLogicNode: unrecognized subsubfield expression - $memport")
- }
- case _ => throwInternalError(s"toLogicNode: unrecognized subfield expression - $s")
- }
- }
-
-
- private def getExprDeps(deps: MutableDiGraph[LogicNode], v: LogicNode)(e: Expression): Unit = e match {
- case r: WRef => deps.addEdgeIfValid(v, toLogicNode(r))
- case s: WSubField => deps.addEdgeIfValid(v, toLogicNode(s))
- case _ => e.foreach(getExprDeps(deps, v))
+ private def getExprDeps(deps: MutableConnMap, v: LogicNode, info: Info)(e: Expression): Unit = e match {
+ case r: WRef => deps.addEdgeIfValid(v, LogicNode(r), info)
+ case s: WSubField => deps.addEdgeIfValid(v, LogicNode(s), info)
+ case _ => e.foreach(getExprDeps(deps, v, info))
}
private def getStmtDeps(
- simplifiedModules: mutable.Map[String,DiGraph[LogicNode]],
- deps: MutableDiGraph[LogicNode])(s: Statement): Unit = s match {
- case Connect(_,loc,expr) =>
- val lhs = toLogicNode(loc)
+ simplifiedModules: mutable.Map[String, AbstractConnMap],
+ deps: MutableConnMap)(s: Statement): Unit = s match {
+ case Connect(info, loc, expr) =>
+ val lhs = LogicNode(loc)
if (deps.contains(lhs)) {
- getExprDeps(deps, lhs)(expr)
+ getExprDeps(deps, lhs, info)(expr)
}
case w: DefWire =>
deps.addVertex(LogicNode(w.name))
- case n: DefNode =>
- val lhs = LogicNode(n.name)
+ case DefNode(info, name, value) =>
+ val lhs = LogicNode(name)
deps.addVertex(lhs)
- getExprDeps(deps, lhs)(n.value)
+ getExprDeps(deps, lhs, info)(value)
case m: DefMemory if (m.readLatency == 0) =>
for (rp <- m.readers) {
- val dataNode = deps.addVertex(LogicNode("data",Some(m.name),Some(rp)))
- deps.addEdge(dataNode, deps.addVertex(LogicNode("addr",Some(m.name),Some(rp))))
- deps.addEdge(dataNode, deps.addVertex(LogicNode("en",Some(m.name),Some(rp))))
+ val dataNode = deps.addVertex(LogicNode("data", Some(m.name), Some(rp)))
+ val addr = LogicNode("addr", Some(m.name), Some(rp))
+ val en = LogicNode("en", Some(m.name), Some(rp))
+ deps.addEdge(dataNode, deps.addVertex(addr), m.info)
+ deps.addEdge(dataNode, deps.addVertex(en), m.info)
}
case i: WDefInstance =>
val iGraph = simplifiedModules(i.module).transformNodes(n => n.copy(inst = Some(i.name)))
@@ -136,6 +143,11 @@ class CheckCombLoops extends Transform with RegisteredTransform {
s.foreach(getStmtDeps(simplifiedModules,deps))
}
+ // Pretty-print a LogicNode with a prepended hierarchical path
+ private def prettyPrintAbsoluteRef(hierPrefix: Seq[String], node: LogicNode): String = {
+ (hierPrefix ++ node.inst ++ node.memport :+ node.name).mkString(".")
+ }
+
/*
* Recover the full path from a path passing through simplified
* instances. Since edges may pass through simplified instances, the
@@ -144,23 +156,25 @@ class CheckCombLoops extends Transform with RegisteredTransform {
*/
private def expandInstancePaths(
m: String,
- moduleGraphs: mutable.Map[String,DiGraph[LogicNode]],
- moduleDeps: Map[String, Map[String,String]],
- prefix: Seq[String],
+ moduleGraphs: mutable.Map[String, ConnMap],
+ moduleDeps: Map[String, Map[String, String]],
+ hierPrefix: Seq[String],
path: Seq[LogicNode]): Seq[String] = {
- def absNodeName(prefix: Seq[String], n: LogicNode) =
- (prefix ++ n.inst ++ n.memport :+ n.name).mkString(".")
- val pathNodes = (path zip path.tail) map { case (a, b) =>
- if (a.inst.isDefined && !a.memport.isDefined && a.inst == b.inst) {
- val child = moduleDeps(m)(a.inst.get)
- val newprefix = prefix :+ a.inst.get
- val subpath = moduleGraphs(child).path(b.copy(inst=None),a.copy(inst=None)).tail.reverse
- expandInstancePaths(child,moduleGraphs,moduleDeps,newprefix,subpath)
+ // Recover info from edge data, add to error string
+ def info(u: LogicNode, v: LogicNode): String =
+ moduleGraphs(m).getEdgeData(u, v).map(_.toString).mkString("\t", "", "")
+ // lhs comes after rhs
+ val pathNodes = (path zip path.tail) map { case (rhs, lhs) =>
+ if (lhs.inst.isDefined && !lhs.memport.isDefined && lhs.inst == rhs.inst) {
+ val child = moduleDeps(m)(lhs.inst.get)
+ val newHierPrefix = hierPrefix :+ lhs.inst.get
+ val subpath = moduleGraphs(child).path(lhs.copy(inst=None),rhs.copy(inst=None)).reverse
+ expandInstancePaths(child, moduleGraphs, moduleDeps, newHierPrefix, subpath)
} else {
- Seq(absNodeName(prefix,a))
+ Seq(prettyPrintAbsoluteRef(hierPrefix, lhs) ++ info(lhs, rhs))
}
}
- pathNodes.flatten :+ absNodeName(prefix, path.last)
+ pathNodes.flatten
}
/*
@@ -216,36 +230,38 @@ class CheckCombLoops extends Transform with RegisteredTransform {
val iGraph = new InstanceGraph(c).graph
val moduleDeps = iGraph.getEdgeMap.map({ case (k,v) => (k.module, (v map { i => (i.name, i.module) }).toMap) }).toMap
val topoSortedModules = iGraph.transformNodes(_.module).linearize.reverse map { moduleMap(_) }
- val moduleGraphs = new mutable.HashMap[String,DiGraph[LogicNode]]
- val simplifiedModuleGraphs = new mutable.HashMap[String,DiGraph[LogicNode]]
+ val moduleGraphs = new mutable.HashMap[String, ConnMap]
+ val simplifiedModuleGraphs = new mutable.HashMap[String, AbstractConnMap]
topoSortedModules.foreach {
case em: ExtModule =>
val portSet = em.ports.map(p => LogicNode(p.name)).toSet
- val extModuleDeps = new MutableDiGraph[LogicNode]
+ val extModuleDeps = new MutableDiGraph[LogicNode] with MutableEdgeData[LogicNode, Info]
portSet.foreach(extModuleDeps.addVertex(_))
extModulePaths.getOrElse(ModuleTarget(c.main, em.name), Nil).collect {
case a: ExtModulePathAnnotation => extModuleDeps.addPairWithEdge(LogicNode(a.sink.ref), LogicNode(a.source.ref))
}
- moduleGraphs(em.name) = DiGraph(extModuleDeps).simplify(portSet)
- simplifiedModuleGraphs(em.name) = moduleGraphs(em.name)
+ moduleGraphs(em.name) = extModuleDeps
+ simplifiedModuleGraphs(em.name) = extModuleDeps.simplify(portSet)
case m: Module =>
val portSet = m.ports.map(p => LogicNode(p.name)).toSet
- val internalDeps = new MutableDiGraph[LogicNode]
+ val internalDeps = new MutableDiGraph[LogicNode] with MutableEdgeData[LogicNode, Info]
portSet.foreach(internalDeps.addVertex(_))
m.foreach(getStmtDeps(simplifiedModuleGraphs, internalDeps))
- val moduleGraph = DiGraph(internalDeps)
- moduleGraphs(m.name) = moduleGraph
+ moduleGraphs(m.name) = internalDeps
simplifiedModuleGraphs(m.name) = moduleGraphs(m.name).simplify(portSet)
// Find combinational nodes with self-edges; this is *NOT* the same as length-1 SCCs!
- for (unitLoopNode <- moduleGraph.getVertices.filter(v => moduleGraph.getEdges(v).contains(v))) {
+ for (unitLoopNode <- internalDeps.getVertices.filter(v => internalDeps.getEdges(v).contains(v))) {
errors.append(new CombLoopException(m.info, m.name, Seq(unitLoopNode.name)))
}
- for (scc <- moduleGraph.findSCCs.filter(_.length > 1)) {
- val sccSubgraph = moduleGraph.subgraph(scc.toSet)
+ for (scc <- internalDeps.findSCCs.filter(_.length > 1)) {
+ val sccSubgraph = internalDeps.subgraph(scc.toSet)
val cycle = findCycleInSCC(sccSubgraph)
- (cycle zip cycle.tail).foreach({ case (a,b) => require(moduleGraph.getEdges(a).contains(b)) })
- val expandedCycle = expandInstancePaths(m.name, moduleGraphs, moduleDeps, Seq(m.name), cycle.reverse)
- errors.append(new CombLoopException(m.info, m.name, expandedCycle))
+ (cycle zip cycle.tail).foreach({ case (a,b) => require(internalDeps.getEdges(a).contains(b)) })
+ // Reverse to make sure LHS comes after RHS, print repeated vertex at start for legibility
+ val intuitiveCycle = cycle.reverse
+ val repeatedInitial = prettyPrintAbsoluteRef(Seq(m.name), intuitiveCycle.head)
+ val expandedCycle = expandInstancePaths(m.name, moduleGraphs, moduleDeps, Seq(m.name), intuitiveCycle)
+ errors.append(new CombLoopException(m.info, m.name, repeatedInitial +: expandedCycle))
}
case m => throwInternalError(s"Module ${m.name} has unrecognized type")
}
diff --git a/src/main/scala/firrtl/transforms/Flatten.scala b/src/main/scala/firrtl/transforms/Flatten.scala
index 658f0987..26d2b06d 100644
--- a/src/main/scala/firrtl/transforms/Flatten.scala
+++ b/src/main/scala/firrtl/transforms/Flatten.scala
@@ -15,16 +15,19 @@ case class FlattenAnnotation(target: Named) extends SingleTargetAnnotation[Named
}
/**
- * Takes flatten annotations for module instances and modules and inline the entire hierarchy of modules down from the annotations.
- * This transformation instantiates and is based on the InlineInstances transformation.
- * Note: Inlining a module means inlining all its children module instances
- */
+ * Takes flatten annotations for module instances and modules and inline the entire hierarchy of
+ * modules down from the annotations. This transformation instantiates and is based on the
+ * InlineInstances transformation.
+ *
+ * @note Flattening a module means inlining all its fully-defined child instances
+ * @note Instances of extmodules are not (and cannot be) inlined
+ */
class Flatten extends Transform {
def inputForm = LowForm
def outputForm = LowForm
val inlineTransform = new InlineInstances
-
+
private def collectAnns(circuit: Circuit, anns: Iterable[Annotation]): (Set[ModuleName], Set[ComponentName]) =
anns.foldLeft( (Set.empty[ModuleName], Set.empty[ComponentName]) ) {
case ((modNames, instNames), ann) => ann match {
@@ -40,7 +43,7 @@ class Flatten extends Transform {
/**
* Modifies the circuit by replicating the hierarchy under the annotated objects (mods and insts) and
- * by rewriting the original circuit to refer to the new modules that will be inlined later.
+ * by rewriting the original circuit to refer to the new modules that will be inlined later.
* @return modified circuit and ModuleNames to inline
*/
def duplicateSubCircuitsFromAnno(c: Circuit, mods: Set[ModuleName], insts: Set[ComponentName]): (Circuit, Set[ModuleName]) = {
@@ -49,10 +52,10 @@ class Flatten extends Transform {
val newModDefs = mutable.Set.empty[DefModule]
val nsp = Namespace(c)
- /**
- * We start with rewriting DefInstances in the modules with annotations to refer to replicated modules to be created later.
- * It populates seedMods where we capture the mapping between the original module name of the instances came from annotation
- * to a new module name that we will create as a replica of the original one.
+ /**
+ * We start with rewriting DefInstances in the modules with annotations to refer to replicated modules to be created later.
+ * It populates seedMods where we capture the mapping between the original module name of the instances came from annotation
+ * to a new module name that we will create as a replica of the original one.
* Note: We replace old modules with it replicas so that other instances of the same module can be left unchanged.
*/
def rewriteMod(parent: DefModule)(x: Statement): Statement = x match {
@@ -66,12 +69,12 @@ class Flatten extends Transform {
} else x
case _ => x
}
-
+
val modifMods = c.modules map { m => m map rewriteMod(m) }
-
- /**
- * Recursively rewrites modules in the hierarchy starting with modules in seedMods (originally annotations).
- * Populates newModDefs, which are replicated modules used in the subcircuit that we create
+
+ /**
+ * Recursively rewrites modules in the hierarchy starting with modules in seedMods (originally annotations).
+ * Populates newModDefs, which are replicated modules used in the subcircuit that we create
* by recursively traversing modules captured inside seedMods and replicating them
*/
def recDupMods(mods: Map[String, String]): Unit = {
@@ -79,20 +82,23 @@ class Flatten extends Transform {
def dupMod(x: Statement): Statement = x match {
case _: Block => x map dupMod
- case WDefInstance(info, instName, moduleName, instTpe) =>
- val newModName = if (replMods.contains(moduleName)) replMods(moduleName) else nsp.newName(moduleName+"_TO_FLATTEN")
- replMods += moduleName -> newModName
- WDefInstance(info, instName, newModName, instTpe)
- case _ => x
+ case WDefInstance(info, instName, moduleName, instTpe) => modMap(moduleName) match {
+ case m: Module =>
+ val newModName = if (replMods.contains(moduleName)) replMods(moduleName) else nsp.newName(moduleName+"_TO_FLATTEN")
+ replMods += moduleName -> newModName
+ WDefInstance(info, instName, newModName, instTpe)
+ case _ => x // Ignore extmodules
+ }
+ case _ => x
}
-
+
def dupName(name: String): String = mods(name)
val newMods = mods map { case (origName, newName) => modMap(origName) map dupMod map dupName }
-
+
newModDefs ++= newMods
-
+
if(replMods.size > 0) recDupMods(replMods.toMap)
-
+
}
recDupMods(seedMods.toMap)
@@ -100,7 +106,7 @@ class Flatten extends Transform {
val modsToInline = newModDefs map { m => ModuleName(m.name, CircuitName(c.main)) }
(c.copy(modules = modifMods ++ newModDefs), modsToInline.toSet)
}
-
+
override def execute(state: CircuitState): CircuitState = {
val annos = state.annotations.collect { case a @ FlattenAnnotation(_) => a }
annos match {
diff --git a/src/test/scala/firrtlTests/FlattenTests.scala b/src/test/scala/firrtlTests/FlattenTests.scala
index 82c3ebdc..468cc1c4 100644
--- a/src/test/scala/firrtlTests/FlattenTests.scala
+++ b/src/test/scala/firrtlTests/FlattenTests.scala
@@ -25,7 +25,7 @@ class FlattenTests extends LowTransformSpec {
val name = if (parts.size == 1) modName else ComponentName(parts.tail.mkString("."), modName)
FlattenAnnotation(name)
}
-
+
"The modules inside Top " should "be inlined" in {
val input =
"""circuit Top :
@@ -55,7 +55,7 @@ class FlattenTests extends LowTransformSpec {
| b <= a""".stripMargin
execute(input, check, Seq(flatten("Top")))
}
-
+
"Two instances of the same module inside Top " should "be inlined" in {
val input =
"""circuit Top :
@@ -112,14 +112,14 @@ class FlattenTests extends LowTransformSpec {
| input a : UInt<32>
| output b : UInt<32>
| inst i of Inline2
- | i.a <= a
- | b <= i.a
+ | i.a <= a
+ | b <= i.a
| module Inline1 :
| input a : UInt<32>
| output b : UInt<32>
| inst i of Inline2
- | i.a <= a
- | b <= i.a
+ | i.a <= a
+ | b <= i.a
| module Inline2 :
| input a : UInt<32>
| output b : UInt<32>
@@ -147,13 +147,13 @@ class FlattenTests extends LowTransformSpec {
| input a : UInt<32>
| output b : UInt<32>
| inst i of Inline2
- | b <= i.a
- | i.a <= a
+ | b <= i.a
+ | i.a <= a
| module Inline1 :
| input a : UInt<32>
| output b : UInt<32>
- | inst i of Inline2
- | b <= i.a
+ | inst i of Inline2
+ | b <= i.a
| i.a <= a
| module Inline2 :
| input a : UInt<32>
@@ -179,14 +179,14 @@ class FlattenTests extends LowTransformSpec {
| input a : UInt<32>
| output b : UInt<32>
| inst i of Inline2
- | i.a <= a
- | b <= i.a
+ | i.a <= a
+ | b <= i.a
| module Inline1 :
| input a : UInt<32>
| output b : UInt<32>
| inst i of Inline2
- | i.a <= a
- | b <= i.a
+ | i.a <= a
+ | b <= i.a
| module Inline2 :
| input a : UInt<32>
| output b : UInt<32>
@@ -208,8 +208,8 @@ class FlattenTests extends LowTransformSpec {
| input a : UInt<32>
| output b : UInt<32>
| inst i of Inline2
- | b <= i.a
- | i.a <= a
+ | b <= i.a
+ | i.a <= a
| module Inline1 :
| input a : UInt<32>
| output b : UInt<32>
@@ -234,4 +234,50 @@ class FlattenTests extends LowTransformSpec {
|""".stripMargin
execute(input, input, Seq.empty)
}
+
+ "The Flatten transform" should "ignore extmodules" in {
+ val input = """
+ |circuit Top :
+ | module Top :
+ | input a : UInt<32>
+ | output b : UInt<32>
+ | inst i of Inline
+ | i.a <= a
+ | b <= i.b
+ | module Inline :
+ | input a : UInt<32>
+ | output b : UInt<32>
+ | inst i of ExternalMod
+ | i.a <= a
+ | b <= i.b
+ | extmodule ExternalMod :
+ | input a : UInt<32>
+ | output b : UInt<32>
+ | defname = ExternalMod
+ """.stripMargin
+ val check = """
+ |circuit Top :
+ | module Top :
+ | input a : UInt<32>
+ | output b : UInt<32>
+ | wire i_a : UInt<32>
+ | wire i_b : UInt<32>
+ | inst i_i of ExternalMod
+ | i_b <= i_i.b
+ | i_i.a <= i_a
+ | b <= i_b
+ | i_a <= a
+ | module Inline :
+ | input a : UInt<32>
+ | output b : UInt<32>
+ | inst i of ExternalMod
+ | b <= i.b
+ | i.a <= a
+ | extmodule ExternalMod :
+ | input a : UInt<32>
+ | output b : UInt<32>
+ | defname = ExternalMod
+ """.stripMargin
+ execute(input, check, Seq(flatten("Top")))
+ }
}
diff --git a/src/test/scala/firrtlTests/ParserSpec.scala b/src/test/scala/firrtlTests/ParserSpec.scala
index 711df5ed..4f28e100 100644
--- a/src/test/scala/firrtlTests/ParserSpec.scala
+++ b/src/test/scala/firrtlTests/ParserSpec.scala
@@ -169,6 +169,29 @@ class ParserSpec extends FirrtlFlatSpec {
Driver.execute(manager)
}
}
+
+ "Trailing syntax errors" should "be caught in the parser" in {
+ val input = s"""
+ |circuit Foo:
+ | module Bar:
+ | input a: UInt<1>
+ |output b: UInt<1>
+ | b <- a
+ |
+ | module Foo:
+ | input a: UInt<1>
+ | output b: UInt<1>
+ | inst bar of Bar
+ | bar.a <- a
+ | b <- bar.b
+ """.stripMargin
+ val manager = new ExecutionOptionsManager("test") with HasFirrtlOptions {
+ firrtlOptions = FirrtlExecutionOptions(firrtlSource = Some(input))
+ }
+ a [SyntaxErrorsException] shouldBe thrownBy {
+ Driver.execute(manager)
+ }
+ }
}
class ParserPropSpec extends FirrtlPropSpec {
diff --git a/src/test/scala/firrtlTests/VerilogEmitterTests.scala b/src/test/scala/firrtlTests/VerilogEmitterTests.scala
index 7ea2f03a..cf2ff320 100644
--- a/src/test/scala/firrtlTests/VerilogEmitterTests.scala
+++ b/src/test/scala/firrtlTests/VerilogEmitterTests.scala
@@ -267,6 +267,24 @@ class VerilogEmitterSpec extends FirrtlFlatSpec {
}
}
+ "Initial Blocks" should "be guarded by ifndef SYNTHESIS" in {
+ val input =
+ """circuit Test :
+ | module Test :
+ | input clock : Clock
+ | input reset : AsyncReset
+ | input in : UInt<8>
+ | output out : UInt<8>
+ | reg r : UInt<8>, clock with : (reset => (reset, UInt(0)))
+ | r <= in
+ | out <= r
+ """.stripMargin
+ val state = CircuitState(parse(input), ChirrtlForm)
+ val result = (new VerilogCompiler).compileAndEmit(state, List())
+ result should containLines ("`ifndef SYNTHESIS", "initial begin")
+ result should containLines ("end // initial", "`endif // SYNTHESIS")
+ }
+
"Verilog name conflicts" should "be resolved" in {
val input =
"""|circuit parameter:
diff --git a/src/test/scala/firrtlTests/analyses/InstanceGraphTests.scala b/src/test/scala/firrtlTests/analyses/InstanceGraphTests.scala
index e98c1895..eb62c564 100644
--- a/src/test/scala/firrtlTests/analyses/InstanceGraphTests.scala
+++ b/src/test/scala/firrtlTests/analyses/InstanceGraphTests.scala
@@ -1,12 +1,8 @@
package firrtlTests.analyses
-import java.io._
-import org.scalatest._
-import org.scalatest.prop._
-import org.scalatest.Matchers._
import firrtl.analyses.InstanceGraph
import firrtl.graph.DiGraph
-import firrtl.Parser.parse
+import firrtl.WDefInstance
import firrtl.passes._
import firrtlTests._
@@ -39,6 +35,41 @@ circuit Top :
getEdgeSet(graph) shouldBe Map("Top" -> Set("Child1", "Child2"), "Child1" -> Set("Child1a", "Child1b"), "Child2" -> Set(), "Child1a" -> Set(), "Child1b" -> Set())
}
+ it should "find hierarchical instances correctly in disconnected hierarchies" in {
+ val input = """
+circuit Top :
+ module Top :
+ inst c of Child1
+ module Child1 :
+ skip
+
+ module Top2 :
+ inst a of Child2
+ inst b of Child3
+ skip
+ module Child2 :
+ inst a of Child2a
+ inst b of Child2b
+ skip
+ module Child2a :
+ skip
+ module Child2b :
+ skip
+ module Child3 :
+ skip
+"""
+
+ val circuit = ToWorkingIR.run(parse(input))
+ val iGraph = new InstanceGraph(circuit)
+ iGraph.findInstancesInHierarchy("Top") shouldBe Seq(Seq(WDefInstance("Top", "Top")))
+ iGraph.findInstancesInHierarchy("Child1") shouldBe Seq(Seq(WDefInstance("Top", "Top"), WDefInstance("c", "Child1")))
+ iGraph.findInstancesInHierarchy("Top2") shouldBe Nil
+ iGraph.findInstancesInHierarchy("Child2") shouldBe Nil
+ iGraph.findInstancesInHierarchy("Child2a") shouldBe Nil
+ iGraph.findInstancesInHierarchy("Child2b") shouldBe Nil
+ iGraph.findInstancesInHierarchy("Child3") shouldBe Nil
+ }
+
it should "recognize disconnected hierarchies" in {
val input = """
circuit Top :