hdl.ast: don't inherit Shape from NamedTuple.
[nmigen.git] / nmigen / test / test_hdl_ast.py
index 9022ec0942b66eafcf9316f6d86ec272974299c6..691408a0363e9a4d3bdea0f659b0888863420cec 100644 (file)
@@ -2,7 +2,7 @@ import warnings
 from enum import Enum
 
 from ..hdl.ast import *
-from .tools import *
+from .utils import *
 
 
 class UnsignedEnum(Enum):
@@ -39,6 +39,20 @@ class ShapeTestCase(FHDLTestCase):
                 msg="Width must be a non-negative integer, not -1"):
             Shape(-1)
 
+    def test_compare_wrong(self):
+        with self.assertRaises(TypeError,
+                msg="Shapes may be compared with other Shapes and (int, bool) tuples, not 'hi'"):
+            Shape(1, True) == 'hi'
+
+    def test_compare_tuple_wrong(self):
+        with self.assertRaises(TypeError,
+                msg="Shapes may be compared with other Shapes and (int, bool) tuples, not (2, 3)"):
+            Shape(1, True) == (2, 3)
+
+    def test_repr(self):
+        self.assertEqual(repr(Shape()), "unsigned(1)")
+        self.assertEqual(repr(Shape(2, True)), "signed(2)")
+
     def test_tuple(self):
         width, signed = Shape()
         self.assertEqual(width, 1)
@@ -56,6 +70,14 @@ class ShapeTestCase(FHDLTestCase):
         self.assertEqual(s1.width, 2)
         self.assertEqual(s1.signed, True)
 
+    def test_cast_shape(self):
+        s1 = Shape.cast(unsigned(1))
+        self.assertEqual(s1.width, 1)
+        self.assertEqual(s1.signed, False)
+        s2 = Shape.cast(signed(3))
+        self.assertEqual(s2.width, 3)
+        self.assertEqual(s2.signed, True)
+
     def test_cast_int(self):
         s1 = Shape.cast(2)
         self.assertEqual(s1.width, 2)
@@ -67,17 +89,18 @@ class ShapeTestCase(FHDLTestCase):
             Shape.cast(-1)
 
     def test_cast_tuple(self):
-        s1 = Shape.cast((1, False))
-        self.assertEqual(s1.width, 1)
-        self.assertEqual(s1.signed, False)
-        s2 = Shape.cast((3, True))
-        self.assertEqual(s2.width, 3)
-        self.assertEqual(s2.signed, True)
+        with warnings.catch_warnings():
+            warnings.filterwarnings(action="ignore", category=DeprecationWarning)
+            s1 = Shape.cast((1, True))
+            self.assertEqual(s1.width, 1)
+            self.assertEqual(s1.signed, True)
 
     def test_cast_tuple_wrong(self):
-        with self.assertRaises(TypeError,
-                msg="Width must be a non-negative integer, not -1"):
-            Shape.cast((-1, True))
+        with warnings.catch_warnings():
+            warnings.filterwarnings(action="ignore", category=DeprecationWarning)
+            with self.assertRaises(TypeError,
+                    msg="Width must be a non-negative integer, not -1"):
+                Shape.cast((-1, True))
 
     def test_cast_range(self):
         s1 = Shape.cast(range(0, 8))
@@ -134,10 +157,10 @@ class ValueTestCase(FHDLTestCase):
     def test_cast_enum(self):
         e1 = Value.cast(UnsignedEnum.FOO)
         self.assertIsInstance(e1, Const)
-        self.assertEqual(e1.shape(), (2, False))
+        self.assertEqual(e1.shape(), unsigned(2))
         e2 = Value.cast(SignedEnum.FOO)
         self.assertIsInstance(e2, Const)
-        self.assertEqual(e2.shape(), (2, True))
+        self.assertEqual(e2.shape(), signed(2))
 
     def test_cast_enum_wrong(self):
         with self.assertRaises(TypeError,
@@ -146,7 +169,7 @@ class ValueTestCase(FHDLTestCase):
 
     def test_bool(self):
         with self.assertRaises(TypeError,
-                msg="Attempted to convert nMigen value to boolean"):
+                msg="Attempted to convert nMigen value to Python boolean"):
             if Const(0):
                 pass
 
@@ -157,11 +180,11 @@ class ValueTestCase(FHDLTestCase):
         s1 = Const(10)[0]
         self.assertIsInstance(s1, Slice)
         self.assertEqual(s1.start, 0)
-        self.assertEqual(s1.end, 1)
+        self.assertEqual(s1.stop, 1)
         s2 = Const(10)[-1]
         self.assertIsInstance(s2, Slice)
         self.assertEqual(s2.start, 3)
-        self.assertEqual(s2.end, 4)
+        self.assertEqual(s2.stop, 4)
         with self.assertRaises(IndexError,
                 msg="Cannot index 5 bits into 4-bit value"):
             Const(10)[5]
@@ -170,41 +193,129 @@ class ValueTestCase(FHDLTestCase):
         s1 = Const(10)[1:3]
         self.assertIsInstance(s1, Slice)
         self.assertEqual(s1.start, 1)
-        self.assertEqual(s1.end, 3)
+        self.assertEqual(s1.stop, 3)
         s2 = Const(10)[1:-2]
         self.assertIsInstance(s2, Slice)
         self.assertEqual(s2.start, 1)
-        self.assertEqual(s2.end, 2)
+        self.assertEqual(s2.stop, 2)
         s3 = Const(31)[::2]
         self.assertIsInstance(s3, Cat)
         self.assertIsInstance(s3.parts[0], Slice)
         self.assertEqual(s3.parts[0].start, 0)
-        self.assertEqual(s3.parts[0].end, 1)
+        self.assertEqual(s3.parts[0].stop, 1)
         self.assertIsInstance(s3.parts[1], Slice)
         self.assertEqual(s3.parts[1].start, 2)
-        self.assertEqual(s3.parts[1].end, 3)
+        self.assertEqual(s3.parts[1].stop, 3)
         self.assertIsInstance(s3.parts[2], Slice)
         self.assertEqual(s3.parts[2].start, 4)
