diff options
Diffstat (limited to 'src/main/scala/firrtl/Driver.scala')
| -rw-r--r-- | src/main/scala/firrtl/Driver.scala | 323 |
1 files changed, 163 insertions, 160 deletions
diff --git a/src/main/scala/firrtl/Driver.scala b/src/main/scala/firrtl/Driver.scala index 2a5a379a..6c9e34d5 100644 --- a/src/main/scala/firrtl/Driver.scala +++ b/src/main/scala/firrtl/Driver.scala @@ -1,189 +1,192 @@ -/* -Copyright (c) 2014 - 2016 The Regents of the University of -California (Regents). All Rights Reserved. Redistribution and use in -source and binary forms, with or without modification, are permitted -provided that the following conditions are met: - * Redistributions of source code must retain the above - copyright notice, this list of conditions and the following - two paragraphs of disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - two paragraphs of disclaimer in the documentation and/or other materials - provided with the distribution. - * Neither the name of the Regents nor the names of its contributors - may be used to endorse or promote products derived from this - software without specific prior written permission. -IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, -SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, -ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF -REGENTS HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF -ANY, PROVIDED HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO OBLIGATION -TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR -MODIFICATIONS. -*/ +// See License package firrtl +import java.io.FileNotFoundException + import scala.io.Source -import scala.collection.mutable import Annotations._ -import Utils._ -import Parser.{InfoMode, IgnoreInfo, UseInfo, GenInfo, AppendInfo} +import Parser.{InfoMode, IgnoreInfo} +import scala.collection._ + +/** + * The driver provides methods to access the firrtl compiler. + * Invoke the compiler with either a FirrtlExecutionOption + * + * @example + * {{{ + * val optionsManager = 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 + */ object Driver { - /** - * Implements the default Firrtl compilers and an inlining pass. - * - * Arguments specify the compiler, input file, output file, and - * optionally the module/instances to inline. - */ - def main(args: Array[String]) = { - val usage = """ -Usage: firrtl -i <input_file> -o <output_file> -X <compiler> [options] - sbt "run-main firrtl.Driver -i <input_file> -o <output_file> -X <compiler> [options]" - -Required Arguments: - -i <filename> Specify the input *.fir file - -o <filename> Specify the output file - -X <compiler> Specify the target compiler - Currently supported: high low verilog - -Optional Arguments: - --info-mode <mode> Specify Info Mode - Supported modes: ignore, use, gen, append - --inferRW <circuit> Enable readwrite port inference for the target circuit - --inline <module>|<instance> Inline a module (e.g. "MyModule") or instance (e.g. "MyModule.myinstance") - - --replSeqMem -c:<circuit>:-i:<filename>:-o:<filename> - *** Replace sequential memories with blackboxes + configuration file - *** Input configuration file optional - *** Note: sub-arguments to --replSeqMem should be delimited by : and not white space! - - [--help|-h] Print usage string -""" - - def handleInlineOption(value: String): Annotation = - value.split('.') match { - case Array(circuit) => - passes.InlineAnnotation(CircuitName(circuit), TransID(0)) - case Array(circuit, module) => - passes.InlineAnnotation(ModuleName(module, CircuitName(circuit)), TransID(0)) - case Array(circuit, module, inst) => - passes.InlineAnnotation(ComponentName(inst, ModuleName(module, CircuitName(circuit))), TransID(0)) - case _ => throw new Exception(s"Bad inline instance/module name: $value" + usage) - } - - def handleInferRWOption(value: String) = - passes.InferReadWriteAnnotation(value, TransID(-1)) - - def handleReplSeqMem(value: String) = - passes.memlib.ReplSeqMemAnnotation(value, TransID(-2)) - - run(args: Array[String], - Map( "high" -> new HighFirrtlCompiler(), - "low" -> new LowFirrtlCompiler(), - "verilog" -> new VerilogCompiler()), - Map("--inline" -> handleInlineOption _, - "--inferRW" -> handleInferRWOption _, - "--replSeqMem" -> handleReplSeqMem _), - usage - ) - } - - // Compiles circuit. First parses a circuit from an input file, // executes all compiler passes, and writes result to an output // file. def compile( - input: String, - output: String, - compiler: Compiler, + input: String, + output: String, + compiler: Compiler, infoMode: InfoMode = IgnoreInfo, - annotations: AnnotationMap = new AnnotationMap(Seq.empty)) = { - val parsedInput = Parser.parse(Source.fromFile(input).getLines, infoMode) + annotations: AnnotationMap = new AnnotationMap(Seq.empty) + ): String = { + val parsedInput = Parser.parse(Source.fromFile(input).getLines(), infoMode) val outputBuffer = new java.io.CharArrayWriter compiler.compile(parsedInput, annotations, outputBuffer) val outputFile = new java.io.PrintWriter(output) - outputFile.write(outputBuffer.toString) + val outputString = outputBuffer.toString + outputFile.write(outputString) outputFile.close() + outputString } /** - * Runs a Firrtl compiler. - * - * @param args list of commandline arguments - * @param compilers mapping a compiler name to a compiler - * @param customOptions mapping a custom option name to a function that returns an annotation - * @param usage describes the commandline API - */ - def run(args: Array[String], compilers: Map[String,Compiler], customOptions: Map[String, String=>Annotation], usage: String) = { - /** - * Keys commandline values specified by user in OptionMap - */ - sealed trait CompilerOption - case object InputFileName extends CompilerOption - case object OutputFileName extends CompilerOption - case object CompilerName extends CompilerOption - case object AnnotationOption extends CompilerOption - case object InfoModeOption extends CompilerOption - /** - * Maps compiler option to user-specified value - */ - type OptionMap = Map[CompilerOption, String] - - /** - * Populated by custom annotations returned from corresponding function - * held in customOptions - */ - val annotations = mutable.ArrayBuffer[Annotation]() - def nextOption(map: OptionMap, list: List[String]): OptionMap = { - list match { - case Nil => map - case "-i" :: value :: tail => - nextOption(map + (InputFileName -> value), tail) - case "-o" :: value :: tail => - nextOption(map + (OutputFileName -> value), tail) - case "-X" :: value :: tail => - nextOption(map + (CompilerName -> value), tail) - case "--info-mode" :: value :: tail => - nextOption(map + (InfoModeOption -> value), tail) - case flag :: value :: tail if customOptions.contains(flag) => - annotations += customOptions(flag)(value) - nextOption(map, tail) - case ("-h" | "--help") :: tail => println(usage); sys.exit(0) - case option :: tail => - throw new Exception("Unknown option " + option + usage) - } - } + * print the message in red + * + * @param message error message + */ + def dramaticError(message: String): Unit = { + println(Console.RED + "-"*78) + println(s"Error: $message") + println("-"*78 + Console.RESET) + } - val arglist = args.toList - val options = nextOption(Map[CompilerOption, String](), arglist) + /** + * Run the firrtl compiler using the provided option + * + * @param optionsManager the desired flags to the compiler + * @return a FirrtlExectionResult indicating success or failure, provide access to emitted data on success + * for downstream tools as desired + */ + def execute(optionsManager: ExecutionOptionsManager with HasFirrtlOptions): FirrtlExecutionResult = { + val firrtlConfig = optionsManager.firrtlOptions + + val firrtlSource = firrtlConfig.firrtlSource match { + case Some(text) => text.split("\n").toIterator + case None => + if(optionsManager.topName.isEmpty && firrtlConfig.inputFileNameOverride.isEmpty) { + val message = "either top-name or input-file-override must be set" + dramaticError(message) + return FirrtlExecutionFailure(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" + dramaticError(message) + return FirrtlExecutionFailure(message) + } + val inputFileName = firrtlConfig.getInputFileName(optionsManager) + try { + io.Source.fromFile(inputFileName).getLines() + } + catch { + case e: FileNotFoundException => + val message = s"Input file $inputFileName not found" + dramaticError(message) + return FirrtlExecutionFailure(message) + } + } - // Get input circuit/output filenames - val input = options.getOrElse(InputFileName, throw new Exception("No input file provided!" + usage)) - val output = options.getOrElse(OutputFileName, throw new Exception("No output file provided!" + usage)) + val parsedInput = Parser.parse(firrtlSource, firrtlConfig.infoMode) + val outputBuffer = new java.io.CharArrayWriter + firrtlConfig.compiler.compile(parsedInput, new AnnotationMap(Seq.empty), outputBuffer) - val infoMode = options.get(InfoModeOption) match { - case (Some("append") | None) => AppendInfo(input) - case Some("use") => UseInfo - case Some("ignore") => IgnoreInfo - case Some("gen") => GenInfo(input) - case Some(other) => throw new Exception("Unknown info mode option: " + other + usage) - } + val outputFileName = firrtlConfig.getOutputFileName(optionsManager) + val outputFile = new java.io.PrintWriter(outputFileName) + val outputString = outputBuffer.toString + outputFile.write(outputString) + outputFile.close() - // Execute selected compiler - error if not recognized compiler - options.get(CompilerName) match { - case Some(name) => - compilers.get(name) match { - case Some(compiler) => compile(input, output, compiler, infoMode, new AnnotationMap(annotations.toSeq)) - case None => throw new Exception("Unknown compiler option: " + name + usage) + FirrtlExecutionSuccess(firrtlConfig.compilerName, outputBuffer.toString) + } + + /** + * 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 + + optionsManager.parse(args) match { + case true => + execute(optionsManager) match { + case success: FirrtlExecutionSuccess => + success + case failure: FirrtlExecutionFailure => + optionsManager.showUsageAsError() + failure + case result => + throw new Exception(s"Error: Unknown Firrtl Execution result $result") } - case None => throw new Exception("No specified compiler option." + usage) + case _ => + FirrtlExecutionFailure("Could not parser command line options") } } + + def main(args: Array[String]): Unit = { + execute(args) + } } + +object FileUtils { + /** + * recursive create directory and all parents + * + * @param directoryName a directory string with one or more levels + * @return + */ + def makeDirectory(directoryName: String): Boolean = { + val dirFile = new java.io.File(directoryName) + if(dirFile.exists()) { + if(dirFile.isDirectory) { + true + } + else { + false + } + } + else { + dirFile.mkdirs() + } + } + + /** + * recursively delete all directories in a relative path + * DO NOT DELETE absolute paths + * + * @param directoryPathName a directory hierarchy to delete + */ + def deleteDirectoryHierarchy(directoryPathName: String): Unit = { + if(directoryPathName.isEmpty || directoryPathName.startsWith("/")) { + // don't delete absolute path + } + else { + val directory = new java.io.File(directoryPathName) + if(directory.isDirectory) { + directory.delete() + val directories = directoryPathName.split("/+").reverse.tail + if (directories.nonEmpty) { + deleteDirectoryHierarchy(directories.reverse.mkString("/")) + } + } + } + } +}
\ No newline at end of file |
