@@ -11,6 +11,22 @@ import chisel3.internal.ExceptionHelpers
11
11
import firrtl .options .StageUtils .dramaticMessage
12
12
import scala .util .control .NoStackTrace
13
13
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
+
14
30
object PeekPokeAPI extends PeekPokeAPI
15
31
16
32
trait PeekPokeAPI {
@@ -24,20 +40,37 @@ trait PeekPokeAPI {
24
40
)
25
41
)
26
42
with NoStackTrace
43
+
27
44
object FailedExpectationException {
28
45
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
34
50
): FailedExpectationException [T ] = {
51
+ val extraContext =
52
+ sourceInfo match {
53
+ case sl : SourceLine =>
54
+ ExceptionHelpers .getErrorLineInFile(Seq (), sl)
55
+ case _ =>
56
+ Seq ()
57
+ }
35
58
val fullMessage = s " $message ${sourceInfo.makeMessage()}" +
36
59
(if (extraContext.nonEmpty) s " \n ${extraContext.mkString(" \n " )}" else " " )
37
- new FailedExpectationException (observed, expected, fullMessage)
60
+ new FailedExpectationException [ T ] (observed, expected, fullMessage)
38
61
}
39
62
}
40
63
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
+
41
74
implicit class testableClock (clock : Clock ) extends AnyTestableData [Clock ] {
42
75
val data = clock
43
76
@@ -72,72 +105,16 @@ trait PeekPokeAPI {
72
105
}
73
106
}
74
107
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
-
132
108
sealed trait TestableElement [T <: Element ] extends PeekPokable [T ] {
109
+ protected def isSigned = false
133
110
134
- private [simulator] def encode (width : Int , value : BigInt ): T
111
+ private [simulator] protected def encode (width : Int , value : BigInt ): T
135
112
136
- protected final def encode (value : Simulation .Value ): T = {
113
+ private [simulator] final def encode (value : Simulation .Value ): T = {
137
114
encode(value.bitCount, value.asBigInt)
138
115
}
139
116
140
- protected def peekValue (): Simulation .Value = {
117
+ protected final def peekValue (): Simulation .Value = {
141
118
simulatedModule.willPeek()
142
119
simulationPort.get(isSigned = isSigned)
143
120
}
@@ -151,51 +128,67 @@ trait PeekPokeAPI {
151
128
simulationPort.set(value)
152
129
}
153
130
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
+
154
156
override def expect (expected : T )(implicit sourceInfo : SourceInfo ): Unit = {
155
157
expect(
156
158
expected,
157
- (observed, expected) => s " Expectation failed: observed value $observed != $expected"
159
+ (observed : String , expected : T ) => s " Expectation failed: observed value $observed != $expected"
158
160
)
159
161
}
160
162
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 = {
162
164
require(expected.isLit, s " Expected value: $expected must be a literal " )
163
165
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 = {
188
166
expect(
189
167
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),
192
171
sourceInfo
193
172
)
194
173
}
195
174
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)
199
192
200
193
}
201
194
@@ -242,56 +235,50 @@ trait PeekPokeAPI {
242
235
override def encode (width : Int , value : BigInt ): T = {
243
236
data.factory.all.find(_.litValue == value).get.asInstanceOf [T ]
244
237
}
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
- }
255
238
}
256
239
257
240
implicit class testableRecord [T <: Record ](val data : T )(implicit sourceInfo : SourceInfo ) extends PeekPokable [T ] {
258
241
259
242
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
+ )
269
248
}
270
249
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))
275
252
}
276
253
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 = {
279
257
require(DataMirror .checkTypeEquivalence(data, expected), " Type mismatch" )
280
258
281
259
// 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
283
261
284
262
data.elements.foreach { case (name, d) =>
285
263
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 =>
288
271
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"
291
275
)
292
276
}
293
277
}
294
278
}
279
+
280
+ override def expect (expected : T , buildMessage : (String , T ) => String )(implicit sourceInfo : SourceInfo ): Unit =
281
+ expect(expected, buildMessage, allowPartial = false )
295
282
}
296
283
297
284
implicit class testableData [T <: Data ](val data : T ) extends PeekPokable [T ] {
@@ -312,11 +299,11 @@ trait PeekPokeAPI {
312
299
313
300
override def expect (
314
301
expected : T ,
315
- buildMessage : (T , T ) => String
302
+ buildMessage : (String , T ) => String
316
303
)(implicit sourceInfo : SourceInfo ): Unit = {
317
304
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 ])
320
307
321
308
(data, expected) match {
322
309
case (dat : Bool , exp : Bool ) =>
@@ -334,15 +321,15 @@ trait PeekPokeAPI {
334
321
exp.length == dat.length,
335
322
s " Vec length mismatch: Data port has ${dat.length} elements while the expected value is of length ${exp.length}"
336
323
)
337
- val peekedValue = dat.peek()
324
+ val peekedValue = dat.peek().toString
338
325
dat.getElements.zip(exp.getElements).zipWithIndex.foreach { case ((datEl, valEl), index) =>
339
326
valEl match {
340
327
case DontCare =>
341
328
// TODO: missing elements?
342
329
case ve =>
343
330
datEl.expect(
344
331
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"
346
333
)
347
334
}
348
335
}
0 commit comments