Skip to content

Commit 9523b2b

Browse files
committed
Merge branch 'master' into 1.5-release
2 parents 9036836 + a1a0260 commit 9523b2b

File tree

10 files changed

+1056
-2
lines changed

10 files changed

+1056
-2
lines changed

src/main/scala/firrtl/Driver.scala

Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
package firrtl
4+
5+
import scala.collection._
6+
import scala.util.{Failure, Try}
7+
import java.io.{File, FileNotFoundException}
8+
import annotations._
9+
import firrtl.transforms._
10+
import firrtl.Utils.throwInternalError
11+
import firrtl.stage.{FirrtlExecutionResultView, FirrtlStage}
12+
import firrtl.stage.phases.DriverCompatibility
13+
import firrtl.options.{Dependency, Phase, PhaseManager, StageUtils, Viewer}
14+
import firrtl.options.phases.DeletedWrapper
15+
16+
/**
17+
* The driver provides methods to access the firrtl compiler.
18+
* Invoke the compiler with either a FirrtlExecutionOption
19+
*
20+
* @example
21+
* {{{
22+
* val optionsManager = new ExecutionOptionsManager("firrtl")
23+
* optionsManager.register(
24+
* FirrtlExecutionOptionsKey ->
25+
* new FirrtlExecutionOptions(topName = "Dummy", compilerName = "verilog"))
26+
* firrtl.Driver.execute(optionsManager)
27+
* }}}
28+
* or a series of command line arguments
29+
* @example
30+
* {{{
31+
* firrtl.Driver.execute(Array("--top-name Dummy --compiler verilog".split(" +"))
32+
* }}}
33+
* each approach has its own endearing aspects
34+
* @see firrtlTests/DriverSpec.scala in the test directory for a lot more examples
35+
* @see [[CompilerUtils.mergeTransforms]] to see how customTransformations are inserted
36+
*/
37+
@deprecated("Use firrtl.stage.FirrtlStage", "FIRRTL 1.2")
38+
object Driver {
39+
40+
/** Print a warning message
41+
*
42+
* @param message error message
43+
*/
44+
@deprecated("Use firrtl.options.StageUtils.dramaticWarning", "FIRRTL 1.2")
45+
def dramaticWarning(message: String): Unit = StageUtils.dramaticWarning(message)
46+
47+
/**
48+
* print the message in red
49+
*
50+
* @param message error message
51+
*/
52+
@deprecated("Use firrtl.options.StageUtils.dramaticWarning", "FIRRTL 1.2")
53+
def dramaticError(message: String): Unit = StageUtils.dramaticError(message)
54+
55+
/** Load annotation file based on options
56+
* @param optionsManager use optionsManager config to load annotation file if it exists
57+
* update the firrtlOptions with new annotations if it does
58+
*/
59+
@deprecated("Use side-effect free getAnnotation instead", "FIRRTL 1.1")
60+
def loadAnnotations(optionsManager: ExecutionOptionsManager with HasFirrtlOptions): Unit = {
61+
val msg = "Driver.loadAnnotations is deprecated, use Driver.getAnnotations instead"
62+
Driver.dramaticWarning(msg)
63+
optionsManager.firrtlOptions = optionsManager.firrtlOptions.copy(
64+
annotations = Driver.getAnnotations(optionsManager).toList
65+
)
66+
}
67+
68+
/** Get annotations from specified files and options
69+
*
70+
* @param optionsManager use optionsManager config to load annotation files
71+
* @return Annotations read from files
72+
*/
73+
def getAnnotations(
74+
optionsManager: ExecutionOptionsManager with HasFirrtlOptions
75+
): Seq[Annotation] = {
76+
val firrtlConfig = optionsManager.firrtlOptions
77+
78+
//noinspection ScalaDeprecation
79+
val oldAnnoFileName = firrtlConfig.getAnnotationFileName(optionsManager)
80+
val oldAnnoFile = new File(oldAnnoFileName).getCanonicalFile
81+
82+
val (annoFiles, usingImplicitAnnoFile) = {
83+
val afs = firrtlConfig.annotationFileNames.map { x =>
84+
new File(x).getCanonicalFile
85+
}
86+
// Implicit anno file could be included explicitly, only include it and
87+
// warn if it's not also explicit
88+
val use = oldAnnoFile.exists && !afs.contains(oldAnnoFile)
89+
if (use) (oldAnnoFile +: afs, true) else (afs, false)
90+
}
91+
92+
// Warnings to get people to change to drop old API
93+
if (firrtlConfig.annotationFileNameOverride.nonEmpty) {
94+
val msg = "annotationFileNameOverride has been removed, file will be ignored! " +
95+
"Use annotationFileNames"
96+
dramaticError(msg)
97+
} else if (usingImplicitAnnoFile) {
98+
val msg = "Implicit .anno file from top-name has been removed, file will be ignored!\n" +
99+
(" " * 9) + "Use explicit -faf option or annotationFileNames"
100+
dramaticError(msg)
101+
}
102+
103+
val loadedAnnos = annoFiles.flatMap { file =>
104+
if (!file.exists) {
105+
throw new AnnotationFileNotFoundException(file)
106+
}
107+
JsonProtocol.deserialize(file)
108+
}
109+
110+
val targetDirAnno = List(BlackBoxTargetDirAnno(optionsManager.targetDirName))
111+
112+
// Output Annotations
113+
val outputAnnos = firrtlConfig.getEmitterAnnos(optionsManager)
114+
115+
val globalAnnos = Seq(TargetDirAnnotation(optionsManager.targetDirName)) ++
116+
(if (firrtlConfig.dontCheckCombLoops) Seq(DontCheckCombLoopsAnnotation) else Seq()) ++
117+
(if (firrtlConfig.noDCE) Seq(NoDCEAnnotation) else Seq())
118+
119+
targetDirAnno ++ outputAnnos ++ globalAnnos ++ firrtlConfig.annotations ++ loadedAnnos
120+
}
121+
122+
private sealed trait FileExtension
123+
private case object FirrtlFile extends FileExtension
124+
private case object ProtoBufFile extends FileExtension
125+
126+
private def getFileExtension(filename: String): FileExtension =
127+
filename.drop(filename.lastIndexOf('.')) match {
128+
case ".pb" => ProtoBufFile
129+
case _ => FirrtlFile // Default to FIRRTL File
130+
}
131+
132+
// Useful for handling erros in the options
133+
case class OptionsException(message: String) extends Exception(message)
134+
135+
/** Get the Circuit from the compile options
136+
*
137+
* Handles the myriad of ways it can be specified
138+
*/
139+
def getCircuit(optionsManager: ExecutionOptionsManager with HasFirrtlOptions): Try[ir.Circuit] = {
140+
val firrtlConfig = optionsManager.firrtlOptions
141+
Try {
142+
// Check that only one "override" is used
143+
val circuitSources = Map(
144+
"firrtlSource" -> firrtlConfig.firrtlSource.isDefined,
145+
"firrtlCircuit" -> firrtlConfig.firrtlCircuit.isDefined,
146+
"inputFileNameOverride" -> firrtlConfig.inputFileNameOverride.nonEmpty
147+
)
148+
if (circuitSources.values.count(x => x) > 1) {
149+
val msg = circuitSources.collect { case (s, true) => s }.mkString(" and ") +
150+
" are set, only 1 can be set at a time!"
151+
throw new OptionsException(msg)
152+
}
153+
firrtlConfig.firrtlCircuit.getOrElse {
154+
firrtlConfig.firrtlSource.map(x => Parser.parseString(x, firrtlConfig.infoMode)).getOrElse {
155+
if (optionsManager.topName.isEmpty && firrtlConfig.inputFileNameOverride.isEmpty) {
156+
val message = "either top-name or input-file-override must be set"
157+
throw new OptionsException(message)
158+
}
159+
if (
160+
optionsManager.topName.isEmpty &&
161+
firrtlConfig.inputFileNameOverride.nonEmpty &&
162+
firrtlConfig.outputFileNameOverride.isEmpty
163+
) {
164+
val message = "inputFileName set but neither top-name or output-file-override is set"
165+
throw new OptionsException(message)
166+
}
167+
val inputFileName = firrtlConfig.getInputFileName(optionsManager)
168+
try {
169+
// TODO What does InfoMode mean to ProtoBuf?
170+
getFileExtension(inputFileName) match {
171+
case ProtoBufFile => proto.FromProto.fromFile(inputFileName)
172+
case FirrtlFile => Parser.parseFile(inputFileName, firrtlConfig.infoMode)
173+
}
174+
} catch {
175+
case _: FileNotFoundException =>
176+
val message = s"Input file $inputFileName not found"
177+
throw new OptionsException(message)
178+
}
179+
}
180+
}
181+
}
182+
}
183+
184+
/**
185+
* Run the firrtl compiler using the provided option
186+
*
187+
* @param optionsManager the desired flags to the compiler
188+
* @return a FirrtlExecutionResult indicating success or failure, provide access to emitted data on success
189+
* for downstream tools as desired
190+
*/
191+
def execute(optionsManager: ExecutionOptionsManager with HasFirrtlOptions): FirrtlExecutionResult = {
192+
StageUtils.dramaticWarning("firrtl.Driver is deprecated since 1.2!\nPlease switch to firrtl.stage.FirrtlMain")
193+
194+
val annos = optionsManager.firrtlOptions.toAnnotations ++ optionsManager.commonOptions.toAnnotations
195+
196+
val phases: Seq[Phase] = {
197+
import DriverCompatibility._
198+
new PhaseManager(
199+
List(
200+
Dependency[AddImplicitFirrtlFile],
201+
Dependency[AddImplicitAnnotationFile],
202+
Dependency[AddImplicitOutputFile],
203+
Dependency[AddImplicitEmitter],
204+
Dependency[FirrtlStage]
205+
)
206+
).transformOrder
207+
.map(DeletedWrapper(_))
208+
}
209+
210+
val annosx =
211+
try {
212+
phases.foldLeft(annos)((a, p) => p.transform(a))
213+
} catch {
214+
case e: firrtl.options.OptionsException => return FirrtlExecutionFailure(e.message)
215+
}
216+
217+
Viewer[FirrtlExecutionResult].view(annosx)
218+
}
219+
220+
/**
221+
* this is a wrapper for execute that builds the options from a standard command line args,
222+
* for example, like strings passed to main()
223+
*
224+
* @param args an Array of string s containing legal arguments
225+
* @return
226+
*/
227+
def execute(args: Array[String]): FirrtlExecutionResult = {
228+
val optionsManager = new ExecutionOptionsManager("firrtl") with HasFirrtlOptions
229+
230+
if (optionsManager.parse(args)) {
231+
execute(optionsManager) match {
232+
case success: FirrtlExecutionSuccess =>
233+
success
234+
case failure: FirrtlExecutionFailure =>
235+
optionsManager.showUsageAsError()
236+
failure
237+
case result =>
238+
throwInternalError(s"Error: Unknown Firrtl Execution result $result")
239+
}
240+
} else {
241+
FirrtlExecutionFailure("Could not parser command line options")
242+
}
243+
}
244+
245+
def main(args: Array[String]): Unit = {
246+
execute(args)
247+
}
248+
}

0 commit comments

Comments
 (0)