Skip to content

Commit 9992dfd

Browse files
committed
[chiselsim] Add RunUntilSuccess stimulus
Add new stimulus, `RunUntilSuccess`, which can be used to run a simulation until a user-specified "success" port asserts. This is added as a complementary testing approach to `RunUntilFinished` whish is needed for some environments where the test harness cannot terminate on its own or the test harness is re-used in a separate, non-ChiselSim testing context where it cannot terminate on its own. Signed-off-by: Schuyler Eldridge <[email protected]>
1 parent 06a85c3 commit 9992dfd

File tree

3 files changed

+122
-0
lines changed

3 files changed

+122
-0
lines changed

src/main/scala/chisel3/simulator/stimulus/RunUntilFinished.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import chisel3.simulator.Exceptions
88

99
/** Stimulus that will run a simulation, expecting a [[chisel3.stop]] (a Verilog
1010
* `$finish`) to occur before a maximum number of cycles has elapsed.
11+
*
12+
* @see [[RunUntilSuccess]]
1113
*/
1214
trait RunUntilFinished[A] extends Stimulus.Type[A] {
1315

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
package chisel3.simulator.stimulus
4+
5+
import chisel3.{Bool, Clock, Module}
6+
import chisel3.simulator.AnySimulatedModule
7+
import chisel3.simulator.Exceptions
8+
9+
/** Stimulus that will run a simulation expecting a "success" port to assert.
10+
*
11+
* If the specified "success" port does not assert, then an
12+
* [[Exceptions.Timeout]] will be thrown.
13+
*
14+
* @see [[RunUntilFinished]]
15+
*/
16+
trait RunUntilSuccess[A] extends Stimulus.Type[A] {
17+
18+
/** The maximum number of cycles. */
19+
protected def _maxCycles: Int
20+
21+
/** A function that returns the clock to tick. */
22+
protected def _getClock: (A) => Clock
23+
24+
/** A function that returns the success port. */
25+
protected def _getSuccess: (A) => Bool
26+
27+
/** Apply stimulus to the unit
28+
*
29+
* @param the unit to apply stimulus to
30+
*/
31+
override final def apply(dut: A): Unit = {
32+
val module = AnySimulatedModule.current
33+
val clock = module.port(_getClock(dut))
34+
val success = module.port(_getSuccess(dut))
35+
36+
clock
37+
.tick(
38+
timestepsPerPhase = 1,
39+
maxCycles = _maxCycles,
40+
inPhaseValue = 1,
41+
outOfPhaseValue = 0,
42+
sentinel = Some((success, 1)),
43+
checkElapsedCycleCount = (count: BigInt) => {
44+
if (count == _maxCycles)
45+
throw new Exceptions.Timeout(_maxCycles, "Expected simulation to assert 'success' port")
46+
}
47+
)
48+
}
49+
50+
}
51+
52+
object RunUntilSuccess {
53+
54+
/** Return stimulus for a [[Module]]
55+
*
56+
* @param maxCycles the maximum number of cycles to run the unit for before a
57+
* timeout
58+
* @param getSuccess a function to return a port which asserts when the
59+
* simulation has sucessfully finished
60+
*/
61+
def module[A <: Module](maxCycles: Int, getSuccess: A => Bool): RunUntilSuccess[A] = new RunUntilSuccess[A] {
62+
63+
override protected final val _maxCycles = maxCycles
64+
65+
override protected final val _getClock = _.clock
66+
67+
override protected final val _getSuccess = getSuccess
68+
69+
}
70+
71+
/** Return stimulus for any type. This requires the user to specify how to
72+
* extract the clock and the success port from the type.
73+
*
74+
* @param maxCycles the maximum number of cycles to run the unit for before a
75+
* timeout
76+
* @param getClock a function to return a clock from the unit
77+
* @param getSuccess a function to return a port which asserts when the
78+
* simulation has sucessfully finished
79+
*/
80+
def any[A](maxCycles: Int, getClock: A => Clock, getSuccess: A => Bool): RunUntilSuccess[A] = new RunUntilSuccess[A] {
81+
82+
override protected final val _maxCycles = maxCycles
83+
84+
override protected final val _getClock = getClock
85+
86+
override protected final val _getSuccess = getSuccess
87+
88+
}
89+
90+
/** Return default stimulus. This is the same as [[module]].
91+
*
92+
* @param maxCycles the maximum number of cycles to run the unit for before a
93+
* timeout
94+
* @param getSuccess a function to return a port which asserts when the
95+
* simulation has sucessfully finished
96+
*/
97+
def apply[A <: Module](maxCycles: Int, getSuccess: A => Bool): RunUntilSuccess[A] = module(maxCycles, getSuccess)
98+
99+
}

src/test/scala-2/chiselTests/simulator/scalatest/ChiselSimSpec.scala

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@ package chiselTests.simulator.scalatest
55
import chisel3._
66
import chisel3.simulator.PeekPokeAPI.FailedExpectationException
77
import chisel3.simulator.{ChiselSim, HasSimulator, MacroText, Settings}
8+
import chisel3.simulator.stimulus.RunUntilSuccess
89
import chisel3.testing.HasTestingDirectory
910
import chisel3.testing.scalatest.{FileCheck, TestingDirectory}
11+
import chisel3.util.Counter
1012
import chisel3.util.circt.{PlusArgsTest, PlusArgsValue}
1113
import java.nio.file.FileSystems
1214
import org.scalatest.funspec.AnyFunSpec
@@ -285,4 +287,23 @@ class ChiselSimSpec extends AnyFunSpec with Matchers with ChiselSim with FileChe
285287
}
286288
}
287289

290+
describe("ChiselSim RunUntilSuccess stimulus") {
291+
292+
class SuccessAfterFourCycles extends Module {
293+
val success = IO(Output(Bool()))
294+
success :<= Counter(true.B, 4)._2
295+
}
296+
297+
it("should report success for a passing Module") {
298+
simulate(new SuccessAfterFourCycles)(RunUntilSuccess(maxCycles = 8, getSuccess = _.success))
299+
}
300+
301+
it("should throw an exception for a failing Module") {
302+
intercept[chisel3.simulator.Exceptions.Timeout] {
303+
simulate(new SuccessAfterFourCycles)(RunUntilSuccess(maxCycles = 2, getSuccess = _.success))
304+
}
305+
}
306+
307+
}
308+
288309
}

0 commit comments

Comments
 (0)