-        self.assertEqual(s3.parts[2].end, 5)
+        self.assertEqual(s3.parts[2].stop, 5)
 
     def test_getitem_wrong(self):
         with self.assertRaises(TypeError,
                 msg="Cannot index value with 'str'"):
             Const(31)["str"]
 
+    def test_shift_left(self):
+        self.assertRepr(Const(256, unsigned(9)).shift_left(0),
+                        "(cat (const 0'd0) (const 9'd256))")
+
+        self.assertRepr(Const(256, unsigned(9)).shift_left(1),
+                        "(cat (const 1'd0) (const 9'd256))")
+        self.assertRepr(Const(256, unsigned(9)).shift_left(5),
+                        "(cat (const 5'd0) (const 9'd256))")
+        self.assertRepr(Const(256, signed(9)).shift_left(1),
+                        "(s (cat (const 1'd0) (const 9'sd-256)))")
+        self.assertRepr(Const(256, signed(9)).shift_left(5),
+                        "(s (cat (const 5'd0) (const 9'sd-256)))")
+
+        self.assertRepr(Const(256, unsigned(9)).shift_left(-1),
+                        "(slice (const 9'd256) 1:9)")
+        self.assertRepr(Const(256, unsigned(9)).shift_left(-5),
+                        "(slice (const 9'd256) 5:9)")
+        self.assertRepr(Const(256, signed(9)).shift_left(-1),
+                        "(s (slice (const 9'sd-256) 1:9))")
+        self.assertRepr(Const(256, signed(9)).shift_left(-5),
+                        "(s (slice (const 9'sd-256) 5:9))")
+        self.assertRepr(Const(256, signed(9)).shift_left(-15),
+                        "(s (slice (const 9'sd-256) 9:9))")
+
+    def test_shift_left_wrong(self):
+        with self.assertRaises(TypeError,
+                msg="Shift amount must be an integer, not 'str'"):
+            Const(31).shift_left("str")
+
+    def test_shift_right(self):
+        self.assertRepr(Const(256, unsigned(9)).shift_right(0),
+                        "(slice (const 9'd256) 0:9)")
+
+        self.assertRepr(Const(256, unsigned(9)).shift_right(-1),
+                        "(cat (const 1'd0) (const 9'd256))")
+        self.assertRepr(Const(256, unsigned(9)).shift_right(-5),
+                        "(cat (const 5'd0) (const 9'd256))")
+        self.assertRepr(Const(256, signed(9)).shift_right(-1),
+                        "(s (cat (const 1'd0) (const 9'sd-256)))")
+        self.assertRepr(Const(256, signed(9)).shift_right(-5),
+                        "(s (cat (const 5'd0) (const 9'sd-256)))")
+
+        self.assertRepr(Const(256, unsigned(9)).shift_right(1),
+                        "(slice (const 9'd256) 1:9)")
+        self.assertRepr(Const(256, unsigned(9)).shift_right(5),
+                        "(slice (const 9'd256) 5:9)")
+        self.assertRepr(Const(256, signed(9)).shift_right(1),
+                        "(s (slice (const 9'sd-256) 1:9))")
+        self.assertRepr(Const(256, signed(9)).shift_right(5),
+                        "(s (slice (const 9'sd-256) 5:9))")
+        self.assertRepr(Const(256, signed(9)).shift_right(15),
+                        "(s (slice (const 9'sd-256) 9:9))")
+
+    def test_shift_right_wrong(self):
+        with self.assertRaises(TypeError,
+                msg="Shift amount must be an integer, not 'str'"):
+            Const(31).shift_left("str")
+
+    def test_rotate_left(self):
+        self.assertRepr(Const(256).rotate_left(1),
+                        "(cat (slice (const 9'd256) 8:9) (slice (const 9'd256) 0:8))")
+        self.assertRepr(Const(256).rotate_left(7),
+                        "(cat (slice (const 9'd256) 2:9) (slice (const 9'd256) 0:2))")
+        self.assertRepr(Const(256).rotate_left(-1),
+                        "(cat (slice (const 9'd256) 1:9) (slice (const 9'd256) 0:1))")
+        self.assertRepr(Const(256).rotate_left(-7),
+                        "(cat (slice (const 9'd256) 7:9) (slice (const 9'd256) 0:7))")
+
+    def test_rotate_left_wrong(self):
+        with self.assertRaises(TypeError,
+                msg="Rotate amount must be an integer, not 'str'"):
+            Const(31).rotate_left("str")
+
+    def test_rotate_right(self):
+        self.assertRepr(Const(256).rotate_right(1),
+                        "(cat (slice (const 9'd256) 1:9) (slice (const 9'd256) 0:1))")
+        self.assertRepr(Const(256).rotate_right(7),
+                        "(cat (slice (const 9'd256) 7:9) (slice (const 9'd256) 0:7))")
+        self.assertRepr(Const(256).rotate_right(-1),
+                        "(cat (slice (const 9'd256) 8:9) (slice (const 9'd256) 0:8))")
+        self.assertRepr(Const(256).rotate_right(-7),
+                        "(cat (slice (const 9'd256) 2:9) (slice (const 9'd256) 0:2))")
+
+    def test_rotate_right_wrong(self):
+        with self.assertRaises(TypeError,
+                msg="Rotate amount must be an integer, not 'str'"):
+            Const(31).rotate_right("str")
+
 
 class ConstTestCase(FHDLTestCase):
     def test_shape(self):
-        self.assertEqual(Const(0).shape(),   (1, False))
+        self.assertEqual(Const(0).shape(),   unsigned(1))
         self.assertIsInstance(Const(0).shape(), Shape)
-        self.assertEqual(Const(1).shape(),   (1, False))
-        self.assertEqual(Const(10).shape(),  (4, False))
-        self.assertEqual(Const(-10).shape(), (5, True))
+        self.assertEqual(Const(1).shape(),   unsigned(1))
+        self.assertEqual(Const(10).shape(),  unsigned(4))
+        self.assertEqual(Const(-10).shape(), signed(5))
 
