Skip to content

Commit 6af1874

Browse files
committed
WIP: rethink and simplify API, make DRYer
1 parent 1128ec4 commit 6af1874

File tree

1 file changed

+119
-132
lines changed

1 file changed

+119
-132
lines changed

src/main/scala/chisel3/simulator/PeekPokeAPI.scala

+119-132
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,22 @@ import chisel3.internal.ExceptionHelpers
1111
import firrtl.options.StageUtils.dramaticMessage
1212
import scala.util.control.NoStackTrace
1313

14+
private[simulator] trait Peekable[T <: Data] {
15+
def peek(): T
16+
17+
def expect(expected: T, buildMessage: (String, T) => String)(implicit sourceInfo: SourceInfo): Unit
18+
19+
def expect(expected: T, message: String)(implicit sourceInfo: SourceInfo): Unit =
20+
expect(expected, (_, _) => message)
21+
22+
def expect(expected: T)(implicit sourceInfo: SourceInfo): Unit =
23+
expect(expected, (observed, expected) => s"Expectation failed: observed value $observed != $expected")
24+
}
25+
26+
private[simulator] trait Pokable[T <: Data] {
27+
def poke(literal: T): Unit
28+
}
29+
1430
object PeekPokeAPI extends PeekPokeAPI
1531

1632
trait PeekPokeAPI {
@@ -24,20 +40,37 @@ trait PeekPokeAPI {
2440
)
2541
)
2642
with NoStackTrace
43+
2744
object FailedExpectationException {
2845
def apply[T](
29-
observed: T,
30-
expected: T,
31-
message: String,
32-
sourceInfo: SourceInfo,
33-
extraContext: Seq[String]
46+
observed: T,
47+
expected: T,
48+
message: String,
49+
sourceInfo: SourceInfo
3450
): FailedExpectationException[T] = {
51+
val extraContext =
52+
sourceInfo match {
53+
case sl: SourceLine =>
54+
ExceptionHelpers.getErrorLineInFile(Seq(), sl)
55+
case _ =>
56+
Seq()
57+
}
3558
val fullMessage = s"$message ${sourceInfo.makeMessage()}" +
3659
(if (extraContext.nonEmpty) s"\n${extraContext.mkString("\n")}" else "")
37-
new FailedExpectationException(observed, expected, fullMessage)
60+
new FailedExpectationException[T](observed, expected, fullMessage)
3861
}
3962
}
4063

64+
sealed trait AnyTestableData[T <: Data] {
65+
protected def data: T
66+
67+
protected val simulatedModule = AnySimulatedModule.current
68+
69+
protected final def simulationPort = simulatedModule.port(data)
70+
}
71+
72+
private[simulator] trait PeekPokable[T <: Data] extends Peekable[T] with Pokable[T] with AnyTestableData[T]
73+
4174
implicit class testableClock(clock: Clock) extends AnyTestableData[Clock] {
4275
val data = clock
4376

@@ -72,72 +105,16 @@ trait PeekPokeAPI {
72105
}
73106
}
74107

