Skip to content

Commit eab8fe6

Browse files
committed
Add SimulationTest marker
The FIRRTL spec is about to define a `simulation` construct to indicate that a module should be executed as a simulation test. Add a user-facing `SimulationTest` class that can be used to mark a module as to be executed as a simulation test. This would commonly be used inside a test harness module to mark the surrounding module as a test. For example: class TestHarness extends RawModule { SimulationTest(this) } The simulation test can be given an optional name and a map of parameters. The parameters are identical to the ones accepted by the `FormalTest` marker. Multiple simulation test markers may be added to a single module, which may be useful if a test should be run with different sets of user-defined parameters.
1 parent 27198b3 commit eab8fe6

File tree

8 files changed

+196
-42
lines changed

8 files changed

+196
-42
lines changed

core/src/main/scala/chisel3/FormalTest.scala

Lines changed: 0 additions & 33 deletions
This file was deleted.
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
package chisel3
4+
5+
import chisel3.experimental.{BaseModule, Param}
6+
import chisel3.internal.Builder
7+
import chisel3.internal.firrtl.ir._
8+
import chisel3.internal.throwException
9+
import chisel3.experimental.{SourceInfo, UnlocatableSourceInfo}
10+
11+
object FormalTest {
12+
/** Mark a module as a formal test.
13+
*
14+
* Other tools can use this information to, for example, collect all modules
15+
* marked as formal tests and run formal verification on them. This is
16+
* particularly useful in combination with the `UnitTest` trait.
17+
*
18+
* @param module The module to be marked.
19+
* @param params Optional user-defined test parameters.
20+
* @param name Optional name for the test. Uses the module name by default.
21+
*
22+
* @example
23+
* The following creates a module marked as a formal test:
24+
*
25+
* {{{
26+
* class TestHarness extends RawModule {
27+
* FormalTest(this)
28+
* }
29+
* }}}
30+
*
31+
* Additional parameters may be passed to the test, which other tools may use
32+
* to control how the test is interpreted or executed:
33+
*
34+
* {{{
35+
* class TestHarness extends RawModule {
36+
* FormalTest(
37+
* this,
38+
* MapTestParam(Map(
39+
* "a" -> IntTestParam(9001),
40+
* "b" -> DoubleTestParam(13.37),
41+
* "c" -> StringTestParam("hello"),
42+
* "d" -> ArrayTestParam(Seq(
43+
* IntTestParam(9001),
44+
* StringTestParam("hello")
45+
* )),
46+
* "e" -> MapTestParam(Map(
47+
* "x" -> IntTestParam(9001),
48+
* "y" -> StringTestParam("hello"),
49+
* ))
50+
* ))
51+
* )
52+
* }
53+
* }}}
54+
*/
55+
def apply(
56+
module: BaseModule,
57+
params: MapTestParam = MapTestParam(Map.empty),
58+
name: String = ""
59+
)(implicit sourceInfo: SourceInfo): Unit = {
60+
val proposedName = if (name != "") {
61+
name
62+
} else {
63+
module._proposedName
64+
}
65+
val sanitizedName = Builder.globalNamespace.name(proposedName)
66+
Builder.components += DefTestMarker("formal", sanitizedName, module, params, sourceInfo)
67+
}
68+
}
69+
70+
object SimulationTest {
71+
/** Mark a module as a simulation test.
72+
*
73+
* Other tools can use this information to, for example, collect all modules
74+
* marked as simulation tests and run them in a simulator. This is
75+
* particularly useful in combination with the `UnitTest` trait.
76+
*
77+
* @param module The module to be marked.
78+
* @param params Optional user-defined test parameters.
79+
* @param name Optional name for the test. Uses the module name by default.
80+
*
81+
* @example
82+
* The following creates a module marked as a simulation test:
83+
*
84+
* {{{
85+
* class TestHarness extends RawModule {
86+
* SimulationTest(this)
87+
* }
88+
* }}}
89+
*
90+
* Additional parameters may be passed to the test, which other tools may use
91+
* to control how the test is interpreted or executed:
92+
*
93+
* {{{
94+
* class TestHarness extends RawModule {
95+
* SimulationTest(
96+
* this,
97+
* MapTestParam(Map(
98+
* "a" -> IntTestParam(9001),
99+
* "b" -> DoubleTestParam(13.37),
100+
* "c" -> StringTestParam("hello"),
101+
* "d" -> ArrayTestParam(Seq(
102+
* IntTestParam(9001),
103+
* StringTestParam("hello")
104+
* )),
105+
* "e" -> MapTestParam(Map(
106+
* "x" -> IntTestParam(9001),
107+
* "y" -> StringTestParam("hello"),
108+
* ))
109+
* ))
110+
* )
111+
* }
112+
* }}}
113+
*/
114+
def apply(
115+
module: BaseModule,
116+
params: MapTestParam = MapTestParam(Map.empty),
117+
name: String = ""
118+
)(implicit sourceInfo: SourceInfo): Unit = {
119+
val proposedName = if (name != "") {
120+
name
121+
} else {
122+
module._proposedName
123+
}
124+
val sanitizedName = Builder.globalNamespace.name(proposedName)
125+
Builder.components += DefTestMarker("simulation", sanitizedName, module, params, sourceInfo)
126+
}
127+
}
128+
129+
/** Parameters for test declarations. */
130+
sealed abstract class TestParam
131+
case class IntTestParam(value: BigInt) extends TestParam
132+
case class DoubleTestParam(value: Double) extends TestParam
133+
case class StringTestParam(value: String) extends TestParam
134+
case class ArrayTestParam(value: Seq[TestParam]) extends TestParam
135+
case class MapTestParam(value: Map[String, TestParam]) extends TestParam

