691408a0363e9a4d3bdea0f659b0888863420cec
[nmigen.git] / nmigen / test / test_hdl_ast.py
1 import warnings
2 from enum import Enum
3
4 from ..hdl.ast import *
5 from .utils import *
6
7
8 class UnsignedEnum(Enum):
9 FOO = 1
10 BAR = 2
11 BAZ = 3
12
13
14 class SignedEnum(Enum):
15 FOO = -1
16 BAR = 0
17 BAZ = +1
18
19
20 class StringEnum(Enum):
21 FOO = "a"
22 BAR = "b"
23
24
25 class ShapeTestCase(FHDLTestCase):
26 def test_make(self):
27 s1 = Shape()
28 self.assertEqual(s1.width, 1)
29 self.assertEqual(s1.signed, False)
30 s2 = Shape(signed=True)
31 self.assertEqual(s2.width, 1)
32 self.assertEqual(s2.signed, True)
33 s3 = Shape(3, True)
34 self.assertEqual(s3.width, 3)
35 self.assertEqual(s3.signed, True)
36
37 def test_make_wrong(self):
38 with self.assertRaises(TypeError,
39 msg="Width must be a non-negative integer, not -1"):
40 Shape(-1)
41
42 def test_compare_wrong(self):
43 with self.assertRaises(TypeError,
44 msg="Shapes may be compared with other Shapes and (int, bool) tuples, not 'hi'"):
45 Shape(1, True) == 'hi'
46
47 def test_compare_tuple_wrong(self):
48 with self.assertRaises(TypeError,
49 msg="Shapes may be compared with other Shapes and (int, bool) tuples, not (2, 3)"):
50 Shape(1, True) == (2, 3)
51
52 def test_repr(self):
53 self.assertEqual(repr(Shape()), "unsigned(1)")
54 self.assertEqual(repr(Shape(2, True)), "signed(2)")
55
56 def test_tuple(self):
57 width, signed = Shape()
58 self.assertEqual(width, 1)
59 self.assertEqual(signed, False)
60
61 def test_unsigned(self):
62 s1 = unsigned(2)
63 self.assertIsInstance(s1, Shape)
64 self.assertEqual(s1.width, 2)
65 self.assertEqual(s1.signed, False)
66
67 def test_signed(self):
68 s1 = signed(2)
69 self.assertIsInstance(s1, Shape)
70 self.assertEqual(s1.width, 2)
71 self.assertEqual(s1.signed, True)
72
73 def test_cast_shape(self):
74 s1 = Shape.cast(unsigned(1))
75 self.assertEqual(s1.width, 1)
76 self.assertEqual(s1.signed, False)
77 s2 = Shape.cast(signed(3))
78 self.assertEqual(s2.width, 3)
79 self.assertEqual(s2.signed, True)
80
81 def test_cast_int(self):
82 s1 = Shape.cast(2)
83 self.assertEqual(s1.width, 2)
84 self.assertEqual(s1.signed, False)
85
86 def test_cast_int_wrong(self):
87 with self.assertRaises(TypeError,
88 msg="Width must be a non-negative integer, not -1"):
89 Shape.cast(-1)
90
91 def test_cast_tuple(self):
92 with warnings.catch_warnings():
93 warnings.filterwarnings(action="ignore", category=DeprecationWarning)
94 s1 = Shape.cast((1, True))
95 self.assertEqual(s1.width, 1)
96 self.assertEqual(s1.signed, True)
97
98 def test_cast_tuple_wrong(self):
99 with warnings.catch_warnings():
100 warnings.filterwarnings(action="ignore", category=DeprecationWarning)
101 with self.assertRaises(TypeError,
102 msg="Width must be a non-negative integer, not -1"):
103 Shape.cast((-1, True))
104
105 def test_cast_range(self):
106 s1 = Shape.cast(range(0, 8))
107 self.assertEqual(s1.width, 3)
108 self.assertEqual(s1.signed, False)
109 s2 = Shape.cast(range(0, 9))
110 self.assertEqual(s2.width, 4)
111 self.assertEqual(s2.signed, False)
112 s3 = Shape.cast(range(-7, 8))
113 self.assertEqual(s3.width, 4)
114 self.assertEqual(s3.signed, True)
115 s4 = Shape.cast(range(0, 1))
116 self.assertEqual(s4.width, 1)
117 self.assertEqual(s4.signed, False)
118 s5 = Shape.cast(range(-1, 0))
119 self.assertEqual(s5.width, 1)
120 self.assertEqual(s5.signed, True)
121 s6 = Shape.cast(range(0, 0))
122 self.assertEqual(s6.width, 0)
123 self.assertEqual(s6.signed, False)
124 s7 = Shape.cast(range(-1, -1))
125 self.assertEqual(s7.width, 0)
126 self.assertEqual(s7.signed, True)
127
128 def test_cast_enum(self):
129 s1 = Shape.cast(UnsignedEnum)
130 self.assertEqual(s1.width, 2)
131 self.assertEqual(s1.signed, False)
132 s2 = Shape.cast(SignedEnum)
133 self.assertEqual(s2.width, 2)
134 self.assertEqual(s2.signed, True)
135
136 def test_cast_enum_bad(self):
137 with self.assertRaises(TypeError,
138 msg="Only enumerations with integer values can be used as value shapes"):
139 Shape.cast(StringEnum)
140
141 def test_cast_bad(self):
142 with self.assertRaises(TypeError,
143 msg="Object 'foo' cannot be used as value shape"):
144 Shape.cast("foo")
145
146
147 class ValueTestCase(FHDLTestCase):
148 def test_cast(self):
149 self.assertIsInstance(Value.cast(0), Const)
150 self.assertIsInstance(Value.cast(True), Const)
151 c = Const(0)
152 self.assertIs(Value.cast(c), c)
153 with self.assertRaises(TypeError,
154 msg="Object 'str' cannot be converted to an nMigen value"):
155 Value.cast("str")
156
157 def test_cast_enum(self):
158 e1 = Value.cast(UnsignedEnum.FOO)
159 self.assertIsInstance(e1, Const)
160 self.assertEqual(e1.shape(), unsigned(2))
161 e2 = Value.cast(SignedEnum.FOO)
162 self.assertIsInstance(e2, Const)
163 self.assertEqual(e2.shape(), signed(2))
164
165 def test_cast_enum_wrong(self):
166 with self.assertRaises(TypeError,
167 msg="Only enumerations with integer values can be used as value shapes"):
168 Value.cast(StringEnum.FOO)
169
170 def test_bool(self):
171 with self.assertRaises(TypeError,
172 msg="Attempted to convert nMigen value to Python boolean"):
173 if Const(0):
174 pass
175
176 def test_len(self):
177 self.assertEqual(len(Const(10)), 4)
178
179 def test_getitem_int(self):
180 s1 = Const(10)[0]
181 self.assertIsInstance(s1, Slice)
182 self.assertEqual(s1.start, 0)
183 self.assertEqual(s1.stop, 1)
184 s2 = Const(10)[-1]
185 self.assertIsInstance(s2, Slice)
186 self.assertEqual(s2.start, 3)
187 self.assertEqual(s2.stop, 4)
188 with self.assertRaises(IndexError,
189 msg="Cannot index 5 bits into 4-bit value"):
190 Const(10)[5]
191
192 def test_getitem_slice(self):
193 s1 = Const(10)[1:3]
194 self.assertIsInstance(s1, Slice)
195 self.assertEqual(s1.start, 1)
196 self.assertEqual(s1.stop, 3)
197 s2 = Const(10)[1:-2]
198 self.assertIsInstance(s2, Slice)
199 self.assertEqual(s2.start, 1)
200 self.assertEqual(s2.stop, 2)
201 s3 = Const(31)[::2]
202 self.assertIsInstance(s3, Cat)
203 self.assertIsInstance(s3.parts[0], Slice)
204 self.assertEqual(s3.parts[0].start, 0)
205 self.assertEqual(s3.parts[0].stop, 1)
206 self.assertIsInstance(s3.parts[1], Slice)
207 self.assertEqual(s3.parts[1].start, 2)
208 self.assertEqual(s3.parts[1].stop, 3)
209 self.assertIsInstance(s3.parts[2], Slice)
210 self.assertEqual(s3.parts[2].start, 4)
211 self.assertEqual(s3.parts[2].stop, 5)
212
213 def test_getitem_wrong(self):
214 with self.assertRaises(TypeError,
215 msg="Cannot index value with 'str'"):
216 Const(31)["str"]
217
218 def test_shift_left(self):
219 self.assertRepr(Const(256, unsigned(9)).shift_left(0),
220 "(cat (const 0'd0) (const 9'd256))")
221
222 self.assertRepr(Const(256, unsigned(9)).shift_left(1),
223 "(cat (const 1'd0) (const 9'd256))")
224 self.assertRepr(Const(256, unsigned(9)).shift_left(5),
225 "(cat (const 5'd0) (const 9'd256))")
226 self.assertRepr(Const(256, signed(9)).shift_left(1),
227 "(s (cat (const 1'd0) (const 9'sd-256)))")
228 self.assertRepr(Const(256, signed(9)).shift_left(5),
229 "(s (cat (const 5'd0) (const 9'sd-256)))")
230
231 self.assertRepr(Const(256, unsigned(9)).shift_left(-1),
232 "(slice (const 9'd256) 1:9)")
233 self.assertRepr(Const(256, unsigned(9)).shift_left(-5),
234 "(slice (const 9'd256) 5:9)")
235 self.assertRepr(Const(256, signed(9)).shift_left(-1),
236 "(s (slice (const 9'sd-256) 1:9))")
237 self.assertRepr(Const(256, signed(9)).shift_left(-5),
238 "(s (slice (const 9'sd-256) 5:9))")
239 self.assertRepr(Const(256, signed(9)).shift_left(-15),
240 "(s (slice (const 9'sd-256) 9:9))")
241
242 def test_shift_left_wrong(self):
243 with self.assertRaises(TypeError,
244 msg="Shift amount must be an integer, not 'str'"):
245 Const(31).shift_left("str")
246
247 def test_shift_right(self):
248 self.assertRepr(Const(256, unsigned(9)).shift_right(0),
249 "(slice (const 9'd256) 0:9)")
250
251 self.assertRepr(Const(256, unsigned(9)).shift_right(-1),
252 "(cat (const 1'd0) (const 9'd256))")
253 self.assertRepr(Const(256, unsigned(9)).shift_right(-5),
254 "(cat (const 5'd0) (const 9'd256))")
255 self.assertRepr(Const(256, signed(9)).shift_right(-1),
256 "(s (cat (const 1'd0) (const 9'sd-256)))")
257 self.assertRepr(Const(256, signed(9)).shift_right(-5),
258 "(s (cat (const 5'd0) (const 9'sd-256)))")
259
260 self.assertRepr(Const(256, unsigned(9)).shift_right(1),
261 "(slice (const 9'd256) 1:9)")
262 self.assertRepr(Const(256, unsigned(9)).shift_right(5),
263 "(slice (const 9'd256) 5:9)")
264 self.assertRepr(Const(256, signed(9)).shift_right(1),
265 "(s (slice (const 9'sd-256) 1:9))")
266 self.assertRepr(Const(256, signed(9)).shift_right(5),
267 "(s (slice (const 9'sd-256) 5:9))")
268 self.assertRepr(Const(256, signed(9)).shift_right(15),
269 "(s (slice (const 9'sd-256) 9:9))")
270
271 def test_shift_right_wrong(self):
272 with self.assertRaises(TypeError,
273 msg="Shift amount must be an integer, not 'str'"):
274 Const(31).shift_left("str")
275
276 def test_rotate_left(self):
277 self.assertRepr(Const(256).rotate_left(1),
278 "(cat (slice (const 9'd256) 8:9) (slice (const 9'd256) 0:8))")
279 self.assertRepr(Const(256).rotate_left(7),
280 "(cat (slice (const 9'd256) 2:9) (slice (const 9'd256) 0:2))")
281 self.assertRepr(Const(256).rotate_left(-1),
282 "(cat (slice (const 9'd256) 1:9) (slice (const 9'd256) 0:1))")
283 self.assertRepr(Const(256).rotate_left(-7),
284 "(cat (slice (const 9'd256) 7:9) (slice (const 9'd256) 0:7))")
285
286 def test_rotate_left_wrong(self):
287 with self.assertRaises(TypeError,
288 msg="Rotate amount must be an integer, not 'str'"):
289 Const(31).rotate_left("str")
290
291 def test_rotate_right(self):
292 self.assertRepr(Const(256).rotate_right(1),
293 "(cat (slice (const 9'd256) 1:9) (slice (const 9'd256) 0:1))")
294 self.assertRepr(Const(256).rotate_right(7),
295 "(cat (slice (const 9'd256) 7:9) (slice (const 9'd256) 0:7))")
296 self.assertRepr(Const(256).rotate_right(-1),
297 "(cat (slice (const 9'd256) 8:9) (slice (const 9'd256) 0:8))")
298 self.assertRepr(Const(256).rotate_right(-7),
299 "(cat (slice (const 9'd256) 2:9) (slice (const 9'd256) 0:2))")
300
301 def test_rotate_right_wrong(self):
302 with self.assertRaises(TypeError,
303 msg="Rotate amount must be an integer, not 'str'"):
304 Const(31).rotate_right("str")
305
306
307 class ConstTestCase(FHDLTestCase):
308 def test_shape(self):
309 self.assertEqual(Const(0).shape(), unsigned(1))
310 self.assertIsInstance(Const(0).shape(), Shape)
311 self.assertEqual(Const(1).shape(), unsigned(1))
312 self.assertEqual(Const(10).shape(), unsigned(4))
313 self.assertEqual(Const(-10).shape(), signed(5))
314
315 self.assertEqual(Const(1, 4).shape(), unsigned(4))
316 self.assertEqual(Const(-1, 4).shape(), signed(4))
317 self.assertEqual(Const(1, signed(4)).shape(), signed(4))
318 self.assertEqual(Const(0, unsigned(0)).shape(), unsigned(0))
319
320 def test_shape_wrong(self):
321 with self.assertRaises(TypeError,
322 msg="Width must be a non-negative integer, not -1"):
323 Const(1, -1)
324
325 def test_normalization(self):
326 self.assertEqual(Const(0b10110, signed(5)).value, -10)
327
328 def test_value(self):
329 self.assertEqual(Const(10).value, 10)
330
331 def test_repr(self):
332 self.assertEqual(repr(Const(10)), "(const 4'd10)")
333 self.assertEqual(repr(Const(-10)), "(const 5'sd-10)")
334
335 def test_hash(self):
336 with self.assertRaises(TypeError):
337 hash(Const(0))
338
339
340 class OperatorTestCase(FHDLTestCase):
341 def test_bool(self):
342 v = Const(0, 4).bool()
343 self.assertEqual(repr(v), "(b (const 4'd0))")
344 self.assertEqual(v.shape(), unsigned(1))
345
346 def test_invert(self):
347 v = ~Const(0, 4)
348 self.assertEqual(repr(v), "(~ (const 4'd0))")
349 self.assertEqual(v.shape(), unsigned(4))
350
351 def test_as_unsigned(self):
352 v = Const(-1, signed(4)).as_unsigned()
353 self.assertEqual(repr(v), "(u (const 4'sd-1))")
354 self.assertEqual(v.shape(), unsigned(4))
355
356 def test_as_signed(self):
357 v = Const(1, unsigned(4)).as_signed()
358 self.assertEqual(repr(v), "(s (const 4'd1))")
359 self.assertEqual(v.shape(), signed(4))
360
361 def test_neg(self):
362 v1 = -Const(0, unsigned(4))
363 self.assertEqual(repr(v1), "(- (const 4'd0))")
364 self.assertEqual(v1.shape(), signed(5))
365 v2 = -Const(0, signed(4))
366 self.assertEqual(repr(v2), "(- (const 4'sd0))")
367 self.assertEqual(v2.shape(), signed(5))
368
369 def test_add(self):
370 v1 = Const(0, unsigned(4)) + Const(0, unsigned(6))
371 self.assertEqual(repr(v1), "(+ (const 4'd0) (const 6'd0))")
372 self.assertEqual(v1.shape(), unsigned(7))
373 v2 = Const(0, signed(4)) + Const(0, signed(6))
374 self.assertEqual(v2.shape(), signed(7))
375 v3 = Const(0, signed(4)) + Const(0, unsigned(4))
376 self.assertEqual(v3.shape(), signed(6))
377 v4 = Const(0, unsigned(4)) + Const(0, signed(4))
378 self.assertEqual(v4.shape(), signed(6))
379 v5 = 10 + Const(0, 4)
380 self.assertEqual(v5.shape(), unsigned(5))
381
382 def test_sub(self):
383 v1 = Const(0, unsigned(4)) - Const(0, unsigned(6))
384 self.assertEqual(repr(v1), "(- (const 4'd0) (const 6'd0))")
385 self.assertEqual(v1.shape(), unsigned(7))
386 v2 = Const(0, signed(4)) - Const(0, signed(6))
387 self.assertEqual(v2.shape(), signed(7))
388 v3 = Const(0, signed(4)) - Const(0, unsigned(4))
389 self.assertEqual(v3.shape(), signed(6))
390 v4 = Const(0, unsigned(4)) - Const(0, signed(4))
391 self.assertEqual(v4.shape(), signed(6))
392 v5 = 10 - Const(0, 4)
393 self.assertEqual(v5.shape(), unsigned(5))
394
395 def test_mul(self):
396 v1 = Const(0, unsigned(4)) * Const(0, unsigned(6))
397 self.assertEqual(repr(v1), "(* (const 4'd0) (const 6'd0))")
398 self.assertEqual(v1.shape(), unsigned(10))
399 v2 = Const(0, signed(4)) * Const(0, signed(6))
400 self.assertEqual(v2.shape(), signed(10))
401 v3 = Const(0, signed(4)) * Const(0, unsigned(4))
402 self.assertEqual(v3.shape(), signed(8))
403 v5 = 10 * Const(0, 4)
404 self.assertEqual(v5.shape(), unsigned(8))
405
406 def test_mod(self):
407 v1 = Const(0, unsigned(4)) % Const(0, unsigned(6))
408 self.assertEqual(repr(v1), "(% (const 4'd0) (const 6'd0))")
409 self.assertEqual(v1.shape(), unsigned(4))
410 v3 = Const(0, signed(4)) % Const(0, unsigned(4))
411 self.assertEqual(v3.shape(), signed(4))
412 v5 = 10 % Const(0, 4)
413 self.assertEqual(v5.shape(), unsigned(4))
414
415 def test_mod_wrong(self):
416 with self.assertRaises(NotImplementedError,
417 msg="Division by a signed value is not supported"):
418 Const(0, signed(4)) % Const(0, signed(6))
419
420 def test_floordiv(self):
421 v1 = Const(0, unsigned(4)) // Const(0, unsigned(6))
422 self.assertEqual(repr(v1), "(// (const 4'd0) (const 6'd0))")
423 self.assertEqual(v1.shape(), unsigned(4))
424 v3 = Const(0, signed(4)) // Const(0, unsigned(4))
425 self.assertEqual(v3.shape(), signed(4))
426 v5 = 10 // Const(0, 4)
427 self.assertEqual(v5.shape(), unsigned(4))
428
429 def test_floordiv_wrong(self):
430 with self.assertRaises(NotImplementedError,
431 msg="Division by a signed value is not supported"):
432 Const(0, signed(4)) // Const(0, signed(6))
433
434 def test_and(self):
435 v1 = Const(0, unsigned(4)) & Const(0, unsigned(6))
436 self.assertEqual(repr(v1), "(& (const 4'd0) (const 6'd0))")
437 self.assertEqual(v1.shape(), unsigned(6))
438 v2 = Const(0, signed(4)) & Const(0, signed(6))
439 self.assertEqual(v2.shape(), signed(6))
440 v3 = Const(0, signed(4)) & Const(0, unsigned(4))
441 self.assertEqual(v3.shape(), signed(5))
442 v4 = Const(0, unsigned(4)) & Const(0, signed(4))
443 self.assertEqual(v4.shape(), signed(5))
444 v5 = 10 & Const(0, 4)
445 self.assertEqual(v5.shape(), unsigned(4))
446
447 def test_or(self):
448 v1 = Const(0, unsigned(4)) | Const(0, unsigned(6))
449 self.assertEqual(repr(v1), "(| (const 4'd0) (const 6'd0))")
450 self.assertEqual(v1.shape(), unsigned(6))
451 v2 = Const(0, signed(4)) | Const(0, signed(6))
452 self.assertEqual(v2.shape(), signed(6))
453 v3 = Const(0, signed(4)) | Const(0, unsigned(4))
454 self.assertEqual(v3.shape(), signed(5))
455 v4 = Const(0, unsigned(4)) | Const(0, signed(4))
456 self.assertEqual(v4.shape(), signed(5))
457 v5 = 10 | Const(0, 4)
458 self.assertEqual(v5.shape(), unsigned(4))
459
460 def test_xor(self):
461 v1 = Const(0, unsigned(4)) ^ Const(0, unsigned(6))
462 self.assertEqual(repr(v1), "(^ (const 4'd0) (const 6'd0))")
463 self.assertEqual(v1.shape(), unsigned(6))
464 v2 = Const(0, signed(4)) ^ Const(0, signed(6))
465 self.assertEqual(v2.shape(), signed(6))
466 v3 = Const(0, signed(4)) ^ Const(0, unsigned(4))
467 self.assertEqual(v3.shape(), signed(5))
468 v4 = Const(0, unsigned(4)) ^ Const(0, signed(4))
469 self.assertEqual(v4.shape(), signed(5))
470 v5 = 10 ^ Const(0, 4)
471 self.assertEqual(v5.shape(), unsigned(4))
472
473 def test_shl(self):
474 v1 = Const(1, 4) << Const(4)
475 self.assertEqual(repr(v1), "(<< (const 4'd1) (const 3'd4))")
476 self.assertEqual(v1.shape(), unsigned(11))
477
478 def test_shl_wrong(self):
479 with self.assertRaises(TypeError,
480 msg="Shift amount must be unsigned"):
481 1 << Const(0, signed(6))
482 with self.assertRaises(TypeError,
483 msg="Shift amount must be unsigned"):
484 Const(1, unsigned(4)) << -1
485
486 def test_shr(self):
487 v1 = Const(1, 4) >> Const(4)
488 self.assertEqual(repr(v1), "(>> (const 4'd1) (const 3'd4))")
489 self.assertEqual(v1.shape(), unsigned(4))
490
491 def test_shr_wrong(self):
492 with self.assertRaises(TypeError,
493 msg="Shift amount must be unsigned"):
494 1 << Const(0, signed(6))
495 with self.assertRaises(TypeError,
496 msg="Shift amount must be unsigned"):
497 Const(1, unsigned(4)) << -1
498
499 def test_lt(self):
500 v = Const(0, 4) < Const(0, 6)
501 self.assertEqual(repr(v), "(< (const 4'd0) (const 6'd0))")
502 self.assertEqual(v.shape(), unsigned(1))
503
504 def test_le(self):
505 v = Const(0, 4) <= Const(0, 6)
506 self.assertEqual(repr(v), "(<= (const 4'd0) (const 6'd0))")
507 self.assertEqual(v.shape(), unsigned(1))
508
509 def test_gt(self):
510 v = Const(0, 4) > Const(0, 6)
511 self.assertEqual(repr(v), "(> (const 4'd0) (const 6'd0))")
512 self.assertEqual(v.shape(), unsigned(1))
513
514 def test_ge(self):
515 v = Const(0, 4) >= Const(0, 6)
516 self.assertEqual(repr(v), "(>= (const 4'd0) (const 6'd0))")
517 self.assertEqual(v.shape(), unsigned(1))
518
519 def test_eq(self):
520 v = Const(0, 4) == Const(0, 6)
521 self.assertEqual(repr(v), "(== (const 4'd0) (const 6'd0))")
522 self.assertEqual(v.shape(), unsigned(1))
523
524 def test_ne(self):
525 v = Const(0, 4) != Const(0, 6)
526 self.assertEqual(repr(v), "(!= (const 4'd0) (const 6'd0))")
527 self.assertEqual(v.shape(), unsigned(1))
528
529 def test_mux(self):
530 s = Const(0)
531 v1 = Mux(s, Const(0, unsigned(4)), Const(0, unsigned(6)))
532 self.assertEqual(repr(v1), "(m (const 1'd0) (const 4'd0) (const 6'd0))")
533 self.assertEqual(v1.shape(), unsigned(6))
534 v2 = Mux(s, Const(0, signed(4)), Const(0, signed(6)))
535 self.assertEqual(v2.shape(), signed(6))
536 v3 = Mux(s, Const(0, signed(4)), Const(0, unsigned(4)))
537 self.assertEqual(v3.shape(), signed(5))
538 v4 = Mux(s, Const(0, unsigned(4)), Const(0, signed(4)))
539 self.assertEqual(v4.shape(), signed(5))
540
541 def test_mux_wide(self):
542 s = Const(0b100)
543 v = Mux(s, Const(0, unsigned(4)), Const(0, unsigned(6)))
544 self.assertEqual(repr(v), "(m (b (const 3'd4)) (const 4'd0) (const 6'd0))")
545
546 def test_mux_bool(self):
547 v = Mux(True, Const(0), Const(0))
548 self.assertEqual(repr(v), "(m (const 1'd1) (const 1'd0) (const 1'd0))")
549
550 def test_bool(self):
551 v = Const(0).bool()
552 self.assertEqual(repr(v), "(b (const 1'd0))")
553 self.assertEqual(v.shape(), unsigned(1))
554
555 def test_any(self):
556 v = Const(0b101).any()
557 self.assertEqual(repr(v), "(r| (const 3'd5))")
558
559 def test_all(self):
560 v = Const(0b101).all()
561 self.assertEqual(repr(v), "(r& (const 3'd5))")
562
563 def test_xor(self):
564 v = Const(0b101).xor()
565 self.assertEqual(repr(v), "(r^ (const 3'd5))")
566
567 def test_matches(self):
568 s = Signal(4)
569 self.assertRepr(s.matches(), "(const 1'd0)")
570 self.assertRepr(s.matches(1), """
571 (== (sig s) (const 1'd1))
572 """)
573 self.assertRepr(s.matches(0, 1), """
574 (r| (cat (== (sig s) (const 1'd0)) (== (sig s) (const 1'd1))))
575 """)
576 self.assertRepr(s.matches("10--"), """
577 (== (& (sig s) (const 4'd12)) (const 4'd8))
578 """)
579 self.assertRepr(s.matches("1 0--"), """
580 (== (& (sig s) (const 4'd12)) (const 4'd8))
581 """)
582
583 def test_matches_enum(self):
584 s = Signal(SignedEnum)
585 self.assertRepr(s.matches(SignedEnum.FOO), """
586 (== (sig s) (const 1'sd-1))
587 """)
588
589 def test_matches_width_wrong(self):
590 s = Signal(4)
591 with self.assertRaises(SyntaxError,
592 msg="Match pattern '--' must have the same width as match value (which is 4)"):
593 s.matches("--")
594 with self.assertWarns(SyntaxWarning,
595 msg="Match pattern '10110' is wider than match value (which has width 4); "
596 "comparison will never be true"):
597 s.matches(0b10110)
598
599 def test_matches_bits_wrong(self):
600 s = Signal(4)
601 with self.assertRaises(SyntaxError,
602 msg="Match pattern 'abc' must consist of 0, 1, and - (don't care) bits, "
603 "and may include whitespace"):
604 s.matches("abc")
605
606 def test_matches_pattern_wrong(self):
607 s = Signal(4)
608 with self.assertRaises(SyntaxError,
609 msg="Match pattern must be an integer, a string, or an enumeration, not 1.0"):
610 s.matches(1.0)
611
612 def test_hash(self):
613 with self.assertRaises(TypeError):
614 hash(Const(0) + Const(0))
615
616
617 class SliceTestCase(FHDLTestCase):
618 def test_shape(self):
619 s1 = Const(10)[2]
620 self.assertEqual(s1.shape(), unsigned(1))
621 self.assertIsInstance(s1.shape(), Shape)
622 s2 = Const(-10)[0:2]
623 self.assertEqual(s2.shape(), unsigned(2))
624
625 def test_start_end_negative(self):
626 c = Const(0, 8)
627 s1 = Slice(c, 0, -1)
628 self.assertEqual((s1.start, s1.stop), (0, 7))
629 s1 = Slice(c, -4, -1)
630 self.assertEqual((s1.start, s1.stop), (4, 7))
631
632 def test_start_end_wrong(self):
633 with self.assertRaises(TypeError,
634 msg="Slice start must be an integer, not 'x'"):
635 Slice(0, "x", 1)
636 with self.assertRaises(TypeError,
637 msg="Slice stop must be an integer, not 'x'"):
638 Slice(0, 1, "x")
639
640 def test_start_end_out_of_range(self):
641 c = Const(0, 8)
642 with self.assertRaises(IndexError,
643 msg="Cannot start slice 10 bits into 8-bit value"):
644 Slice(c, 10, 12)
645 with self.assertRaises(IndexError,
646 msg="Cannot stop slice 12 bits into 8-bit value"):
647 Slice(c, 0, 12)
648 with self.assertRaises(IndexError,
649 msg="Slice start 4 must be less than slice stop 2"):
650 Slice(c, 4, 2)
651
652 def test_repr(self):
653 s1 = Const(10)[2]
654 self.assertEqual(repr(s1), "(slice (const 4'd10) 2:3)")
655
656
657 class BitSelectTestCase(FHDLTestCase):
658 def setUp(self):
659 self.c = Const(0, 8)
660 self.s = Signal(range(self.c.width))
661
662 def test_shape(self):
663 s1 = self.c.bit_select(self.s, 2)
664 self.assertIsInstance(s1, Part)
665 self.assertEqual(s1.shape(), unsigned(2))
666 self.assertIsInstance(s1.shape(), Shape)
667 s2 = self.c.bit_select(self.s, 0)
668 self.assertIsInstance(s2, Part)
669 self.assertEqual(s2.shape(), unsigned(0))
670
671 def test_stride(self):
672 s1 = self.c.bit_select(self.s, 2)
673 self.assertIsInstance(s1, Part)
674 self.assertEqual(s1.stride, 1)
675
676 def test_const(self):
677 s1 = self.c.bit_select(1, 2)
678 self.assertIsInstance(s1, Slice)
679 self.assertRepr(s1, """(slice (const 8'd0) 1:3)""")
680
681 def test_width_wrong(self):
682 with self.assertRaises(TypeError):
683 self.c.bit_select(self.s, -1)
684
685 def test_repr(self):
686 s = self.c.bit_select(self.s, 2)
687 self.assertEqual(repr(s), "(part (const 8'd0) (sig s) 2 1)")
688
689
690 class WordSelectTestCase(FHDLTestCase):
691 def setUp(self):
692 self.c = Const(0, 8)
693 self.s = Signal(range(self.c.width))
694
695 def test_shape(self):
696 s1 = self.c.word_select(self.s, 2)
697 self.assertIsInstance(s1, Part)
698 self.assertEqual(s1.shape(), unsigned(2))
699 self.assertIsInstance(s1.shape(), Shape)
700
701 def test_stride(self):
702 s1 = self.c.word_select(self.s, 2)
703 self.assertIsInstance(s1, Part)
704 self.assertEqual(s1.stride, 2)
705
706 def test_const(self):
707 s1 = self.c.word_select(1, 2)
708 self.assertIsInstance(s1, Slice)
709 self.assertRepr(s1, """(slice (const 8'd0) 2:4)""")
710
711 def test_width_wrong(self):
712 with self.assertRaises(TypeError):
713 self.c.word_select(self.s, 0)
714 with self.assertRaises(TypeError):
715 self.c.word_select(self.s, -1)
716
717 def test_repr(self):
718 s = self.c.word_select(self.s, 2)
719 self.assertEqual(repr(s), "(part (const 8'd0) (sig s) 2 2)")
720
721
722 class CatTestCase(FHDLTestCase):
723 def test_shape(self):
724 c0 = Cat()
725 self.assertEqual(c0.shape(), unsigned(0))
726 self.assertIsInstance(c0.shape(), Shape)
727 c1 = Cat(Const(10))
728 self.assertEqual(c1.shape(), unsigned(4))
729 c2 = Cat(Const(10), Const(1))
730 self.assertEqual(c2.shape(), unsigned(5))
731 c3 = Cat(Const(10), Const(1), Const(0))
732 self.assertEqual(c3.shape(), unsigned(6))
733
734 def test_repr(self):
735 c1 = Cat(Const(10), Const(1))
736 self.assertEqual(repr(c1), "(cat (const 4'd10) (const 1'd1))")
737
738
739 class ReplTestCase(FHDLTestCase):
740 def test_shape(self):
741 s1 = Repl(Const(10), 3)
742 self.assertEqual(s1.shape(), unsigned(12))
743 self.assertIsInstance(s1.shape(), Shape)
744 s2 = Repl(Const(10), 0)
745 self.assertEqual(s2.shape(), unsigned(0))
746
747 def test_count_wrong(self):
748 with self.assertRaises(TypeError):
749 Repl(Const(10), -1)
750 with self.assertRaises(TypeError):
751 Repl(Const(10), "str")
752
753 def test_repr(self):
754 s = Repl(Const(10), 3)
755 self.assertEqual(repr(s), "(repl (const 4'd10) 3)")
756
757
758 class ArrayTestCase(FHDLTestCase):
759 def test_acts_like_array(self):
760 a = Array([1,2,3])
761 self.assertSequenceEqual(a, [1,2,3])
762 self.assertEqual(a[1], 2)
763 a[1] = 4
764 self.assertSequenceEqual(a, [1,4,3])
765 del a[1]
766 self.assertSequenceEqual(a, [1,3])
767 a.insert(1, 2)
768 self.assertSequenceEqual(a, [1,2,3])
769
770 def test_becomes_immutable(self):
771 a = Array([1,2,3])
772 s1 = Signal(range(len(a)))
773 s2 = Signal(range(len(a)))
774 v1 = a[s1]
775 v2 = a[s2]
776 with self.assertRaisesRegex(ValueError,
777 r"^Array can no longer be mutated after it was indexed with a value at "):
778 a[1] = 2
779 with self.assertRaisesRegex(ValueError,
780 r"^Array can no longer be mutated after it was indexed with a value at "):
781 del a[1]
782 with self.assertRaisesRegex(ValueError,
783 r"^Array can no longer be mutated after it was indexed with a value at "):
784 a.insert(1, 2)
785
786 def test_repr(self):
787 a = Array([1,2,3])
788 self.assertEqual(repr(a), "(array mutable [1, 2, 3])")
789 s = Signal(range(len(a)))
790 v = a[s]
791 self.assertEqual(repr(a), "(array [1, 2, 3])")
792
793
794 class ArrayProxyTestCase(FHDLTestCase):
795 def test_index_shape(self):
796 m = Array(Array(x * y for y in range(1, 4)) for x in range(1, 4))
797 a = Signal(range(3))
798 b = Signal(range(3))
799 v = m[a][b]
800 self.assertEqual(v.shape(), unsigned(4))
801
802 def test_attr_shape(self):
803 from collections import namedtuple
804 pair = namedtuple("pair", ("p", "n"))
805 a = Array(pair(i, -i) for i in range(10))
806 s = Signal(range(len(a)))
807 v = a[s]
808 self.assertEqual(v.p.shape(), unsigned(4))
809 self.assertEqual(v.n.shape(), signed(6))
810
811 def test_repr(self):
812 a = Array([1, 2, 3])
813 s = Signal(range(3))
814 v = a[s]
815 self.assertEqual(repr(v), "(proxy (array [1, 2, 3]) (sig s))")
816
817
818 class SignalTestCase(FHDLTestCase):
819 def test_shape(self):
820 s1 = Signal()
821 self.assertEqual(s1.shape(), unsigned(1))
822 self.assertIsInstance(s1.shape(), Shape)
823 s2 = Signal(2)
824 self.assertEqual(s2.shape(), unsigned(2))
825 s3 = Signal(unsigned(2))
826 self.assertEqual(s3.shape(), unsigned(2))
827 s4 = Signal(signed(2))
828 self.assertEqual(s4.shape(), signed(2))
829 s5 = Signal(0)
830 self.assertEqual(s5.shape(), unsigned(0))
831 s6 = Signal(range(16))
832 self.assertEqual(s6.shape(), unsigned(4))
833 s7 = Signal(range(4, 16))
834 self.assertEqual(s7.shape(), unsigned(4))
835 s8 = Signal(range(-4, 16))
836 self.assertEqual(s8.shape(), signed(5))
837 s9 = Signal(range(-20, 16))
838 self.assertEqual(s9.shape(), signed(6))
839 s10 = Signal(range(0))
840 self.assertEqual(s10.shape(), unsigned(0))
841 s11 = Signal(range(1))
842 self.assertEqual(s11.shape(), unsigned(1))
843
844 def test_shape_wrong(self):
845 with self.assertRaises(TypeError,
846 msg="Width must be a non-negative integer, not -10"):
847 Signal(-10)
848
849 def test_name(self):
850 s1 = Signal()
851 self.assertEqual(s1.name, "s1")
852 s2 = Signal(name="sig")
853 self.assertEqual(s2.name, "sig")
854
855 def test_reset(self):
856 s1 = Signal(4, reset=0b111, reset_less=True)
857 self.assertEqual(s1.reset, 0b111)
858 self.assertEqual(s1.reset_less, True)
859
860 def test_reset_enum(self):
861 s1 = Signal(2, reset=UnsignedEnum.BAR)
862 self.assertEqual(s1.reset, 2)
863 with self.assertRaises(TypeError,
864 msg="Reset value has to be an int or an integral Enum"
865 ):
866 Signal(1, reset=StringEnum.FOO)
867
868 def test_reset_narrow(self):
869 with self.assertWarns(SyntaxWarning,
870 msg="Reset value 8 requires 4 bits to represent, but the signal only has 3 bits"):
871 Signal(3, reset=8)
872 with self.assertWarns(SyntaxWarning,
873 msg="Reset value 4 requires 4 bits to represent, but the signal only has 3 bits"):
874 Signal(signed(3), reset=4)
875 with self.assertWarns(SyntaxWarning,
876 msg="Reset value -5 requires 4 bits to represent, but the signal only has 3 bits"):
877 Signal(signed(3), reset=-5)
878
879 def test_attrs(self):
880 s1 = Signal()
881 self.assertEqual(s1.attrs, {})
882 s2 = Signal(attrs={"no_retiming": True})
883 self.assertEqual(s2.attrs, {"no_retiming": True})
884
885 def test_repr(self):
886 s1 = Signal()
887 self.assertEqual(repr(s1), "(sig s1)")
888
889 def test_like(self):
890 s1 = Signal.like(Signal(4))
891 self.assertEqual(s1.shape(), unsigned(4))
892 s2 = Signal.like(Signal(range(-15, 1)))
893 self.assertEqual(s2.shape(), signed(5))
894 s3 = Signal.like(Signal(4, reset=0b111, reset_less=True))
895 self.assertEqual(s3.reset, 0b111)
896 self.assertEqual(s3.reset_less, True)
897 s4 = Signal.like(Signal(attrs={"no_retiming": True}))
898 self.assertEqual(s4.attrs, {"no_retiming": True})
899 s5 = Signal.like(Signal(decoder=str))
900 self.assertEqual(s5.decoder, str)
901 s6 = Signal.like(10)
902 self.assertEqual(s6.shape(), unsigned(4))
903 s7 = [Signal.like(Signal(4))][0]
904 self.assertEqual(s7.name, "$like")
905 s8 = Signal.like(s1, name_suffix="_ff")
906 self.assertEqual(s8.name, "s1_ff")
907
908 def test_decoder(self):
909 class Color(Enum):
910 RED = 1
911 BLUE = 2
912 s = Signal(decoder=Color)
913 self.assertEqual(s.decoder(1), "RED/1")
914 self.assertEqual(s.decoder(3), "3")
915
916 def test_enum(self):
917 s1 = Signal(UnsignedEnum)
918 self.assertEqual(s1.shape(), unsigned(2))
919 s2 = Signal(SignedEnum)
920 self.assertEqual(s2.shape(), signed(2))
921 self.assertEqual(s2.decoder(SignedEnum.FOO), "FOO/-1")
922
923
924 class ClockSignalTestCase(FHDLTestCase):
925 def test_domain(self):
926 s1 = ClockSignal()
927 self.assertEqual(s1.domain, "sync")
928 s2 = ClockSignal("pix")
929 self.assertEqual(s2.domain, "pix")
930
931 with self.assertRaises(TypeError,
932 msg="Clock domain name must be a string, not 1"):
933 ClockSignal(1)
934
935 def test_shape(self):
936 s1 = ClockSignal()
937 self.assertEqual(s1.shape(), unsigned(1))
938 self.assertIsInstance(s1.shape(), Shape)
939
940 def test_repr(self):
941 s1 = ClockSignal()
942 self.assertEqual(repr(s1), "(clk sync)")
943
944 def test_wrong_name_comb(self):
945 with self.assertRaises(ValueError,
946 msg="Domain 'comb' does not have a clock"):
947 ClockSignal("comb")
948
949
950 class ResetSignalTestCase(FHDLTestCase):
951 def test_domain(self):
952 s1 = ResetSignal()
953 self.assertEqual(s1.domain, "sync")
954 s2 = ResetSignal("pix")
955 self.assertEqual(s2.domain, "pix")
956
957 with self.assertRaises(TypeError,
958 msg="Clock domain name must be a string, not 1"):
959 ResetSignal(1)
960
961 def test_shape(self):
962 s1 = ResetSignal()
963 self.assertEqual(s1.shape(), unsigned(1))
964 self.assertIsInstance(s1.shape(), Shape)
965
966 def test_repr(self):
967 s1 = ResetSignal()
968 self.assertEqual(repr(s1), "(rst sync)")
969
970 def test_wrong_name_comb(self):
971 with self.assertRaises(ValueError,
972 msg="Domain 'comb' does not have a reset"):
973 ResetSignal("comb")
974
975
976 class MockUserValue(UserValue):
977 def __init__(self, lowered):
978 super().__init__()
979 self.lower_count = 0
980 self.lowered = lowered
981
982 def lower(self):
983 self.lower_count += 1
984 return self.lowered
985
986
987 class UserValueTestCase(FHDLTestCase):
988 def test_shape(self):
989 uv = MockUserValue(1)
990 self.assertEqual(uv.shape(), unsigned(1))
991 self.assertIsInstance(uv.shape(), Shape)
992 uv.lowered = 2
993 self.assertEqual(uv.shape(), unsigned(1))
994 self.assertEqual(uv.lower_count, 1)
995
996 def test_lower_to_user_value(self):
997 uv = MockUserValue(MockUserValue(1))
998 self.assertEqual(uv.shape(), unsigned(1))
999 self.assertIsInstance(uv.shape(), Shape)
1000 uv.lowered = MockUserValue(2)
1001 self.assertEqual(uv.shape(), unsigned(1))
1002 self.assertEqual(uv.lower_count, 1)
1003
1004
1005 class SampleTestCase(FHDLTestCase):
1006 def test_const(self):
1007 s = Sample(1, 1, "sync")
1008 self.assertEqual(s.shape(), unsigned(1))
1009
1010 def test_signal(self):
1011 s1 = Sample(Signal(2), 1, "sync")
1012 self.assertEqual(s1.shape(), unsigned(2))
1013 s2 = Sample(ClockSignal(), 1, "sync")
1014 s3 = Sample(ResetSignal(), 1, "sync")
1015
1016 def test_wrong_value_operator(self):
1017 with self.assertRaises(TypeError,
1018 "Sampled value must be a signal or a constant, not "
1019 "(+ (sig $signal) (const 1'd1))"):
1020 Sample(Signal() + 1, 1, "sync")
1021
1022 def test_wrong_clocks_neg(self):
1023 with self.assertRaises(ValueError,
1024 "Cannot sample a value 1 cycles in the future"):
1025 Sample(Signal(), -1, "sync")
1026
1027 def test_wrong_domain(self):
1028 with self.assertRaises(TypeError,
1029 "Domain name must be a string or None, not 0"):
1030 Sample(Signal(), 1, 0)
1031
1032
1033 class InitialTestCase(FHDLTestCase):
1034 def test_initial(self):
1035 i = Initial()
1036 self.assertEqual(i.shape(), unsigned(1))