* [stevo]: combine generates, make it a trait * [stevo]: add Generator ala rocket-chip, some other cleanup * [stevo]: remove Generator, since that generates firrtl... * [stevo]: still debugging * [stevo]: okay i think it works now * [stevo]: oops * Refactor new generate code. Mostly just style stuff.
218 lines
7.1 KiB
Scala
218 lines
7.1 KiB
Scala
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
|
|
}
|