summaryrefslogtreecommitdiff
path: root/src/main
diff options
context:
space:
mode:
authorchick2019-12-17 13:26:08 -0800
committerchick2020-06-29 10:22:11 -0700
commit0a17d89fe76c11efadc3d0f90dc1d93a690d861a (patch)
treea4cfb7c210767a315473014ecd600e9857d1799f /src/main
parenta1edc8f4cd525c8475e847ff7ddd9cb8fc1d3c51 (diff)
This adds a mechanism for the unittests to be run with the TreadleBackend
This mechanism is not enabled and should not change the behavior of existing tests A following PR will deliver a switch that will allow changing the backend. The reasons for this PR - Treadle tests run much faster, enabling quicker debugging and CI cycles - This will help ensure fidelity of Treadle to the Verilator backend A few tests are marked as verilator only due to black box limitations Change treadle to a direct dependency I tried to make it a test only dependency but the TesterDriver sits in src/main requiring that regular compile have access to treadle Oops, made treadle the default A number of changes in response to @ducky64 review - made backend check clearer and add error handling for multiple backends specified - Fixed duplicate TargetDirAnnotation uses in Treadle backend - Cleaned up BlackBox test formatting - Undid unnecessary debugging changes from Counter - Undid .gitignore change, that should be on another PR A number of changes in response to @ducky64 review - Undid debugging changes made to BitWiseOps
Diffstat (limited to 'src/main')
-rw-r--r--src/main/scala/chisel3/testers/TesterDriver.scala132
1 files changed, 122 insertions, 10 deletions
diff --git a/src/main/scala/chisel3/testers/TesterDriver.scala b/src/main/scala/chisel3/testers/TesterDriver.scala
index 03fb1899..e63e86e7 100644
--- a/src/main/scala/chisel3/testers/TesterDriver.scala
+++ b/src/main/scala/chisel3/testers/TesterDriver.scala
@@ -2,10 +2,9 @@
package chisel3.testers
-import chisel3._
import java.io._
-import chisel3.aop.Aspect
+import chisel3._
import chisel3.experimental.RunFirrtlTransform
import chisel3.stage.phases.{AspectPhase, Convert, Elaborate, Emitter}
import chisel3.stage.{
@@ -19,8 +18,34 @@ import firrtl.{Driver => _, _}
import firrtl.options.{Dependency, Phase, PhaseManager}
import firrtl.stage.{FirrtlCircuitAnnotation, FirrtlStage}
import firrtl.transforms.BlackBoxSourceHelper.writeResourceToDirectory
+import treadle.executable.StopException
+import treadle.stage.TreadleTesterPhase
+import treadle.{CallResetAtStartupAnnotation, TreadleTesterAnnotation, WriteVcdAnnotation}
+//scalastyle:off magic.number method.length
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.
+ */
+ 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)
+ }
+ }
/** Set the target directory to the name of the top module after elaboration */
final class AddImplicitTesterDirectory extends Phase {
@@ -41,9 +66,35 @@ 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,
+ def execute(t: () => BasicTester,
additionalVResources: Seq[String] = Seq(),
- annotations: AnnotationSeq = Seq()
+ annotations: AnnotationSeq = Seq(),
+ nameHint: Option[String] = None): Boolean = {
+
+ val backendAnnotations = annotations.collect { case anno: Backend => anno }
+ val backendAnnotation = if (backendAnnotations.length == 1) {
+ backendAnnotations.head
+ } else if (backendAnnotations.isEmpty) {
+ defaultBackend
+ } 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 _ =>
+ 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],
@@ -76,15 +127,76 @@ object TesterDriver extends BackendCompilationUtilities {
false
}
}
+
+ //scalastyle:off cyclomatic.complexity method.length
+ 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 = 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
+ }
+ }
+
/**
* Calls the finish method of an BasicTester or a class that extends it.
* The finish method is a hook for code that augments the circuit built in the constructor.
*/
- def finishWrapper(test: () => BasicTester): () => BasicTester = {
- () => {
- val tester = test()
- tester.finish()
- tester
- }
+ def finishWrapper(test: () => BasicTester): () => BasicTester = { () =>
+ {
+ val tester = test()
+ tester.finish()
+ tester
}
+ }
+
}