From 91570ec8d7ab6bede24eb8da4a7e005f00ac076f Mon Sep 17 00:00:00 2001 From: Jim Lawson Date: Fri, 27 Jan 2017 12:19:38 -0800 Subject: Move BackendCompilationUtilities into a util package for use by chisel3. (#400) * Move BackendCompilationUtilities into a util package for use by chisel3. Some of this could be moved into a more general tools package, but since chisel3 already has a dependency on firrtl ... * Push util down into firrtl so as not to conflict with scala.util. --- .../firrtl/util/BackendCompilationUtilities.scala | 130 +++++++++++++++++++++ src/test/scala/firrtlTests/DriverSpec.scala | 1 + src/test/scala/firrtlTests/FirrtlSpec.scala | 93 +-------------- 3 files changed, 132 insertions(+), 92 deletions(-) create mode 100644 src/main/scala/firrtl/util/BackendCompilationUtilities.scala (limited to 'src') diff --git a/src/main/scala/firrtl/util/BackendCompilationUtilities.scala b/src/main/scala/firrtl/util/BackendCompilationUtilities.scala new file mode 100644 index 00000000..c1ead8b9 --- /dev/null +++ b/src/main/scala/firrtl/util/BackendCompilationUtilities.scala @@ -0,0 +1,130 @@ +// See LICENSE for license details. + +package firrtl.util + +import scala.sys.process._ +import java.io._ + +import firrtl._ +import firrtl.{Driver, ExecutionOptionsManager} + +import scala.sys.process.{ProcessBuilder, ProcessLogger} + + +trait BackendCompilationUtilities { + /** Copy the contents of a resource to a destination file. + */ + def copyResourceToFile(name: String, file: File) { + val in = getClass.getResourceAsStream(name) + if (in == null) { + throw new FileNotFoundException(s"Resource '$name'") + } + val out = new FileOutputStream(file) + Iterator.continually(in.read).takeWhile(-1 != _).foreach(out.write) + out.close() + } + + /** Create a temporary directory with the prefix name. Exists here because it doesn't in Java 6. + */ + def createTempDirectory(prefix: String): File = { + val temp = File.createTempFile(prefix, "") + if (!temp.delete()) { + throw new IOException(s"Unable to delete temp file '$temp'") + } + if (!temp.mkdir()) { + throw new IOException(s"Unable to create temp directory '$temp'") + } + temp + } + + def makeHarness(template: String => String, post: String)(f: File): File = { + val prefix = f.toString.split("/").last + val vf = new File(f.toString + post) + val w = new FileWriter(vf) + w.write(template(prefix)) + w.close() + vf + } + + /** + * 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", + "-i", s"$prefix.fir", + "-o", s"$prefix.v", + "-X", "verilog"), + dir) + } + + /** Generates a Verilator invocation to convert Verilog sources to C++ + * simulation sources. + * + * The Verilator prefix will be V\$dutFile, and running this will generate + * C++ sources and headers as well as a makefile to compile them. + * + * Verilator will automatically locate the top-level module as the one among + * all the files which are not included elsewhere. If multiple ones exist, + * the compilation will fail. + * + * @param dutFile name of the DUT .v without the .v extension + * @param dir output directory + * @param vSources list of additional Verilog sources to compile + * @param cppHarness C++ testharness to compile/link against + */ + def verilogToCpp( + dutFile: String, + dir: File, + vSources: Seq[File], + cppHarness: File + ): ProcessBuilder = { + val topModule = dutFile + val command = Seq("verilator", + "--cc", s"$dutFile.v") ++ + vSources.map(file => Seq("-v", file.toString)).flatten ++ + Seq("--assert", + "-Wno-fatal", + "-Wno-WIDTH", + "-Wno-STMTDLY", + "--trace", + "-O1", + "--top-module", topModule, + "+define+TOP_TYPE=V" + dutFile, + s"+define+PRINTF_COND=!$topModule.reset", + s"+define+STOP_COND=!$topModule.reset", + "-CFLAGS", + s"""-Wno-undefined-bool-conversion -O1 -DTOP_TYPE=V$dutFile -include V$dutFile.h""", + "-Mdir", dir.toString, + "--exe", cppHarness.toString) + System.out.println(s"${command.mkString(" ")}") // scalastyle:ignore regex + command + } + + def cppToExe(prefix: String, dir: File): ProcessBuilder = + Seq("make", "-C", dir.toString, "-j", "-f", s"V${prefix}.mk", s"V${prefix}") + + def executeExpectingFailure( + prefix: String, + dir: File, + assertionMsg: String = ""): Boolean = { + var triggered = false + val assertionMessageSupplied = assertionMsg != "" + val e = Process(s"./V${prefix}", dir) ! + ProcessLogger(line => { + triggered = triggered || (assertionMessageSupplied && line.contains(assertionMsg)) + System.out.println(line) // scalastyle:ignore regex + }) + // Fail if a line contained an assertion or if we get a non-zero exit code + // or, we get a SIGABRT (assertion failure) and we didn't provide a specific assertion message + triggered || (e != 0 && (e != 134 || !assertionMessageSupplied)) + } + + def executeExpectingSuccess(prefix: String, dir: File): Boolean = { + !executeExpectingFailure(prefix, dir) + } +} diff --git a/src/test/scala/firrtlTests/DriverSpec.scala b/src/test/scala/firrtlTests/DriverSpec.scala index 9f29b918..ff888e2e 100644 --- a/src/test/scala/firrtlTests/DriverSpec.scala +++ b/src/test/scala/firrtlTests/DriverSpec.scala @@ -8,6 +8,7 @@ import org.scalatest.{FreeSpec, Matchers} import firrtl.passes.InlineInstances import firrtl.passes.memlib.{InferReadWrite, ReplSeqMem} import firrtl._ +import firrtl.util.BackendCompilationUtilities class DriverSpec extends FreeSpec with Matchers with BackendCompilationUtilities { "CommonOptions are some simple options available across the chisel3 ecosystem" - { diff --git a/src/test/scala/firrtlTests/FirrtlSpec.scala b/src/test/scala/firrtlTests/FirrtlSpec.scala index a2c880c3..ec3d075e 100644 --- a/src/test/scala/firrtlTests/FirrtlSpec.scala +++ b/src/test/scala/firrtlTests/FirrtlSpec.scala @@ -13,98 +13,7 @@ import scala.io.Source import firrtl._ import firrtl.Parser.IgnoreInfo import firrtl.annotations - -// This trait is borrowed from Chisel3, ideally this code should only exist in one location -trait BackendCompilationUtilities { - /** Create a temporary directory with the prefix name. Exists here because it doesn't in Java 6. - */ - def createTempDirectory(prefix: String): File = { - val temp = File.createTempFile(prefix, "") - if (!temp.delete()) { - throw new IOException(s"Unable to delete temp file '$temp'") - } - if (!temp.mkdir()) { - throw new IOException(s"Unable to create temp directory '$temp'") - } - temp - } - - /** Copy the contents of a resource to a destination file. - */ - def copyResourceToFile(name: String, file: File) { - val in = getClass().getResourceAsStream(name) - if (in == null) { - throw new FileNotFoundException(s"Resource '$name'") - } - val out = new FileOutputStream(file) - Iterator.continually(in.read).takeWhile(-1 !=).foreach(out.write) - out.close() - } - - - def makeHarness(template: String => String, post: String)(f: File): File = { - val prefix = f.toString.split("/").last - val vf = new File(f.toString + post) - val w = new FileWriter(vf) - w.write(template(prefix)) - w.close() - vf - } - - /** Generates a Verilator invocation to convert Verilog sources to C++ - * simulation sources. - * - * The Verilator prefix will be V$dutFile, and running this will generate - * C++ sources and headers as well as a makefile to compile them. - * - * Verilator will automatically locate the top-level module as the one among - * all the files which are not included elsewhere. If multiple ones exist, - * the compilation will fail. - * - * @param dutFile name of the DUT .v without the .v extension - * @param dir output directory - * @param vSources list of additional Verilog sources to compile - * @param cppHarness C++ testharness to compile/link against - */ - def verilogToCpp( - dutFile: String, - dir: File, - vSources: Seq[File], - cppHarness: File): ProcessBuilder = - - Seq("verilator", - "--cc", s"$dutFile.v") ++ - vSources.map(file => Seq("-v", file.toString)).flatten ++ - Seq("--assert", - "--Wno-fatal", - "--trace", - "-O2", - "--top-module", dutFile, - "+define+TOP_TYPE=V" + dutFile, - "-CFLAGS", s"""-Wno-undefined-bool-conversion -O2 -DTOP_TYPE=V$dutFile -include V$dutFile.h""", - "-Mdir", dir.toString, - "--exe", cppHarness.toString) - - def cppToExe(prefix: String, dir: File): ProcessBuilder = - Seq("make", "-C", dir.toString, "-j", "-f", s"V${prefix}.mk", s"V${prefix}") - - def executeExpectingFailure( - prefix: String, - dir: File, - assertionMsg: String = "Assertion failed"): Boolean = { - var triggered = false - val e = Process(s"./V${prefix}", dir) ! - ProcessLogger(line => { - triggered = triggered || line.contains(assertionMsg) - System.out.println(line) - }) - triggered - } - - def executeExpectingSuccess(prefix: String, dir: File): Boolean = { - !executeExpectingFailure(prefix, dir) - } -} +import firrtl.util.BackendCompilationUtilities trait FirrtlRunners extends BackendCompilationUtilities { def parse(str: String) = Parser.parse(str.split("\n").toIterator, IgnoreInfo) -- cgit v1.2.3