Skip to content

Commit

Permalink
Merge branch 'master' into 1.5-release
Browse files Browse the repository at this point in the history
  • Loading branch information
jackkoenig committed Dec 18, 2021
2 parents 9036836 + a1a0260 commit 9523b2b
Show file tree
Hide file tree
Showing 10 changed files with 1,056 additions and 2 deletions.
248 changes: 248 additions & 0 deletions src/main/scala/firrtl/Driver.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
// SPDX-License-Identifier: Apache-2.0

package firrtl

import scala.collection._
import scala.util.{Failure, Try}
import java.io.{File, FileNotFoundException}
import annotations._
import firrtl.transforms._
import firrtl.Utils.throwInternalError
import firrtl.stage.{FirrtlExecutionResultView, FirrtlStage}
import firrtl.stage.phases.DriverCompatibility
import firrtl.options.{Dependency, Phase, PhaseManager, StageUtils, Viewer}
import firrtl.options.phases.DeletedWrapper

/**
* The driver provides methods to access the firrtl compiler.
* Invoke the compiler with either a FirrtlExecutionOption
*
* @example
* {{{
* val optionsManager = new ExecutionOptionsManager("firrtl")
* optionsManager.register(
* FirrtlExecutionOptionsKey ->
* new FirrtlExecutionOptions(topName = "Dummy", compilerName = "verilog"))
* firrtl.Driver.execute(optionsManager)
* }}}
* or a series of command line arguments
* @example
* {{{
* firrtl.Driver.execute(Array("--top-name Dummy --compiler verilog".split(" +"))
* }}}
* each approach has its own endearing aspects
* @see firrtlTests/DriverSpec.scala in the test directory for a lot more examples
* @see [[CompilerUtils.mergeTransforms]] to see how customTransformations are inserted
*/
@deprecated("Use firrtl.stage.FirrtlStage", "FIRRTL 1.2")
object Driver {

/** Print a warning message
*
* @param message error message
*/
@deprecated("Use firrtl.options.StageUtils.dramaticWarning", "FIRRTL 1.2")
def dramaticWarning(message: String): Unit = StageUtils.dramaticWarning(message)

/**
* print the message in red
*
* @param message error message
*/
@deprecated("Use firrtl.options.StageUtils.dramaticWarning", "FIRRTL 1.2")
def dramaticError(message: String): Unit = StageUtils.dramaticError(message)

/** Load annotation file based on options
* @param optionsManager use optionsManager config to load annotation file if it exists
* update the firrtlOptions with new annotations if it does
*/
@deprecated("Use side-effect free getAnnotation instead", "FIRRTL 1.1")
def loadAnnotations(optionsManager: ExecutionOptionsManager with HasFirrtlOptions): Unit = {
val msg = "Driver.loadAnnotations is deprecated, use Driver.getAnnotations instead"
Driver.dramaticWarning(msg)
optionsManager.firrtlOptions = optionsManager.firrtlOptions.copy(
annotations = Driver.getAnnotations(optionsManager).toList
)
}

/** Get annotations from specified files and options
*
* @param optionsManager use optionsManager config to load annotation files
* @return Annotations read from files
*/
def getAnnotations(
optionsManager: ExecutionOptionsManager with HasFirrtlOptions
): Seq[Annotation] = {
val firrtlConfig = optionsManager.firrtlOptions

//noinspection ScalaDeprecation
val oldAnnoFileName = firrtlConfig.getAnnotationFileName(optionsManager)
val oldAnnoFile = new File(oldAnnoFileName).getCanonicalFile

val (annoFiles, usingImplicitAnnoFile) = {
val afs = firrtlConfig.annotationFileNames.map { x =>
new File(x).getCanonicalFile
}
// Implicit anno file could be included explicitly, only include it and
// warn if it's not also explicit
val use = oldAnnoFile.exists && !afs.contains(oldAnnoFile)
if (use) (oldAnnoFile +: afs, true) else (afs, false)
}

// Warnings to get people to change to drop old API
if (firrtlConfig.annotationFileNameOverride.nonEmpty) {
val msg = "annotationFileNameOverride has been removed, file will be ignored! " +
"Use annotationFileNames"
dramaticError(msg)
} else if (usingImplicitAnnoFile) {
val msg = "Implicit .anno file from top-name has been removed, file will be ignored!\n" +
(" " * 9) + "Use explicit -faf option or annotationFileNames"
dramaticError(msg)
}

val loadedAnnos = annoFiles.flatMap { file =>
if (!file.exists) {
throw new AnnotationFileNotFoundException(file)
}
JsonProtocol.deserialize(file)
}

val targetDirAnno = List(BlackBoxTargetDirAnno(optionsManager.targetDirName))

// Output Annotations
val outputAnnos = firrtlConfig.getEmitterAnnos(optionsManager)

val globalAnnos = Seq(TargetDirAnnotation(optionsManager.targetDirName)) ++
(if (firrtlConfig.dontCheckCombLoops) Seq(DontCheckCombLoopsAnnotation) else Seq()) ++
(if (firrtlConfig.noDCE) Seq(NoDCEAnnotation) else Seq())

targetDirAnno ++ outputAnnos ++ globalAnnos ++ firrtlConfig.annotations ++ loadedAnnos
}

private sealed trait FileExtension
private case object FirrtlFile extends FileExtension
private case object ProtoBufFile extends FileExtension

private def getFileExtension(filename: String): FileExtension =
filename.drop(filename.lastIndexOf('.')) match {
case ".pb" => ProtoBufFile
case _ => FirrtlFile // Default to FIRRTL File
}

// Useful for handling erros in the options
case class OptionsException(message: String) extends Exception(message)

/** Get the Circuit from the compile options
*
* Handles the myriad of ways it can be specified
*/
def getCircuit(optionsManager: ExecutionOptionsManager with HasFirrtlOptions): Try[ir.Circuit] = {
val firrtlConfig = optionsManager.firrtlOptions
Try {
// Check that only one "override" is used
val circuitSources = Map(
"firrtlSource" -> firrtlConfig.firrtlSource.isDefined,
"firrtlCircuit" -> firrtlConfig.firrtlCircuit.isDefined,
"inputFileNameOverride" -> firrtlConfig.inputFileNameOverride.nonEmpty
)
if (circuitSources.values.count(x => x) > 1) {
val msg = circuitSources.collect { case (s, true) => s }.mkString(" and ") +
" are set, only 1 can be set at a time!"
throw new OptionsException(msg)
}
firrtlConfig.firrtlCircuit.getOrElse {
firrtlConfig.firrtlSource.map(x => Parser.parseString(x, firrtlConfig.infoMode)).getOrElse {
if (optionsManager.topName.isEmpty && firrtlConfig.inputFileNameOverride.isEmpty) {
val message = "either top-name or input-file-override must be set"
throw new OptionsException(message)
}
if (
optionsManager.topName.isEmpty &&
firrtlConfig.inputFileNameOverride.nonEmpty &&
firrtlConfig.outputFileNameOverride.isEmpty
) {
val message = "inputFileName set but neither top-name or output-file-override is set"
throw new OptionsException(message)
}
val inputFileName = firrtlConfig.getInputFileName(optionsManager)
try {
// TODO What does InfoMode mean to ProtoBuf?
getFileExtension(inputFileName) match {
case ProtoBufFile => proto.FromProto.fromFile(inputFileName)
case FirrtlFile => Parser.parseFile(inputFileName, firrtlConfig.infoMode)
}
} catch {
case _: FileNotFoundException =>
val message = s"Input file $inputFileName not found"
throw new OptionsException(message)
}
}
}
}
}

/**
* Run the firrtl compiler using the provided option
*
* @param optionsManager the desired flags to the compiler
* @return a FirrtlExecutionResult indicating success or failure, provide access to emitted data on success
* for downstream tools as desired
*/
def execute(optionsManager: ExecutionOptionsManager with HasFirrtlOptions): FirrtlExecutionResult = {
StageUtils.dramaticWarning("firrtl.Driver is deprecated since 1.2!\nPlease switch to firrtl.stage.FirrtlMain")

val annos = optionsManager.firrtlOptions.toAnnotations ++ optionsManager.commonOptions.toAnnotations

val phases: Seq[Phase] = {
import DriverCompatibility._
new PhaseManager(
List(
Dependency[AddImplicitFirrtlFile],
Dependency[AddImplicitAnnotationFile],
Dependency[AddImplicitOutputFile],
Dependency[AddImplicitEmitter],
Dependency[FirrtlStage]
)
).transformOrder
.map(DeletedWrapper(_))
}

val annosx =
try {
phases.foldLeft(annos)((a, p) => p.transform(a))
} catch {
case e: firrtl.options.OptionsException => return FirrtlExecutionFailure(e.message)
}

Viewer[FirrtlExecutionResult].view(annosx)
}

/**
* this is a wrapper for execute that builds the options from a standard command line args,
* for example, like strings passed to main()
*
* @param args an Array of string s containing legal arguments
* @return
*/
def execute(args: Array[String]): FirrtlExecutionResult = {
val optionsManager = new ExecutionOptionsManager("firrtl") with HasFirrtlOptions

if (optionsManager.parse(args)) {
execute(optionsManager) match {
case success: FirrtlExecutionSuccess =>
success
case failure: FirrtlExecutionFailure =>
optionsManager.showUsageAsError()
failure
case result =>
throwInternalError(s"Error: Unknown Firrtl Execution result $result")
}
} else {
FirrtlExecutionFailure("Could not parser command line options")
}
}

def main(args: Array[String]): Unit = {
execute(args)
}
}
Loading

0 comments on commit 9523b2b

Please # to comment.