-        self.assertEqual(Const(1, 4).shape(),          (4, False))
-        self.assertEqual(Const(-1, 4).shape(),         (4, True))
-        self.assertEqual(Const(1, (4, True)).shape(),  (4, True))
-        self.assertEqual(Const(0, (0, False)).shape(), (0, False))
+        self.assertEqual(Const(1, 4).shape(),          unsigned(4))
+        self.assertEqual(Const(-1, 4).shape(),         signed(4))
+        self.assertEqual(Const(1, signed(4)).shape(),  signed(4))
+        self.assertEqual(Const(0, unsigned(0)).shape(), unsigned(0))
 
     def test_shape_wrong(self):
         with self.assertRaises(TypeError,
@@ -212,7 +323,7 @@ class ConstTestCase(FHDLTestCase):
             Const(1, -1)
 
     def test_normalization(self):
-        self.assertEqual(Const(0b10110, (5, True)).value, -10)
+        self.assertEqual(Const(0b10110, signed(5)).value, -10)
 
     def test_value(self):
         self.assertEqual(Const(10).value, 10)
@@ -230,184 +341,206 @@ class OperatorTestCase(FHDLTestCase):
     def test_bool(self):
         v = Const(0, 4).bool()
         self.assertEqual(repr(v), "(b (const 4'd0))")
-        self.assertEqual(v.shape(), (1, False))
+        self.assertEqual(v.shape(), unsigned(1))
 
     def test_invert(self):
         v = ~Const(0, 4)
         self.assertEqual(repr(v), "(~ (const 4'd0))")
-        self.assertEqual(v.shape(), (4, False))
+        self.assertEqual(v.shape(), unsigned(4))
+
+    def test_as_unsigned(self):
+        v = Const(-1, signed(4)).as_unsigned()
+        self.assertEqual(repr(v), "(u (const 4'sd-1))")
+        self.assertEqual(v.shape(), unsigned(4))
+
+    def test_as_signed(self):
+        v = Const(1, unsigned(4)).as_signed()
+        self.assertEqual(repr(v), "(s (const 4'd1))")
+        self.assertEqual(v.shape(), signed(4))
 
     def test_neg(self):
-        v1 = -Const(0, (4, False))
+        v1 = -Const(0, unsigned(4))
         self.assertEqual(repr(v1), "(- (const 4'd0))")
-        self.assertEqual(v1.shape(), (5, True))
-        v2 = -Const(0, (4, True))
+        self.assertEqual(v1.shape(), signed(5))
+        v2 = -Const(0, signed(4))
         self.assertEqual(repr(v2), "(- (const 4'sd0))")
-        self.assertEqual(v2.shape(), (4, True))
+        self.assertEqual(v2.shape(), signed(5))
 
     def test_add(self):
-        v1 = Const(0, (4, False)) + Const(0, (6, False))
+        v1 = Const(0, unsigned(4)) + Const(0, unsigned(6))
         self.assertEqual(repr(v1), "(+ (const 4'd0) (const 6'd0))")
-        self.assertEqual(v1.shape(), (7, False))
-        v2 = Const(0, (4, True)) + Const(0, (6, True))
-        self.assertEqual(v2.shape(), (7, True))
-        v3 = Const(0, (4, True)) + Const(0, (4, False))
-        self.assertEqual(v3.shape(), (6, True))
-        v4 = Const(0, (4, False)) + Const(0, (4, True))
-        self.assertEqual(v4.shape(), (6, True))
+        self.assertEqual(v1.shape(), unsigned(7))
+        v2 = Const(0, signed(4)) + Const(0, signed(6))
+        self.assertEqual(v2.shape(), signed(7))
+        v3 = Const(0, signed(4)) + Const(0, unsigned(4))
+        self.assertEqual(v3.shape(), signed(6))
+        v4 = Const(0, unsigned(4)) + Const(0, signed(4))
+        self.assertEqual(v4.shape(), signed(6))
         v5 = 10 + Const(0, 4)
-        self.assertEqual(v5.shape(), (5, False))
+        self.assertEqual(v5.shape(), unsigned(5))
 
     def test_sub(self):
-        v1 = Const(0, (4, False)) - Const(0, (6, False))
+        v1 = Const(0, unsigned(4)) - Const(0, unsigned(6))
         self.assertEqual(repr(v1), "(- (const 4'd0) (const 6'd0))")
-        self.assertEqual(v1.shape(), (7, False))
-        v2 = Const(0, (4, True)) - Const(0, (6, True))
-        self.assertEqual(v2.shape(), (7, True))
-        v3 = Const(0, (4, True)) - Const(0, (4, False))
-        self.assertEqual(v3.shape(), (6, True))
-        v4 = Const(0, (4, False)) - Const(0, (4, True))
-        self.assertEqual(v4.shape(), (6, True))
+        self.assertEqual(v1.shape(), unsigned(7))
+        v2 = Const(0, signed(4)) - Const(0, signed(6))
+        self.assertEqual(v2.shape(), signed(7))
+        v3 = Const(0, signed(4)) - Const(0, unsigned(4))
+        self.assertEqual(v3.shape(), signed(6))
+        v4 = Const(0, unsigned(4)) - Const(0, signed(4))
+        self.assertEqual(v4.shape(), signed(6))
         v5 = 10 - Const(0, 4)
-        self.assertEqual(v5.shape(), (5, False))
+        self.assertEqual(v5.shape(), unsigned(5))
 
     def test_mul(self):
-        v1 = Const(0, (4, False)) * Const(0, (6, False))
+        v1 = Const(0, unsigned(4)) * Const(0, unsigned(6))
         self.assertEqual(repr(v1), "(* (const 4'd0) (const 6'd0))")
-        self.assertEqual(v1.shape(), (10, False))
-        v2 = Const(0, (4, True)) * Const(0, (6, True))
-        self.assertEqual(v2.shape(), (10, True))
-        v3 = Const(0, (4, True)) * Const(0, (4, False))
-        self.assertEqual(v3.shape(), (8, True))
+        self.assertEqual(v1.shape(), unsigned(10))
+        v2 = Const(0, signed(4)) * Const(0, signed(6))
+        self.assertEqual(v2.shape(), signed(10))
+        v3 = Const(0, signed(4)) * Const(0, unsigned(4))
+        self.assertEqual(v3.shape(), signed(8))
         v5 = 10 * Const(0, 4)
-        self.assertEqual(v5.shape(), (8, False))
+        self.assertEqual(v5.shape(), unsigned(8))
 
     def test_mod(self):
-        v1 = Const(0, (4, False)) % Const(0, (6, False))
+        v1 = Const(0, unsigned(4)) % Const(0, unsigned(6))
         self.assertEqual(repr(v1), "(% (const 4'd0) (const 6'd0))")
-        self.assertEqual(v1.shape(), (4, False))
-        v3 = Const(0, (4, True)) % Const(0, (4, False))
-        self.assertEqual(v3.shape(), (4, True))
+        self.assertEqual(v1.shape(), unsigned(4))
+        v3 = Const(0, signed(4)) % Const(0, unsigned(4))
+        self.assertEqual(v3.shape(), signed(4))
         v5 = 10 % Const(0, 4)
-        self.assertEqual(v5.shape(), (4, False))
+        self.assertEqual(v5.shape(), unsigned(4))
 
     def test_mod_wrong(self):
         with self.assertRaises(NotImplementedError,
                 msg="Division by a signed value is not supported"):
-            Const(0, (4, True)) % Const(0, (6, True))
+            Const(0, signed(4)) % Const(0, signed(6))
 
     def test_floordiv(self):
-        v1 = Const(0, (4, False)) // Const(0, (6, False))
+        v1 = Const(0, unsigned(4)) // Const(0, unsigned(6))
         self.assertEqual(repr(v1), "(// (const 4'd0) (const 6'd0))")
-        self.assertEqual(v1.shape(), (4, False))
-        v3 = Const(0, (4, True)) // Const(0, (4, False))
-        self.assertEqual(v3.shape(), (4, True))
+        self.assertEqual(v1.shape(), unsigned(4))
+        v3 = Const(0, signed(4)) // Const(0, unsigned(4))
+        self.assertEqual(v3.shape(), signed(4))
         v5 = 10 // Const(0, 4)
-        self.assertEqual(v5.shape(), (4, False))
+        self.assertEqual(v5.shape(), unsigned(4))
 
     def test_floordiv_wrong(self):
         with self.assertRaises(NotImplementedError,
                 msg="Division by a signed value is not supported"):
-            Const(0, (4, True)) // Const(0, (6, True))
+            Const(0, signed(4)) // Const(0, signed(6))
 
     def test_and(self):
-        v1 = Const(0, (4, False)) & Const(0, (6, False))
+        v1 = Const(0, unsigned(4)) & Const(0, unsigned(6))
         self.assertEqual(repr(v1), "(& (const 4'd0) (const 6'd0))")
-        self.assertEqual(v1.shape(), (6, False))
-        v2 = Const(0, (4, True)) & Const(0, (6, True))
-        self.assertEqual(v2.shape(), (6, True))
-        v3 = Const(0, (4, True)) & Const(0, (4, False))
-        self.assertEqual(v3.shape(), (5, True))
-        v4 = Const(0, (4, False)) & Const(0, (4, True))
-        self.assertEqual(v4.shape(), (5, True))
+        self.assertEqual(v1.shape(), unsigned(6))
+        v2 = Const(0, signed(4)) & Const(0, signed(6))
+        self.assertEqual(v2.shape(), signed(6))
+        v3 = Const(0, signed(4)) & Const(0, unsigned(4))
+        self.assertEqual(v3.shape(), signed(5))
+        v4 = Const(0, unsigned(4)) & Const(0, signed(4))
+        self.assertEqual(v4.shape(), signed(5))
         v5 = 10 & Const(0, 4)
-        self.assertEqual(v5.shape(), (4, False))
+        self.assertEqual(v5.shape(), unsigned(4))
 
     def test_or(self):
-        v1 = Const(0, (4, False)) | Const(0, (6, False))
+        v1 = Const(0, unsigned(4)) | Const(0, unsigned(6))
         self.assertEqual(repr(v1), "(| (const 4'd0) (const 6'd0))")
-        self.assertEqual(v1.shape(), (6, False))
-        v2 = Const(0, (4, True)) | Const(0, (6, True))
-        self.assertEqual(v2.shape(), (6, True))
-        v3 = Const(0, (4, True)) | Const(0, (4, False))
-        self.assertEqual(v3.shape(), (5, True))
-        v4 = Const(0, (4, False)) | Const(0, (4, True))
-        self.assertEqual(v4.shape(), (5, True))
+        self.assertEqual(v1.shape(), unsigned(6))
+        v2 = Const(0, signed(4)) | Const(0, signed(6))
+        self.assertEqual(v2.shape(), signed(6))
+        v3 = Const(0, signed(4)) | Const(0, unsigned(4))
+        self.assertEqual(v3.shape(), signed(5))
+        v4 = Const(0, unsigned(4)) | Const(0, signed(4))
+        self.assertEqual(v4.shape(), signed(5))
         v5 = 10 | Const(0, 4)
-        self.assertEqual(v5.shape(), (4, False))
+        self.assertEqual(v5.shape(), unsigned(4))
 
     def test_xor(self):
-        v1 = Const(0, (4, False)) ^ Const(0, (6, False))
+        v1 = Const(0, unsigned(4)) ^ Const(0, unsigned(6))
         self.assertEqual(repr(v1), "(^ (const 4'd0) (const 6'd0))")
-        self.assertEqual(v1.shape(), (6, False))
-        v2 = Const(0, (4, True)) ^ Const(0, (6, True))
-        self.assertEqual(v2.shape(), (6, True))
-        v3 = Const(0, (4, True)) ^ Const(0, (4, False))
-        self.assertEqual(v3.shape(), (5, True))
-        v4 = Const(0, (4, False)) ^ Const(0, (4, True))
-        self.assertEqual(v4.shape(), (5, True))
+        self.assertEqual(v1.shape(), unsigned(6))
+        v2 = Const(0, signed(4)) ^ Const(0, signed(6))
+        self.assertEqual(v2.shape(), signed(6))
+        v3 = Const(0, signed(4)) ^ Const(0, unsigned(4))
+        self.assertEqual(v3.shape(), signed(5))
+        v4 = Const(0, unsigned(4)) ^ Const(0, signed(4))
+        self.assertEqual(v4.shape(), signed(5))
         v5 = 10 ^ Const(0, 4)
-        self.assertEqual(v5.shape(), (4, False))
+        self.assertEqual(v5.shape(), unsigned(4))
 
     def test_shl(self):
         v1 = Const(1, 4) << Const(4)
         self.assertEqual(repr(v1), "(<< (const 4'd1) (const 3'd4))")
-        self.assertEqual(v1.shape(), (11, False))
-        v2 = Const(1, 4) << Const(-3)
-        self.assertEqual(v2.shape(), (7, False))
+        self.assertEqual(v1.shape(), unsigned(11))
+
+    def test_shl_wrong(self):
+        with self.assertRaises(TypeError,
+                msg="Shift amount must be unsigned"):
+            1 << Const(0, signed(6))
+        with self.assertRaises(TypeError,
+                msg="Shift amount must be unsigned"):
+            Const(1, unsigned(4)) << -1
 
     def test_shr(self):
         v1 = Const(1, 4) >> Const(4)
         self.assertEqual(repr(v1), "(>> (const 4'd1) (const 3'd4))")
-        self.assertEqual(v1.shape(), (4, False))
-        v2 = Const(1, 4) >> Const(-3)
-        self.assertEqual(v2.shape(), (8, False))
+        self.assertEqual(v1.shape(), unsigned(4))
+
+    def test_shr_wrong(self):
+        with self.assertRaises(TypeError,
+                msg="Shift amount must be unsigned"):
+            1 << Const(0, signed(6))
+        with self.assertRaises(TypeError,
+                msg="Shift amount must be unsigned"):
+            Const(1, unsigned(4)) << -1
 
     def test_lt(self):
         v = Const(0, 4) < Const(0, 6)
         self.assertEqual(repr(v), "(< (const 4'd0) (const 6'd0))")
-        self.assertEqual(v.shape(), (1, False))
+        self.assertEqual(v.shape(), unsigned(1))
 
     def test_le(self):
         v = Const(0, 4) <= Const(0, 6)
         self.assertEqual(repr(v), "(<= (const 4'd0) (const 6'd0))")
-        self.assertEqual(v.shape(), (1, False))
+        self.assertEqual(v.shape(), unsigned(1))
 
     def test_gt(self):
         v = Const(0, 4) > Const(0, 6)
         self.assertEqual(repr(v), "(> (const 4'd0) (const 6'd0))")
-        self.assertEqual(v.shape(), (1, False))
+        self.assertEqual(v.shape(), unsigned(1))
 
     def test_ge(self):
         v = Const(0, 4) >= Const(0, 6)
         self.assertEqual(repr(v), "(>= (const 4'd0) (const 6'd0))")
-        self.assertEqual(v.shape(), (1, False))
+        self.assertEqual(v.shape(), unsigned(1))
 
     def test_eq(self):
         v = Const(0, 4) == Const(0, 6)
         self.assertEqual(repr(v), "(== (const 4'd0) (const 6'd0))")
-        self.assertEqual(v.shape(), (1, False))
+        self.assertEqual(v.shape(), unsigned(1))
 
     def test_ne(self):
         v = Const(0, 4) != Const(0, 6)
         self.assertEqual(repr(v), "(!= (const 4'd0) (const 6'd0))")
-        self.assertEqual(v.shape(), (1, False))
+        self.assertEqual(v.shape(), unsigned(1))
 
     def test_mux(self):
         s  = Const(0)
-        v1 = Mux(s, Const(0, (4, False)), Const(0, (6, False)))
+        v1 = Mux(s, Const(0, unsigned(4)), Const(0, unsigned(6)))
         self.assertEqual(repr(v1), "(m (const 1'd0) (const 4'd0) (const 6'd0))")
-        self.assertEqual(v1.shape(), (6, False))
-        v2 = Mux(s, Const(0, (4, True)), Const(0, (6, True)))
-        self.assertEqual(v2.shape(), (6, True))
-        v3 = Mux(s, Const(0, (4, True)), Const(0, (4, False)))
-        self.assertEqual(v3.shape(), (5, True))
-        v4 = Mux(s, Const(0, (4, False)), Const(0, (4, True)))
-        self.assertEqual(v4.shape(), (5, True))
+        self.assertEqual(v1.shape(), unsigned(6))
+        v2 = Mux(s, Const(0, signed(4)), Const(0, signed(6)))
+        self.assertEqual(v2.shape(), signed(6))
+        v3 = Mux(s, Const(0, signed(4)), Const(0, unsigned(4)))
+        self.assertEqual(v3.shape(), signed(5))
+        v4 = Mux(s, Const(0, unsigned(4)), Const(0, signed(4)))
+        self.assertEqual(v4.shape(), signed(5))
 
     def test_mux_wide(self):
         s = Const(0b100)
-        v = Mux(s, Const(0, (4, False)), Const(0, (6, False)))
+        v = Mux(s, Const(0, unsigned(4)), Const(0, unsigned(6)))
         self.assertEqual(repr(v), "(m (b (const 3'd4)) (const 4'd0) (const 6'd0))")
 
     def test_mux_bool(self):
@@ -417,7 +550,7 @@ class OperatorTestCase(FHDLTestCase):
     def test_bool(self):
         v = Const(0).bool()
         self.assertEqual(repr(v), "(b (const 1'd0))")
-        self.assertEqual(v.shape(), (1, False))
+        self.assertEqual(v.shape(), unsigned(1))
 
     def test_any(self):
         v = Const(0b101).any()
@@ -443,9 +576,12 @@ class OperatorTestCase(FHDLTestCase):
         self.assertRepr(s.matches("10--"), """
         (== (& (sig s) (const 4'd12)) (const 4'd8))
         """)
+        self.assertRepr(s.matches("1 0--"), """
+        (== (& (sig s) (const 4'd12)) (const 4'd8))
+        """)
 
     def test_matches_enum(self):
-        s = Signal.enum(SignedEnum)
+        s = Signal(SignedEnum)
         self.assertRepr(s.matches(SignedEnum.FOO), """
         (== (sig s) (const 1'sd-1))
         """)
@@ -463,7 +599,8 @@ class OperatorTestCase(FHDLTestCase):
     def test_matches_bits_wrong(self):
         s = Signal(4)
         with self.assertRaises(SyntaxError,
-                msg="Match pattern 'abc' must consist of 0, 1, and - (don't care) bits"):
+                msg="Match pattern 'abc' must consist of 0, 1, and - (don't care) bits, "
+                    "and may include whitespace"):
             s.matches("abc")
 
     def test_matches_pattern_wrong(self):
