Skip to content

Commit e918e92

Browse files
committed
remove PeekPokableData
1 parent 64c1354 commit e918e92

File tree

2 files changed

+121
-117
lines changed

2 files changed

+121
-117
lines changed

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

Lines changed: 120 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package chisel3.simulator
22

33
import chisel3._
4-
import chisel3.experimental.BundleLiterals
54
import chisel3.experimental.VecLiterals._
65
import chisel3.experimental.{SourceInfo, SourceLine}
76
import chisel3.internal.ExceptionHelpers
@@ -22,34 +21,21 @@ trait Peekable[T <: Data] {
2221
def peek()(implicit sourceInfo: SourceInfo): T
2322

2423
/**
25-
* Expect the value of a data port to be equal to the expected value.
26-
*
27-
* @param expected the expected value
28-
* @throws FailedExpectationException if the observed value does not match the expected value
29-
*/
24+
* Expect the value of a data port to be equal to the expected value.
25+
*
26+
* @param expected the expected value
27+
* @throws FailedExpectationException if the observed value does not match the expected value
28+
*/
3029
def expect(expected: T)(implicit sourceInfo: SourceInfo): Unit = expect(expected, "")
3130

3231
/**
33-
* Expect the value of a data port to be equal to the expected value.
34-
*
35-
* @param expected the expected value
36-
* @param message a message for the failure case
37-
* @throws FailedExpectationException if the observed value does not match the expected value
38-
*/
32+
* Expect the value of a data port to be equal to the expected value.
33+
*
34+
* @param expected the expected value
35+
* @param message a message for the failure case
36+
* @throws FailedExpectationException if the observed value does not match the expected value
37+
*/
3938
def expect(expected: T, message: String)(implicit sourceInfo: SourceInfo): Unit
40-
41-
private[simulator] def dataToString(value: Data): String = {
42-
value match {
43-
case x: Bundle =>
44-
x.elements.map { case (name, elt) =>
45-
s"$name: ${dataToString(elt)}"
46-
}.mkString("{", ", ", "}")
47-
case x: Vec[_] => x.getElements.map(dataToString).mkString("[", ", ", "]")
48-
case x: EnumType => x.toString
49-
case x if x.isLit => x.litValue.toString
50-
case _ => value.toString
51-
}
52-
}
5339
}
5440

