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