@@ -480,24 +617,24 @@ class OperatorTestCase(FHDLTestCase):
 class SliceTestCase(FHDLTestCase):
     def test_shape(self):
         s1 = Const(10)[2]
-        self.assertEqual(s1.shape(), (1, False))
+        self.assertEqual(s1.shape(), unsigned(1))
         self.assertIsInstance(s1.shape(), Shape)
         s2 = Const(-10)[0:2]
-        self.assertEqual(s2.shape(), (2, False))
+        self.assertEqual(s2.shape(), unsigned(2))
 
     def test_start_end_negative(self):
         c  = Const(0, 8)
         s1 = Slice(c, 0, -1)
-        self.assertEqual((s1.start, s1.end), (0, 7))
+        self.assertEqual((s1.start, s1.stop), (0, 7))
         s1 = Slice(c, -4, -1)
-        self.assertEqual((s1.start, s1.end), (4, 7))
+        self.assertEqual((s1.start, s1.stop), (4, 7))
 
     def test_start_end_wrong(self):
         with self.assertRaises(TypeError,
                 msg="Slice start must be an integer, not 'x'"):
             Slice(0, "x", 1)
         with self.assertRaises(TypeError,
-                msg="Slice end must be an integer, not 'x'"):
+                msg="Slice stop must be an integer, not 'x'"):
             Slice(0, 1, "x")
 
     def test_start_end_out_of_range(self):