5541
trait Pokable[T <: Data] {
@@ -134,20 +120,20 @@ case class UninitializedElementException(message: String)(implicit sourceInfo: S
134120
sealed trait TestableAggregate[T <: Aggregate] extends PeekPokable[T] {
135121

136122
/**
137-
* Expect the value of a data port to be equal to the expected value, skipping all uninitialized elements.
138-
*
139-
* @param expected the expected value
140-
* @param message a message for the failure case
141-
* @throws FailedExpectationException if the observed value does not match the expected value
142-
*/
123+
* Expect the value of a data port to be equal to the expected value, skipping all uninitialized elements.
124+
*
125+
* @param expected the expected value
126+
* @param message a message for the failure case
127+
* @throws FailedExpectationException if the observed value does not match the expected value
128+
*/
143129
def expectPartial(expected: T, message: String)(implicit sourceInfo: SourceInfo): Unit
144130

145131
/**
146-
* Expect the value of a data port to be equal to the expected value, skipping all uninitialized elements.
147-
*
148-
* @param expected the expected value
149-
* @throws FailedExpectationException if the observed value does not match the expected value
150-
*/
132+
* Expect the value of a data port to be equal to the expected value, skipping all uninitialized elements.
133+
*
134+
* @param expected the expected value
135+
* @throws FailedExpectationException if the observed value does not match the expected value
136+
*/
151137
def expectPartial(expected: T)(implicit sourceInfo: SourceInfo): Unit =
152138
expectPartial(expected, "")
153139
}
@@ -231,12 +217,12 @@ sealed trait TestableElement[T <: Element] extends PeekPokable[T] {
231217
)
232218

233219
/**
234-
* Expect the value of a data port to be equal to the expected value.
235-
*
236-
* @param expected the expected value
237-
* @param buildMessage a function taking (observedValue: T, expectedValue: T) and returning a String message for the failure case
238-
* @throws FailedExpectationException if the observed value does not match the expected value
239-
*/
220+
* Expect the value of a data port to be equal to the expected value.
221+
*
222+
* @param expected the expected value
223+
* @param buildMessage a function taking (observedValue: T, expectedValue: T) and returning a String message for the failure case
224+
* @throws FailedExpectationException if the observed value does not match the expected value
225+
*/
240226
def expect(expected: T, buildMessage: (T, T) => String)(
241227
implicit sourceInfo: SourceInfo
242228
): Unit = {
@@ -283,6 +269,64 @@ sealed trait TestableElement[T <: Element] extends PeekPokable[T] {
283269
}
284270

285271
object PeekPokeAPI {
272+
private def dataToString(value: Data): String = {
273+
value match {
274+
case x: Record =>
275+
x.elements.map { case (name, elt) =>
276+
s"$name: ${dataToString(elt)}"
277+
}.mkString("{", ", ", "}")
278+
case x: Vec[_] => x.getElements.map(dataToString).mkString("[", ", ", "]")
279+
case x: EnumType => x.toString
280+
case x if x.isLit => x.litValue.toString
281+
case _ => value.toString
282+
}
283+
}
284+
285+
private def pokeData[T <: Data](data: T, literal: T)(implicit sourceInfo: SourceInfo): Unit = (data, literal) match {
286+
case (x: UInt, lit: UInt) => new TestableUInt(x).poke(lit)
287+
case (x: SInt, lit: SInt) => new TestableSInt(x).poke(lit)
288+
case (x: EnumType, lit: EnumType) if x.factory == lit.factory =>
289+
new TestableEnum(x).poke(lit)
290+
case (x: Record, lit: Record) => new TestableRecord(x).poke(lit)
291+
case (x: Vec[_], lit: Vec[_]) if x.getClass == lit.getClass =>
292+
new TestableVec(x).poke(lit.asInstanceOf[x.type])
293+
case (x, lit) => throw new Exception(s"Don't know how to poke $x with $lit")
294+
}
295+
296+
private def peekData[T <: Data](data: T)(implicit sourceInfo: SourceInfo): T = {
297+
data match {
298+
case x: Bool => new TestableBool(x).peek().asInstanceOf[T]
299+
case x: UInt => new TestableUInt(x).peek().asInstanceOf[T]
300+
case x: SInt => new TestableSInt(x).peek().asInstanceOf[T]
301+
case x: EnumType => new TestableEnum(x).peek().asInstanceOf[T]
302+
case x: Record => new TestableRecord(x).peek().asInstanceOf[T]
303+
case x: Vec[_] => new TestableVec(x).peek().asInstanceOf[T]
304+
case x => throw new Exception(s"don't know how to peek $x")
305+
}
306+
}
307+
308+
private def expectData[T <: Data](
309+
data: T,
310+
expected: T,
311+
msgGen: () => String
312+
)(implicit sourceInfo: SourceInfo): Unit = {
313+
(data, expected) match {
314+
case (dat: Bool, exp: Bool) =>
315+
new TestableBool(dat).expect(exp, (_, _) => msgGen())
316+
case (dat: UInt, exp: UInt) =>
317+
new TestableUInt(dat).expect(exp, (_, _) => msgGen())
318+
case (dat: SInt, exp: SInt) =>
319+
new TestableSInt(dat).expect(exp, (_, _) => msgGen())
320+
case (dat: EnumType, exp: EnumType) if dat.factory == exp.factory =>
321+
new TestableEnum(dat).expect(exp, (_, _) => msgGen())
322+
case (dat: Record, exp: Record) =>
323+
new TestableRecord(dat).expect(exp, msgGen, allowPartial = false)
324+
case (dat: Vec[_], exp: Vec[_]) if dat.getClass == exp.getClass =>
325+
new TestableVec(dat).expect(exp.asInstanceOf[dat.type], msgGen, allowPartial = false)
326+
case (dat, exp) => throw new Exception(s"Don't know how to expect $exp from $dat")
327+
}
328+
}
329+
286330
implicit class TestableClock(clock: Clock) extends AnyTestableData[Clock] {
287331
val data = clock
288332

@@ -302,9 +346,9 @@ object PeekPokeAPI {
302346
}
303347

304348
/** Ticks this clock up to `maxCycles`.
305-
*
306-
* Stops early if the `sentinelPort` is equal to the `sentinelValue`.
307-
*/
349+
*
350+
* Stops early if the `sentinelPort` is equal to the `sentinelValue`.
351+
*/
308352
def stepUntil(sentinelPort: Data, sentinelValue: BigInt, maxCycles: Int): Unit = {
309353
simulatedModule.willEvaluate()
310354
simulationPort.tick(
@@ -372,7 +416,7 @@ object PeekPokeAPI {
372416
override def peek()(implicit sourceInfo: SourceInfo): T = {
373417
chiselTypeOf(data)._makeLit(
374418
data.elements.toSeq.map { case (name: String, elt: Data) =>
375-
(rec: T) => rec.elements(name) -> elt.peek()
419+
(rec: T) => rec.elements(name) -> peekData(elt)
376420
}: _*
377421
)
378422
}
@@ -389,17 +433,18 @@ object PeekPokeAPI {
389433
)
390434
}
391435
case expEl if expEl.getClass == portEl.getClass =>
392-
// Not peeking the value beforehand or using `def` or `lazy val` results in a mysterious infinite recursion and StackOverflowError
393-
// The value is not used if the expected value matches and incurs an overhead
394-
// TODO: dig deeper amd see if we can avoid this
395-
val message = buildMessage(peek(), expected, elName)
396-
(allowPartial, portEl) match {
397-
case (true, rec: Record) =>
398-
rec.expectPartial(expEl.asInstanceOf[rec.type], message)
399-
case (true, vec: Vec[_]) =>
400-
vec.expectPartial(expEl.asInstanceOf[vec.type], message)
436+
def mkMsg = () => buildMessage(peek(), expected, elName)
437+
portEl match {
438+
case rec: Record =>
439+
rec.expect(expEl.asInstanceOf[rec.type], mkMsg, allowPartial)
440+
case vec: Vec[_] =>
441+
vec.expect(expEl.asInstanceOf[vec.type], mkMsg, allowPartial)
401442
case _ =>
402-
portEl.expect(expEl, message)
443+
// Not peeking the value beforehand or using `def` or `lazy val` results in a mysterious infinite recursion and StackOverflowError
444+
// The value is not used if the expected value matches and incurs an overhead
445+
// TODO: dig deeper amd see if we can avoid this
446+
val message = mkMsg()
447+
expectData(portEl, expEl, () => message)
403448
}
404449
case expEl =>
405450
throw new Exception(
@@ -413,13 +458,16 @@ object PeekPokeAPI {
413458
observed: T,
414459
expected: T,
415460
elName: String,
416-
userMessage: String = ""
461+
userMessage: => String = ""
417462
): String = (if (userMessage.nonEmpty) s"$userMessage\n" else "") +
418463
s"Expectation failed for element '$elName': observed value ${dataToString(observed.elements(elName))} != expected value ${dataToString(expected.elements(elName))}"
419464

420465
override def expectPartial(expected: T, message: String)(implicit sourceInfo: SourceInfo): Unit =
421466
expect(expected, defaultMessageBuilder(_, _, _, message), allowPartial = true)
422467

468+
def expect(expected: T, genMsg: () => String, allowPartial: Boolean)(implicit sourceInfo: SourceInfo): Unit =
469+
expect(expected, defaultMessageBuilder(_, _, _, genMsg()), allowPartial = allowPartial)
470+
423471
override def expect(expected: T, message: String)(implicit sourceInfo: SourceInfo): Unit =
424472
expect(expected, defaultMessageBuilder(_, _, _, message), allowPartial = false)
425473

@@ -429,21 +477,21 @@ object PeekPokeAPI {
429477
portEl.getClass == valueEl.getClass,
430478
s"Type mismatch for Record element '$name': expected ${portEl.getClass}, got ${valueEl.getClass}"
431479
)
432-
portEl.poke(valueEl)
480+
pokeData(portEl, valueEl)
433481
}
434482
}
435483

436484
implicit class TestableVec[T <: Data](val data: Vec[T]) extends TestableAggregate[Vec[T]] {
437485
override def peek()(implicit sourceInfo: SourceInfo): Vec[T] = {
438-
val elementValues = data.getElements.map(_.peek().asInstanceOf[T])
486+
val elementValues = data.getElements.map(peekData(_).asInstanceOf[T])
439487
chiselTypeOf(data).Lit(elementValues.zipWithIndex.map { _.swap }: _*)
440488
}
441489

442490
override def poke(literal: Vec[T]): Unit = {
443491
require(data.length == literal.length, s"Vec length mismatch: expected ${data.length}, got ${literal.length}")
444492
data.getElements.zip(literal).foreach {
445493
case (portEl, valueEl) if portEl.getClass == valueEl.getClass =>
446-
portEl.poke(valueEl)
494+
pokeData(portEl, valueEl)
447495
case (portEl, valueEl) =>
448496
throw new Exception(
449497
s"Port element type: ${portEl.getClass} != literal element ${valueEl.getClass}"
@@ -455,7 +503,7 @@ object PeekPokeAPI {
455503
observed: Vec[T],
456504
expected: Vec[T],
457505
elIndex: Int,
458-
userMessage: String = ""
506+
userMessage: => String = ""
459507
): String = (if (userMessage.nonEmpty) s"$userMessage\n" else "") +
460508
s"Expectation failed for Vec element at index $elIndex: observed value ${dataToString(observed(elIndex))} != expected value ${dataToString(expected(elIndex))}"
461509

@@ -465,6 +513,9 @@ object PeekPokeAPI {
465513
override def expect(expected: Vec[T], message: String)(implicit sourceInfo: SourceInfo): Unit =
466514
expect(expected, defaultMessageBuilder(_, _, _, message), allowPartial = false)
467515

516+
def expect(expected: Vec[T], msgGen: () => String, allowPartial: Boolean)(implicit sourceInfo: SourceInfo): Unit =
517+
expect(expected, defaultMessageBuilder(_, _, _, msgGen()), allowPartial = allowPartial)
518+
468519
def expect(expected: Vec[T], buildMessage: (Vec[T], Vec[T], Int) => String, allowPartial: Boolean = false)(
469520
implicit sourceInfo: SourceInfo
470521
): Unit = {
@@ -475,14 +526,15 @@ object PeekPokeAPI {
475526
s"Vec element at index $idx in the expected value is not initialized"
476527
)
477528
case ((datEl, expEl), idx) if datEl.getClass == expEl.getClass =>
478-
val message = buildMessage(peek(), expected, idx)
479-
(allowPartial, datEl) match {
480-
case (true, rec: Record) =>
481-
rec.expectPartial(expEl.asInstanceOf[rec.type], message)
482-
case (true, vec: Vec[_]) =>
483-
vec.expectPartial(expEl.asInstanceOf[vec.type], message)
529+
def mkMsg = () => buildMessage(peek(), expected, idx)
530+
datEl match {
531+
case rec: Record =>
532+
rec.expect(expEl.asInstanceOf[rec.type], mkMsg, allowPartial)
533+
case vec: Vec[_] =>
534+
vec.expect(expEl.asInstanceOf[vec.type], mkMsg, allowPartial)
484535
case _ =>
485-
datEl.expect(expEl, message)
536+
val message = mkMsg() // TODO would otherwise lead to infinite recursion!
537+
expectData(datEl, expEl, () => message)
486538
}
487539
case ((datEl, expEl), _) =>
488540
throw new Exception(
@@ -492,55 +544,9 @@ object PeekPokeAPI {
492544
}
493545
}
494546

495-
implicit class TestableData[T <: Data](val data: T) extends PeekPokable[T] {
496-
497-
private def toPeekable: Peekable[_] = {
498-
data match {
499-
case x: Bool => new TestableBool(x)
500-
case x: UInt => new TestableUInt(x)
501-
case x: SInt => new TestableSInt(x)
502-
case x: EnumType => new TestableEnum(x)
503-
case x: Record => new TestableRecord(x)
504-
case x: Vec[_] => new TestableVec(x)
505-
case x => throw new Exception(s"don't know how to peek $x")
506-
}
507-
}
508-
509-
def peek()(implicit sourceInfo: SourceInfo): T = toPeekable.peek().asInstanceOf[T]
510-
511-
override def expect(expected: T, message: String)(implicit sourceInfo: SourceInfo): Unit = {
512-
(data, expected) match {
513-
case (dat: Bool, exp: Bool) =>
514-
new TestableBool(dat).expect(exp, message)
515-
case (dat: UInt, exp: UInt) =>
516-
new TestableUInt(dat).expect(exp, message)
517-
case (dat: SInt, exp: SInt) =>
518-
new TestableSInt(dat).expect(exp, message)
519-
case (dat: EnumType, exp: EnumType) if dat.factory == exp.factory =>
520-
new TestableEnum(dat).expect(exp, message)
521-
case (dat: Record, exp: Record) =>
522-
new TestableRecord(dat).expect(exp, message)
523-
case (dat: Vec[_], exp: Vec[_]) if dat.getClass == exp.getClass =>
524-
new TestableVec(dat).expect(exp.asInstanceOf[dat.type], message)
525-
case (dat, exp) => throw new Exception(s"Don't know how to expect $exp from $dat")
526-
}
527-
}
528-
529-
def poke(literal: T): Unit = (data, literal) match {
530-
case (x: UInt, lit: UInt) => new TestableUInt(x).poke(lit)
531-
case (x: SInt, lit: SInt) => new TestableSInt(x).poke(lit)
532-
case (x: EnumType, lit: EnumType) if x.factory == lit.factory =>
533-
new TestableEnum(x).poke(lit)
534-
case (x: Record, lit: Record) => new TestableRecord(x).poke(lit)
535-
case (x: Vec[_], lit: Vec[_]) if x.getClass == lit.getClass =>
536-
new TestableVec(x).poke(lit.asInstanceOf[x.type])
537-
case (x, lit) => throw new Exception(s"Don't know how to poke $x with $lit")
538-
}
539-
}
540547
}
541548

542549
trait PeekPokeAPI {
543-
544550
implicit def toTestableClock(clock: Clock): PeekPokeAPI.TestableClock = new PeekPokeAPI.TestableClock(clock)
545551

546552
implicit def toTestableSInt(sint: SInt): PeekPokeAPI.TestableSInt = new PeekPokeAPI.TestableSInt(sint)
@@ -557,6 +563,4 @@ trait PeekPokeAPI {
557563
new PeekPokeAPI.TestableRecord(record)
558564

559565
implicit def toTestableVec[T <: Data](vec: Vec[T]): PeekPokeAPI.TestableVec[T] = new PeekPokeAPI.TestableVec(vec)
560-
561-
implicit def toTestableData[T <: Data](data: T): PeekPokeAPI.TestableData[T] = new PeekPokeAPI.TestableData(data)
562566
}

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

Lines changed: 1 addition & 1 deletion
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 = 20
16+
val numTests = 10
1717

1818
describe("PeekPokeAPI with TestableData") {
1919
val w = 32

0 commit comments

Comments
 (0)