diff --git a/tapeout/src/main/scala/transforms/Generate.scala b/tapeout/src/main/scala/transforms/Generate.scala new file mode 100644 index 00000000..79bbd3b0 --- /dev/null +++ b/tapeout/src/main/scala/transforms/Generate.scala @@ -0,0 +1,217 @@ +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._ +import com.typesafe.scalalogging.LazyLogging + +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 + } +} + +case class ParsedInput(args: Seq[String]) extends LazyLogging { + var input: Option[String] = None + var output: 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 "-o" => { + output = 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)) { + logger.error("Unknown option " + arg) + } + } + } + } + +} + +// Requires two phases, one to collect modules below synTop in the hierarchy +// and a second to remove those modules to generate the test harness +sealed trait GenerateTopAndHarnessApp extends App with LazyLogging { + lazy val options: ParsedInput = ParsedInput(args) + lazy val input = options.input + lazy val output = options.output + lazy val topOutput = options.topOutput + lazy val harnessOutput = options.harnessOutput + lazy val annoFile = options.annoFile + lazy val synTop = options.synTop + lazy val harnessTop = options.harnessTop + lazy val seqMemFlags = options.seqMemFlags + lazy val listClocks = options.listClocks + + private def getFirstPhasePasses(top: Boolean, harness: Boolean): Seq[Transform] = { + val pre = Seq( + new ReParentCircuit(synTop.get), + new RemoveUnusedModules + ) + + val enumerate = if (harness) { Seq( + new EnumerateModules( { m => if (m.name != options.synTop.get) { AllModules.add(m.name) } } ) + ) } else Seq() + + val post = if (top) { Seq( + new passes.memlib.InferReadWrite(), + new passes.memlib.ReplSeqMem(), + new passes.clocklist.ClockListTransform() + ) } else Seq() + + pre ++ enumerate ++ post + } + + private def getFirstPhaseAnnotations(top: Boolean): AnnotationMap = { + if (top) { + //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]() + } + } + } + // add new annotations + 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) + } else { AnnotationMap(Seq.empty) } + } + + private def getSecondPhasePasses: Seq[Transform] = { + // always the same for now + Seq( + new ConvertToExtMod((m) => m.name == synTop.get), + new RemoveUnusedModules, + new RenameModulesAndInstances((m) => AllModules.rename(m)) + ) + } + + // always the same for now + private def getSecondPhaseAnnotations: AnnotationMap = AnnotationMap(Seq.empty) + + // Top Generation + protected def firstPhase(top: Boolean, harness: Boolean): Unit = { + require(top || harness, "Must specify either top or harness") + firrtl.Driver.compile( + input.get, + topOutput.getOrElse(output.get), + new VerilogCompiler(), + Parser.UseInfo, + getFirstPhasePasses(top, harness), + getFirstPhaseAnnotations(top) + ) + } + + // Harness Generation + protected def secondPhase: Unit = { + firrtl.Driver.compile( + input.get, + harnessOutput.getOrElse(output.get), + new VerilogCompiler(), + Parser.UseInfo, + getSecondPhasePasses, + getSecondPhaseAnnotations + ) + } +} + +object GenerateTop extends GenerateTopAndHarnessApp { + // warn about unused options + harnessOutput.foreach(n => logger.warn(s"Not using harness output filename $n since you asked for just a top-level output.")) + topOutput.foreach(_.foreach{ + n => logger.warn(s"Not using generic output filename $n since you asked for just a top-level output and also specified a generic output.")}) + // Only need a single phase to generate the top module + firstPhase(top = true, harness = false) +} + +object GenerateHarness extends GenerateTopAndHarnessApp { + // warn about unused options + topOutput.foreach(n => logger.warn(s"Not using top-level output filename $n since you asked for just a test harness.")) + annoFile.foreach(n => logger.warn(s"Not using annotations file $n since you asked for just a test harness.")) + seqMemFlags.filter(_ != "-o:unused.confg").foreach { + n => logger.warn(s"Not using SeqMem flags $n since you asked for just a test harness.") } + listClocks.filter(_ != "-o:unused.clocks").foreach { + n => logger.warn(s"Not using clocks list $n since you asked for just a test harness.") } + harnessOutput.foreach(_.foreach{ + n => logger.warn(s"Not using generic output filename $n since you asked for just a test harness and also specified a generic output.")}) + // Do minimal work for the first phase to generate test harness + firstPhase(top = false, harness = true) + secondPhase +} + +object GenerateTopAndHarness extends GenerateTopAndHarnessApp { + // warn about unused options + output.foreach(n => logger.warn(s"Not using generic output filename $n since you asked for both a top-level output and a test harness.")) + // Do everything, top and harness generation + firstPhase(top = true, harness = true) + secondPhase +} diff --git a/tapeout/src/main/scala/transforms/GenerateHarness.scala b/tapeout/src/main/scala/transforms/GenerateHarness.scala deleted file mode 100644 index eea7960e..00000000 --- a/tapeout/src/main/scala/transforms/GenerateHarness.scala +++ /dev/null @@ -1,79 +0,0 @@ -// 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 deleted file mode 100644 index dc069a5b..00000000 --- a/tapeout/src/main/scala/transforms/GenerateTop.scala +++ /dev/null @@ -1,77 +0,0 @@ -// 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 deleted file mode 100644 index 06dbe155..00000000 --- a/tapeout/src/main/scala/transforms/GenerateTopAndHarness.scala +++ /dev/null @@ -1,120 +0,0 @@ -// 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)) - ) - ) -} -