@@ -506,10 +643,10 @@ class SliceTestCase(FHDLTestCase):
                 msg="Cannot start slice 10 bits into 8-bit value"):
             Slice(c, 10, 12)
         with self.assertRaises(IndexError,
-                msg="Cannot end slice 12 bits into 8-bit value"):
+                msg="Cannot stop slice 12 bits into 8-bit value"):
             Slice(c, 0, 12)
         with self.assertRaises(IndexError,
-                msg="Slice start 4 must be less than slice end 2"):
+                msg="Slice start 4 must be less than slice stop 2"):
             Slice(c, 4, 2)
 
     def test_repr(self):
@@ -520,19 +657,27 @@ class SliceTestCase(FHDLTestCase):
 class BitSelectTestCase(FHDLTestCase):
     def setUp(self):
         self.c = Const(0, 8)
-        self.s = Signal.range(self.c.width)
+        self.s = Signal(range(self.c.width))
 
     def test_shape(self):
         s1 = self.c.bit_select(self.s, 2)
-        self.assertEqual(s1.shape(), (2, False))
+        self.assertIsInstance(s1, Part)
+        self.assertEqual(s1.shape(), unsigned(2))
         self.assertIsInstance(s1.shape(), Shape)
         s2 = self.c.bit_select(self.s, 0)
