diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/main/scala/firrtl/passes/Inline.scala | 110 | ||||
| -rw-r--r-- | src/main/scala/firrtl/passes/clocklist/ClockList.scala | 2 | ||||
| -rw-r--r-- | src/test/scala/firrtlTests/FlattenTests.scala | 60 | ||||
| -rw-r--r-- | src/test/scala/firrtlTests/InlineInstancesTests.scala | 165 |
4 files changed, 241 insertions, 96 deletions
diff --git a/src/main/scala/firrtl/passes/Inline.scala b/src/main/scala/firrtl/passes/Inline.scala index 0ba0c5d9..f963e762 100644 --- a/src/main/scala/firrtl/passes/Inline.scala +++ b/src/main/scala/firrtl/passes/Inline.scala @@ -6,6 +6,7 @@ package passes import firrtl.ir._ import firrtl.Mappers._ import firrtl.annotations._ +import firrtl.analyses.InstanceGraph // Datastructures import scala.collection.mutable @@ -15,13 +16,14 @@ case class InlineAnnotation(target: Named) extends SingleTargetAnnotation[Named] def duplicate(n: Named) = InlineAnnotation(n) } -// Only use on legal Firrtl. Specifically, the restriction of -// instance loops must have been checked, or else this pass can -// infinitely recurse +/** Inline instances as indicated by existing [[InlineAnnotation]]s + * @note Only use on legal Firrtl. Specifically, the restriction of instance loops must have been checked, or else this + * pass can infinitely recurse. + */ class InlineInstances extends Transform { def inputForm = LowForm def outputForm = LowForm - val inlineDelim = "$" + private [firrtl] val inlineDelim: String = "_" private def collectAnns(circuit: Circuit, anns: Iterable[Annotation]): (Set[ModuleName], Set[ComponentName]) = anns.foldLeft(Set.empty[ModuleName], Set.empty[ComponentName]) { @@ -52,7 +54,7 @@ class InlineInstances extends Transform { // 3) All annotated instances exist, and their modules can be inline def check(c: Circuit, moduleNames: Set[ModuleName], instanceNames: Set[ComponentName]): Unit = { val errors = mutable.ArrayBuffer[PassException]() - val moduleMap = (for(m <- c.modules) yield m.name -> m).toMap + val moduleMap = new InstanceGraph(c).moduleMap def checkExists(name: String): Unit = if (!moduleMap.contains(name)) errors += new PassException(s"Annotated module does not exist: $name") @@ -108,36 +110,86 @@ class InlineInstances extends Transform { check(c, modsToInline, instsToInline) val flatModules = modsToInline.map(m => m.name) val flatInstances = instsToInline.map(i => i.module.name + "." + i.name) ++ getInstancesOf(c, flatModules) - val moduleMap = c.modules.foldLeft(Map[String, DefModule]()) { (map, m) => map + (m.name -> m) } - - def appendNamePrefix(prefix: String)(name:String): String = prefix + name - def appendRefPrefix(prefix: String, currentModule: String)(e: Expression): Expression = e match { - case WSubField(WRef(ref, _, InstanceKind, _), field, tpe, gen) if flatInstances.contains(currentModule + "." + ref) => - WRef(prefix + ref + inlineDelim + field, tpe, WireKind, gen) - case WRef(name, tpe, kind, gen) => WRef(prefix + name, tpe, kind, gen) - case ex => ex map appendRefPrefix(prefix, currentModule) + val iGraph = new InstanceGraph(c) + val namespaceMap = collection.mutable.Map[String, Namespace]() + + /** Add a prefix to all declarations updating a [[Namespace]] and appending to a [[RenameMap]] */ + def appendNamePrefix(prefix: String, ns: Namespace, renames: RenameMap)(name:String): String = { + if (prefix.nonEmpty && !ns.tryName(prefix + name)) + throw new Exception(s"Inlining failed. Inlined name '${prefix + name}' already exists") + renames.rename(name, prefix + name) + prefix + name } - def onStmt(prefix: String, currentModule: String)(s: Statement): Statement = s match { - case WDefInstance(info, instName, moduleName, instTpe) => - // Rewrites references in inlined statements from ref to inst$ref - val shouldInline = flatInstances.contains(currentModule + "." + instName) - // Used memoized instance if available - if (shouldInline) { - val toInline = moduleMap(moduleName) match { - case m: ExtModule => throw new PassException("Cannot inline external module") - case m: Module => m + /** Modify all references */ + def appendRefPrefix(currentModule: ModuleName, renames: RenameMap) + (e: Expression): Expression = e match { + case wsf@ WSubField(wr@ WRef(ref, _, InstanceKind, _), field, tpe, gen) => + val port = ComponentName(s"$ref.$field", currentModule) + val inst = ComponentName(s"$ref", currentModule) + (renames.get(port), renames.get(inst)) match { + case (Some(p :: Nil), None) => WRef(p.name, tpe, WireKind, gen) + case (None, Some(i :: Nil)) => wsf.map(appendRefPrefix(currentModule, renames)) + case (None, None) => wsf + case (Some(p), Some(i)) => throw new PassException( + s"Inlining found multiple renames for ports ($p) and/or instances ($i). This should be impossible...") + } + case wr@ WRef(name, _, _, _) => + val comp = ComponentName(name, currentModule) + renames.get(comp).orElse(Some(Seq(comp))) match { + case Some(car :: Nil) => wr.copy(name=car.name) + case c@ Some(_) => throw new PassException( + s"Inlining found mlutiple renames for ref $comp -> $c. This should be impossible...") + } + case ex => ex.map(appendRefPrefix(currentModule, renames)) + } + + def onStmt(currentModule: ModuleName, renames: RenameMap)(s: Statement): Statement = { + val ns = namespaceMap.getOrElseUpdate(currentModule.name, Namespace(iGraph.moduleMap(currentModule.name))) + renames.setModule(currentModule.name) + s match { + case wDef@ WDefInstance(_, instName, modName, _) if flatInstances.contains(s"${currentModule.name}.$instName") => + val toInline = iGraph.moduleMap(modName) match { + case m: ExtModule => throw new PassException(s"Cannot inline external module ${m.name}") + case m: Module => m } - val stmts = toInline.ports.map(p => DefWire(p.info, p.name, p.tpe)) :+ toInline.body - onStmt(prefix + instName + inlineDelim, moduleName)(Block(stmts)) - } else WDefInstance(info, prefix + instName, moduleName, instTpe) - case sx => sx map appendRefPrefix(prefix, currentModule) map onStmt(prefix, currentModule) map appendNamePrefix(prefix) + + val ports = toInline.ports.map(p => DefWire(p.info, p.name, p.tpe)) + + val subRenames = RenameMap() + subRenames.setCircuit(currentModule.circuit.name) + val bodyx = Block(ports :+ toInline.body) map onStmt(currentModule.copy(name=modName), subRenames) + + val names = "" +: Uniquify + .enumerateNames(Uniquify.stmtToType(bodyx)(NoInfo, "")) + .map(_.mkString("_")) + + /** The returned prefix will not be "prefix unique". It may be the same as other existing prefixes in the namespace. + * However, prepending this prefix to all inlined components is guaranteed to not conflict with this module's + * namespace. To make it prefix unique, this requires expanding all names in the namespace to include their + * prefixes before calling findValidPrefix. + */ + val safePrefix = Uniquify.findValidPrefix(instName + inlineDelim, names, ns.cloneUnderlying - instName) + + ports.foreach( p => renames.rename(s"$instName.${p.name}", safePrefix + p.name) ) + + def recName(s: Statement): Statement = s.map(recName).map(appendNamePrefix(safePrefix, ns, subRenames)) + def recRef(s: Statement): Statement = s.map(recRef).map(appendRefPrefix(currentModule.copy(name=modName), subRenames)) + + bodyx + .map(recName) + .map(recRef) + case sx => sx + .map(appendRefPrefix(currentModule, renames)) + .map(onStmt(currentModule, renames)) + } } - val flatCircuit = c.copy(modules = c.modules.flatMap { + val renames = RenameMap() + renames.setCircuit(c.main) + val flatCircuit = c.copy(modules = c.modules.flatMap { case m if flatModules.contains(m.name) => None - case m => - Some(m map onStmt("", m.name)) + case m => Some(m.map(onStmt(ModuleName(m.name, CircuitName(c.main)), renames))) }) CircuitState(flatCircuit, LowForm, annos, None) } diff --git a/src/main/scala/firrtl/passes/clocklist/ClockList.scala b/src/main/scala/firrtl/passes/clocklist/ClockList.scala index 43583726..7be43471 100644 --- a/src/main/scala/firrtl/passes/clocklist/ClockList.scala +++ b/src/main/scala/firrtl/passes/clocklist/ClockList.scala @@ -42,7 +42,7 @@ class ClockList(top: String, writer: Writer) extends Pass { // Inline the clock-only circuit up to the specified top module val modulesToInline = (c.modules.collect { case Module(_, n, _, _) if n != top => ModuleName(n, CircuitName(c.main)) }).toSet - val inlineTransform = new InlineInstances + val inlineTransform = new InlineInstances{ override val inlineDelim = "$" } val inlinedCircuit = inlineTransform.run(onlyClockCircuit, modulesToInline, Set(), Seq()).circuit val topModule = inlinedCircuit.modules.find(_.name == top).getOrElse(throwInternalError("no top module")) diff --git a/src/test/scala/firrtlTests/FlattenTests.scala b/src/test/scala/firrtlTests/FlattenTests.scala index 570d03bf..77a221be 100644 --- a/src/test/scala/firrtlTests/FlattenTests.scala +++ b/src/test/scala/firrtlTests/FlattenTests.scala @@ -44,11 +44,11 @@ class FlattenTests extends LowTransformSpec { | module Top : | input a : UInt<32> | output b : UInt<32> - | wire i$a : UInt<32> - | wire i$b : UInt<32> - | i$b <= i$a - | b <= i$b - | i$a <= a + | wire i_a : UInt<32> + | wire i_b : UInt<32> + | i_b <= i_a + | b <= i_b + | i_a <= a | module Inline1 : | input a : UInt<32> | output b : UInt<32> @@ -77,16 +77,16 @@ class FlattenTests extends LowTransformSpec { | module Top : | input a : UInt<32> | output b : UInt<32> - | wire i1$a : UInt<32> - | wire i1$b : UInt<32> - | i1$b <= i1$a - | wire i2$a : UInt<32> - | wire i2$b : UInt<32> - | i2$b <= i2$a - | node tmp = i1$b - | b <= i2$b - | i1$a <= a - | i2$a <= tmp + | wire i1_a : UInt<32> + | wire i1_b : UInt<32> + | i1_b <= i1_a + | wire i2_a : UInt<32> + | wire i2_b : UInt<32> + | i2_b <= i2_a + | node tmp = i1_b + | b <= i2_b + | i1_a <= a + | i2_a <= tmp | module Inline1 : | input a : UInt<32> | output b : UInt<32> @@ -131,17 +131,17 @@ class FlattenTests extends LowTransformSpec { | input na : UInt<32> | output b : UInt<32> | output nb : UInt<32> - | wire i$a : UInt<32> - | wire i$b : UInt<32> - | wire i$i$a : UInt<32> - | wire i$i$b : UInt<32> - | i$i$b <= i$i$a - | i$b <= i$i$a - | i$i$a <= i$a + | wire i_a : UInt<32> + | wire i_b : UInt<32> + | wire i_i_a : UInt<32> + | wire i_i_b : UInt<32> + | i_i_b <= i_i_a + | i_b <= i_i_a + | i_i_a <= i_a | inst ni of NotInline1 - | b <= i$b - | nb <= ni.b - | i$a <= a + | b <= i_b + | nb <= ni.b + | i_a <= a | ni.a <= na | module NotInline1 : | input a : UInt<32> @@ -213,11 +213,11 @@ class FlattenTests extends LowTransformSpec { | module Inline1 : | input a : UInt<32> | output b : UInt<32> - | wire i$a : UInt<32> - | wire i$b : UInt<32> - | i$b <= i$a - | b <= i$a - | i$a <= a + | wire i_a : UInt<32> + | wire i_b : UInt<32> + | i_b <= i_a + | b <= i_a + | i_a <= a | module Inline2 : | input a : UInt<32> | output b : UInt<32> diff --git a/src/test/scala/firrtlTests/InlineInstancesTests.scala b/src/test/scala/firrtlTests/InlineInstancesTests.scala index 4398df48..6d386d48 100644 --- a/src/test/scala/firrtlTests/InlineInstancesTests.scala +++ b/src/test/scala/firrtlTests/InlineInstancesTests.scala @@ -46,11 +46,11 @@ class InlineInstancesTests extends LowTransformSpec { | module Top : | input a : UInt<32> | output b : UInt<32> - | wire i$a : UInt<32> - | wire i$b : UInt<32> - | i$b <= i$a - | b <= i$b - | i$a <= a""".stripMargin + | wire i_a : UInt<32> + | wire i_b : UInt<32> + | i_b <= i_a + | b <= i_b + | i_a <= a""".stripMargin execute(input, check, Seq(inline("Inline"))) } @@ -74,15 +74,15 @@ class InlineInstancesTests extends LowTransformSpec { | module Top : | input a : UInt<32> | output b : UInt<32> - | wire i0$a : UInt<32> - | wire i0$b : UInt<32> - | i0$b <= i0$a - | wire i1$a : UInt<32> - | wire i1$b : UInt<32> - | i1$b <= i1$a - | b <= i1$b - | i0$a <= a - | i1$a <= i0$b""".stripMargin + | wire i0_a : UInt<32> + | wire i0_b : UInt<32> + | i0_b <= i0_a + | wire i1_a : UInt<32> + | wire i1_b : UInt<32> + | i1_b <= i1_a + | b <= i1_b + | i0_a <= a + | i1_a <= i0_b""".stripMargin execute(input, check, Seq(inline("Simple"))) } @@ -106,13 +106,13 @@ class InlineInstancesTests extends LowTransformSpec { | module Top : | input a : UInt<32> | output b : UInt<32> - | wire i0$a : UInt<32> - | wire i0$b : UInt<32> - | i0$b <= i0$a + | wire i0_a : UInt<32> + | wire i0_b : UInt<32> + | i0_b <= i0_a | inst i1 of Simple | b <= i1.b - | i0$a <= a - | i1.a <= i0$b + | i0_a <= a + | i1.a <= i0_b | module Simple : | input a : UInt<32> | output b : UInt<32> @@ -146,21 +146,21 @@ class InlineInstancesTests extends LowTransformSpec { | module Top : | input a : UInt<32> | output b : UInt<32> - | wire i0$a : UInt<32> - | wire i0$b : UInt<32> - | i0$b <= i0$a + | wire i0_a : UInt<32> + | wire i0_b : UInt<32> + | i0_b <= i0_a | inst i1 of B | b <= i1.b - | i0$a <= a - | i1.a <= i0$b + | i0_a <= a + | i1.a <= i0_b | module B : | input a : UInt<32> | output b : UInt<32> - | wire i$a : UInt<32> - | wire i$b : UInt<32> - | i$b <= i$a - | b <= i$b - | i$a <= a""".stripMargin + | wire i_a : UInt<32> + | wire i_b : UInt<32> + | i_b <= i_a + | b <= i_b + | i_a <= a""".stripMargin execute(input, check, Seq(inline("A"))) } @@ -188,13 +188,13 @@ class InlineInstancesTests extends LowTransformSpec { | module Top : | input a : UInt<32> | output b : UInt<32> - | wire i$a : UInt<32> - | wire i$b : UInt<32> - | inst i$i of B - | i$b <= i$i.b - | i$i.a <= i$a - | b <= i$b - | i$a <= a + | wire i_a : UInt<32> + | wire i_b : UInt<32> + | inst i_i of B + | i_b <= i_i.b + | i_i.a <= i_a + | b <= i_b + | i_a <= a | module B : | input a : UInt<32> | output b : UInt<32> @@ -202,6 +202,99 @@ class InlineInstancesTests extends LowTransformSpec { execute(input, check, Seq(inline("A"))) } + "A module with nested inlines" should "still prepend prefixes" in { + val input = + """|circuit Top: + | module Top: + | inst foo of Foo + | module Foo: + | inst bar of Bar + | inst baz of Bar + | node foo = UInt<1>("h0") + | module Bar: + | node bar = UInt<1>("h0") + |""".stripMargin + val check = + """|circuit Top: + | module Top: + | node foo_bar_bar = UInt<1>("h0") + | inst foo_baz of Bar + | node foo_foo = UInt<1>("h0") + | module Bar: + | node bar = UInt<1>("h0") + |""".stripMargin + execute(input, check, Seq(inline("Foo"), inline("Foo.bar"))) + } + + "An inlined module" should "NOT be prefix unique" in { + val input = + """|circuit Top: + | module Top: + | inst a of A + | node a_foo = UInt<1>("h0") + | node a__bar = UInt<1>("h0") + | module A: + | node bar = UInt<1>("h0") + |""".stripMargin + val check = + """|circuit Top: + | module Top: + | node a_bar = UInt<1>("h0") + | node a_foo = UInt<1>("h0") + | node a__bar = UInt<1>("h0") + |""".stripMargin + execute(input, check, Seq(inline("A"))) + } + + /* This test is mutually exclusive with the above */ + ignore should "be prefix unique" in { + val input = + """|circuit Top: + | module Top: + | inst a of A + | node a_foo = UInt<1>("h0") + | node a__bar = UInt<1>("h0") + | module A: + | node bar = UInt<1>("h0") + |""".stripMargin + val check = + """|circuit Top: + | module Top: + | node a___bar = UInt<1>("h0") + | node a_foo = UInt<1>("h0") + | node a__bar = UInt<1>("h0") + |""".stripMargin + execute(input, check, Seq(inline("A"))) + } + + it should "uniquify sanely" in { + val input = + """|circuit Top: + | module Top: + | inst foo of Foo + | node foo_ = UInt<1>("h0") + | node foo__bar = UInt<1>("h0") + | module Foo: + | inst bar of Bar + | inst baz of Bar + | node foo = UInt<1>("h0") + | module Bar: + | node bar = UInt<1>("h0") + |""".stripMargin + val check = + """|circuit Top: + | module Top: + | node foo__bar_bar = UInt<1>("h0") + | inst foo__baz of Bar + | node foo__foo = UInt<1>("h0") + | node foo_ = UInt<1>("h0") + | node foo__bar = UInt<1>("h0") + | module Bar: + | node bar = UInt<1>("h0") + |""".stripMargin + execute(input, check, Seq(inline("Foo"), inline("Foo.bar"))) + } + // ---- Errors ---- // 1) ext module "External module" should "not be inlined" in { |
