From 43f1a699ad54721096d203f50072d2181d1c0bcd Mon Sep 17 00:00:00 2001 From: Colin Schmidt Date: Tue, 21 Feb 2017 11:11:33 -0800 Subject: [PATCH] Move passes from pfpmp to barstools. (#5) * Move passes from pfpmp to barstools. * add an app that does both the harness and top generation This reduces the number of firrtl.compile calls * Add the ability to read annotations file This helps with chisel annotation integration --- .../transforms/ConvertToExtModPass.scala | 36 ++++++ .../scala/transforms/EnumerateModules.scala | 32 +++++ .../scala/transforms/GenerateHarness.scala | 79 ++++++++++++ .../main/scala/transforms/GenerateTop.scala | 77 +++++++++++ .../transforms/GenerateTopAndHarness.scala | 120 ++++++++++++++++++ .../scala/transforms/ReParentCircuit.scala | 26 ++++ .../transforms/RemoveUnusedModules.scala | 59 +++++++++ .../RenameModulesAndInstances.scala | 42 ++++++ 8 files changed, 471 insertions(+) create mode 100644 tapeout/src/main/scala/transforms/ConvertToExtModPass.scala create mode 100644 tapeout/src/main/scala/transforms/EnumerateModules.scala create mode 100644 tapeout/src/main/scala/transforms/GenerateHarness.scala create mode 100644 tapeout/src/main/scala/transforms/GenerateTop.scala create mode 100644 tapeout/src/main/scala/transforms/GenerateTopAndHarness.scala create mode 100644 tapeout/src/main/scala/transforms/ReParentCircuit.scala create mode 100644 tapeout/src/main/scala/transforms/RemoveUnusedModules.scala create mode 100644 tapeout/src/main/scala/transforms/RenameModulesAndInstances.scala diff --git a/tapeout/src/main/scala/transforms/ConvertToExtModPass.scala b/tapeout/src/main/scala/transforms/ConvertToExtModPass.scala new file mode 100644 index 00000000..98425fd0 --- /dev/null +++ b/tapeout/src/main/scala/transforms/ConvertToExtModPass.scala @@ -0,0 +1,36 @@ +// See LICENSE for license details. + +package barstools.tapeout.transforms + +import firrtl._ +import firrtl.ir._ +import firrtl.passes.Pass + +// Converts some modules to external modules, based on a given function. If +// that function returns "true" then the module is converted into an ExtModule, +// otherwise it's left alone. +class ConvertToExtModPass(classify: (Module) => Boolean) extends Pass { + def name = "Convert to External Modules" + + def run(c: Circuit): Circuit = { + val modulesx = c.modules.map { + case m: ExtModule => m + case m: Module => + if (classify(m)) { + new ExtModule(m.info, m.name, m.ports, m.name, Seq.empty) + } else { + m + } + } + Circuit(c.info, modulesx, c.main) + } +} +class ConvertToExtMod(classify: (Module) => Boolean) extends Transform with PassBased { + def inputForm = MidForm + def outputForm = MidForm + def passSeq = Seq(new ConvertToExtModPass(classify)) + + def execute(state: CircuitState): CircuitState = { + CircuitState(runPasses(state.circuit), state.form) + } +} diff --git a/tapeout/src/main/scala/transforms/EnumerateModules.scala b/tapeout/src/main/scala/transforms/EnumerateModules.scala new file mode 100644 index 00000000..ec4389c6 --- /dev/null +++ b/tapeout/src/main/scala/transforms/EnumerateModules.scala @@ -0,0 +1,32 @@ +// See LICENSE for license details. + +package barstools.tapeout.transforms + +import firrtl._ +import firrtl.ir._ +import firrtl.passes.Pass + +class EnumerateModulesPass(enumerate: (Module) => Unit) extends Pass { + def name = "Enumurate Modules" + + def run(c: Circuit): Circuit = { + val modulesx = c.modules.map { + case m: ExtModule => m + case m: Module => { + enumerate(m) + m + } + } + Circuit(c.info, modulesx, c.main) + } +} + +class EnumerateModules(enumerate: (Module) => Unit) extends Transform with PassBased { + def inputForm = LowForm + def outputForm = LowForm + def passSeq = Seq(new EnumerateModulesPass(enumerate)) + + def execute(state: CircuitState): CircuitState = { + CircuitState(runPasses(state.circuit), state.form) + } +} diff --git a/tapeout/src/main/scala/transforms/GenerateHarness.scala b/tapeout/src/main/scala/transforms/GenerateHarness.scala new file mode 100644 index 00000000..eea7960e --- /dev/null +++ b/tapeout/src/main/scala/transforms/GenerateHarness.scala @@ -0,0 +1,79 @@ +// See LICENSE for license details. + +package barstools.tapeout.transforms + +import firrtl._ +import firrtl.ir._ +import firrtl.annotations._ +import firrtl.passes.Pass + +object AllModules { + private var modules = Set[String]() + def add(module: String) = { + modules = modules | Set(module) + } + def rename(module: String) = { + var new_name = module + while (modules.contains(new_name)) + new_name = new_name + "_inTestHarness" + new_name + } +} + +object GenerateHarness extends App { + var input: Option[String] = None + var output: Option[String] = None + var synTop: Option[String] = None + var harnessTop: Option[String] = None + + var usedOptions = Set.empty[Integer] + args.zipWithIndex.foreach{ case (arg, i) => + arg match { + case "-i" => { + input = Some(args(i+1)) + usedOptions = usedOptions | Set(i+1) + } + case "-o" => { + output = Some(args(i+1)) + usedOptions = usedOptions | Set(i+1) + } + case "--syn-top" => { + synTop = Some(args(i+1)) + usedOptions = usedOptions | Set(i+1) + } + case "--harness-top" => { + harnessTop = Some(args(i+1)) + usedOptions = usedOptions | Set(i+1) + } + case _ => { + if (! (usedOptions contains i)) { + error("Unknown option " + arg) + } + } + } + } + + firrtl.Driver.compile( + input.get, + output.get, + new VerilogCompiler(), + Parser.UseInfo, + Seq( + new ReParentCircuit(synTop.get), + new RemoveUnusedModules, + new EnumerateModules( { m => if (m.name != synTop.get) { AllModules.add(m.name) } } ) + ) + ) + + firrtl.Driver.compile( + input.get, + output.get, + new VerilogCompiler(), + Parser.UseInfo, + Seq( + new ConvertToExtMod((m) => m.name == synTop.get), + new RemoveUnusedModules, + new RenameModulesAndInstances((m) => AllModules.rename(m)) + ) + ) +} diff --git a/tapeout/src/main/scala/transforms/GenerateTop.scala b/tapeout/src/main/scala/transforms/GenerateTop.scala new file mode 100644 index 00000000..dc069a5b --- /dev/null +++ b/tapeout/src/main/scala/transforms/GenerateTop.scala @@ -0,0 +1,77 @@ +// See LICENSE for license details. + +package barstools.tapeout.transforms + +import firrtl._ +import firrtl.ir._ +import firrtl.annotations._ +import firrtl.passes.Pass + +object GenerateTop extends App { + var input: Option[String] = None + var output: Option[String] = None + var synTop: Option[String] = None + var harnessTop: Option[String] = None + var seqMemFlags: Option[String] = Some("-o:unused.confg") + var listClocks: Option[String] = Some("-o:unused.clocks") + + var usedOptions = Set.empty[Integer] + args.zipWithIndex.foreach{ case (arg, i) => + arg match { + case "-i" => { + input = Some(args(i+1)) + usedOptions = usedOptions | Set(i+1) + } + case "-o" => { + output = Some(args(i+1)) + usedOptions = usedOptions | Set(i+1) + } + case "--syn-top" => { + synTop = Some(args(i+1)) + usedOptions = usedOptions | Set(i+1) + } + case "--harness-top" => { + harnessTop = Some(args(i+1)) + usedOptions = usedOptions | Set(i+1) + } + case "--seq-mem-flags" => { + seqMemFlags = Some(args(i+1)) + usedOptions = usedOptions | Set(i+1) + } + case "--list-clocks" => { + listClocks = Some(args(i+1)) + usedOptions = usedOptions | Set(i+1) + } + case _ => { + if (! (usedOptions contains i)) { + error("Unknown option " + arg) + } + } + } + } + + firrtl.Driver.compile( + input.get, + output.get, + new VerilogCompiler(), + Parser.UseInfo, + Seq( + new ReParentCircuit(synTop.get), + new RemoveUnusedModules, + new passes.memlib.InferReadWrite(), + new passes.memlib.ReplSeqMem(), + new passes.clocklist.ClockListTransform() + ), + AnnotationMap(Seq( + passes.memlib.InferReadWriteAnnotation( + s"${synTop.get}" + ), + passes.clocklist.ClockListAnnotation( + s"-c:${synTop.get}:-m:${synTop.get}:${listClocks.get}" + ), + passes.memlib.ReplSeqMemAnnotation( + s"-c:${synTop.get}:${seqMemFlags.get}" + ) + )) + ) +} diff --git a/tapeout/src/main/scala/transforms/GenerateTopAndHarness.scala b/tapeout/src/main/scala/transforms/GenerateTopAndHarness.scala new file mode 100644 index 00000000..06dbe155 --- /dev/null +++ b/tapeout/src/main/scala/transforms/GenerateTopAndHarness.scala @@ -0,0 +1,120 @@ +// See LICENSE for license details. + +package barstools.tapeout.transforms + +import firrtl._ +import firrtl.ir._ +import firrtl.annotations._ +import firrtl.passes.Pass + +import java.io.File +import firrtl.annotations.AnnotationYamlProtocol._ +import net.jcazevedo.moultingyaml._ + +object GenerateTopAndHarness extends App { + var input: Option[String] = None + var topOutput: Option[String] = None + var harnessOutput: Option[String] = None + var annoFile: Option[String] = None + var synTop: Option[String] = None + var harnessTop: Option[String] = None + var seqMemFlags: Option[String] = Some("-o:unused.confg") + var listClocks: Option[String] = Some("-o:unused.clocks") + + var usedOptions = Set.empty[Integer] + args.zipWithIndex.foreach{ case (arg, i) => + arg match { + case "-i" => { + input = Some(args(i+1)) + usedOptions = usedOptions | Set(i+1) + } + case "--top-o" => { + topOutput = Some(args(i+1)) + usedOptions = usedOptions | Set(i+1) + } + case "--harness-o" => { + harnessOutput = Some(args(i+1)) + usedOptions = usedOptions | Set(i+1) + } + case "--anno-file" => { + annoFile = Some(args(i+1)) + usedOptions = usedOptions | Set(i+1) + } + case "--syn-top" => { + synTop = Some(args(i+1)) + usedOptions = usedOptions | Set(i+1) + } + case "--harness-top" => { + harnessTop = Some(args(i+1)) + usedOptions = usedOptions | Set(i+1) + } + case "--seq-mem-flags" => { + seqMemFlags = Some(args(i+1)) + usedOptions = usedOptions | Set(i+1) + } + case "--list-clocks" => { + listClocks = Some(args(i+1)) + usedOptions = usedOptions | Set(i+1) + } + case _ => { + if (! (usedOptions contains i)) { + error("Unknown option " + arg) + } + } + } + } + //Load annotations from file + val annotationArray = annoFile match { + case None => Array[Annotation]() + case Some(fileName) => { + val annotations = new File(fileName) + if(annotations.exists) { + val annotationsYaml = io.Source.fromFile(annotations).getLines().mkString("\n").parseYaml + annotationsYaml.convertTo[Array[Annotation]] + } else { + Array[Annotation]() + } + } + } + + //Top Generation + firrtl.Driver.compile( + input.get, + topOutput.get, + new VerilogCompiler(), + Parser.UseInfo, + Seq( + new ReParentCircuit(synTop.get), + new RemoveUnusedModules, + new EnumerateModules( { m => if (m.name != synTop.get) { AllModules.add(m.name) } } ), + new passes.memlib.InferReadWrite(), + new passes.memlib.ReplSeqMem(), + new passes.clocklist.ClockListTransform() + ), + AnnotationMap(Seq( + passes.memlib.InferReadWriteAnnotation( + s"${synTop.get}" + ), + passes.clocklist.ClockListAnnotation( + s"-c:${synTop.get}:-m:${synTop.get}:${listClocks.get}" + ), + passes.memlib.ReplSeqMemAnnotation( + s"-c:${synTop.get}:${seqMemFlags.get}" + ) + ) ++ annotationArray) + ) + + //Harness Generation + firrtl.Driver.compile( + input.get, + harnessOutput.get, + new VerilogCompiler(), + Parser.UseInfo, + Seq( + new ConvertToExtMod((m) => m.name == synTop.get), + new RemoveUnusedModules, + new RenameModulesAndInstances((m) => AllModules.rename(m)) + ) + ) +} + diff --git a/tapeout/src/main/scala/transforms/ReParentCircuit.scala b/tapeout/src/main/scala/transforms/ReParentCircuit.scala new file mode 100644 index 00000000..da3f079a --- /dev/null +++ b/tapeout/src/main/scala/transforms/ReParentCircuit.scala @@ -0,0 +1,26 @@ +// See LICENSE for license details. + +package barstools.tapeout.transforms + +import firrtl._ +import firrtl.ir._ +import firrtl.passes.Pass + +// "Re-Parents" a circuit, which changes the top module to something else. +class ReParentCircuitPass(newTopName: String) extends Pass { + def name = "Re-Parent Circuit" + + def run(c: Circuit): Circuit = { + Circuit(c.info, c.modules, newTopName) + } +} + +class ReParentCircuit(newTopName: String) extends Transform with PassBased { + def inputForm = HighForm + def outputForm = HighForm + def passSeq = Seq(new ReParentCircuitPass(newTopName)) + + def execute(state: CircuitState): CircuitState = { + CircuitState(runPasses(state.circuit), state.form) + } +} diff --git a/tapeout/src/main/scala/transforms/RemoveUnusedModules.scala b/tapeout/src/main/scala/transforms/RemoveUnusedModules.scala new file mode 100644 index 00000000..d68edbea --- /dev/null +++ b/tapeout/src/main/scala/transforms/RemoveUnusedModules.scala @@ -0,0 +1,59 @@ +// See LICENSE for license details. + +package barstools.tapeout.transforms + +import firrtl._ +import firrtl.ir._ +import firrtl.passes.Pass + +// Removes all the unused modules in a circuit by recursing through every +// instance (starting at the main module) +class RemoveUnusedModulesPass extends Pass { + def name = "Remove Unused Modules" + + def run(c: Circuit): Circuit = { + val modulesByName = c.modules.map{ + case m: Module => (m.name, Some(m)) + case m: ExtModule => (m.name, None) + }.toMap + + def getUsedModules(om: Option[Module]): Set[String] = { + om match { + case Some(m) => { + def someStatements(statement: Statement): Seq[Statement] = + statement match { + case b: Block => + b.stmts.map{ someStatements(_) } + .foldLeft(Seq[Statement]())(_ ++ _) + case i: DefInstance => Seq(i) + case w: WDefInstance => Seq(w) + case _ => Seq() + } + + someStatements(m.body).map{ + case s: DefInstance => Set(s.module) | getUsedModules(modulesByName(s.module)) + case s: WDefInstance => Set(s.module) | getUsedModules(modulesByName(s.module)) + case _ => Set[String]() + }.foldLeft(Set(m.name))(_ | _) + } + + case None => Set.empty[String] + } + } + val usedModuleSet = getUsedModules(modulesByName(c.main)) + + val usedModuleSeq = c.modules.filter { usedModuleSet contains _.name } + + Circuit(c.info, usedModuleSeq, c.main) + } +} + +class RemoveUnusedModules extends Transform with PassBased { + def inputForm = MidForm + def outputForm = MidForm + def passSeq = Seq(new RemoveUnusedModulesPass) + + def execute(state: CircuitState): CircuitState = { + CircuitState(runPasses(state.circuit), state.form) + } +} diff --git a/tapeout/src/main/scala/transforms/RenameModulesAndInstances.scala b/tapeout/src/main/scala/transforms/RenameModulesAndInstances.scala new file mode 100644 index 00000000..2a940563 --- /dev/null +++ b/tapeout/src/main/scala/transforms/RenameModulesAndInstances.scala @@ -0,0 +1,42 @@ +// See LICENSE for license details. + +package barstools.tapeout.transforms + +import firrtl._ +import firrtl.ir._ +import firrtl.passes.Pass + +// This doesn't rename ExtModules under the assumption that they're some +// Verilog black box and therefor can't be renamed. Since the point is to +// allow FIRRTL to be linked together using "cat" and ExtModules don't get +// emitted, this should be safe. +class RenameModulesAndInstancesPass(rename: (String) => String) extends Pass { + def name = "Rename Modules and Instances" + + def renameInstances(body: Statement): Statement = { + body match { + case m: DefInstance => new DefInstance(m.info, m.name, rename(m.module)) + case m: WDefInstance => new WDefInstance(m.info, m.name, rename(m.module), m.tpe) + case b: Block => new Block( b.stmts map { s => renameInstances(s) } ) + case s: Statement => s + } + } + + def run(c: Circuit): Circuit = { + val modulesx = c.modules.map { + case m: ExtModule => m + case m: Module => new Module(m.info, rename(m.name), m.ports, renameInstances(m.body)) + } + Circuit(c.info, modulesx, c.main) + } +} + +class RenameModulesAndInstances(rename: (String) => String) extends Transform with PassBased { + def inputForm = LowForm + def outputForm = LowForm + def passSeq = Seq(new RenameModulesAndInstancesPass(rename)) + + def execute(state: CircuitState): CircuitState = { + CircuitState(runPasses(state.circuit), state.form) + } +}