-        self.assertEqual(s2.shape(), (0, False))
+        self.assertIsInstance(s2, Part)
+        self.assertEqual(s2.shape(), unsigned(0))
 
     def test_stride(self):
         s1 = self.c.bit_select(self.s, 2)
+        self.assertIsInstance(s1, Part)
         self.assertEqual(s1.stride, 1)
 
+    def test_const(self):
+        s1 = self.c.bit_select(1, 2)
+        self.assertIsInstance(s1, Slice)
+        self.assertRepr(s1, """(slice (const 8'd0) 1:3)""")
+
     def test_width_wrong(self):
         with self.assertRaises(TypeError):
             self.c.bit_select(self.s, -1)
@@ -545,17 +690,24 @@ class BitSelectTestCase(FHDLTestCase):
 class WordSelectTestCase(FHDLTestCase):
     def setUp(self):
         self.c = Const(0, 8)
-        self.s = Signal.range(self.c.width)
+        self.s = Signal(range(self.c.width))
 
     def test_shape(self):
         s1 = self.c.word_select(self.s, 2)
-        self.assertEqual(s1.shape(), (2, False))
+        self.assertIsInstance(s1, Part)
+        self.assertEqual(s1.shape(), unsigned(2))
         self.assertIsInstance(s1.shape(), Shape)
 
     def test_stride(self):
         s1 = self.c.word_select(self.s, 2)
+        self.assertIsInstance(s1, Part)
         self.assertEqual(s1.stride, 2)
 
+    def test_const(self):
+        s1 = self.c.word_select(1, 2)
+        self.assertIsInstance(s1, Slice)
+        self.assertRepr(s1, """(slice (const 8'd0) 2:4)""")
+
     def test_width_wrong(self):
         with self.assertRaises(TypeError):
             self.c.word_select(self.s, 0)
@@ -570,14 +722,14 @@ class WordSelectTestCase(FHDLTestCase):
 class CatTestCase(FHDLTestCase):
     def test_shape(self):
         c0 = Cat()
-        self.assertEqual(c0.shape(), (0, False))
+        self.assertEqual(c0.shape(), unsigned(0))
         self.assertIsInstance(c0.shape(), Shape)
         c1 = Cat(Const(10))
-        self.assertEqual(c1.shape(), (4, False))
+        self.assertEqual(c1.shape(), unsigned(4))
         c2 = Cat(Const(10), Const(1))
-        self.assertEqual(c2.shape(), (5, False))
+        self.assertEqual(c2.shape(), unsigned(5))
         c3 = Cat(Const(10), Const(1), Const(0))
-        self.assertEqual(c3.shape(), (6, False))
+        self.assertEqual(c3.shape(), unsigned(6))
 
     def test_repr(self):
         c1 = Cat(Const(10), Const(1))
@@ -587,10 +739,10 @@ class CatTestCase(FHDLTestCase):
 class ReplTestCase(FHDLTestCase):
     def test_shape(self):
         s1 = Repl(Const(10), 3)
-        self.assertEqual(s1.shape(), (12, False))
+        self.assertEqual(s1.shape(), unsigned(12))
         self.assertIsInstance(s1.shape(), Shape)
         s2 = Repl(Const(10), 0)
