diff options
| -rw-r--r-- | build.sbt | 4 | ||||
| -rw-r--r-- | build.sc | 8 | ||||
| -rw-r--r-- | src/main/scala/chisel3/testers/TesterDriver.scala | 180 | ||||
| -rw-r--r-- | src/test/scala/chisel3/testers/TestUtils.scala | 12 | ||||
| -rw-r--r-- | src/test/scala/chisel3/testers/TreadleBackend.scala | 77 | ||||
| -rw-r--r-- | src/test/scala/chiselTests/ChiselSpec.scala | 8 |
6 files changed, 152 insertions, 137 deletions
@@ -29,8 +29,7 @@ def javacOptionsVersion(scalaVersion: String): Seq[String] = { } val defaultVersions = Seq( - "edu.berkeley.cs" %% "firrtl" % "1.4-SNAPSHOT", - "edu.berkeley.cs" %% "treadle" % "1.3-SNAPSHOT" + "edu.berkeley.cs" %% "firrtl" % "1.4-SNAPSHOT" ) lazy val commonSettings = Seq ( @@ -110,6 +109,7 @@ lazy val chiselSettings = Seq ( "junit" % "junit" % "4.13" % "test", "org.scalatest" %% "scalatest" % "3.1.2" % "test", "org.scalatestplus" %% "scalacheck-1-14" % "3.1.1.1" % "test", + "edu.berkeley.cs" %% "treadle" % "1.3-SNAPSHOT" % "test", "com.github.scopt" %% "scopt" % "3.7.1" ), javacOptions ++= javacOptionsVersion(scalaVersion.value) @@ -97,9 +97,7 @@ class chisel3CrossModule(crossVersionValue: String) extends CommonModule with Pu def mainClass = Some("chisel3.stage.ChiselMain") - def ivyDeps = super.ivyDeps() ++ treadleIvyDeps - - override def moduleDeps = super.moduleDeps ++ Seq(macros, core) ++ treadleModule + override def moduleDeps = super.moduleDeps ++ Seq(macros, core) ++ firrtlModule object test extends Tests { private def ivyCrossDeps = majorVersion match { @@ -111,7 +109,9 @@ class chisel3CrossModule(crossVersionValue: String) extends CommonModule with Pu ivy"org.scalatest::scalatest:3.1.2", ivy"org.scalatestplus::scalacheck-1-14:3.1.1.1", ivy"com.github.scopt::scopt:3.7.1" - ) ++ ivyCrossDeps + ) ++ ivyCrossDeps ++ m.treadleIvyDeps + + override def moduleDeps = super.moduleDeps ++ treadleModule def testFrameworks = Seq("org.scalatest.tools.Framework") diff --git a/src/main/scala/chisel3/testers/TesterDriver.scala b/src/main/scala/chisel3/testers/TesterDriver.scala index 2a66526c..70dd4999 100644 --- a/src/main/scala/chisel3/testers/TesterDriver.scala +++ b/src/main/scala/chisel3/testers/TesterDriver.scala @@ -7,39 +7,67 @@ import java.io._ import chisel3._ import chisel3.stage.phases.{Convert, Elaborate, Emitter} import chisel3.stage.{ChiselCircuitAnnotation, ChiselGeneratorAnnotation, ChiselStage, NoRunFirrtlCompilerAnnotation} -import treadle.stage.TreadleTesterPhase import firrtl.AnnotationSeq import firrtl.annotations.NoTargetAnnotation import firrtl.options.{Dependency, Phase, PhaseManager, TargetDirAnnotation, Unserializable} import firrtl.stage.{FirrtlCircuitAnnotation, FirrtlStage} import firrtl.transforms.BlackBoxSourceHelper.writeResourceToDirectory -import treadle.executable.StopException -import treadle.{CallResetAtStartupAnnotation, TreadleTesterAnnotation, WriteVcdAnnotation} object TesterDriver extends BackendCompilationUtilities { - var MaxTreadleCycles = 10000L - - trait Backend extends NoTargetAnnotation with Unserializable - case object VerilatorBackend extends Backend - case object TreadleBackend extends Backend - case object NoBackend extends Backend - - /* - Currently the only mechanism for running with the Treadle backend is to edit this - statement locally. To: - `val defaultBackend: Backend = TreadleBackend` - */ + + private[chisel3] trait Backend extends NoTargetAnnotation with Unserializable { + def execute(t: () => BasicTester, + additionalVResources: Seq[String] = Seq(), + annotations: AnnotationSeq = Seq(), + nameHint: Option[String] = None + ): Boolean + } + case object VerilatorBackend extends Backend { + /** 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(), + annotations: AnnotationSeq = Seq(), + nameHint: Option[String] = None + ): Boolean = { + val pm = new PhaseManager( + targets = Seq(Dependency[AddImplicitTesterDirectory], + Dependency[Emitter], + Dependency[Convert])) + + val annotationsx = pm.transform(ChiselGeneratorAnnotation(finishWrapper(t)) +: annotations) + + val target: String = annotationsx.collectFirst { case FirrtlCircuitAnnotation(cir) => cir.main }.get + val path = annotationsx.collectFirst { case TargetDirAnnotation(dir) => dir }.map(new File(_)).get + + // Copy CPP harness and other Verilog sources from resources into files + val cppHarness = new File(path, "top.cpp") + copyResourceToFile("/chisel3/top.cpp", cppHarness) + // NOTE: firrtl.Driver.execute() may end up copying these same resources in its BlackBoxSourceHelper code. + // As long as the same names are used for the output files, and we avoid including duplicate files + // in BackendCompilationUtilities.verilogToCpp(), we should be okay. + // To that end, we use the same method to write the resource to the target directory. + val additionalVFiles = additionalVResources.map((name: String) => { + writeResourceToDirectory(name, path) + }) + + (new FirrtlStage).execute(Array("--compiler", "verilog"), annotationsx) + + // Use sys.Process to invoke a bunch of backend stuff, then run the resulting exe + if ((verilogToCpp(target, path, additionalVFiles, cppHarness) #&& + cppToExe(target, path)).! == 0) { + executeExpectingSuccess(target, path) + } else { + false + } + } + } + val defaultBackend: Backend = VerilatorBackend /** Use this to force a test to be run only with backends that are restricted to verilator backend */ - def verilatorOnly: AnnotationSeq = { - if (defaultBackend == TreadleBackend) { - Seq(NoBackend) - } else { - Seq(defaultBackend) - } - } + def verilatorOnly: AnnotationSeq = Seq(VerilatorBackend) /** Set the target directory to the name of the top module after elaboration */ final class AddImplicitTesterDirectory extends Phase { @@ -73,113 +101,7 @@ object TesterDriver extends BackendCompilationUtilities { } else { throw new ChiselException(s"Only one backend annotation allowed, found: ${backendAnnotations.mkString(", ")}") } - backendAnnotation match { - case TreadleBackend => - executeTreadle(t, additionalVResources, annotations, nameHint) - case VerilatorBackend => - executeVerilog(t, additionalVResources, annotations, nameHint) - case NoBackend => - true - case _ => - throw new ChiselException(s"Unknown backend specified: $backendAnnotation") - } - } - - /** For use with modules that should successfully be elaborated by the - * frontend, and which can be turned into executables with assertions. */ - def executeVerilog(t: () => BasicTester, - additionalVResources: Seq[String] = Seq(), - annotations: AnnotationSeq = Seq(), - nameHint: Option[String] = None - ): Boolean = { - val pm = new PhaseManager( - targets = Seq(Dependency[AddImplicitTesterDirectory], - Dependency[Emitter], - Dependency[Convert])) - - val annotationsx = pm.transform(ChiselGeneratorAnnotation(finishWrapper(t)) +: annotations) - - val target: String = annotationsx.collectFirst { case FirrtlCircuitAnnotation(cir) => cir.main }.get - val path = annotationsx.collectFirst { case TargetDirAnnotation(dir) => dir }.map(new File(_)).get - - // Copy CPP harness and other Verilog sources from resources into files - val cppHarness = new File(path, "top.cpp") - copyResourceToFile("/chisel3/top.cpp", cppHarness) - // NOTE: firrtl.Driver.execute() may end up copying these same resources in its BlackBoxSourceHelper code. - // As long as the same names are used for the output files, and we avoid including duplicate files - // in BackendCompilationUtilities.verilogToCpp(), we should be okay. - // To that end, we use the same method to write the resource to the target directory. - val additionalVFiles = additionalVResources.map((name: String) => { - writeResourceToDirectory(name, path) - }) - - (new FirrtlStage).execute(Array("--compiler", "verilog"), annotationsx) - - // Use sys.Process to invoke a bunch of backend stuff, then run the resulting exe - if ((verilogToCpp(target, path, additionalVFiles, cppHarness) #&& - cppToExe(target, path)).! == 0) { - executeExpectingSuccess(target, path) - } else { - false - } - } - - def executeTreadle(t: () => BasicTester, - additionalVResources: Seq[String] = Seq(), - annotations: AnnotationSeq = Seq(), - nameHint: Option[String] = None): Boolean = { - val generatorAnnotation = chisel3.stage.ChiselGeneratorAnnotation(t) - - // This provides an opportunity to translate from top level generic flags to backend specific annos - var annotationSeq = annotations :+ WriteVcdAnnotation - - // This produces a chisel circuit annotation, a later pass will generate a firrtl circuit - // Can't do both at once currently because generating the latter deletes the former - annotationSeq = (new chisel3.stage.phases.Elaborate).transform(annotationSeq :+ generatorAnnotation) - - val circuit = annotationSeq.collect { case x: ChiselCircuitAnnotation => x }.head.circuit - - val targetName: File = createTestDirectory(circuit.name) - - if (!annotationSeq.exists(_.isInstanceOf[NoTargetAnnotation])) { - annotationSeq = annotationSeq :+ TargetDirAnnotation(targetName.getPath) - } - if (!annotationSeq.exists { case CallResetAtStartupAnnotation => true ; case _ => false }) { - annotationSeq = annotationSeq :+ CallResetAtStartupAnnotation - } - - // This generates the firrtl circuit needed by the TreadleTesterPhase - annotationSeq = (new ChiselStage).run( - annotationSeq ++ Seq(NoRunFirrtlCompilerAnnotation) - ) - - // This generates a TreadleTesterAnnotation with a treadle tester instance - annotationSeq = (new TreadleTesterPhase).transform(annotationSeq) - - val treadleTester = annotationSeq.collectFirst { case TreadleTesterAnnotation(t) => t }.getOrElse( - throw new Exception( - s"TreadleTesterPhase could not build a treadle tester from these annotations" + - annotationSeq.mkString("Annotations:\n", "\n ", "") - ) - ) - - try { - var cycle = 0L - while (cycle < MaxTreadleCycles) { - cycle += 1 - treadleTester.step() - } - throw new ChiselException(s"Treadle backend exceeded MaxTreadleCycles ($MaxTreadleCycles)") - } catch { - case _: StopException => - } - treadleTester.finish - - treadleTester.getStopResult match { - case None => true - case Some(0) => true - case _ => false - } + backendAnnotation.execute(t, additionalVResources, annotations, nameHint) } /** diff --git a/src/test/scala/chisel3/testers/TestUtils.scala b/src/test/scala/chisel3/testers/TestUtils.scala new file mode 100644 index 00000000..97cf9190 --- /dev/null +++ b/src/test/scala/chisel3/testers/TestUtils.scala @@ -0,0 +1,12 @@ +// See LICENSE for license details. + +package chisel3.testers + +import TesterDriver.Backend +import firrtl.AnnotationSeq + +object TestUtils { + // Useful because TesterDriver.Backend is chisel3 package private + def containsBackend(annos: AnnotationSeq): Boolean = + annos.collectFirst { case b: Backend => b }.isDefined +} diff --git a/src/test/scala/chisel3/testers/TreadleBackend.scala b/src/test/scala/chisel3/testers/TreadleBackend.scala new file mode 100644 index 00000000..e432ce35 --- /dev/null +++ b/src/test/scala/chisel3/testers/TreadleBackend.scala @@ -0,0 +1,77 @@ +// See LICENSE for license details. + +package chisel3.testers + +import TesterDriver.createTestDirectory +import chisel3._ +import chisel3.stage._ +import firrtl.AnnotationSeq +import firrtl.annotations.NoTargetAnnotation +import firrtl.options.TargetDirAnnotation +import treadle.stage.TreadleTesterPhase +import treadle.executable.StopException +import treadle.{CallResetAtStartupAnnotation, TreadleTesterAnnotation, WriteVcdAnnotation} + +import java.io.File + +case object TreadleBackend extends TesterDriver.Backend { + val MaxTreadleCycles = 10000L + + def execute(t: () => BasicTester, + additionalVResources: Seq[String] = Seq(), + annotations: AnnotationSeq = Seq(), + nameHint: Option[String] = None): Boolean = { + val generatorAnnotation = chisel3.stage.ChiselGeneratorAnnotation(t) + + // This provides an opportunity to translate from top level generic flags to backend specific annos + var annotationSeq = annotations :+ WriteVcdAnnotation + + // This produces a chisel circuit annotation, a later pass will generate a firrtl circuit + // Can't do both at once currently because generating the latter deletes the former + annotationSeq = (new chisel3.stage.phases.Elaborate).transform(annotationSeq :+ generatorAnnotation) + + val circuit = annotationSeq.collect { case x: ChiselCircuitAnnotation => x }.head.circuit + + val targetName: File = createTestDirectory(circuit.name) + + if (!annotationSeq.exists(_.isInstanceOf[NoTargetAnnotation])) { + annotationSeq = annotationSeq :+ TargetDirAnnotation(targetName.getPath) + } + if (!annotationSeq.exists { case CallResetAtStartupAnnotation => true ; case _ => false }) { + annotationSeq = annotationSeq :+ CallResetAtStartupAnnotation + } + + // This generates the firrtl circuit needed by the TreadleTesterPhase + annotationSeq = (new ChiselStage).run( + annotationSeq ++ Seq(NoRunFirrtlCompilerAnnotation) + ) + + // This generates a TreadleTesterAnnotation with a treadle tester instance + annotationSeq = (new TreadleTesterPhase).transform(annotationSeq) + + val treadleTester = annotationSeq.collectFirst { case TreadleTesterAnnotation(t) => t }.getOrElse( + throw new Exception( + s"TreadleTesterPhase could not build a treadle tester from these annotations" + + annotationSeq.mkString("Annotations:\n", "\n ", "") + ) + ) + + try { + var cycle = 0L + while (cycle < MaxTreadleCycles) { + cycle += 1 + treadleTester.step() + } + throw new ChiselException(s"Treadle backend exceeded MaxTreadleCycles ($MaxTreadleCycles)") + } catch { + case _: StopException => + } + treadleTester.finish + + treadleTester.getStopResult match { + case None => true + case Some(0) => true + case _ => false + } + } +} diff --git a/src/test/scala/chiselTests/ChiselSpec.scala b/src/test/scala/chiselTests/ChiselSpec.scala index e50f26e8..eb3ec3e6 100644 --- a/src/test/scala/chiselTests/ChiselSpec.scala +++ b/src/test/scala/chiselTests/ChiselSpec.scala @@ -10,7 +10,7 @@ import chisel3._ import chisel3.stage.{ChiselGeneratorAnnotation, ChiselStage} import chisel3.testers._ import firrtl.{AnnotationSeq, CommonOptions, EmittedVerilogCircuitAnnotation, ExecutionOptionsManager, FirrtlExecutionFailure, FirrtlExecutionSuccess, HasFirrtlOptions} -import firrtl.annotations.DeletedAnnotation +import firrtl.annotations.{Annotation, DeletedAnnotation} import firrtl.util.BackendCompilationUtilities import java.io.ByteArrayOutputStream import java.security.Permission @@ -26,7 +26,11 @@ trait ChiselRunners extends Assertions with BackendCompilationUtilities { additionalVResources: Seq[String] = Seq(), annotations: AnnotationSeq = Seq() ): Boolean = { - TesterDriver.execute(() => t, additionalVResources, annotations) + // Change this to enable Treadle as a backend + val defaultBackend = chisel3.testers.TesterDriver.defaultBackend + val hasBackend = TestUtils.containsBackend(annotations) + val annos: Seq[Annotation] = if (hasBackend) annotations else defaultBackend +: annotations + TesterDriver.execute(() => t, additionalVResources, annos) } def assertTesterPasses(t: => BasicTester, additionalVResources: Seq[String] = Seq(), |
