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
, 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)
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))")
553 self
.assertEqual(repr(v
), "(b (const 1'd0))")
554 self
.assertEqual(v
.shape(), unsigned(1))
557 v
= Const(0b101).any()
558 self
.assertEqual(repr(v
), "(r| (const 3'd5))")
561 v
= Const(0b101).all()
562 self
.assertEqual(repr(v
), "(r& (const 3'd5))")
565 v
= Const(0b101).xor()
566 self
.assertEqual(repr(v
), "(r^ (const 3'd5))")
568 def test_matches(self
):
570 self
.assertRepr(s
.matches(), "(const 1'd0)")
571 self
.assertRepr(s
.matches(1), """
572 (== (sig s) (const 1'd1))
574 self
.assertRepr(s
.matches(0, 1), """
575 (r| (cat (== (sig s) (const 1'd0)) (== (sig s) (const 1'd1))))
577 self
.assertRepr(s
.matches("10--"), """
578 (== (& (sig s) (const 4'd12)) (const 4'd8))
580 self
.assertRepr(s
.matches("1 0--"), """
581 (== (& (sig s) (const 4'd12)) (const 4'd8))
584 def test_matches_enum(self
):
585 s
= Signal(SignedEnum
)
586 self
.assertRepr(s
.matches(SignedEnum
.FOO
), """
587 (== (sig s) (const 1'sd-1))
590 def test_matches_width_wrong(self
):
592 with self
.assertRaisesRegex(SyntaxError,
593 r
"^Match pattern '--' must have the same width as match value \(which is 4\)$"):
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$")):
600 def test_matches_bits_wrong(self
):
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$")):
607 def test_matches_pattern_wrong(self
):
609 with self
.assertRaisesRegex(SyntaxError,
610 r
"^Match pattern must be an integer, a string, or an enumeration, not 1\.0$"):
614 with self
.assertRaises(TypeError):
615 hash(Const(0) + Const(0))
618 class SliceTestCase(FHDLTestCase
):
619 def test_shape(self
):
621 self
.assertEqual(s1
.shape(), unsigned(1))
622 self
.assertIsInstance(s1
.shape(), Shape
)
624 self
.assertEqual(s2
.shape(), unsigned(2))
626 def test_start_end_negative(self
):
629 self
.assertEqual((s1
.start
, s1
.stop
), (0, 7))
630 s1
= Slice(c
, -4, -1)
631 self
.assertEqual((s1
.start
, s1
.stop
), (4, 7))
633 def test_start_end_wrong(self
):
634 with self
.assertRaisesRegex(TypeError,
635 r
"^Slice start must be an integer, not 'x'$"):
637 with self
.assertRaisesRegex(TypeError,
638 r
"^Slice stop must be an integer, not 'x'$"):
641 def test_start_end_out_of_range(self
):
643 with self
.assertRaisesRegex(IndexError,
644 r
"^Cannot start slice 10 bits into 8-bit value$"):
646 with self
.assertRaisesRegex(IndexError,
647 r
"^Cannot stop slice 12 bits into 8-bit value$"):
649 with self
.assertRaisesRegex(IndexError,
650 r
"^Slice start 4 must be less than slice stop 2$"):
655 self
.assertEqual(repr(s1
), "(slice (const 4'd10) 2:3)")
658 class BitSelectTestCase(FHDLTestCase
):
661 self
.s
= Signal(range(self
.c
.width
))
663 def test_shape(self
):
664 s1
= self
.c
.bit_select(self
.s
, 2)
665 self
.assertIsInstance(s1
, Part
)
666 self
.assertEqual(s1
.shape(), unsigned(2))
667 self
.assertIsInstance(s1
.shape(), Shape
)
668 s2
= self
.c
.bit_select(self
.s
, 0)
669 self
.assertIsInstance(s2
, Part
)
670 self
.assertEqual(s2
.shape(), unsigned(0))
672 def test_stride(self
):
673 s1
= self
.c
.bit_select(self
.s
, 2)
674 self
.assertIsInstance(s1
, Part
)
675 self
.assertEqual(s1
.stride
, 1)
677 def test_const(self
):
678 s1
= self
.c
.bit_select(1, 2)
679 self
.assertIsInstance(s1
, Slice
)
680 self
.assertRepr(s1
, """(slice (const 8'd0) 1:3)""")
682 def test_width_wrong(self
):
683 with self
.assertRaises(TypeError):
684 self
.c
.bit_select(self
.s
, -1)
687 s
= self
.c
.bit_select(self
.s
, 2)
688 self
.assertEqual(repr(s
), "(part (const 8'd0) (sig s) 2 1)")
691 class WordSelectTestCase(FHDLTestCase
):
694 self
.s
= Signal(range(self
.c
.width
))
696 def test_shape(self
):
697 s1
= self
.c
.word_select(self
.s
, 2)
698 self
.assertIsInstance(s1
, Part
)
699 self
.assertEqual(s1
.shape(), unsigned(2))
700 self
.assertIsInstance(s1
.shape(), Shape
)
702 def test_stride(self
):
703 s1
= self
.c
.word_select(self
.s
, 2)
704 self
.assertIsInstance(s1
, Part
)
705 self
.assertEqual(s1
.stride
, 2)
707 def test_const(self
):
708 s1
= self
.c
.word_select(1, 2)
709 self
.assertIsInstance(s1
, Slice
)
710 self
.assertRepr(s1
, """(slice (const 8'd0) 2:4)""")
712 def test_width_wrong(self
):
713 with self
.assertRaises(TypeError):
714 self
.c
.word_select(self
.s
, 0)
715 with self
.assertRaises(TypeError):
716 self
.c
.word_select(self
.s
, -1)
719 s
= self
.c
.word_select(self
.s
, 2)
720 self
.assertEqual(repr(s
), "(part (const 8'd0) (sig s) 2 2)")
723 class CatTestCase(FHDLTestCase
):
724 def test_shape(self
):
726 self
.assertEqual(c0
.shape(), unsigned(0))
727 self
.assertIsInstance(c0
.shape(), Shape
)
729 self
.assertEqual(c1
.shape(), unsigned(4))
730 c2
= Cat(Const(10), Const(1))
731 self
.assertEqual(c2
.shape(), unsigned(5))
732 c3
= Cat(Const(10), Const(1), Const(0))
733 self
.assertEqual(c3
.shape(), unsigned(6))
736 c1
= Cat(Const(10), Const(1))
737 self
.assertEqual(repr(c1
), "(cat (const 4'd10) (const 1'd1))")
740 class ReplTestCase(FHDLTestCase
):
741 def test_shape(self
):
742 s1
= Repl(Const(10), 3)
743 self
.assertEqual(s1
.shape(), unsigned(12))
744 self
.assertIsInstance(s1
.shape(), Shape
)
745 s2
= Repl(Const(10), 0)
746 self
.assertEqual(s2
.shape(), unsigned(0))
748 def test_count_wrong(self
):
749 with self
.assertRaises(TypeError):
751 with self
.assertRaises(TypeError):
752 Repl(Const(10), "str")
755 s
= Repl(Const(10), 3)
756 self
.assertEqual(repr(s
), "(repl (const 4'd10) 3)")
759 class ArrayTestCase(FHDLTestCase
):
760 def test_acts_like_array(self
):
762 self
.assertSequenceEqual(a
, [1,2,3])
763 self
.assertEqual(a
[1], 2)
765 self
.assertSequenceEqual(a
, [1,4,3])
767 self
.assertSequenceEqual(a
, [1,3])
769 self
.assertSequenceEqual(a
, [1,2,3])
771 def test_becomes_immutable(self
):
773 s1
= Signal(range(len(a
)))
774 s2
= Signal(range(len(a
)))
777 with self
.assertRaisesRegex(ValueError,
778 r
"^Array can no longer be mutated after it was indexed with a value at "):
780 with self
.assertRaisesRegex(ValueError,
781 r
"^Array can no longer be mutated after it was indexed with a value at "):
783 with self
.assertRaisesRegex(ValueError,
784 r
"^Array can no longer be mutated after it was indexed with a value at "):
789 self
.assertEqual(repr(a
), "(array mutable [1, 2, 3])")
790 s
= Signal(range(len(a
)))
792 self
.assertEqual(repr(a
), "(array [1, 2, 3])")
795 class ArrayProxyTestCase(FHDLTestCase
):
796 def test_index_shape(self
):
797 m
= Array(Array(x
* y
for y
in range(1, 4)) for x
in range(1, 4))
801 self
.assertEqual(v
.shape(), unsigned(4))
803 def test_attr_shape(self
):
804 from collections
import namedtuple
805 pair
= namedtuple("pair", ("p", "n"))
806 a
= Array(pair(i
, -i
) for i
in range(10))
807 s
= Signal(range(len(a
)))
809 self
.assertEqual(v
.p
.shape(), unsigned(4))
810 self
.assertEqual(v
.n
.shape(), signed(5))
812 def test_attr_shape_signed(self
):
813 # [unsigned(1), unsigned(1)] → unsigned(1)
816 self
.assertEqual(v1
.shape(), unsigned(1))
817 # [signed(1), signed(1)] → signed(1)
820 self
.assertEqual(v2
.shape(), signed(1))
821 # [unsigned(1), signed(2)] → signed(2)
824 self
.assertEqual(v3
.shape(), signed(2))
825 # [unsigned(1), signed(1)] → signed(2); 1st operand padded with sign bit!
828 self
.assertEqual(v4
.shape(), signed(2))
829 # [unsigned(2), signed(1)] → signed(3); 1st operand padded with sign bit!
832 self
.assertEqual(v5
.shape(), signed(2))
838 self
.assertEqual(repr(v
), "(proxy (array [1, 2, 3]) (sig s))")
841 class SignalTestCase(FHDLTestCase
):
842 def test_shape(self
):
844 self
.assertEqual(s1
.shape(), unsigned(1))
845 self
.assertIsInstance(s1
.shape(), Shape
)
847 self
.assertEqual(s2
.shape(), unsigned(2))
848 s3
= Signal(unsigned(2))
849 self
.assertEqual(s3
.shape(), unsigned(2))
850 s4
= Signal(signed(2))
851 self
.assertEqual(s4
.shape(), signed(2))
853 self
.assertEqual(s5
.shape(), unsigned(0))
854 s6
= Signal(range(16))
855 self
.assertEqual(s6
.shape(), unsigned(4))
856 s7
= Signal(range(4, 16))
857 self
.assertEqual(s7
.shape(), unsigned(4))
858 s8
= Signal(range(-4, 16))
859 self
.assertEqual(s8
.shape(), signed(5))
860 s9
= Signal(range(-20, 16))
861 self
.assertEqual(s9
.shape(), signed(6))
862 s10
= Signal(range(0))
863 self
.assertEqual(s10
.shape(), unsigned(0))
864 s11
= Signal(range(1))
865 self
.assertEqual(s11
.shape(), unsigned(1))
867 def test_shape_wrong(self
):
868 with self
.assertRaisesRegex(TypeError,
869 r
"^Width must be a non-negative integer, not -10$"):
874 self
.assertEqual(s1
.name
, "s1")
875 s2
= Signal(name
="sig")
876 self
.assertEqual(s2
.name
, "sig")
878 def test_reset(self
):
879 s1
= Signal(4, reset
=0b111, reset_less
=True)
880 self
.assertEqual(s1
.reset
, 0b111)
881 self
.assertEqual(s1
.reset_less
, True)
883 def test_reset_enum(self
):
884 s1
= Signal(2, reset
=UnsignedEnum
.BAR
)
885 self
.assertEqual(s1
.reset
, 2)
886 with self
.assertRaisesRegex(TypeError,
887 r
"^Reset value has to be an int or an integral Enum$"
889 Signal(1, reset
=StringEnum
.FOO
)
891 def test_reset_narrow(self
):
892 with self
.assertWarnsRegex(SyntaxWarning,
893 r
"^Reset value 8 requires 4 bits to represent, but the signal only has 3 bits$"):
895 with self
.assertWarnsRegex(SyntaxWarning,
896 r
"^Reset value 4 requires 4 bits to represent, but the signal only has 3 bits$"):
897 Signal(signed(3), reset
=4)
898 with self
.assertWarnsRegex(SyntaxWarning,
899 r
"^Reset value -5 requires 4 bits to represent, but the signal only has 3 bits$"):
900 Signal(signed(3), reset
=-5)
902 def test_attrs(self
):
904 self
.assertEqual(s1
.attrs
, {})
905 s2
= Signal(attrs
={"no_retiming": True})
906 self
.assertEqual(s2
.attrs
, {"no_retiming": True})
910 self
.assertEqual(repr(s1
), "(sig s1)")
913 s1
= Signal
.like(Signal(4))
914 self
.assertEqual(s1
.shape(), unsigned(4))
915 s2
= Signal
.like(Signal(range(-15, 1)))
916 self
.assertEqual(s2
.shape(), signed(5))
917 s3
= Signal
.like(Signal(4, reset
=0b111, reset_less
=True))
918 self
.assertEqual(s3
.reset
, 0b111)
919 self
.assertEqual(s3
.reset_less
, True)
920 s4
= Signal
.like(Signal(attrs
={"no_retiming": True}))
921 self
.assertEqual(s4
.attrs
, {"no_retiming": True})
922 s5
= Signal
.like(Signal(decoder
=str))
923 self
.assertEqual(s5
.decoder
, str)
925 self
.assertEqual(s6
.shape(), unsigned(4))
926 s7
= [Signal
.like(Signal(4))][0]
927 self
.assertEqual(s7
.name
, "$like")
928 s8
= Signal
.like(s1
, name_suffix
="_ff")
929 self
.assertEqual(s8
.name
, "s1_ff")
931 def test_decoder(self
):
935 s
= Signal(decoder
=Color
)
936 self
.assertEqual(s
.decoder(1), "RED/1")
937 self
.assertEqual(s
.decoder(3), "3")
940 s1
= Signal(UnsignedEnum
)
941 self
.assertEqual(s1
.shape(), unsigned(2))
942 s2
= Signal(SignedEnum
)
943 self
.assertEqual(s2
.shape(), signed(2))
944 self
.assertEqual(s2
.decoder(SignedEnum
.FOO
), "FOO/-1")
947 class ClockSignalTestCase(FHDLTestCase
):
948 def test_domain(self
):
950 self
.assertEqual(s1
.domain
, "sync")
951 s2
= ClockSignal("pix")
952 self
.assertEqual(s2
.domain
, "pix")
954 with self
.assertRaisesRegex(TypeError,
955 r
"^Clock domain name must be a string, not 1$"):
958 def test_shape(self
):
960 self
.assertEqual(s1
.shape(), unsigned(1))
961 self
.assertIsInstance(s1
.shape(), Shape
)
965 self
.assertEqual(repr(s1
), "(clk sync)")
967 def test_wrong_name_comb(self
):
968 with self
.assertRaisesRegex(ValueError,
969 r
"^Domain 'comb' does not have a clock$"):
973 class ResetSignalTestCase(FHDLTestCase
):
974 def test_domain(self
):
976 self
.assertEqual(s1
.domain
, "sync")
977 s2
= ResetSignal("pix")
978 self
.assertEqual(s2
.domain
, "pix")
980 with self
.assertRaisesRegex(TypeError,
981 r
"^Clock domain name must be a string, not 1$"):
984 def test_shape(self
):
986 self
.assertEqual(s1
.shape(), unsigned(1))
987 self
.assertIsInstance(s1
.shape(), Shape
)
991 self
.assertEqual(repr(s1
), "(rst sync)")
993 def test_wrong_name_comb(self
):
994 with self
.assertRaisesRegex(ValueError,
995 r
"^Domain 'comb' does not have a reset$"):
999 class MockUserValue(UserValue
):
1000 def __init__(self
, lowered
):
1002 self
.lower_count
= 0
1003 self
.lowered
= lowered
1006 self
.lower_count
+= 1
1010 class UserValueTestCase(FHDLTestCase
):
1011 def test_shape(self
):
1012 with warnings
.catch_warnings():
1013 warnings
.filterwarnings(action
="ignore", category
=DeprecationWarning)
1014 uv
= MockUserValue(1)
1015 self
.assertEqual(uv
.shape(), unsigned(1))
1016 self
.assertIsInstance(uv
.shape(), Shape
)
1018 self
.assertEqual(uv
.shape(), unsigned(1))
1019 self
.assertEqual(uv
.lower_count
, 1)
1021 def test_lower_to_user_value(self
):
1022 with warnings
.catch_warnings():
1023 warnings
.filterwarnings(action
="ignore", category
=DeprecationWarning)
1024 uv
= MockUserValue(MockUserValue(1))
1025 self
.assertEqual(uv
.shape(), unsigned(1))
1026 self
.assertIsInstance(uv
.shape(), Shape
)
1027 uv
.lowered
= MockUserValue(2)
1028 self
.assertEqual(uv
.shape(), unsigned(1))
1029 self
.assertEqual(uv
.lower_count
, 1)
1032 class MockValueCastableChanges(ValueCastable
):
1033 def __init__(self
, width
=0):
1036 @ValueCastable.lowermethod
1038 return Signal(self
.width
)
1041 class MockValueCastableNotDecorated(ValueCastable
):
1049 class MockValueCastableNoOverride(ValueCastable
):
1054 class ValueCastableTestCase(FHDLTestCase
):
1055 def test_not_decorated(self
):
1056 with self
.assertRaisesRegex(TypeError,
1057 r
"^Class 'MockValueCastableNotDecorated' deriving from `ValueCastable` must decorate the `as_value` "
1058 r
"method with the `ValueCastable.lowermethod` decorator$"):
1059 vc
= MockValueCastableNotDecorated()
1061 def test_no_override(self
):
1062 with self
.assertRaisesRegex(TypeError,
1063 r
"^Class 'MockValueCastableNoOverride' deriving from `ValueCastable` must override the `as_value` "
1065 vc
= MockValueCastableNoOverride()
1067 def test_memoized(self
):
1068 vc
= MockValueCastableChanges(1)
1069 sig1
= vc
.as_value()
1071 sig2
= vc
.as_value()
1072 self
.assertIs(sig1
, sig2
)
1074 sig3
= Value
.cast(vc
)
1075 self
.assertIs(sig1
, sig3
)
1078 class SampleTestCase(FHDLTestCase
):
1079 def test_const(self
):
1080 s
= Sample(1, 1, "sync")
1081 self
.assertEqual(s
.shape(), unsigned(1))
1083 def test_signal(self
):
1084 s1
= Sample(Signal(2), 1, "sync")
1085 self
.assertEqual(s1
.shape(), unsigned(2))
1086 s2
= Sample(ClockSignal(), 1, "sync")
1087 s3
= Sample(ResetSignal(), 1, "sync")
1089 def test_wrong_value_operator(self
):
1090 with self
.assertRaisesRegex(TypeError,
1091 (r
"^Sampled value must be a signal or a constant, not "
1092 r
"\(\+ \(sig \$signal\) \(const 1'd1\)\)$")):
1093 Sample(Signal() + 1, 1, "sync")
1095 def test_wrong_clocks_neg(self
):
1096 with self
.assertRaisesRegex(ValueError,
1097 r
"^Cannot sample a value 1 cycles in the future$"):
1098 Sample(Signal(), -1, "sync")
1100 def test_wrong_domain(self
):
1101 with self
.assertRaisesRegex(TypeError,
1102 r
"^Domain name must be a string or None, not 0$"):
1103 Sample(Signal(), 1, 0)
1106 class InitialTestCase(FHDLTestCase
):
1107 def test_initial(self
):
1109 self
.assertEqual(i
.shape(), unsigned(1))
1112 class SwitchTestCase(FHDLTestCase
):
1113 def test_default_case(self
):
1114 s
= Switch(Const(0), {None: []})
1115 self
.assertEqual(s
.cases
, {(): []})
1117 def test_int_case(self
):
1118 s
= Switch(Const(0, 8), {10: []})
1119 self
.assertEqual(s
.cases
, {("00001010",): []})
1121 def test_int_neg_case(self
):
1122 s
= Switch(Const(0, 8), {-10: []})
1123 self
.assertEqual(s
.cases
, {("11110110",): []})
1125 def test_enum_case(self
):
1126 s
= Switch(Const(0, UnsignedEnum
), {UnsignedEnum
.FOO
: []})
1127 self
.assertEqual(s
.cases
, {("01",): []})
1129 def test_str_case(self
):
1130 s
= Switch(Const(0, 8), {"0000 11\t01": []})
1131 self
.assertEqual(s
.cases
, {("00001101",): []})
1133 def test_two_cases(self
):
1134 s
= Switch(Const(0, 8), {("00001111", 123): []})
1135 self
.assertEqual(s
.cases
, {("00001111", "01111011"): []})