-        self.assertEqual(s2.shape(), (0, False))
+        self.assertEqual(s2.shape(), unsigned(0))
 
     def test_count_wrong(self):
         with self.assertRaises(TypeError):
@@ -617,24 +769,24 @@ class ArrayTestCase(FHDLTestCase):
 
     def test_becomes_immutable(self):
         a = Array([1,2,3])
-        s1 = Signal.range(len(a))
-        s2 = Signal.range(len(a))
+        s1 = Signal(range(len(a)))
+        s2 = Signal(range(len(a)))
         v1 = a[s1]
         v2 = a[s2]
         with self.assertRaisesRegex(ValueError,
-                regex=r"^Array can no longer be mutated after it was indexed with a value at "):
+                r"^Array can no longer be mutated after it was indexed with a value at "):
             a[1] = 2
         with self.assertRaisesRegex(ValueError,
-                regex=r"^Array can no longer be mutated after it was indexed with a value at "):
+                r"^Array can no longer be mutated after it was indexed with a value at "):
             del a[1]
         with self.assertRaisesRegex(ValueError,
-                regex=r"^Array can no longer be mutated after it was indexed with a value at "):
+                r"^Array can no longer be mutated after it was indexed with a value at "):
             a.insert(1, 2)
 
     def test_repr(self):
         a = Array([1,2,3])
         self.assertEqual(repr(a), "(array mutable [1, 2, 3])")
-        s = Signal.range(len(a))
+        s = Signal(range(len(a)))
         v = a[s]
         self.assertEqual(repr(a), "(array [1, 2, 3])")
 
@@ -642,23 +794,23 @@ class ArrayTestCase(FHDLTestCase):
 class ArrayProxyTestCase(FHDLTestCase):
     def test_index_shape(self):
         m = Array(Array(x * y for y in range(1, 4)) for x in range(1, 4))
-        a = Signal.range(3)
-        b = Signal.range(3)
+        a = Signal(range(3))
+        b = Signal(range(3))
         v = m[a][b]
-        self.assertEqual(v.shape(), (4, False))
+        self.assertEqual(v.shape(), unsigned(4))
 
     def test_attr_shape(self):
         from collections import namedtuple
         pair = namedtuple("pair", ("p", "n"))
         a = Array(pair(i, -i) for i in range(10))
-        s = Signal.range(len(a))
+        s = Signal(range(len(a)))
         v = a[s]
-        self.assertEqual(v.p.shape(), (4, False))
-        self.assertEqual(v.n.shape(), (6, True))
+        self.assertEqual(v.p.shape(), unsigned(4))
+        self.assertEqual(v.n.shape(), signed(6))
 
     def test_repr(self):
         a = Array([1, 2, 3])
-        s = Signal.range(3)
+        s = Signal(range(3))
         v = a[s]
         self.assertEqual(repr(v), "(proxy (array [1, 2, 3]) (sig s))")
 
@@ -666,60 +818,34 @@ class ArrayProxyTestCase(FHDLTestCase):
 class SignalTestCase(FHDLTestCase):
     def test_shape(self):
         s1 = Signal()
-        self.assertEqual(s1.shape(), (1, False))
+        self.assertEqual(s1.shape(), unsigned(1))
         self.assertIsInstance(s1.shape(), Shape)
         s2 = Signal(2)
-        self.assertEqual(s2.shape(), (2, False))
-        s3 = Signal((2, False))
-        self.assertEqual(s3.shape(), (2, False))
-        s4 = Signal((2, True))
-        self.assertEqual(s4.shape(), (2, True))
+        self.assertEqual(s2.shape(), unsigned(2))
+        s3 = Signal(unsigned(2))
+        self.assertEqual(s3.shape(), unsigned(2))
+        s4 = Signal(signed(2))
+        self.assertEqual(s4.shape(), signed(2))
         s5 = Signal(0)
-        self.assertEqual(s5.shape(), (0, False))
-        s6 = Signal.range(16)
-        self.assertEqual(s6.shape(), (4, False))
-        s7 = Signal.range(4, 16)
-        self.assertEqual(s7.shape(), (4, False))
-        s8 = Signal.range(-4, 16)
-        self.assertEqual(s8.shape(), (5, True))
-        s9 = Signal.range(-20, 16)
-        self.assertEqual(s9.shape(), (6, True))
-        s10 = Signal.range(0)
-        self.assertEqual(s10.shape(), (0, False))
-        s11 = Signal.range(1)
-        self.assertEqual(s11.shape(), (1, False))
-        # deprecated
-        with warnings.catch_warnings():
-            warnings.filterwarnings(action="ignore", category=DeprecationWarning)
-            d6 = Signal(max=16)
-            self.assertEqual(d6.shape(), (4, False))
-            d7 = Signal(min=4, max=16)
-            self.assertEqual(d7.shape(), (4, False))
-            d8 = Signal(min=-4, max=16)
-            self.assertEqual(d8.shape(), (5, True))
-            d9 = Signal(min=-20, max=16)
-            self.assertEqual(d9.shape(), (6, True))
-            d10 = Signal(max=1)
-            self.assertEqual(d10.shape(), (0, False))
+        self.assertEqual(s5.shape(), unsigned(0))
+        s6 = Signal(range(16))
+        self.assertEqual(s6.shape(), unsigned(4))
+        s7 = Signal(range(4, 16))
+        self.assertEqual(s7.shape(), unsigned(4))
+        s8 = Signal(range(-4, 16))
+        self.assertEqual(s8.shape(), signed(5))
+        s9 = Signal(range(-20, 16))
+        self.assertEqual(s9.shape(), signed(6))
+        s10 = Signal(range(0))
+        self.assertEqual(s10.shape(), unsigned(0))
+        s11 = Signal(range(1))
+        self.assertEqual(s11.shape(), unsigned(1))
 
     def test_shape_wrong(self):
         with self.assertRaises(TypeError,
                 msg="Width must be a non-negative integer, not -10"):
             Signal(-10)
 
