Refactor the harness generation; use upstream arguments and passes where appropriate

This commit is contained in:
John Wright
2019-02-06 21:48:14 -08:00
committed by John Wright
parent 22e6d9c5d4
commit c8efc5e88b

View File

@@ -15,10 +15,10 @@ object AllModules {
def add(module: String) = {
modules = modules | Set(module)
}
def rename(module: String) = {
def rename(module: String, suffix: String = "_inTestHarness") = {
var new_name = module
while (modules.contains(new_name))
new_name = new_name + "_inTestHarness"
new_name = new_name + suffix
new_name
}
}
@@ -28,17 +28,6 @@ trait HasTapeoutOptions { self: ExecutionOptionsManager with HasFirrtlOptions =>
parser.note("tapeout options")
parser.opt[String]("top-o")
.abbr("tto")
.valueName("<top-output>")
.foreach { x =>
tapeoutOptions = tapeoutOptions.copy(
topOutput = Some(x)
)
}.text {
"use this to generate top at <top-output>"
}
parser.opt[String]("harness-o")
.abbr("tho")
.valueName("<harness-output>")
@@ -72,173 +61,87 @@ trait HasTapeoutOptions { self: ExecutionOptionsManager with HasFirrtlOptions =>
"use this to set harnessTop"
}
parser.opt[String]("list-clocks")
.abbr("tlc")
.valueName("<clocks>")
.foreach { x =>
tapeoutOptions = tapeoutOptions.copy(
listClocks = Some(x)
)
}.text {
"use this to list <clocks>"
}
parser.note("")
}
case class TapeoutOptions(
input: Option[String] = None,
output: Option[String] = None,
topOutput: Option[String] = None,
harnessOutput: Option[String] = None,
annoFile: Option[String] = None,
synTop: Option[String] = None,
harnessTop: Option[String] = None,
seqMemFlags: Option[String] = Some("-o:unused.confg"),
listClocks: Option[String] = Some("-o:unused.clocks")
harnessTop: Option[String] = None
) extends LazyLogging
// 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 LazyLogging { this: App =>
lazy val optionsManager = {
def getOptionsManager = {
val optionsManager = new ExecutionOptionsManager("tapeout") with HasFirrtlOptions with HasTapeoutOptions
if (!optionsManager.parse(args)) {
throw new Exception("Error parsing options!")
}
optionsManager
}
lazy val options = optionsManager.tapeoutOptions
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
lazy val optionsManager = getOptionsManager
lazy val tapeoutOptions = optionsManager.tapeoutOptions
// Tapeout options
lazy val harnessOutput = tapeoutOptions.harnessOutput
lazy val synTop = tapeoutOptions.synTop
lazy val harnessTop = tapeoutOptions.harnessTop
private def getFirstPhasePasses(top: Boolean, harness: Boolean): Seq[Transform] = {
val pre = Seq(
lazy val firrtlOptions = optionsManager.firrtlOptions
// FIRRTL options
lazy val annoFiles = firrtlOptions.annotationFileNames
private def getFirstPhasePasses: Seq[Transform] = {
Seq(
new ReParentCircuit(synTop.get),
new RemoveUnusedModules
)
val enumerate = if (harness) { Seq(
new EnumerateModules( { m => if (m.name != options.harnessTop.get && m.name != options.synTop.get) { AllModules.add(m.name) } } ),
) } else Seq()
val post = if (top) { Seq(
new RemoveUnusedModules,
new passes.memlib.InferReadWrite(),
new passes.clocklist.ClockListTransform()
) } else Seq()
pre ++ enumerate ++ post
}
private def getFirstPhaseAnnotations(top: Boolean): AnnotationSeq = {
if (top) {
//Load annotations from file
val annotationArray: Seq[Annotation] = 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")
Seq(AnnotationUtils.fromYaml(annotationsYaml)) // TODO
} else {
Seq[Annotation]()
}
}
}
// add new annotations
AnnotationSeq(Seq(
passes.memlib.InferReadWriteAnnotation,
passes.clocklist.ClockListAnnotation.parse(
s"-c:${synTop.get}:-m:${synTop.get}:${listClocks.get}"
),
passes.memlib.ReplSeqMemAnnotation.parse(
s"-c:${synTop.get}:${seqMemFlags.get}"
)
) ++ annotationArray)
} else { AnnotationSeq(Seq.empty) }
}
private def getSecondPhasePasses: Seq[Transform] = {
// always the same for now
Seq(
new ConvertToExtMod((m) => m.name == synTop.get),
new RenameModulesAndInstances((m) => AllModules.rename(m)),
// new RemoveUnusedModules,
new EnumerateModules( { m => if (m.name != tapeoutOptions.harnessTop.get && m.name != tapeoutOptions.synTop.get) { AllModules.add(m.name) } } ),
new RenameModulesAndInstances((m) => AllModules.rename(m, "_in" + harnessTop.get)),
new RemoveUnusedModules
)
}
// always the same for now
private def getSecondPhaseAnnotations: AnnotationSeq = AnnotationSeq(Seq.empty)
// Top Generation
protected def firstPhase(top: Boolean, harness: Boolean): Unit = {
require(top || harness, "Must specify either top or harness")
protected def firstPhase: Unit = {
val firrtlOptions = optionsManager.firrtlOptions
optionsManager.firrtlOptions = firrtlOptions.copy(
annotations = firrtlOptions.annotations ++ getFirstPhaseAnnotations(top)
val firstPhaseOptions = getOptionsManager
firstPhaseOptions.firrtlOptions = firstPhaseOptions.firrtlOptions.copy(
customTransforms = firrtlOptions.customTransforms ++ getFirstPhasePasses
)
optionsManager.firrtlOptions = firrtlOptions.copy(
customTransforms = firrtlOptions.customTransforms ++ getFirstPhasePasses(top, harness)
)
firrtl.Driver.execute(firstPhaseOptions)
}
// Harness Generation
protected def secondPhase: Unit = {
val firrtlOptions = optionsManager.firrtlOptions
optionsManager.firrtlOptions = firrtlOptions.copy(
annotations = firrtlOptions.annotations ++ getSecondPhaseAnnotations
val secondPhaseOptions = getOptionsManager
secondPhaseOptions.firrtlOptions = secondPhaseOptions.firrtlOptions.copy(
outputFileNameOverride = harnessOutput.get,
customTransforms = getSecondPhasePasses
)
optionsManager.firrtlOptions = firrtlOptions.copy(
customTransforms = firrtlOptions.customTransforms ++ getSecondPhasePasses
)
}
protected def execute: Unit = {
firrtl.Driver.execute(optionsManager)
firrtl.Driver.execute(secondPhaseOptions)
}
}
object GenerateTop extends App with 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(
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)
execute
firstPhase
}
object GenerateHarness extends App with 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(
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
execute
}
object GenerateTopAndHarness extends App with 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)
firstPhase
secondPhase
execute
}