diff options
Diffstat (limited to 'src/test')
| l--------- | src/test/resources/integration | 1 | ||||
| -rw-r--r-- | src/test/scala/firrtlTests/FirrtlSpec.scala | 124 | ||||
| -rw-r--r-- | src/test/scala/firrtlTests/IntegrationSpec.scala | 26 |
3 files changed, 151 insertions, 0 deletions
diff --git a/src/test/resources/integration b/src/test/resources/integration new file mode 120000 index 00000000..5e63f954 --- /dev/null +++ b/src/test/resources/integration @@ -0,0 +1 @@ +../../../test/integration
\ No newline at end of file diff --git a/src/test/scala/firrtlTests/FirrtlSpec.scala b/src/test/scala/firrtlTests/FirrtlSpec.scala new file mode 100644 index 00000000..438a5282 --- /dev/null +++ b/src/test/scala/firrtlTests/FirrtlSpec.scala @@ -0,0 +1,124 @@ + +package firrtlTests + +import java.io._ + +import scala.sys.process._ +import org.scalatest._ +import org.scalatest.prop._ + +import firrtl._ + +// 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", + "+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) + } +} + +trait FirrtlRunners extends BackendCompilationUtilities { + lazy val cpp = new File(s"/integration/top.cpp") + def compileFirrtlTest(prefix: String, srcDir: String): File = { + val testDir = createTempDirectory(prefix) + copyResourceToFile(s"${srcDir}/${prefix}.fir", new File(testDir, s"${prefix}.fir")) + + Driver.compile(s"${testDir}/${prefix}.fir", s"${testDir}/${prefix}.v", VerilogCompiler) + testDir + } + def runFirrtlTest(prefix: String, srcDir: String) { + val testDir = compileFirrtlTest(prefix, srcDir) + val harness = new File(testDir, s"top.cpp") + copyResourceToFile(cpp.toString, harness) + + verilogToCpp(prefix, testDir, Seq(), harness).! + cppToExe(prefix, testDir).! + executeExpectingSuccess(prefix, testDir) + } +} + +class FirrtlPropSpec extends PropSpec with PropertyChecks with FirrtlRunners + diff --git a/src/test/scala/firrtlTests/IntegrationSpec.scala b/src/test/scala/firrtlTests/IntegrationSpec.scala new file mode 100644 index 00000000..eb5a7fa1 --- /dev/null +++ b/src/test/scala/firrtlTests/IntegrationSpec.scala @@ -0,0 +1,26 @@ + +package firrtlTests + +import org.scalatest._ +import org.scalatest.prop._ + +class IntegrationSpec extends FirrtlPropSpec { + + case class Test(name: String, dir: String) + + val runTests = Seq(Test("GCDTester", "/integration")) + + runTests foreach { test => + property(s"${test.name} should execute correctly") { + runFirrtlTest(test.name, test.dir) + } + } + + val compileTests = Seq(Test("rocket", "/regress"), Test("rocket-firrtl", "/regress")) + + compileTests foreach { test => + property(s"${test.name} should compile to Verilog") { + compileFirrtlTest(test.name, test.dir) + } + } +} |
