Skip to content

Commit 87632c9

Browse files
committed
added tests for expectPartial(), fixed custom message
1 parent 2985421 commit 87632c9

File tree

3 files changed

+238
-12
lines changed

3 files changed

+238
-12
lines changed

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

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ sealed trait AnyTestableData[T <: Data] {
7272

7373
trait PeekPokable[T <: Data] extends Peekable[T] with Pokable[T] with AnyTestableData[T]
7474

75+
trait PeekPokeApiException extends NoStackTrace
76+
7577
/**
7678
* Exception thrown when an expectation fails.
7779
*
@@ -88,7 +90,7 @@ case class FailedExpectationException[T <: Serializable](observed: T, expected:
8890
|$message""".stripMargin
8991
)
9092
)
91-
with NoStackTrace
93+
with PeekPokeApiException
9294

9395
object FailedExpectationException {
9496
def apply[T <: Serializable](
@@ -110,6 +112,25 @@ object FailedExpectationException {
110112
}
111113
}
112114

115+
case class UninitializedElementException(message: String)(implicit sourceInfo: SourceInfo)
116+
extends RuntimeException(
117+
{
118+
val extraContext =
119+
sourceInfo match {
120+
case sl: SourceLine =>
121+
ExceptionHelpers.getErrorLineInFile(Seq(), sl)
122+
case _ =>
123+
Seq()
124+
}
125+
dramaticMessage(
126+
header = Some("Uninitialized Element"),
127+
body = s"$message ${sourceInfo.makeMessage()}" +
128+
(if (extraContext.nonEmpty) s"\n${extraContext.mkString("\n")}" else "")
129+
)
130+
}
131+
)
132+
with PeekPokeApiException
133+
113134
sealed trait TestableAggregate[T <: Aggregate] extends PeekPokable[T] {
114135

115136
/**
@@ -361,9 +382,9 @@ object PeekPokeAPI {
361382
): Unit = {
362383
data.elements.foreach { case (elName, portEl) =>
363384
expected.elements(elName) match {
364-
case expEl if expEl.topBindingOpt == Some(DontCareBinding()) =>
385+
case expEl: Element if expEl.topBindingOpt == Some(DontCareBinding()) =>
365386
if (!allowPartial) {
366-
throw new Exception(
387+
throw new UninitializedElementException(
367388
s"Element '$elName' in the expected value is not initialized"
368389
)
369390
}
@@ -397,7 +418,7 @@ object PeekPokeAPI {
397418
s"Expectation failed for element '$elName': observed value ${dataToString(observed.elements(elName))} != expected value ${dataToString(expected.elements(elName))}"
398419

399420
override def expectPartial(expected: T, message: String)(implicit sourceInfo: SourceInfo): Unit =
400-
expect(expected, defaultMessageBuilder(_, _, _), allowPartial = true)
421+
expect(expected, defaultMessageBuilder(_, _, _, message), allowPartial = true)
401422

402423
override def expect(expected: T, message: String)(implicit sourceInfo: SourceInfo): Unit =
403424
expect(expected, defaultMessageBuilder(_, _, _, message), allowPartial = false)
@@ -448,9 +469,11 @@ object PeekPokeAPI {
448469
implicit sourceInfo: SourceInfo
449470
): Unit = {
450471
data.getElements.zip(expected).zipWithIndex.foreach {
451-
case ((datEl, expEl), idx) if expEl.topBindingOpt == Some(DontCareBinding()) =>
472+
case ((datEl: Element, expEl: Element), idx) if expEl.topBindingOpt == Some(DontCareBinding()) =>
452473
if (!allowPartial)
453-
throw new Exception(s"Vec element at index $idx in the expected value is not initialized")
474+
throw new UninitializedElementException(
475+
s"Vec element at index $idx in the expected value is not initialized"
476+
)
454477
case ((datEl, expEl), idx) if datEl.getClass == expEl.getClass =>
455478
val message = buildMessage(peek(), expected, idx)
456479
(allowPartial, datEl) match {

src/test/scala-2/chiselTests/simulator/PeekPokeAPISpec.scala

Lines changed: 206 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ class PeekPokeAPISpec extends AnyFunSpec with ChiselSim with Matchers {
1313

1414
import PeekPokeTestModule._
1515

16-
val numTests = 50
16+
val numTests = 20
1717

1818
describe("PeekPokeAPI with TestableData") {
1919
val w = 32
@@ -150,7 +150,6 @@ class PeekPokeAPISpec extends AnyFunSpec with ChiselSim with Matchers {
150150
simulate(new PeekPokeTestModule(w)) { dut =>
151151
assert(w == dut.io.in.bits.a.getWidth)
152152
val vecDim = dut.vecDim
153-
val truncationMask = (BigInt(1) << w) - 1
154153

155154
dut.io.in.bits.poke(
156155
chiselTypeOf(dut.io.in.bits).Lit(
@@ -172,12 +171,11 @@ class PeekPokeAPISpec extends AnyFunSpec with ChiselSim with Matchers {
172171
thrown.getMessage must include("observed value UInt<32>(3) != UInt<3>(5)")
173172
}
174173

175-
it("reports failed Record expects correctly") {
174+
it("should correctly report failed expect() on a Record") {
176175
val thrown = the[FailedExpectationException[_]] thrownBy {
177176
simulate(new PeekPokeTestModule(w)) { dut =>
178177
assert(w == dut.io.in.bits.a.getWidth)
179178
val vecDim = dut.vecDim
180-
val truncationMask = (BigInt(1) << w) - 1
181179

182180
dut.io.in.bits.poke(
183181
chiselTypeOf(dut.io.in.bits).Lit(
@@ -210,12 +208,11 @@ class PeekPokeAPISpec extends AnyFunSpec with ChiselSim with Matchers {
210208
thrown.getMessage must include("element 'vDot'")
211209
}
212210

213-
it("should report failed expect of Records with Vec fields correctly") {
211+
it("should correctly report failed expect() on Records with Vec fields") {
214212
val thrown = the[FailedExpectationException[_]] thrownBy {
215213
simulate(new PeekPokeTestModule(w)) { dut =>
216214
assert(w == dut.io.in.bits.a.getWidth)
217215
val vecDim = dut.vecDim
218-
val truncationMask = (BigInt(1) << w) - 1
219216

220217
dut.io.in.bits.poke(
221218
chiselTypeOf(dut.io.in.bits).Lit(
@@ -244,5 +241,208 @@ class PeekPokeAPISpec extends AnyFunSpec with ChiselSim with Matchers {
244241
thrown.getMessage must include("dut.io.out.bits.expect(expRecord)")
245242
thrown.getMessage must include("element 'vOutProduct'")
246243
}
244+
245+
it("should support expectPartial() for Records and Vecs") {
246+
simulate(new PeekPokeTestModule(w)) { dut =>
247+
assert(w == dut.io.in.bits.a.getWidth)
248+
val vecDim = dut.vecDim
249+
for {
250+
_ <- 0 until numTests
251+
a = BigInt(w, rand)
252+
b = BigInt(w, rand)
253+
v1 = Seq.fill(vecDim)(BigInt(w, rand))
254+
v2 = Seq.fill(vecDim)(BigInt(w, rand))
255+
op <- TestOp.all
256+
} {
257+
258+
dut.io.in.bits.poke(
259+
chiselTypeOf(dut.io.in.bits).Lit(
260+
_.a -> a.U,
261+
_.b -> b.U,
262+
_.v1 -> Vec.Lit(v1.map(_.U(w.W)): _*),
263+
_.v2 -> Vec.Lit(v2.map(_.U(w.W)): _*)
264+
)
265+
)
266+
dut.io.in.valid.poke(true)
267+
dut.io.op.poke(op)
268+
dut.clock.step()
269+
dut.io.in.valid.poke(false)
270+
271+
val expectedScalar = calcExpectedScalarOpResult(op, a, b, w)
272+
val expectedCmp = calcExpectedCmp(a, b)
273+
val expectedVSum = calcExpectedVSum(v1, v2, w)
274+
val expVecProduct = calcExpectedVecProduct(v1, v2, w)
275+
val expVecProductPartial = chiselTypeOf(dut.io.out.bits.vOutProduct).Lit(
276+
// 0 -> expVecProduct(0)
277+
// 2 -> expVecProduct(2)
278+
)
279+
val expVDot = calcExpectedVDot(v1, v2, w)
280+
281+
dut.io.out.bits.vOutProduct.expectPartial(expVecProductPartial)
282+
283+
val expectedBitsPartial1 = chiselTypeOf(dut.io.out.bits).Lit(
284+
// c -> not set
285+
_.cmp -> expectedCmp,
286+
// vSum -> not set
287+
_.vDot -> expVDot,
288+
_.vOutProduct -> expVecProduct
289+
)
290+
291+
val expectedBitsPartial2 = chiselTypeOf(dut.io.out.bits).Lit(
292+
_.c -> expectedScalar.U,
293+
// cmp -> not set
294+
_.vSum -> expectedVSum,
295+
// vDot -> not set
296+
_.vOutProduct -> expVecProductPartial
297+
)
298+
299+
dut.io.out.bits.expectPartial(expectedBitsPartial1)
300+
301+
dut.io.out.bits.expectPartial(expectedBitsPartial2)
302+
303+
dut.io.out.expectPartial(
304+
chiselTypeOf(dut.io.out).Lit(
305+
_.valid -> true.B,
306+
_.bits -> expectedBitsPartial1
307+
)
308+
)
309+
dut.io.out.expectPartial(
310+
chiselTypeOf(dut.io.out).Lit(
311+
_.valid -> true.B
312+
// bits -> not set
313+
)
314+
)
315+
dut.io.out.expectPartial(
316+
chiselTypeOf(dut.io.out).Lit(
317+
// valid -> not set
318+
_.bits -> expectedBitsPartial1
319+
)
320+
)
321+
dut.io.out.expectPartial(
322+
chiselTypeOf(dut.io.out).Lit(
323+
// valid -> not set
324+
_.bits -> expectedBitsPartial2
325+
)
326+
)
327+
}
328+
}
329+
}
330+
331+
it("should support expectPartial() for partially initialized Vecs") {
332+
val w = 8
333+
simulate(new PeekPokeTestModule(w)) { dut =>
334+
assert(w == dut.io.in.bits.a.getWidth)
335+
val vecDim = dut.vecDim
336+
for {
337+
_ <- 0 until numTests
338+
a = BigInt(w, rand)
339+
b = BigInt(w, rand)
340+
v1 = Seq.fill(vecDim)(BigInt(w, rand))
341+
v2 = Seq.fill(vecDim)(BigInt(w, rand))
342+
op <- TestOp.all
343+
} {
344+
dut.io.in.bits.poke(
345+
chiselTypeOf(dut.io.in.bits).Lit(
346+
_.a -> a.U,
347+
_.b -> b.U,
348+
_.v1 -> Vec.Lit(v1.map(_.U(w.W)): _*),
349+
_.v2 -> Vec.Lit(v2.map(_.U(w.W)): _*)
350+
)
351+
)
352+
353+
val expVecProduct = calcExpectedVecProduct(v1, v2, w)
354+
dut.io.in.valid.poke(true)
355+
dut.io.op.poke(op)
356+
dut.clock.step()
357+
dut.io.in.valid.poke(false)
358+
val expVecProductPartial = chiselTypeOf(dut.io.out.bits.vOutProduct).Lit(
359+
// TODO: uncommenting the following line throws:
360+
// java.util.NoSuchElementException: key not found: UInt<16>(...) from AddVecLiteralConstructor.Lit() Unrelated bug?
361+
362+
// 0 -> expVecProduct.head
363+
)
364+
dut.io.out.bits.vOutProduct.expectPartial(expVecProductPartial)
365+
}
366+
}
367+
}
368+
369+
it("should correctly report failed expectPartial for Aggregates") {
370+
val thrown = the[FailedExpectationException[_]] thrownBy {
371+
val w = 16
372+
simulate(new PeekPokeTestModule(w)) { dut =>
373+
assert(w == dut.io.in.bits.a.getWidth)
374+
val vecDim = dut.vecDim
375+
376+
dut.io.in.bits.poke(
377+
chiselTypeOf(dut.io.in.bits).Lit(
378+
_.a -> 1.U,
379+
_.b -> 2.U,
380+
_.v1 -> Vec.Lit(Seq.fill(vecDim)(3.U(w.W)): _*),
381+
_.v2 -> Vec.Lit(Seq.fill(vecDim)(4.U(w.W)): _*)
382+
)
383+
)
384+
dut.io.in.valid.poke(1)
385+
dut.io.op.poke(TestOp.Add)
386+
dut.clock.step()
387+
388+
val expRecord = chiselTypeOf(dut.io.out.bits).Lit(
389+
// c -> not set
390+
_.cmp -> CmpResult.LT,
391+
_.vSum -> Vec.Lit(Seq.fill(vecDim)(7.U((w + 1).W)): _*),
392+
_.vDot -> 36.U((2 * w + vecDim - 1).W),
393+
_.vOutProduct -> Vec.Lit(
394+
Seq.fill(vecDim)(
395+
// initializing only half of the elements for each inner Vec
396+
chiselTypeOf(dut.io.out.bits.vOutProduct.head)
397+
.Lit(Seq.tabulate(vecDim / 2)(i => i -> (5 + i).U((2 * w).W)): _*)
398+
): _*
399+
)
400+
)
401+
dut.io.out.bits.expectPartial(expRecord, "my " + "custom message")
402+
}
403+
}
404+
thrown.getMessage must include("dut.io.out.bits.expectPartial(expRecord, \"my \" + \"custom message\")")
405+
thrown.getMessage must include("Observed value: 'UInt<32>(12)'")
406+
thrown.getMessage must include("Expected value: 'UInt<32>(5)'")
407+
thrown.getMessage must include("element 'vOutProduct'")
408+
thrown.getMessage must include("my custom message")
409+
}
410+
411+
it("should fail expect() with a partially initialized Aggregate") {
412+
val thrown = the[UninitializedElementException] thrownBy {
413+
simulate(new PeekPokeTestModule(w)) { dut =>
414+
assert(w == dut.io.in.bits.a.getWidth)
415+
val vecDim = dut.vecDim
416+
val v1 = Vec.Lit(Seq.fill(vecDim)(3.U(w.W)): _*)
417+
val v2 = Vec.Lit(Seq.fill(vecDim)(4.U(w.W)): _*)
418+
419+
dut.io.in.bits.poke(
420+
chiselTypeOf(dut.io.in.bits).Lit(
421+
_.a -> 1.U,
422+
_.b -> 2.U,
423+
_.v1 -> v1,
424+
_.v2 -> v2
425+
)
426+
)
427+
dut.io.in.valid.poke(true)
428+
dut.io.op.poke(TestOp.Add)
429+
dut.clock.step()
430+
431+
val expVecProduct = calcExpectedVecProduct(v1, v2, w)
432+
433+
val expRecord = chiselTypeOf(dut.io.out.bits).Lit(
434+
_.c -> 3.U,
435+
// _.cmp -> CmpResult.LT,
436+
_.vSum -> Vec.Lit(Seq.fill(vecDim)(7.U((w + 1).W)): _*),
437+
_.vDot -> 36.U((2 * w + vecDim - 1).W),
438+
_.vOutProduct -> expVecProduct
439+
)
440+
dut.io.out.bits.expect(expRecord)
441+
}
442+
}
443+
thrown.getMessage must include("dut.io.out.bits.expect(expRecord)")
444+
thrown.getMessage must include("Element 'cmp'")
445+
thrown.getMessage must include("not initialized")
446+
}
247447
}
248448
}

src/test/scala-2/chiselTests/simulator/PeekPokeTestModule.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ object PeekPokeTestModule {
5353
}: _*
5454
)
5555

56+
def calcExpectedVecProduct(v1: Vec[UInt], v2: Vec[UInt], w: Int): Vec[Vec[UInt]] =
57+
calcExpectedVecProduct(v1.map(_.litValue), v2.map(_.litValue), w)
58+
5659
def calcExpectedVDotBigInt(v1: Seq[BigInt], v2: Seq[BigInt]): BigInt = v1.zip(v2).map { case (x, y) => x * y }.sum
5760

5861
def calcExpectedVDot(v1: Seq[BigInt], v2: Seq[BigInt], w: Int = -1): UInt = {

0 commit comments

Comments
 (0)