From 0a17d89fe76c11efadc3d0f90dc1d93a690d861a Mon Sep 17 00:00:00 2001 From: chick Date: Tue, 17 Dec 2019 13:26:08 -0800 Subject: 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 --- src/main/scala/chisel3/testers/TesterDriver.scala | 132 ++++++++++++++++++++-- 1 file changed, 122 insertions(+), 10 deletions(-) (limited to 'src/main') 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 } + } + } -- cgit v1.2.3