hdl.ast: add const-shift operations.
authorwhitequark <whitequark@whitequark.org>
Wed, 20 May 2020 03:18:33 +0000 (03:18 +0000)
committerLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Fri, 31 Dec 2021 13:31:40 +0000 (13:31 +0000)
Also, clean up the rotate code a bit.

Fixes #378.

nmigen/hdl/ast.py
nmigen/test/test_hdl_ast.py
nmigen/test/test_sim.py

index a7f77e204ba9fb8096eb15e2f11b5e24fdeb87b3..39093e7c2a4794ac0c5ef38c78aceb394d755a8b 100644 (file)
@@ -423,41 +423,85 @@ class Value(metaclass=ABCMeta):
         else:
             return Cat(*matches).any()
 
-    def rotate_left(self, offset):
+    def shift_left(self, amount):
+        """Shift left by constant amount.
+
+        Parameters
+        ----------
+        amount : int
+            Amount to shift by.
+
+        Returns
+        -------
+        Value, out
+            If the amount is positive, the input shifted left. Otherwise, the input shifted right.
+        """
+        if not isinstance(amount, int):
+            raise TypeError("Shift amount must be an integer, not {!r}".format(amount))
+        if amount < 0:
+            return self.shift_right(-amount)
+        if self.shape().signed:
+            return Cat(Const(0, amount), self).as_signed()
+        else:
+            return Cat(Const(0, amount), self) # unsigned
+
+    def shift_right(self, amount):
+        """Shift right by constant amount.
+
+        Parameters
+        ----------
+        amount : int
+            Amount to shift by.
+
+        Returns
+        -------
+        Value, out
+            If the amount is positive, the input shifted right. Otherwise, the input shifted left.
+        """
+        if not isinstance(amount, int):
+            raise TypeError("Shift amount must be an integer, not {!r}".format(amount))
+        if amount < 0:
+            return self.shift_left(-amount)
+        if self.shape().signed:
+            return self[amount:].as_signed()
+        else:
+            return self[amount:] # unsigned
+
+    def rotate_left(self, amount):
         """Rotate left by constant amount.
 
         Parameters
         ----------
-        offset : int
+        amount : int
             Amount to rotate by.
 
         Returns
         -------
         Value, out
-            If the offset is positive, the input rotated left. Otherwise, the input rotated right.
+            If the amount is positive, the input rotated left. Otherwise, the input rotated right.
         """
-        if not isinstance(offset, int):
-            raise TypeError("Rotate amount must be an integer, not {!r}".format(offset))
-        offset %= len(self)
-        return Cat(self[-offset:], self[:-offset]) # meow :3
+        if not isinstance(amount, int):
+            raise TypeError("Rotate amount must be an integer, not {!r}".format(amount))
+        amount %= len(self)
+        return Cat(self[-amount:], self[:-amount]) # meow :3
 
-    def rotate_right(self, offset):
+    def rotate_right(self, amount):
         """Rotate right by constant amount.
 
         Parameters
         ----------
-        offset : int
+        amount : int
             Amount to rotate by.
 
         Returns
         -------
         Value, out
-            If the offset is positive, the input rotated right. Otherwise, the input rotated right.
+            If the amount is positive, the input rotated right. Otherwise, the input rotated right.
         """
-        if not isinstance(offset, int):
-            raise TypeError("Rotate amount must be an integer, not {!r}".format(offset))
-        offset %= len(self)
-        return Cat(self[offset:], self[:offset])
+        if not isinstance(amount, int):
+            raise TypeError("Rotate amount must be an integer, not {!r}".format(amount))
+        amount %= len(self)
+        return Cat(self[amount:], self[:amount])
 
     def eq(self, value):
         """Assignment.
index 1b7dd58e295db0029d26772ed0aaa11f06ca654b..88ad2644920b2cd15e7619fdaf4d1bc82768683a 100644 (file)
@@ -205,11 +205,73 @@ class ValueTestCase(FHDLTestCase):
                 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(Value.cast(256).rotate_left(1), "(cat (slice (const 9'd256) 8:9) (slice (const 9'd256) 0:8))")
-        self.assertRepr(Value.cast(256).rotate_left(7), "(cat (slice (const 9'd256) 2:9) (slice (const 9'd256) 0:2))")
-        self.assertRepr(Value.cast(256).rotate_left(-1), "(cat (slice (const 9'd256) 1:9) (slice (const 9'd256) 0:1))")
-        self.assertRepr(Value.cast(256).rotate_left(-7), "(cat (slice (const 9'd256) 7:9) (slice (const 9'd256) 0:7))")
+        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,
@@ -217,16 +279,21 @@ class ValueTestCase(FHDLTestCase):
             Const(31).rotate_left("str")
 
     def test_rotate_right(self):
-        self.assertRepr(Value.cast(256).rotate_right(1), "(cat (slice (const 9'd256) 1:9) (slice (const 9'd256) 0:1))")
-        self.assertRepr(Value.cast(256).rotate_right(7), "(cat (slice (const 9'd256) 7:9) (slice (const 9'd256) 0:7))")
-        self.assertRepr(Value.cast(256).rotate_right(-1), "(cat (slice (const 9'd256) 8:9) (slice (const 9'd256) 0:8))")
-        self.assertRepr(Value.cast(256).rotate_right(-7), "(cat (slice (const 9'd256) 2:9) (slice (const 9'd256) 0:2))")
+        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(),   unsigned(1))
index 444522ec3a96a27392ea6eb605539b371d25762a..a3a6913fa8190045e029ed7427565fade980112c 100644 (file)
@@ -301,6 +301,18 @@ class SimulatorUnitTestCase(FHDLTestCase):
         for i in range(10):
             self.assertStatement(stmt, [C(i)], C(0))
 
+    def test_shift_left(self):
+        stmt1 = lambda y, a: y.eq(a.shift_left(1))
+        self.assertStatement(stmt1, [C(0b10100010, 8)], C(   0b101000100, 9))
+        stmt2 = lambda y, a: y.eq(a.shift_left(4))
+        self.assertStatement(stmt2, [C(0b10100010, 8)], C(0b101000100000, 12))
+
+    def test_shift_right(self):
+        stmt1 = lambda y, a: y.eq(a.shift_right(1))
+        self.assertStatement(stmt1, [C(0b10100010, 8)], C(0b1010001, 7))
+        stmt2 = lambda y, a: y.eq(a.shift_right(4))
+        self.assertStatement(stmt2, [C(0b10100010, 8)], C(   0b1010, 4))
+
     def test_rotate_left(self):
         stmt = lambda y, a: y.eq(a.rotate_left(1))
         self.assertStatement(stmt, [C(0b1)], C(0b1))