-    def test_min_max_deprecated(self):
-        with self.assertWarns(DeprecationWarning,
-                msg="instead of `Signal(min=0, max=10)`, use `Signal.range(0, 10)`"):
-            Signal(max=10)
-        with warnings.catch_warnings():
-            warnings.filterwarnings(action="ignore", category=DeprecationWarning)
-            with self.assertRaises(ValueError,
-                    msg="Lower bound 10 should be less or equal to higher bound 4"):
-                Signal(min=10, max=4)
-            with self.assertRaises(ValueError,
-                    msg="Only one of bits/signedness or bounds may be specified"):
-                Signal(2, min=10)
-
     def test_name(self):
         s1 = Signal()
         self.assertEqual(s1.name, "s1")
@@ -731,16 +857,24 @@ class SignalTestCase(FHDLTestCase):
         self.assertEqual(s1.reset, 0b111)
         self.assertEqual(s1.reset_less, True)
 
+    def test_reset_enum(self):
+        s1 = Signal(2, reset=UnsignedEnum.BAR)
+        self.assertEqual(s1.reset, 2)
+        with self.assertRaises(TypeError,
+                msg="Reset value has to be an int or an integral Enum"
+        ):
+            Signal(1, reset=StringEnum.FOO)
+
     def test_reset_narrow(self):
         with self.assertWarns(SyntaxWarning,
                 msg="Reset value 8 requires 4 bits to represent, but the signal only has 3 bits"):
             Signal(3, reset=8)
         with self.assertWarns(SyntaxWarning,
                 msg="Reset value 4 requires 4 bits to represent, but the signal only has 3 bits"):
-            Signal((3, True), reset=4)
+            Signal(signed(3), reset=4)
         with self.assertWarns(SyntaxWarning,
                 msg="Reset value -5 requires 4 bits to represent, but the signal only has 3 bits"):
-            Signal((3, True), reset=-5)
+            Signal(signed(3), reset=-5)
 
     def test_attrs(self):
         s1 = Signal()
@@ -754,9 +888,9 @@ class SignalTestCase(FHDLTestCase):
 
     def test_like(self):
         s1 = Signal.like(Signal(4))
-        self.assertEqual(s1.shape(), (4, False))
-        s2 = Signal.like(Signal.range(-15, 1))
-        self.assertEqual(s2.shape(), (5, True))
+        self.assertEqual(s1.shape(), unsigned(4))
+        s2 = Signal.like(Signal(range(-15, 1)))
+        self.assertEqual(s2.shape(), signed(5))
         s3 = Signal.like(Signal(4, reset=0b111, reset_less=True))
         self.assertEqual(s3.reset, 0b111)
         self.assertEqual(s3.reset_less, True)
@@ -765,7 +899,7 @@ class SignalTestCase(FHDLTestCase):
         s5 = Signal.like(Signal(decoder=str))
         self.assertEqual(s5.decoder, str)
         s6 = Signal.like(10)
-        self.assertEqual(s6.shape(), (4, False))
+        self.assertEqual(s6.shape(), unsigned(4))
         s7 = [Signal.like(Signal(4))][0]
         self.assertEqual(s7.name, "$like")
         s8 = Signal.like(s1, name_suffix="_ff")
@@ -780,10 +914,10 @@ class SignalTestCase(FHDLTestCase):
         self.assertEqual(s.decoder(3), "3")
 
     def test_enum(self):
-        s1 = Signal.enum(UnsignedEnum)
-        self.assertEqual(s1.shape(), (2, False))
-        s2 = Signal.enum(SignedEnum)
-        self.assertEqual(s2.shape(), (2, True))
+        s1 = Signal(UnsignedEnum)
+        self.assertEqual(s1.shape(), unsigned(2))
+        s2 = Signal(SignedEnum)
+        self.assertEqual(s2.shape(), signed(2))
         self.assertEqual(s2.decoder(SignedEnum.FOO), "FOO/-1")
 
 
@@ -800,7 +934,7 @@ class ClockSignalTestCase(FHDLTestCase):
 
     def test_shape(self):
         s1 = ClockSignal()
-        self.assertEqual(s1.shape(), (1, False))
+        self.assertEqual(s1.shape(), unsigned(1))
         self.assertIsInstance(s1.shape(), Shape)
 
     def test_repr(self):
@@ -826,7 +960,7 @@ class ResetSignalTestCase(FHDLTestCase):
 
     def test_shape(self):
         s1 = ResetSignal()
-        self.assertEqual(s1.shape(), (1, False))
+        self.assertEqual(s1.shape(), unsigned(1))
         self.assertIsInstance(s1.shape(), Shape)
 
     def test_repr(self):
@@ -853,21 +987,29 @@ class MockUserValue(UserValue):
 class UserValueTestCase(FHDLTestCase):
     def test_shape(self):
         uv = MockUserValue(1)
-        self.assertEqual(uv.shape(), (1, False))
+        self.assertEqual(uv.shape(), unsigned(1))
         self.assertIsInstance(uv.shape(), Shape)
         uv.lowered = 2
-        self.assertEqual(uv.shape(), (1, False))
+        self.assertEqual(uv.shape(), unsigned(1))
+        self.assertEqual(uv.lower_count, 1)
+
+    def test_lower_to_user_value(self):
+        uv = MockUserValue(MockUserValue(1))
+        self.assertEqual(uv.shape(), unsigned(1))
+        self.assertIsInstance(uv.shape(), Shape)
+        uv.lowered = MockUserValue(2)
+        self.assertEqual(uv.shape(), unsigned(1))
         self.assertEqual(uv.lower_count, 1)
 
 
 class SampleTestCase(FHDLTestCase):
     def test_const(self):
         s = Sample(1, 1, "sync")
-        self.assertEqual(s.shape(), (1, False))
+        self.assertEqual(s.shape(), unsigned(1))
 
     def test_signal(self):
         s1 = Sample(Signal(2), 1, "sync")
-        self.assertEqual(s1.shape(), (2, False))
+        self.assertEqual(s1.shape(), unsigned(2))
         s2 = Sample(ClockSignal(), 1, "sync")
         s3 = Sample(ResetSignal(), 1, "sync")
 
@@ -891,4 +1033,4 @@ class SampleTestCase(FHDLTestCase):
 class InitialTestCase(FHDLTestCase):
     def test_initial(self):
         i = Initial()
-        self.assertEqual(i.shape(), (1, False))
+        self.assertEqual(i.shape(), unsigned(1))