75-
trait AnyTestableData[T <: Data] {
76-
protected def data: T
77-
78-
protected val simulatedModule = AnySimulatedModule.current
79-
80-
protected final def simulationPort = simulatedModule.port(data)
81-
}
82-
83-
trait Peekable[T <: Data] extends AnyTestableData[T] {
84-
def peek(): T
85-
86-
protected def isSigned = false
87-
88-
def expect(expected: T, buildMessage: (T, T) => String)(implicit sourceInfo: SourceInfo): Unit
89-
90-
def expect(expected: T, message: String)(implicit sourceInfo: SourceInfo): Unit =
91-
expect(expected, (_, _) => message)
92-
93-
def expect(expected: T)(implicit sourceInfo: SourceInfo): Unit =
94-
expect(expected, (observed, expected) => s"Expectation failed: observed value $observed != $expected")
95-
96-
def expect[U](
97-
expected: U,
98-
encode: (Simulation.Value) => U,
99-
buildMessage: (U, U) => String,
100-
sourceInfo: SourceInfo
101-
): Unit = {
102-
simulatedModule.willPeek()
103-
104-
simulationPort.check(isSigned = isSigned) { observedValue =>
105-
val observed = encode(observedValue)
106-
if (observed != expected) {
107-
val extraContext =
108-
sourceInfo match {
109-
case sl: SourceLine =>
110-
ExceptionHelpers.getErrorLineInFile(Seq(), sl)
111-
case _ =>
112-
Seq()
113-
}
114-
throw FailedExpectationException(
115-
observed,
116-
expected,
117-
buildMessage(observed, expected),
118-
sourceInfo,
119-
extraContext
120-
)
121-
}
122-
}
123-
}
124-
}
125-
126-
trait Pokable[T <: Data] extends AnyTestableData[T] {
127-
def poke(literal: T): Unit
128-
}
129-
130-
trait PeekPokable[T <: Data] extends Peekable[T] with Pokable[T]
131-
132108
sealed trait TestableElement[T <: Element] extends PeekPokable[T] {
109+
protected def isSigned = false
133110

134-
private[simulator] def encode(width: Int, value: BigInt): T
111+
private[simulator] protected def encode(width: Int, value: BigInt): T
135112

136-
protected final def encode(value: Simulation.Value): T = {
113+
private[simulator] final def encode(value: Simulation.Value): T = {
137114
encode(value.bitCount, value.asBigInt)
138115
}
139116

140-
protected def peekValue(): Simulation.Value = {
117+
protected final def peekValue(): Simulation.Value = {
141118
simulatedModule.willPeek()
142119
simulationPort.get(isSigned = isSigned)
143120
}
@@ -151,51 +128,67 @@ trait PeekPokeAPI {
151128
simulationPort.set(value)
152129
}
153130

131+
private[simulator] protected def check[U](checkFn: Simulation.Value => Unit): Unit = {
132+
simulatedModule.willPeek()
133+
simulationPort.check(isSigned = isSigned)(checkFn)
134+
}
135+
136+
private[simulator] protected final def expect[U](
137+
expected: U,
138+
sameValue: (Simulation.Value, U) => Boolean,
139+
observedValToString: Simulation.Value => String,
140+
buildMessage: (String, U) => String,
141+
sourceInfo: SourceInfo
142+
): Unit = {
143+
check(observedValue =>
144+
if (!sameValue(observedValue, expected)) {
145+
val observedStr = observedValToString(observedValue)
146+
throw FailedExpectationException(
147+
observedStr,
148+
expected.toString,
149+
buildMessage(observedStr, expected),
150+
sourceInfo
151+
)
152+
}
153+
)
154+
}
155+
154156
override def expect(expected: T)(implicit sourceInfo: SourceInfo): Unit = {
155157
expect(
156158
expected,
157-
(observed, expected) => s"Expectation failed: observed value $observed != $expected"
159+
(observed: String, expected: T) => s"Expectation failed: observed value $observed != $expected"
158160
)
159161
}
160162

161-
override def expect(expected: T, buildMessage: (T, T) => String)(implicit sourceInfo: SourceInfo): Unit = {
163+
override def expect(expected: T, buildMessage: (String, T) => String)(implicit sourceInfo: SourceInfo): Unit = {
162164
require(expected.isLit, s"Expected value: $expected must be a literal")
163165

164-
simulatedModule.willPeek()
165-
166-
simulationPort.check(isSigned = isSigned) { observedValue =>
167-
val observed = encode(observedValue)
168-
if (observedValue.asBigInt != expected.litValue) {
169-
val extraContext =
170-
sourceInfo match {
171-
case sl: SourceLine =>
172-
ExceptionHelpers.getErrorLineInFile(Seq(), sl)
173-
case _ =>
174-
Seq()
175-
}
176-
throw FailedExpectationException(
177-
observed,
178-
expected,
179-
buildMessage(observed, expected),
180-
sourceInfo,
181-
extraContext
182-
)
183-
}
184-
}
185-
}
186-
187-
final def expect(expected: BigInt)(implicit sourceInfo: SourceInfo): Unit = {
188166
expect(
189167
expected,
190-
_.asBigInt,
191-
(observed: BigInt, expected: BigInt) => s"Expectation failed: observed value $observed != $expected",
168+
(observed: Simulation.Value, expected: T) => observed.asBigInt == expected.litValue,
169+
encode(_).toString,
170+
(obs: String, exp: T) => buildMessage(obs, exp),
192171
sourceInfo
193172
)
194173
}
195174

196-
final def expect(expected: BigInt, message: String)(implicit sourceInfo: SourceInfo): Unit = {
197-
expect(expected, _.asBigInt, (_: BigInt, _: BigInt) => message, sourceInfo)
198-
}
175+
final def expect(expected: BigInt, buildMessage: (String, BigInt) => String)(
176+
implicit sourceInfo: SourceInfo
177+
): Unit = expect(
178+
expected,
179+
(obs: Simulation.Value, exp: BigInt) => obs.asBigInt == exp,
180+
_.asBigInt.toString,
181+
buildMessage,
182+
sourceInfo
183+
)
184+
185+
final def expect(expected: BigInt)(implicit sourceInfo: SourceInfo): Unit = expect(
186+
expected,
187+
(observed: String, expected: BigInt) => s"Expectation failed: observed value $observed != $expected"
188+
)
189+
190+
final def expect(expected: BigInt, message: String)(implicit sourceInfo: SourceInfo): Unit =
191+
expect(expected, (_: String, _: BigInt) => message)
199192

200193
}
201194

@@ -242,56 +235,50 @@ trait PeekPokeAPI {
242235
override def encode(width: Int, value: BigInt): T = {
243236
data.factory.all.find(_.litValue == value).get.asInstanceOf[T]
244237
}
245-
246-
override def expect(expected: T, buildMessage: (T, T) => String)(implicit sourceInfo: SourceInfo): Unit = {
247-
require(expected.isLit, "Expected value must be a literal")
248-
val observedSimValue = peekValue()
249-
val observedVal = observedSimValue.asBigInt
250-
val expectedVal = expected.litValue
251-
if (observedVal != expectedVal) {
252-
throw FailedExpectationException(observedVal, expectedVal, buildMessage(peek(), expected))
253-
}
254-
}
255238
}
256239

257240
implicit class testableRecord[T <: Record](val data: T)(implicit sourceInfo: SourceInfo) extends PeekPokable[T] {
258241

259242
override def peek(): T = {
260-
val elementValueFns = data.elements.map { case (name: String, elt: Data) =>
261-
(y: Record) =>
262-
(
263-
y.elements(name), {
264-
new testableData(elt).peek()
265-
}
266-
)
267-
}.toSeq
268-
chiselTypeOf(data).Lit(elementValueFns: _*)
243+
chiselTypeOf(data).Lit(
244+
data.elements.toSeq.map { case (name: String, elt: Data) =>
245+
(rec: Record) => rec.elements(name) -> elt.peek()
246+
}: _*
247+
)
269248
}
270249

271-
override def poke(value: T): Unit = {
272-
data.elements.foreach { case (name, d) =>
273-
d.poke(value.elements(name))
274-
}
250+
override def poke(value: T): Unit = data.elements.foreach { case (name, d) =>
251+
d.poke(value.elements(name))
275252
}
276253

277-
override def expect(expected: T, buildMessage: (T, T) => String)(implicit sourceInfo: SourceInfo): Unit = {
278-
// TODO: not checking for isLit to allow partially specified expected record
254+
def expect(expected: T, buildMessage: (String, T) => String, allowPartial: Boolean)(
255+
implicit sourceInfo: SourceInfo
256+
): Unit = {
279257
require(DataMirror.checkTypeEquivalence(data, expected), "Type mismatch")
280258

281259
// FIXME: I can't understand why but _not_ getting the peeked value as a `val` beforehand results in infinite recursion
282-
val peekedValue = peek()
260+
val peekedValue = peek().toString
283261

284262
data.elements.foreach { case (name, d) =>
285263
expected.elements(name) match {
286-
case DontCare => // missing fields are DontCare
287-
case ve =>
264+
case DontCare =>
265+
if (!allowPartial) {
266+
throw new Exception(
267+
s"Field '$name' is not initiazlized in the expected value $expected"
268+
)
269+
}
270+
case exp =>
288271
d.expect(
289-
ve,
290-
(obs, exp) => s"${buildMessage(peekedValue, expected)}:\n Expected value of $name to be $exp, got $obs"
272+
exp,
273+
(obs: String, _) =>
274+
s"${buildMessage(peekedValue, expected)}:\n Expected value of field '$name' to be $exp, got $obs"
291275
)
292276
}
293277
}
294278
}
279+
280+
override def expect(expected: T, buildMessage: (String, T) => String)(implicit sourceInfo: SourceInfo): Unit =
281+
expect(expected, buildMessage, allowPartial = false)
295282
}
296283

297284
implicit class testableData[T <: Data](val data: T) extends PeekPokable[T] {
@@ -312,11 +299,11 @@ trait PeekPokeAPI {
312299

313300
override def expect(
314301
expected: T,
315-
buildMessage: (T, T) => String
302+
buildMessage: (String, T) => String
316303
)(implicit sourceInfo: SourceInfo): Unit = {
317304

318-
def buildMsgFn[S](observed: S, expected: S): String =
319-
buildMessage(observed.asInstanceOf[T], expected.asInstanceOf[T])
305+
def buildMsgFn[S](observed: String, expected: S): String =
306+
buildMessage(observed, expected.asInstanceOf[T])
320307

321308
(data, expected) match {
322309
case (dat: Bool, exp: Bool) =>
@@ -334,15 +321,15 @@ trait PeekPokeAPI {
334321
exp.length == dat.length,
335322
s"Vec length mismatch: Data port has ${dat.length} elements while the expected value is of length ${exp.length}"
336323
)
337-
val peekedValue = dat.peek()
324+
val peekedValue = dat.peek().toString
338325
dat.getElements.zip(exp.getElements).zipWithIndex.foreach { case ((datEl, valEl), index) =>
339326
valEl match {
340327
case DontCare =>
341328
// TODO: missing elements?
342329
case ve =>
343330
datEl.expect(
344331
ve,
345-
(o, e) => buildMessage(peekedValue.asInstanceOf[T], exp.asInstanceOf[T]) + s" at index $index"
332+
(obs: String, exp: Data) => buildMessage(peekedValue, exp.asInstanceOf[T]) + s" at index $index"
346333
)
347334
}
348335
}

0 commit comments

Comments
 (0)