diff options
| author | Jack Koenig | 2019-11-04 19:02:59 -0800 |
|---|---|---|
| committer | GitHub | 2019-11-04 19:02:59 -0800 |
| commit | cae20ae9ff51e7ebc2151b4f88853d3ac3859f65 (patch) | |
| tree | ec0c3eba1789733087a2020fd17ea083957e5d34 /src | |
| parent | cd433e7cd54f53066b7c1f338e828d8e1d0b9d8a (diff) | |
| parent | 0d7defc81b02c41e416237ad226adc5f1ab0f8f2 (diff) | |
Merge branch 'master' into serialization-utils
Diffstat (limited to 'src')
| -rw-r--r-- | src/main/antlr4/FIRRTL.g4 | 2 | ||||
| -rw-r--r-- | src/main/scala/firrtl/Emitter.scala | 4 | ||||
| -rw-r--r-- | src/main/scala/firrtl/analyses/InstanceGraph.scala | 27 | ||||
| -rw-r--r-- | src/main/scala/firrtl/graph/EdgeData.scala | 126 | ||||
| -rw-r--r-- | src/main/scala/firrtl/transforms/CheckCombLoops.scala | 168 | ||||
| -rw-r--r-- | src/main/scala/firrtl/transforms/Flatten.scala | 56 | ||||
| -rw-r--r-- | src/test/scala/firrtlTests/FlattenTests.scala | 78 | ||||
| -rw-r--r-- | src/test/scala/firrtlTests/ParserSpec.scala | 23 | ||||
| -rw-r--r-- | src/test/scala/firrtlTests/VerilogEmitterTests.scala | 18 | ||||
| -rw-r--r-- | src/test/scala/firrtlTests/analyses/InstanceGraphTests.scala | 41 |
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 : |
