4 from nmigen
.hdl
.ast
import *
9 class UnsignedEnum(Enum
):
15 class SignedEnum(Enum
):
21 class StringEnum(Enum
):
26 class ShapeTestCase(FHDLTestCase
):
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)
35 self
.assertEqual(s3
.width
, 3)
36 self
.assertEqual(s3
.signed
, True)
38 def test_make_wrong(self
):
39 with self
.assertRaisesRegex(TypeError,
40 r
"^Width must be a non-negative integer, not -1$"):
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'
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)
54 self
.assertEqual(repr(Shape()), "unsigned(1)")
55 self
.assertEqual(repr(Shape(2, True)), "signed(2)")
58 width
, signed
= Shape()
59 self
.assertEqual(width
, 1)
60 self
.assertEqual(signed
, False)
62 def test_unsigned(self
):
64 self
.assertIsInstance(s1
, Shape
)
65 self
.assertEqual(s1
.width
, 2)
66 self
.assertEqual(s1
.signed
, False)
68 def test_signed(self
):
70 self
.assertIsInstance(s1
, Shape
)
71 self
.assertEqual(s1
.width
, 2)
72 self
.assertEqual(s1
.signed
, True)
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)
82 def test_cast_int(self
):
84 self
.assertEqual(s1
.width
, 2)
85 self
.assertEqual(s1
.signed
, False)
87 def test_cast_int_wrong(self
):
88 with self
.assertRaisesRegex(TypeError,
89 r
"^Width must be a non-negative integer, not -1$"):
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)
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))
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)
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)
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
)
142 def test_cast_bad(self
):
143 with self
.assertRaisesRegex(TypeError,
144 r
"^Object 'foo' cannot be used as value shape$"):
148 class ValueTestCase(FHDLTestCase
):
150 self
.assertIsInstance(Value
.cast(0), Const
)
151 self
.assertIsInstance(Value
.cast(True), Const
)
153 self
.assertIs(Value
.cast(c
), c
)
154 with self
.assertRaisesRegex(TypeError,
155 r
"^Object 'str' cannot be converted to an nMigen value$"):
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))
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
)
172 with self
.assertRaisesRegex(TypeError,
173 r
"^Attempted to convert nMigen value to Python boolean$"):
178 self
.assertEqual(len(Const(10)), 4)
180 def test_getitem_int(self
):
182 self
.assertIsInstance(s1
, Slice
)
183 self
.assertEqual(s1
.start
, 0)
184 self
.assertEqual(s1
.stop
, 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$"):
193 def test_getitem_slice(self
):
195 self
.assertIsInstance(s1
, Slice
)
196 self
.assertEqual(s1
.start
, 1)
197 self
.assertEqual(s1
.stop
, 3)
199 self
.assertIsInstance(s2
, Slice
)
200 self
.assertEqual(s2
.start
, 1)
201 self
.assertEqual(s2
.stop
, 2)
203 self
.assertIsInstance(s3
, _InternalCat
)
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)
214 def test_getitem_wrong(self
):
215 with self
.assertRaisesRegex(TypeError,
216 r
"^Cannot index value with 'str'$"):
219 def test_shift_left(self
):
220 self
.assertRepr(Const(256, unsigned(9)).shift_left(0),
221 "(cat (const 0'd0) (const 9'd256))")
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)))")
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))")
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")
248 def test_shift_right(self
):
249 self
.assertRepr(Const(256, unsigned(9)).shift_right(0),
250 "(slice (const 9'd256) 0:9)")
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)))")
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))")
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")
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))")
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")
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))")
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")
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))
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))
321 def test_shape_wrong(self
):
322 with self
.assertRaisesRegex(TypeError,
323 r
"^Width must be a non-negative integer, not -1$"):
326 def test_normalization(self
):
327 self
.assertEqual(Const(0b10110, signed(5)).value
, -10)
329 def test_value(self
):
330 self
.assertEqual(Const(10).value
, 10)
333 self
.assertEqual(repr(Const(10)), "(const 4'd10)")
334 self
.assertEqual(repr(Const(-10)), "(const 5'sd-10)")
337 with self
.assertRaises(TypeError):
341 class OperatorTestCase(FHDLTestCase
):
343 v
= Const(0, 4).bool()
344 self
.assertEqual(repr(v
), "(b (const 4'd0))")
345 self
.assertEqual(v
.shape(), unsigned(1))
347 def test_invert(self
):
349 self
.assertEqual(repr(v
), "(~ (const 4'd0))")
350 self
.assertEqual(v
.shape(), unsigned(4))
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))
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))
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))
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))
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))
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))
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))
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))
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))
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))
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))
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))
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))
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))
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
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))
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
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))
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))
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))
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))
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))
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))
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))
542 def test_mux_wide(self
):
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))")
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))")
552 v
= Const(0b101).any()
553 self
.assertEqual(repr(v
), "(r| (const 3'd5))")
556 v
= Const(0b101).all()
557 self
.assertEqual(repr(v
), "(r& (const 3'd5))")
559 def test_xor_value(self
):
560 v
= Const(0b101).xor()
561 self
.assertEqual(repr(v
), "(r^ (const 3'd5))")
563 def test_matches(self
):
565 self
.assertRepr(s
.matches(), "(const 1'd0)")
566 self
.assertRepr(s
.matches(1), """
567 (== (sig s) (const 1'd1))
569 self
.assertRepr(s
.matches(0, 1), """
570 (r| (cat (== (sig s) (const 1'd0)) (== (sig s) (const 1'd1))))
572 self
.assertRepr(s
.matches("10--"), """
573 (== (& (sig s) (const 4'd12)) (const 4'd8))
575 self
.assertRepr(s
.matches("1 0--"), """
576 (== (& (sig s) (const 4'd12)) (const 4'd8))
579 def test_matches_enum(self
):
580 s
= Signal(SignedEnum
)
581 self
.assertRepr(s
.matches(SignedEnum
.FOO
), """
582 (== (sig s) (const 1'sd-1))
585 def test_matches_width_wrong(self
):
587 with self
.assertRaisesRegex(SyntaxError,
588 r
"^Match pattern '--' must have the same width as match value \(which is 4\)$"):
590 with self
.assertWarnsRegex(SyntaxWarning,
591 (r
"^Match pattern '10110' is wider than match value \(which has width 4\); "
592 r
"comparison will never be true$")):
595 def test_matches_bits_wrong(self
):
597 with self
.assertRaisesRegex(SyntaxError,
598 (r
"^Match pattern 'abc' must consist of 0, 1, and - \(don't care\) bits, "
599 r
"and may include whitespace$")):
602 def test_matches_pattern_wrong(self
):
604 with self
.assertRaisesRegex(SyntaxError,
605 r
"^Match pattern must be an integer, a string, or an enumeration, not 1\.0$"):
609 with self
.assertRaises(TypeError):
610 hash(Const(0) + Const(0))
613 class SliceTestCase(FHDLTestCase
):
614 def test_shape(self
):
616 self
.assertEqual(s1
.shape(), unsigned(1))
617 self
.assertIsInstance(s1
.shape(), Shape
)
619 self
.assertEqual(s2
.shape(), unsigned(2))
621 def test_start_end_negative(self
):
624 self
.assertEqual((s1
.start
, s1
.stop
), (0, 7))
625 s1
= Slice(c
, -4, -1)
626 self
.assertEqual((s1
.start
, s1
.stop
), (4, 7))
628 def test_start_end_bool(self
):
630 s
= Slice(c
, False, True)
631 self
.assertIs(type(s
.start
), int)
632 self
.assertIs(type(s
.stop
), int)
634 def test_start_end_wrong(self
):
635 with self
.assertRaisesRegex(TypeError,
636 r
"^Slice start must be an integer, not 'x'$"):
638 with self
.assertRaisesRegex(TypeError,
639 r
"^Slice stop must be an integer, not 'x'$"):
642 def test_start_end_out_of_range(self
):
644 with self
.assertRaisesRegex(IndexError,
645 r
"^Cannot start slice 10 bits into 8-bit value$"):
647 with self
.assertRaisesRegex(IndexError,
648 r
"^Cannot stop slice 12 bits into 8-bit value$"):
650 with self
.assertRaisesRegex(IndexError,
651 r
"^Slice start 4 must be less than slice stop 2$"):
656 self
.assertEqual(repr(s1
), "(slice (const 4'd10) 2:3)")
659 class BitSelectTestCase(FHDLTestCase
):
662 self
.s
= Signal(range(self
.c
.width
))
664 def test_shape(self
):
665 s1
= self
.c
.bit_select(self
.s
, 2)
666 self
.assertIsInstance(s1
, Part
)
667 self
.assertEqual(s1
.shape(), unsigned(2))
668 self
.assertIsInstance(s1
.shape(), Shape
)
669 s2
= self
.c
.bit_select(self
.s
, 0)
670 self
.assertIsInstance(s2
, Part
)
671 self
.assertEqual(s2
.shape(), unsigned(0))
673 def test_stride(self
):
674 s1
= self
.c
.bit_select(self
.s
, 2)
675 self
.assertIsInstance(s1
, Part
)
676 self
.assertEqual(s1
.stride
, 1)
678 def test_const(self
):
679 s1
= self
.c
.bit_select(1, 2)
680 self
.assertIsInstance(s1
, Slice
)
681 self
.assertRepr(s1
, """(slice (const 8'd0) 1:3)""")
683 def test_width_wrong(self
):
684 with self
.assertRaises(TypeError):
685 self
.c
.bit_select(self
.s
, -1)
688 s
= self
.c
.bit_select(self
.s
, 2)
689 self
.assertEqual(repr(s
), "(part (const 8'd0) (sig s) 2 1)")
692 class WordSelectTestCase(FHDLTestCase
):
695 self
.s
= Signal(range(self
.c
.width
))
697 def test_shape(self
):
698 s1
= self
.c
.word_select(self
.s
, 2)
699 self
.assertIsInstance(s1
, Part
)
700 self
.assertEqual(s1
.shape(), unsigned(2))
701 self
.assertIsInstance(s1
.shape(), Shape
)
703 def test_stride(self
):
704 s1
= self
.c
.word_select(self
.s
, 2)
705 self
.assertIsInstance(s1
, Part
)
706 self
.assertEqual(s1
.stride
, 2)
708 def test_const(self
):
709 s1
= self
.c
.word_select(1, 2)
710 self
.assertIsInstance(s1
, Slice
)
711 self
.assertRepr(s1
, """(slice (const 8'd0) 2:4)""")
713 def test_width_wrong(self
):
714 with self
.assertRaises(TypeError):
715 self
.c
.word_select(self
.s
, 0)
716 with self
.assertRaises(TypeError):
717 self
.c
.word_select(self
.s
, -1)
720 s
= self
.c
.word_select(self
.s
, 2)
721 self
.assertEqual(repr(s
), "(part (const 8'd0) (sig s) 2 2)")
724 class CatTestCase(FHDLTestCase
):
725 def test_shape(self
):
727 self
.assertEqual(c0
.shape(), unsigned(0))
728 self
.assertIsInstance(c0
.shape(), Shape
)
730 self
.assertEqual(c1
.shape(), unsigned(4))
731 c2
= Cat(Const(10), Const(1))
732 self
.assertEqual(c2
.shape(), unsigned(5))
733 c3
= Cat(Const(10), Const(1), Const(0))
734 self
.assertEqual(c3
.shape(), unsigned(6))
737 c1
= Cat(Const(10), Const(1))
738 self
.assertEqual(repr(c1
), "(cat (const 4'd10) (const 1'd1))")
741 class ReplTestCase(FHDLTestCase
):
742 def test_shape(self
):
743 s1
= Repl(Const(10), 3)
744 self
.assertEqual(s1
.shape(), unsigned(12))
745 self
.assertIsInstance(s1
.shape(), Shape
)
746 s2
= Repl(Const(10), 0)
747 self
.assertEqual(s2
.shape(), unsigned(0))
749 def test_count_wrong(self
):
750 with self
.assertRaises(TypeError):
752 with self
.assertRaises(TypeError):
753 Repl(Const(10), "str")
756 s
= Repl(Const(10), 3)
757 self
.assertEqual(repr(s
), "(repl (const 4'd10) 3)")
760 class ArrayTestCase(FHDLTestCase
):
761 def test_acts_like_array(self
):
763 self
.assertSequenceEqual(a
, [1,2,3])
764 self
.assertEqual(a
[1], 2)
766 self
.assertSequenceEqual(a
, [1,4,3])
768 self
.assertSequenceEqual(a
, [1,3])
770 self
.assertSequenceEqual(a
, [1,2,3])
772 def test_becomes_immutable(self
):
774 s1
= Signal(range(len(a
)))
775 s2
= Signal(range(len(a
)))
778 with self
.assertRaisesRegex(ValueError,
779 r
"^Array can no longer be mutated after it was indexed with a value at "):
781 with self
.assertRaisesRegex(ValueError,
782 r
"^Array can no longer be mutated after it was indexed with a value at "):
784 with self
.assertRaisesRegex(ValueError,
785 r
"^Array can no longer be mutated after it was indexed with a value at "):
790 self
.assertEqual(repr(a
), "(array mutable [1, 2, 3])")
791 s
= Signal(range(len(a
)))
793 self
.assertEqual(repr(a
), "(array [1, 2, 3])")
796 class ArrayProxyTestCase(FHDLTestCase
):
797 def test_index_shape(self
):
798 m
= Array(Array(x
* y
for y
in range(1, 4)) for x
in range(1, 4))
802 self
.assertEqual(v
.shape(), unsigned(4))
804 def test_attr_shape(self
):
805 from collections
import namedtuple
806 pair
= namedtuple("pair", ("p", "n"))
807 a
= Array(pair(i
, -i
) for i
in range(10))
808 s
= Signal(range(len(a
)))
810 self
.assertEqual(v
.p
.shape(), unsigned(4))
811 self
.assertEqual(v
.n
.shape(), signed(5))
813 def test_attr_shape_signed(self
):
814 # [unsigned(1), unsigned(1)] → unsigned(1)
817 self
.assertEqual(v1
.shape(), unsigned(1))
818 # [signed(1), signed(1)] → signed(1)
821 self
.assertEqual(v2
.shape(), signed(1))
822 # [unsigned(1), signed(2)] → signed(2)
825 self
.assertEqual(v3
.shape(), signed(2))
826 # [unsigned(1), signed(1)] → signed(2); 1st operand padded with sign bit!
829 self
.assertEqual(v4
.shape(), signed(2))
830 # [unsigned(2), signed(1)] → signed(3); 1st operand padded with sign bit!
833 self
.assertEqual(v5
.shape(), signed(2))
839 self
.assertEqual(repr(v
), "(proxy (array [1, 2, 3]) (sig s))")
842 class SignalTestCase(FHDLTestCase
):
843 def test_shape(self
):
845 self
.assertEqual(s1
.shape(), unsigned(1))
846 self
.assertIsInstance(s1
.shape(), Shape
)
848 self
.assertEqual(s2
.shape(), unsigned(2))
849 s3
= Signal(unsigned(2))
850 self
.assertEqual(s3
.shape(), unsigned(2))
851 s4
= Signal(signed(2))
852 self
.assertEqual(s4
.shape(), signed(2))
854 self
.assertEqual(s5
.shape(), unsigned(0))
855 s6
= Signal(range(16))
856 self
.assertEqual(s6
.shape(), unsigned(4))
857 s7
= Signal(range(4, 16))
858 self
.assertEqual(s7
.shape(), unsigned(4))
859 s8
= Signal(range(-4, 16))
860 self
.assertEqual(s8
.shape(), signed(5))
861 s9
= Signal(range(-20, 16))
862 self
.assertEqual(s9
.shape(), signed(6))
863 s10
= Signal(range(0))
864 self
.assertEqual(s10
.shape(), unsigned(0))
865 s11
= Signal(range(1))
866 self
.assertEqual(s11
.shape(), unsigned(1))
868 def test_shape_wrong(self
):
869 with self
.assertRaisesRegex(TypeError,
870 r
"^Width must be a non-negative integer, not -10$"):
875 self
.assertEqual(s1
.name
, "s1")
876 s2
= Signal(name
="sig")
877 self
.assertEqual(s2
.name
, "sig")
879 def test_reset(self
):
880 s1
= Signal(4, reset
=0b111, reset_less
=True)
881 self
.assertEqual(s1
.reset
, 0b111)
882 self
.assertEqual(s1
.reset_less
, True)
884 def test_reset_enum(self
):
885 s1
= Signal(2, reset
=UnsignedEnum
.BAR
)
886 self
.assertEqual(s1
.reset
, 2)
887 with self
.assertRaisesRegex(TypeError,
888 r
"^Reset value has to be an int or an integral Enum$"
890 Signal(1, reset
=StringEnum
.FOO
)
892 def test_reset_narrow(self
):
893 with self
.assertWarnsRegex(SyntaxWarning,
894 r
"^Reset value 8 requires 4 bits to represent, but the signal only has 3 bits$"):
896 with self
.assertWarnsRegex(SyntaxWarning,
897 r
"^Reset value 4 requires 4 bits to represent, but the signal only has 3 bits$"):
898 Signal(signed(3), reset
=4)
899 with self
.assertWarnsRegex(SyntaxWarning,
900 r
"^Reset value -5 requires 4 bits to represent, but the signal only has 3 bits$"):
901 Signal(signed(3), reset
=-5)
903 def test_attrs(self
):
905 self
.assertEqual(s1
.attrs
, {})
906 s2
= Signal(attrs
={"no_retiming": True})
907 self
.assertEqual(s2
.attrs
, {"no_retiming": True})
911 self
.assertEqual(repr(s1
), "(sig s1)")
914 s1
= Signal
.like(Signal(4))
915 self
.assertEqual(s1
.shape(), unsigned(4))
916 s2
= Signal
.like(Signal(range(-15, 1)))
917 self
.assertEqual(s2
.shape(), signed(5))
918 s3
= Signal
.like(Signal(4, reset
=0b111, reset_less
=True))
919 self
.assertEqual(s3
.reset
, 0b111)
920 self
.assertEqual(s3
.reset_less
, True)
921 s4
= Signal
.like(Signal(attrs
={"no_retiming": True}))
922 self
.assertEqual(s4
.attrs
, {"no_retiming": True})
923 s5
= Signal
.like(Signal(decoder
=str))
924 self
.assertEqual(s5
.decoder
, str)
926 self
.assertEqual(s6
.shape(), unsigned(4))
927 s7
= [Signal
.like(Signal(4))][0]
928 self
.assertEqual(s7
.name
, "$like")
929 s8
= Signal
.like(s1
, name_suffix
="_ff")
930 self
.assertEqual(s8
.name
, "s1_ff")
932 def test_decoder(self
):
936 s
= Signal(decoder
=Color
)
937 self
.assertEqual(s
.decoder(1), "RED/1")
938 self
.assertEqual(s
.decoder(3), "3")
941 s1
= Signal(UnsignedEnum
)
942 self
.assertEqual(s1
.shape(), unsigned(2))
943 s2
= Signal(SignedEnum
)
944 self
.assertEqual(s2
.shape(), signed(2))
945 self
.assertEqual(s2
.decoder(SignedEnum
.FOO
), "FOO/-1")
948 class ClockSignalTestCase(FHDLTestCase
):
949 def test_domain(self
):
951 self
.assertEqual(s1
.domain
, "sync")
952 s2
= ClockSignal("pix")
953 self
.assertEqual(s2
.domain
, "pix")
955 with self
.assertRaisesRegex(TypeError,
956 r
"^Clock domain name must be a string, not 1$"):
959 def test_shape(self
):
961 self
.assertEqual(s1
.shape(), unsigned(1))
962 self
.assertIsInstance(s1
.shape(), Shape
)
966 self
.assertEqual(repr(s1
), "(clk sync)")
968 def test_wrong_name_comb(self
):
969 with self
.assertRaisesRegex(ValueError,
970 r
"^Domain 'comb' does not have a clock$"):
974 class ResetSignalTestCase(FHDLTestCase
):
975 def test_domain(self
):
977 self
.assertEqual(s1
.domain
, "sync")
978 s2
= ResetSignal("pix")
979 self
.assertEqual(s2
.domain
, "pix")
981 with self
.assertRaisesRegex(TypeError,
982 r
"^Clock domain name must be a string, not 1$"):
985 def test_shape(self
):
987 self
.assertEqual(s1
.shape(), unsigned(1))
988 self
.assertIsInstance(s1
.shape(), Shape
)
992 self
.assertEqual(repr(s1
), "(rst sync)")
994 def test_wrong_name_comb(self
):
995 with self
.assertRaisesRegex(ValueError,
996 r
"^Domain 'comb' does not have a reset$"):
1000 class MockUserValue(UserValue
):
1001 def __init__(self
, lowered
):
1003 self
.lower_count
= 0
1004 self
.lowered
= lowered
1007 self
.lower_count
+= 1
1011 class UserValueTestCase(FHDLTestCase
):
1012 def test_shape(self
):
1013 with warnings
.catch_warnings():
1014 warnings
.filterwarnings(action
="ignore", category
=DeprecationWarning)
1015 uv
= MockUserValue(1)
1016 self
.assertEqual(uv
.shape(), unsigned(1))
1017 self
.assertIsInstance(uv
.shape(), Shape
)
1019 self
.assertEqual(uv
.shape(), unsigned(1))
1020 self
.assertEqual(uv
.lower_count
, 1)
1022 def test_lower_to_user_value(self
):
1023 with warnings
.catch_warnings():
1024 warnings
.filterwarnings(action
="ignore", category
=DeprecationWarning)
1025 uv
= MockUserValue(MockUserValue(1))
1026 self
.assertEqual(uv
.shape(), unsigned(1))
1027 self
.assertIsInstance(uv
.shape(), Shape
)
1028 uv
.lowered
= MockUserValue(2)
1029 self
.assertEqual(uv
.shape(), unsigned(1))
1030 self
.assertEqual(uv
.lower_count
, 1)
1033 class MockValueCastableChanges(ValueCastable
):
1034 def __init__(self
, width
=0):
1037 @ValueCastable.lowermethod
1039 return Signal(self
.width
)
1042 class MockValueCastableNotDecorated(ValueCastable
):
1050 class MockValueCastableNoOverride(ValueCastable
):
1055 class ValueCastableTestCase(FHDLTestCase
):
1056 def test_not_decorated(self
):
1057 with self
.assertRaisesRegex(TypeError,
1058 r
"^Class 'MockValueCastableNotDecorated' deriving from `ValueCastable` must decorate the `as_value` "
1059 r
"method with the `ValueCastable.lowermethod` decorator$"):
1060 vc
= MockValueCastableNotDecorated()
1062 def test_no_override(self
):
1063 with self
.assertRaisesRegex(TypeError,
1064 r
"^Class 'MockValueCastableNoOverride' deriving from `ValueCastable` must override the `as_value` "
1066 vc
= MockValueCastableNoOverride()
1068 def test_memoized(self
):
1069 vc
= MockValueCastableChanges(1)
1070 sig1
= vc
.as_value()
1072 sig2
= vc
.as_value()
1073 self
.assertIs(sig1
, sig2
)
1075 sig3
= Value
.cast(vc
)
1076 self
.assertIs(sig1
, sig3
)
1079 class SampleTestCase(FHDLTestCase
):
1080 def test_const(self
):
1081 s
= Sample(1, 1, "sync")
1082 self
.assertEqual(s
.shape(), unsigned(1))
1084 def test_signal(self
):
1085 s1
= Sample(Signal(2), 1, "sync")
1086 self
.assertEqual(s1
.shape(), unsigned(2))
1087 s2
= Sample(ClockSignal(), 1, "sync")
1088 s3
= Sample(ResetSignal(), 1, "sync")
1090 def test_wrong_value_operator(self
):
1091 with self
.assertRaisesRegex(TypeError,
1092 (r
"^Sampled value must be a signal or a constant, not "
1093 r
"\(\+ \(sig \$signal\) \(const 1'd1\)\)$")):
1094 Sample(Signal() + 1, 1, "sync")
1096 def test_wrong_clocks_neg(self
):
1097 with self
.assertRaisesRegex(ValueError,
1098 r
"^Cannot sample a value 1 cycles in the future$"):
1099 Sample(Signal(), -1, "sync")
1101 def test_wrong_domain(self
):
1102 with self
.assertRaisesRegex(TypeError,
1103 r
"^Domain name must be a string or None, not 0$"):
1104 Sample(Signal(), 1, 0)
1107 class InitialTestCase(FHDLTestCase
):
1108 def test_initial(self
):
1110 self
.assertEqual(i
.shape(), unsigned(1))
1113 class SwitchTestCase(FHDLTestCase
):
1114 def test_default_case(self
):
1115 s
= Switch(Const(0), {None: []})
1116 self
.assertEqual(s
.cases
, {(): []})
1118 def test_int_case(self
):
1119 s
= Switch(Const(0, 8), {10: []})
1120 self
.assertEqual(s
.cases
, {("00001010",): []})
1122 def test_int_neg_case(self
):
1123 s
= Switch(Const(0, 8), {-10: []})
1124 self
.assertEqual(s
.cases
, {("11110110",): []})
1126 def test_enum_case(self
):
1127 s
= Switch(Const(0, UnsignedEnum
), {UnsignedEnum
.FOO
: []})
1128 self
.assertEqual(s
.cases
, {("01",): []})
1130 def test_str_case(self
):
1131 s
= Switch(Const(0, 8), {"0000 11\t01": []})
1132 self
.assertEqual(s
.cases
, {("00001101",): []})
1134 def test_two_cases(self
):
1135 s
= Switch(Const(0, 8), {("00001111", 123): []})
1136 self
.assertEqual(s
.cases
, {("00001111", "01111011"): []})