@@ -11,53 +11,83 @@ import chisel3.experimental.hierarchy.{Definition, Instance}
1111 * @tparam M the type of the DUT module
1212 * @tparam R the type of the result returned by the test body
1313 */
14- class TestParameters [M <: RawModule , R ] private [inlinetest] (
14+ final class TestParameters [M <: RawModule , R ] private [inlinetest] (
1515 /** The [[desiredName ]] of the DUT module. */
1616 val dutName : String ,
1717 /** The user-provided name of the test. */
1818 val testName : String ,
1919 /** A Definition of the DUT module. */
2020 val dutDefinition : Definition [M ],
2121 /** The body for this test, returns a result. */
22- val testBody : Instance [M ] => R ,
22+ private [inlinetest] val testBody : Instance [M ] => R ,
2323 /** The reset type of the DUT module. */
24- val resetType : Option [Module .ResetType .Type ]
24+ private [inlinetest] val dutResetType : Option [Module .ResetType .Type ]
2525) {
26- final def desiredTestModuleName = s " test_ ${dutName}_ ${testName}"
26+
27+ /** The concrete reset type of the testharness module. */
28+ private [inlinetest] final def testHarnessResetType = dutResetType match {
29+ case Some (rt @ Module .ResetType .Synchronous ) => rt
30+ case Some (rt @ Module .ResetType .Asynchronous ) => rt
31+ case _ => Module .ResetType .Synchronous
32+ }
33+
34+ /** The [[desiredName ]] for the testharness module. */
35+ private [inlinetest] final def testHarnessDesiredName = s " test_ ${dutName}_ ${testName}"
2736}
2837
29- /** Contains traits that implement behavior common to generators for unit test testharness modules. */
30- object TestHarness {
31- import chisel3 .{Module => ChiselModule , RawModule => ChiselRawModule }
38+ sealed class TestResultBundle extends Bundle {
3239
33- /** TestHarnesses for inline tests without clock and reset IOs should extend this. This
34- * trait sets the correct desiredName for the module, instantiates the DUT, and provides
35- * methods to elaborate the test.
36- *
37- * @tparam M the type of the DUT module
38- * @tparam R the type of the result returned by the test body
39- */
40- trait RawModule [M <: ChiselRawModule , R ] extends Public { this : ChiselRawModule =>
41- def test : TestParameters [M , R ]
42- override def desiredName = test.desiredTestModuleName
43- val dut = Instance (test.dutDefinition)
44- final def elaborateTest (): R = test.testBody(dut)
45- }
40+ /** The test shall be considered complete on the first positive edge of
41+ * [[finish ]] by the simulation. The [[TestHarness ]] must drive this.
42+ */
43+ val finish = Bool ()
44+
45+ /** The test shall pass if this is asserted when the test is complete.
46+ * The [[TestHarness ]] must drive this.
47+ */
48+ val success = Bool ()
49+ }
4650
47- /** TestHarnesses for inline tests should extend this. This trait sets the correct desiredName for
48- * the module, instantiates the DUT, and provides methods to elaborate the test. By default, the
49- * reset is synchronous, but this can be changed by overriding [[resetType ]].
51+ /** TestHarnesses for inline tests should extend this. This abstract class sets the correct desiredName for
52+ * the module, instantiates the DUT, and provides methods to generate the test. The [[resetType ]] matches
53+ * that of the DUT, or is [[Synchronous ]] if it must be inferred (this can be overriden).
54+ *
55+ * A [[TestHarness ]] has the following ports:
56+ *
57+ * - [[clock ]]: shall be driven at a constant frequency by the simulation.
58+ * - [[reset ]]: shall be asserted for one cycle from the first positive edge of [[clock ]] by the simulation.
59+ * - [[reset ]]: shall be asserted for one cycle from the first positive edge of [[clock ]] by the simulation.
60+ * - [[finish ]]: the test shall be considered complete on the first positive edge of [[finish ]].
5061 *
5162 * @tparam M the type of the DUT module
5263 * @tparam R the type of the result returned by the test body
5364 */
54- trait Module [M <: ChiselRawModule , R ] extends RawModule [M , R ] { this : ChiselModule =>
55- override def resetType = test.resetType match {
56- case Some (rt @ Module .ResetType .Synchronous ) => rt
57- case Some (rt @ Module .ResetType .Asynchronous ) => rt
58- case _ => Module .ResetType .Synchronous
59- }
60- }
65+ abstract class TestHarness [M <: RawModule , R ](test : TestParameters [M , R ])
66+ extends FixedIOModule (new TestResultBundle )
67+ with Public {
68+ override final def desiredName = test.testHarnessDesiredName
69+ override final def resetType = test.testHarnessResetType
70+
71+ // Handle the base case where a test has no result. In this case, we expect
72+ // the test to end the simulation and signal pass/fail.
73+ io.finish := false .B
74+ io.success := true .B
75+
76+ protected final val dut = Instance (test.dutDefinition)
77+ protected final val testResult = test.testBody(dut)
78+ }
79+
80+ /** TestHarnesses for inline tests should extend this. This abstract class sets the correct desiredName for
81+ * the module, instantiates the DUT, and provides methods to generate the test. The [[resetType ]] matches
82+ * that of the DUT, or is [[Synchronous ]] if it must be inferred (this can be overriden).
83+ *
84+ * @tparam M the type of the DUT module
85+ * @tparam R the type of the result returned by the test body
86+ */
87+ abstract class TestHarnessWithResult [M <: RawModule ](test : TestParameters [M , TestResultBundle ])
88+ extends TestHarness [M , TestResultBundle ](test) {
89+ io.finish := testResult.finish
90+ io.success := testResult.success
6191}
6292
6393/** An implementation of a testharness generator. This is a type class that defines how to
@@ -69,22 +99,25 @@ object TestHarness {
6999trait TestHarnessGenerator [M <: RawModule , R ] {
70100
71101 /** Generate a testharness module given the test parameters. */
72- def generate (test : TestParameters [M , R ]): RawModule with Public
102+ def generate (test : TestParameters [M , R ]): TestHarness [ M , R ]
73103}
74104
75105object TestHarnessGenerator {
76106
77- /** The minimal implementation of a unit testharness. Has a clock input and a synchronous reset
78- * input. Connects these to the DUT and does nothing else.
79- */
80- class UnitTestHarness [M <: RawModule ](val test : TestParameters [M , Unit ])
81- extends Module
82- with TestHarness .Module [M , Unit ] {
83- elaborateTest()
107+ /** Factory for a TestHarnessGenerator typeclass. */
108+ def apply [M <: RawModule , R ](gen : TestParameters [M , R ] => TestHarness [M , R ]) =
109+ new TestHarnessGenerator [M , R ] {
110+ override def generate (test : TestParameters [M , R ]) = gen(test)
111+ }
112+
113+ /** Provides a default testharness for tests that return [[Unit ]]. */
114+ implicit def baseTestHarnessGenerator [M <: RawModule ]: TestHarnessGenerator [M , Unit ] = {
115+ TestHarnessGenerator (new TestHarness [M , Unit ](_) {})
84116 }
85117
86- implicit def unitTestHarness [M <: RawModule ]: TestHarnessGenerator [M , Unit ] = new TestHarnessGenerator [M , Unit ] {
87- override def generate (test : TestParameters [M , Unit ]) = new UnitTestHarness (test)
118+ /** Provides a default testharness for tests that return a [[TestResultBundle ]] */
119+ implicit def resultTestHarnessGenerator [M <: RawModule ]: TestHarnessGenerator [M , TestResultBundle ] = {
120+ TestHarnessGenerator (new TestHarnessWithResult [M ](_) {})
88121 }
89122}
90123
@@ -94,7 +127,7 @@ object TestHarnessGenerator {
94127 * hardware indicating completion and/or exit code to the testharness.
95128 */
96129trait HasTests { module : RawModule =>
97- type M = module.type
130+ private type M = module.type
98131
99132 /** Whether inline tests will be elaborated as a top-level definition to the circuit. */
100133 protected def elaborateTests : Boolean = true
0 commit comments