1
1
package chisel3 .simulator
2
2
3
3
import chisel3 ._
4
- import chisel3 .experimental .BundleLiterals
5
4
import chisel3 .experimental .VecLiterals ._
6
5
import chisel3 .experimental .{SourceInfo , SourceLine }
7
6
import chisel3 .internal .ExceptionHelpers
@@ -22,34 +21,21 @@ trait Peekable[T <: Data] {
22
21
def peek ()(implicit sourceInfo : SourceInfo ): T
23
22
24
23
/**
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
+ */
30
29
def expect (expected : T )(implicit sourceInfo : SourceInfo ): Unit = expect(expected, " " )
31
30
32
31
/**
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
+ */
39
38
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
- }
53
39
}
54
40
55
41
trait Pokable [T <: Data ] {
@@ -134,20 +120,20 @@ case class UninitializedElementException(message: String)(implicit sourceInfo: S
134
120
sealed trait TestableAggregate [T <: Aggregate ] extends PeekPokable [T ] {
135
121
136
122
/**
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
+ */
143
129
def expectPartial (expected : T , message : String )(implicit sourceInfo : SourceInfo ): Unit
144
130
145
131
/**
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
+ */
151
137
def expectPartial (expected : T )(implicit sourceInfo : SourceInfo ): Unit =
152
138
expectPartial(expected, " " )
153
139
}
@@ -231,12 +217,12 @@ sealed trait TestableElement[T <: Element] extends PeekPokable[T] {
231
217
)
232
218
233
219
/**
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
+ */
240
226
def expect (expected : T , buildMessage : (T , T ) => String )(
241
227
implicit sourceInfo : SourceInfo
242
228
): Unit = {
@@ -283,6 +269,64 @@ sealed trait TestableElement[T <: Element] extends PeekPokable[T] {
283
269
}
284
270
285
271
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
+
286
330
implicit class TestableClock (clock : Clock ) extends AnyTestableData [Clock ] {
287
331
val data = clock
288
332
@@ -302,9 +346,9 @@ object PeekPokeAPI {
302
346
}
303
347
304
348
/** 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
+ */
308
352
def stepUntil (sentinelPort : Data , sentinelValue : BigInt , maxCycles : Int ): Unit = {
309
353
simulatedModule.willEvaluate()
310
354
simulationPort.tick(
@@ -372,7 +416,7 @@ object PeekPokeAPI {
372
416
override def peek ()(implicit sourceInfo : SourceInfo ): T = {
373
417
chiselTypeOf(data)._makeLit(
374
418
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 )
376
420
}: _*
377
421
)
378
422
}
@@ -389,17 +433,18 @@ object PeekPokeAPI {
389
433
)
390
434
}
391
435
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)
401
442
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)
403
448
}
404
449
case expEl =>
405
450
throw new Exception (
@@ -413,13 +458,16 @@ object PeekPokeAPI {
413
458
observed : T ,
414
459
expected : T ,
415
460
elName : String ,
416
- userMessage : String = " "
461
+ userMessage : => String = " "
417
462
): String = (if (userMessage.nonEmpty) s " $userMessage\n " else " " ) +
418
463
s " Expectation failed for element ' $elName': observed value ${dataToString(observed.elements(elName))} != expected value ${dataToString(expected.elements(elName))}"
419
464
420
465
override def expectPartial (expected : T , message : String )(implicit sourceInfo : SourceInfo ): Unit =
421
466
expect(expected, defaultMessageBuilder(_, _, _, message), allowPartial = true )
422
467
468
+ def expect (expected : T , genMsg : () => String , allowPartial : Boolean )(implicit sourceInfo : SourceInfo ): Unit =
469
+ expect(expected, defaultMessageBuilder(_, _, _, genMsg()), allowPartial = allowPartial)
470
+
423
471
override def expect (expected : T , message : String )(implicit sourceInfo : SourceInfo ): Unit =
424
472
expect(expected, defaultMessageBuilder(_, _, _, message), allowPartial = false )
425
473
@@ -429,21 +477,21 @@ object PeekPokeAPI {
429
477
portEl.getClass == valueEl.getClass,
430
478
s " Type mismatch for Record element ' $name': expected ${portEl.getClass}, got ${valueEl.getClass}"
431
479
)
432
- portEl.poke( valueEl)
480
+ pokeData(portEl, valueEl)
433
481
}
434
482
}
435
483
436
484
implicit class TestableVec [T <: Data ](val data : Vec [T ]) extends TestableAggregate [Vec [T ]] {
437
485
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 ])
439
487
chiselTypeOf(data).Lit (elementValues.zipWithIndex.map { _.swap }: _* )
440
488
}
441
489
442
490
override def poke (literal : Vec [T ]): Unit = {
443
491
require(data.length == literal.length, s " Vec length mismatch: expected ${data.length}, got ${literal.length}" )
444
492
data.getElements.zip(literal).foreach {
445
493
case (portEl, valueEl) if portEl.getClass == valueEl.getClass =>
446
- portEl.poke( valueEl)
494
+ pokeData(portEl, valueEl)
447
495
case (portEl, valueEl) =>
448
496
throw new Exception (
449
497
s " Port element type: ${portEl.getClass} != literal element ${valueEl.getClass}"
@@ -455,7 +503,7 @@ object PeekPokeAPI {
455
503
observed : Vec [T ],
456
504
expected : Vec [T ],
457
505
elIndex : Int ,
458
- userMessage : String = " "
506
+ userMessage : => String = " "
459
507
): String = (if (userMessage.nonEmpty) s " $userMessage\n " else " " ) +
460
508
s " Expectation failed for Vec element at index $elIndex: observed value ${dataToString(observed(elIndex))} != expected value ${dataToString(expected(elIndex))}"
461
509
@@ -465,6 +513,9 @@ object PeekPokeAPI {
465
513
override def expect (expected : Vec [T ], message : String )(implicit sourceInfo : SourceInfo ): Unit =
466
514
expect(expected, defaultMessageBuilder(_, _, _, message), allowPartial = false )
467
515
516
+ def expect (expected : Vec [T ], msgGen : () => String , allowPartial : Boolean )(implicit sourceInfo : SourceInfo ): Unit =
517
+ expect(expected, defaultMessageBuilder(_, _, _, msgGen()), allowPartial = allowPartial)
518
+
468
519
def expect (expected : Vec [T ], buildMessage : (Vec [T ], Vec [T ], Int ) => String , allowPartial : Boolean = false )(
469
520
implicit sourceInfo : SourceInfo
470
521
): Unit = {
@@ -475,14 +526,15 @@ object PeekPokeAPI {
475
526
s " Vec element at index $idx in the expected value is not initialized "
476
527
)
477
528
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 )
484
535
case _ =>
485
- datEl.expect(expEl, message)
536
+ val message = mkMsg() // TODO would otherwise lead to infinite recursion!
537
+ expectData(datEl, expEl, () => message)
486
538
}
487
539
case ((datEl, expEl), _) =>
488
540
throw new Exception (
@@ -492,55 +544,9 @@ object PeekPokeAPI {
492
544
}
493
545
}
494
546
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
- }
540
547
}
541
548
542
549
trait PeekPokeAPI {
543
-
544
550
implicit def toTestableClock (clock : Clock ): PeekPokeAPI .TestableClock = new PeekPokeAPI .TestableClock (clock)
545
551
546
552
implicit def toTestableSInt (sint : SInt ): PeekPokeAPI .TestableSInt = new PeekPokeAPI .TestableSInt (sint)
@@ -557,6 +563,4 @@ trait PeekPokeAPI {
557
563
new PeekPokeAPI .TestableRecord (record)
558
564
559
565
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)
562
566
}
0 commit comments