core/src/main/scala/chisel3/internal/firrtl/Converter.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -468,8 +468,9 @@ private[chisel3] object Converter {
468468
(ports ++ ctx.secretPorts).map(p => convert(p, typeAliases)),
469469
convert(block, ctx, typeAliases)
470470
)
471-
case ctx @ DefFormalTest(name, module, params, sourceInfo) =>
472-
fir.FormalTest(
471+
case ctx @ DefTestMarker(kind, name, module, params, sourceInfo) =>
472+
fir.TestMarker(
473+
kind,
473474
convert(sourceInfo),
474475
name,
475476
module.name,

core/src/main/scala/chisel3/internal/firrtl/IR.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -541,12 +541,14 @@ private[chisel3] object ir {
541541
params: Map[String, Param]
542542
) extends Component
543543

544-
case class DefFormalTest(
544+
case class DefTestMarker(
545+
kind: String,
545546
name: String,
546547
module: BaseModule,
547548
params: MapTestParam,
548549
sourceInfo: SourceInfo
549550
) extends Component {
551+
require(kind == "formal" || kind == "simulation")
550552
def id = module
551553
val ports: Seq[Port] = Seq.empty
552554
override val secretPorts = mutable.ArrayBuffer[Port]()

core/src/main/scala/chisel3/internal/firrtl/Serializer.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -587,9 +587,9 @@ private[chisel3] object Serializer {
587587
}
588588
Iterator(start) ++ serialize(block, ctx, typeAliases)(indent + 1)
589589

590-
case ctx @ DefFormalTest(name, module, params, sourceInfo) =>
590+
case ctx @ DefTestMarker(kind, name, module, params, sourceInfo) =>
591591
implicit val b = new StringBuilder
592-
doIndent(0); b ++= "formal "; b ++= legalize(name); b ++= " of "; b ++= legalize(module.name); b ++= " :";
592+
doIndent(0); b ++= kind; b ++= " "; b ++= legalize(name); b ++= " of "; b ++= legalize(module.name); b ++= " :";
593593
serialize(sourceInfo)
594594
params.value.keys.toSeq.sorted.foreach { case name =>
595595
newLineAndIndent(1); b ++= name; b ++= " = "; serialize(params.value(name))

firrtl/src/main/scala/firrtl/ir/IR.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -656,11 +656,12 @@ case class StringTestParam(value: String) extends TestParam with UseSerializer
656656
case class ArrayTestParam(value: Seq[TestParam]) extends TestParam with UseSerializer
657657
case class MapTestParam(value: Map[String, TestParam]) extends TestParam with UseSerializer
658658

659-
/** Formal Test
659+
/** Formal or Simulation Test
660660
*/
661-
case class FormalTest(info: Info, name: String, moduleName: String, params: MapTestParam)
661+
case class TestMarker(kind: String, info: Info, name: String, moduleName: String, params: MapTestParam)
662662
extends DefModule
663663
with UseSerializer {
664+
require(kind == "formal" || kind == "simulation")
664665
val ports: Seq[Port] = Seq.empty
665666
}
666667

firrtl/src/main/scala/firrtl/ir/Serializer.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -547,9 +547,9 @@ object Serializer {
547547
b.toString
548548
}
549549
Iterator(start) ++ sIt(body)(indent + 1)
550-
case FormalTest(info, name, moduleName, params) =>
550+
case TestMarker(kind, info, name, moduleName, params) =>
551551
implicit val b = new StringBuilder
552-
doIndent(0); b ++= "formal "; b ++= legalize(name); b ++= " of "; b ++= legalize(moduleName); b ++= " :"; s(info)
552+
doIndent(0); b ++= kind; b ++= " "; b ++= legalize(name); b ++= " of "; b ++= legalize(moduleName); b ++= " :"; s(info)
553553
params.value.keys.toSeq.sorted.foreach { case name =>
554554
newLineAndIndent(1); b ++= name; b ++= " = "; s(params.value(name))
555555
}

src/test/scala-2/chiselTests/RawModuleSpec.scala

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,4 +271,52 @@ class RawModuleSpec extends AnyFlatSpec with Matchers with ChiselSim with FileCh
271271
|""".stripMargin
272272
)
273273
}
274+
275+
"RawModule marked as simulation test" should "emit a simulation test declaration" in {
276+
class Foo extends RawModule {
277+
val clock = IO(Input(Clock()))
278+
val init = IO(Input(Bool()))
279+
val done = IO(Output(Bool()))
280+
val success = IO(Output(Bool()))
281+
done := true.B
282+
success := true.B
283+
284+
SimulationTest(this)
285+
SimulationTest(this, MapTestParam(Map("hello" -> StringTestParam("world"))))
286+
SimulationTest(
287+
this,
288+
MapTestParam(
289+
Map(
290+
"a_int" -> IntTestParam(42),
291+
"b_double" -> DoubleTestParam(13.37),
292+
"c_string" -> StringTestParam("hello"),
293+
"d_array" -> ArrayTestParam(Seq(IntTestParam(42), StringTestParam("hello"))),
294+
"e_map" -> MapTestParam(
295+
Map(
296+
"x" -> IntTestParam(42),
297+
"y" -> StringTestParam("hello")
298+
)
299+
)
300+
)
301+
),
302+
"thisBetterWork"
303+
)
304+
}
305+
306+
ChiselStage
307+
.emitCHIRRTL(new Foo)
308+
.fileCheck()(
309+
"""|CHECK: simulation Foo of [[FOO:Foo_.*]] :
310+
|CHECK: simulation Foo_1 of [[FOO]] :
311+
|CHECK: hello = "world"
312+
|CHECK: simulation thisBetterWork of [[FOO]] :
313+
|CHECK: a_int = 42
314+
|CHECK: b_double = 13.37
315+
|CHECK: c_string = "hello"
316+
|CHECK: d_array = [42, "hello"]
317+
|CHECK: e_map = {x = 42, y = "hello"}
318+
|CHECK: module [[FOO]] :
319+
|""".stripMargin
320+
)
321+
}
274322
}

0 commit comments

Comments
 (0)