// SPDX-License-Identifier: Apache-2.0 package firrtlTests.execution import java.io.File import firrtl.ir._ import firrtl.testutils._ import firrtl.util.BackendCompilationUtilities.createTestDirectory sealed trait SimpleTestCommand case class Step(n: Int) extends SimpleTestCommand case class Invalidate(expStr: String) extends SimpleTestCommand case class Poke(expStr: String, value: Int) extends SimpleTestCommand case class Expect(expStr: String, value: Int) extends SimpleTestCommand /** * This trait defines an interface to run a self-contained test circuit. */ trait TestExecution { def runEmittedDUT(c: Circuit, testDir: File): Unit } /** * A class that makes it easier to write execution-driven tests. * * By combining a DUT body (supplied as a string without an enclosing * module or circuit) with a sequence of test operations, an * executable, self-contained Verilog testbench may be automatically * created and checked. * * @note It is necessary to mix in a trait extending TestExecution * @note The DUT has two implicit ports, "clock" and "reset" * @note Execution of the command sequences begins after reset is deasserted * * @see [[firrtlTests.execution.TestExecution]] * @see [[firrtlTests.execution.VerilogExecution]] * * @example {{{ * class AndTester extends SimpleExecutionTest with VerilogExecution { * val body = "reg r : UInt<32>, clock with: (reset => (reset, UInt<32>(0)))" * val commands = Seq( * Expect("r", 0), * Poke("r", 3), * Step(1), * Expect("r", 3) * ) * } * }}} */ abstract class SimpleExecutionTest extends FirrtlPropSpec { this: TestExecution => /** * Text representing the body of the DUT. This is useful for testing * statement-level language features, and cuts out the overhead of * writing a top-level DUT module and having peeks/pokes point at * IOs. */ val body: String /** * A sequence of commands (peeks, pokes, invalidates, steps) that * represents how the testbench will progress. The semantics are * inspired by chisel-testers. */ def commands: Seq[SimpleTestCommand] private def interpretCommand(eth: ExecutionTestHelper, cmd: SimpleTestCommand) = cmd match { case Step(n) => eth.step(n) case Invalidate(expStr) => eth.invalidate(expStr) case Poke(expStr, value) => eth.poke(expStr, UIntLiteral(value)) case Expect(expStr, value) => eth.expect(expStr, UIntLiteral(value)) } private def runTest(): Unit = { val initial = ExecutionTestHelper(body) val test = commands.foldLeft(initial)(interpretCommand(_, _)) val testName = this.getClass.getSimpleName val testDir = createTestDirectory(s"${testName}-generated-src") runEmittedDUT(test.emit, testDir) } property("Execution of the compiled Verilog for ExecutionTestHelper should succeed") { runTest() } }