diff options
| -rw-r--r-- | build.sbt | 27 | ||||
| -rw-r--r-- | src/main/scala/chisel3/ChiselExecutionOptions.scala | 34 | ||||
| -rw-r--r-- | src/main/scala/chisel3/Driver.scala | 144 | ||||
| -rw-r--r-- | src/main/scala/chisel3/testers/TesterDriver.scala | 35 | ||||
| -rw-r--r-- | src/test/scala/chiselTests/DriverSpec.scala | 33 | ||||
| -rw-r--r-- | src/test/scala/chiselTests/Reg.scala | 1 |
6 files changed, 252 insertions, 22 deletions
@@ -21,6 +21,8 @@ lazy val commonSettings = Seq ( scalaVersion := "2.11.7" ) +val defaultVersions = Map("firrtl" -> "1.1-SNAPSHOT") + lazy val chiselSettings = Seq ( name := "chisel3", @@ -62,19 +64,24 @@ lazy val chiselSettings = Seq ( Resolver.sonatypeRepo("releases") ), - /* Bumping "com.novocode" % "junit-interface" % "0.11", causes DelayTest testSeqReadBundle to fail - * in subtly disturbing ways on Linux (but not on Mac): - * - some fields in the generated .h file are re-named, - * - an additional field is added - * - the generated .cpp file has additional differences: - * - different temps in clock_lo - * - missing assignments - * - change of assignment order - * - use of "Tx" vs. "Tx.values" - */ + libraryDependencies ++= (Seq("firrtl").map { + dep: String => "edu.berkeley.cs" %% dep % sys.props.getOrElse(dep + "Version", defaultVersions(dep)) }), + + +/* Bumping "com.novocode" % "junit-interface" % "0.11", causes DelayTest testSeqReadBundle to fail + * in subtly disturbing ways on Linux (but not on Mac): + * - some fields in the generated .h file are re-named, + * - an additional field is added + * - the generated .cpp file has additional differences: + * - different temps in clock_lo + * - missing assignments + * - change of assignment order + * - use of "Tx" vs. "Tx.values" + */ libraryDependencies += "org.scalatest" %% "scalatest" % "2.2.5" % "test", libraryDependencies += "org.scala-lang" % "scala-reflect" % scalaVersion.value, libraryDependencies += "org.scalacheck" %% "scalacheck" % "1.12.4" % "test", + libraryDependencies += "com.github.scopt" %% "scopt" % "3.4.0", // Tests from other projects may still run concurrently. parallelExecution in Test := true, diff --git a/src/main/scala/chisel3/ChiselExecutionOptions.scala b/src/main/scala/chisel3/ChiselExecutionOptions.scala new file mode 100644 index 00000000..6f58153f --- /dev/null +++ b/src/main/scala/chisel3/ChiselExecutionOptions.scala @@ -0,0 +1,34 @@ +// See LICENSE for license details. + +package chisel3 + +import firrtl.{ExecutionOptionsManager, ComposableOptions} + +//TODO: provide support for running firrtl as separate process, could alternatively be controlled by external driver +//TODO: provide option for not saving chirrtl file, instead calling firrtl with in memory chirrtl +/** + * Options that are specific to chisel. + * + * @param runFirrtlCompiler when true just run chisel, when false run chisel then compile its output with firrtl + * @note this extends FirrtlExecutionOptions which extends CommonOptions providing easy access to down chain options + */ +case class ChiselExecutionOptions( + runFirrtlCompiler: Boolean = true + // var runFirrtlAsProcess: Boolean = false + ) extends ComposableOptions + +trait HasChiselExecutionOptions { + self: ExecutionOptionsManager => + + var chiselOptions = ChiselExecutionOptions() + + parser.note("chisel3 options") + + parser.opt[Unit]("no-run-firrtl") + .abbr("chnrf") + .foreach { _ => + chiselOptions = chiselOptions.copy(runFirrtlCompiler = false) + } + .text("Stop after chisel emits chirrtl file") +} + diff --git a/src/main/scala/chisel3/Driver.scala b/src/main/scala/chisel3/Driver.scala index b0c7aeda..dbd9b464 100644 --- a/src/main/scala/chisel3/Driver.scala +++ b/src/main/scala/chisel3/Driver.scala @@ -2,11 +2,36 @@ package chisel3 +import chisel3.internal.firrtl.Emitter + import scala.sys.process._ import java.io._ -import internal._ import internal.firrtl._ +import firrtl._ + +/** + * The Driver provides methods to invoke the chisel3 compiler and the firrtl compiler. + * By default firrtl is automatically run after chisel. an [[ExecutionOptionsManager]] + * is needed to manage options. It can parser command line arguments or coordinate + * multiple chisel toolchain tools options. + * + * @example + * {{{ + * val optionsManager = new ExecutionOptionsManager("chisel3") + * with FirrtlExecutionOptions + * with ChiselExecutionOptions { + * commonOptions = CommonOption(targetDirName = "my_target_dir") + * chiselOptions = ChiselExecutionOptions(runFirrtlCompiler = false) + * } + * chisel3.Driver.execute(optionsManager, () => new Dut) + * }}} + * or via command line arguments + * @example {{{ + * args = "--no-run-firrtl --target-dir my-target-dir".split(" +") + * chisel3.execute(args, () => new DUT) + * }}} + */ import BuildInfo._ trait BackendCompilationUtilities { @@ -32,6 +57,32 @@ trait BackendCompilationUtilities { vf } + /** + * like 'firrtlToVerilog' except it runs the process inside the same JVM + * + * @param prefix basename of the file + * @param dir directory where file lives + * @return true if compiler completed successfully + */ + def compileFirrtlToVerilog(prefix: String, dir: File): Boolean = { + val optionsManager = new ExecutionOptionsManager("chisel3") with HasChiselExecutionOptions with HasFirrtlOptions { + commonOptions = CommonOptions(topName = prefix, targetDirName = dir.getAbsolutePath) + firrtlOptions = FirrtlExecutionOptions(compilerName = "verilog") + } + + firrtl.Driver.execute(optionsManager) match { + case _: FirrtlExecutionSuccess => true + case _: FirrtlExecutionFailure => false + } + } + + /** + * compule chirrtl to verilog by using a separate process + * + * @param prefix basename of the file + * @param dir directory where file lives + * @return true if compiler completed successfully + */ def firrtlToVerilog(prefix: String, dir: File): ProcessBuilder = { Process( Seq("firrtl", @@ -105,6 +156,30 @@ trait BackendCompilationUtilities { } } +/** + * This family provides return values from the chisel3 and possibly firrtl compile steps + */ +trait ChiselExecutionResult + +/** + * + * @param circuitOption Optional circuit, has information like circuit name + * @param emitted The emitted Chirrrl text + * @param firrtlResultOption Optional Firrtl result, @see ucb-bar/firrtl for details + */ +case class ChiselExecutionSucccess( + circuitOption: Option[Circuit], + emitted: String, + firrtlResultOption: Option[FirrtlExecutionResult] + ) extends ChiselExecutionResult + +/** + * Getting one of these indicates failure of some sort + * + * @param message a clue perhaps will be provided in the here + */ +case class ChiselExecutionFailure(message: String) extends ChiselExecutionResult + object Driver extends BackendCompilationUtilities { /** Elaborates the Module specified in the gen function into a Circuit @@ -112,7 +187,7 @@ object Driver extends BackendCompilationUtilities { * @param gen a function that creates a Module hierarchy * @return the resulting Chisel IR in the form of a Circuit (TODO: Should be FIRRTL IR) */ - def elaborate[T <: Module](gen: () => T): Circuit = Builder.build(Module(gen())) + def elaborate[T <: Module](gen: () => T): Circuit = internal.Builder.build(Module(gen())) def emit[T <: Module](gen: () => T): String = Emitter.emit(elaborate(gen)) @@ -137,6 +212,71 @@ object Driver extends BackendCompilationUtilities { def targetDir(): String = { target_dir getOrElse new File(".").getCanonicalPath } + /** + * Run the chisel3 compiler and possibly the firrtl compiler with options specified + * + * @param optionsManager The options specified + * @param dut The device under test + * @return An execution result with useful stuff, or failure with message + */ + def execute( + optionsManager: ExecutionOptionsManager with HasChiselExecutionOptions with HasFirrtlOptions, + dut: () => Module): ChiselExecutionResult = { + val circuit = elaborate(dut) + + // this little hack let's us set the topName with the circuit name if it has not been set from args + optionsManager.setTopNameIfNotSet(circuit.name) + + val firrtlOptions = optionsManager.firrtlOptions + val chiselOptions = optionsManager.chiselOptions + + // use input because firrtl will be reading this + val firrtlString = Emitter.emit(circuit) + val firrtlFileName = firrtlOptions.getInputFileName(optionsManager) + val firrtlFile = new File(firrtlFileName) + + val w = new FileWriter(firrtlFile) + w.write(firrtlString) + w.close() + + val firrtlExecutionResult = if(chiselOptions.runFirrtlCompiler) { + Some(firrtl.Driver.execute(optionsManager)) + } + else { + None + } + ChiselExecutionSucccess(Some(circuit), firrtlString, firrtlExecutionResult) + } + + /** + * Run the chisel3 compiler and possibly the firrtl compiler with options specified via an array of Strings + * + * @param args The options specified, command line style + * @param dut The device under test + * @return An execution result with useful stuff, or failure with message + */ + def execute(args: Array[String], dut: () => Module): ChiselExecutionResult = { + val optionsManager = new ExecutionOptionsManager("chisel3") with HasChiselExecutionOptions with HasFirrtlOptions + + optionsManager.parse(args) match { + case true => + execute(optionsManager, dut) + case _ => + ChiselExecutionFailure("could not parse results") + } + } + + /** + * This is just here as command line way to see what the options are + * It will not successfully run + * TODO: Look into dynamic class loading as way to make this main useful + * + * @param args unused args + */ + def main(args: Array[String]) { + execute(Array("--help"), null) + } + val version = BuildInfo.version val chiselVersionString = BuildInfo.toString } diff --git a/src/main/scala/chisel3/testers/TesterDriver.scala b/src/main/scala/chisel3/testers/TesterDriver.scala index 586fa780..76b9a2e9 100644 --- a/src/main/scala/chisel3/testers/TesterDriver.scala +++ b/src/main/scala/chisel3/testers/TesterDriver.scala @@ -3,15 +3,13 @@ package chisel3.testers import chisel3._ -import scala.io.Source -import scala.sys.process._ import java.io._ object TesterDriver extends BackendCompilationUtilities { /** Copy the contents of a resource to a destination file. */ def copyResourceToFile(name: String, file: File) { - val in = getClass().getResourceAsStream(name) + val in = getClass.getResourceAsStream(name) if (in == null) { throw new FileNotFoundException(s"Resource '$name'") } @@ -22,7 +20,9 @@ object TesterDriver extends BackendCompilationUtilities { /** For use with modules that should successfully be elaborated by the * frontend, and which can be turned into executables with assertions. */ - def execute(t: () => BasicTester, additionalVResources: Seq[String] = Seq()): Boolean = { + def execute(t: () => BasicTester, + additionalVResources: Seq[String] = Seq(), + runFirrtlasProcess: Boolean = false): Boolean = { // Invoke the chisel compiler to get the circuit's IR val circuit = Driver.elaborate(finishWrapper(t)) @@ -46,13 +46,28 @@ object TesterDriver extends BackendCompilationUtilities { out }) - // Use sys.Process to invoke a bunch of backend stuff, then run the resulting exe - if ((firrtlToVerilog(target, path) #&& + if(runFirrtlasProcess) { + // Use sys.Process to invoke a bunch of backend stuff, then run the resulting exe + if ((firrtlToVerilog(target, path) #&& verilogToCpp(target, target, path, additionalVFiles, cppHarness) #&& - cppToExe(target, path)).! == 0) { - executeExpectingSuccess(target, path) - } else { - false + cppToExe(target, path)).! == 0) { + executeExpectingSuccess(target, path) + } else { + false + } + } + else { + // Compile firrtl + if (!compileFirrtlToVerilog(target, path)) { + return false + } + // Use sys.Process to invoke a bunch of backend stuff, then run the resulting exe + if ((verilogToCpp(target, target, path, additionalVFiles, cppHarness) #&& + cppToExe(target, path)).! == 0) { + executeExpectingSuccess(target, path) + } else { + false + } } } /** diff --git a/src/test/scala/chiselTests/DriverSpec.scala b/src/test/scala/chiselTests/DriverSpec.scala new file mode 100644 index 00000000..4f9619e3 --- /dev/null +++ b/src/test/scala/chiselTests/DriverSpec.scala @@ -0,0 +1,33 @@ +// See LICENSE for license details. + +package chiselTests + +import chisel3._ + +import org.scalatest.{Matchers, FreeSpec} + +class DummyModule extends Module { + val io = IO(new Bundle { + val in = UInt(INPUT, 1) + val out = UInt(OUTPUT, 1) + }) + io.out := io.in +} + +class DriverSpec extends FreeSpec with Matchers { + "Driver's execute methods are used to run chisel and firrtl" - { + "options can be picked up from comand line with no args" in { + Driver.execute(Array.empty[String], () => new DummyModule) + } + "options can be picked up from comand line setting top name" in { + Driver.execute(Array("-tn", "dm", "-td", "local-build"), () => new DummyModule) + } + "execute returns a chisel execution result" in { + val args = Array("--compiler", "low") + val result = Driver.execute(Array.empty[String], () => new DummyModule) + result shouldBe a[ChiselExecutionSucccess] + val successResult = result.asInstanceOf[ChiselExecutionSucccess] + successResult.emitted should include ("circuit DummyModule") + } + } +} diff --git a/src/test/scala/chiselTests/Reg.scala b/src/test/scala/chiselTests/Reg.scala index a9086223..90992c01 100644 --- a/src/test/scala/chiselTests/Reg.scala +++ b/src/test/scala/chiselTests/Reg.scala @@ -2,6 +2,7 @@ package chiselTests +import firrtl.ir.Input import org.scalatest._ import chisel3._ import chisel3.core.DataMirror |
