From: Dan Ravensloft Date: Mon, 13 Apr 2020 13:40:39 +0000 (+0100) Subject: hdl.ast: add Value.{rotate_left,rotate_right}. X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=a568da719b099c51d52683bc7678257ff5f7ccb8;p=nmigen.git hdl.ast: add Value.{rotate_left,rotate_right}. --- diff --git a/nmigen/hdl/ast.py b/nmigen/hdl/ast.py index fa2c9d1..7e42c2a 100644 --- a/nmigen/hdl/ast.py +++ b/nmigen/hdl/ast.py @@ -423,6 +423,42 @@ class Value(metaclass=ABCMeta): else: return Cat(*matches).any() + def rotate_left(self, offset): + """Rotate left by constant modulo 2**len(self). + + Parameters + ---------- + offset : int + Amount to rotate by. + + Returns + ------- + Value, out + The input rotated left by offset if offset is positive, else the input rotated right by -offset. + """ + 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 + + def rotate_right(self, offset): + """Rotate right by constant modulo 2**len(self). + + Parameters + ---------- + offset : int + Amount to rotate by. + + Returns + ------- + Value, out + The input rotated right by offset if offset is positive, else the input rotated left by -offset. + """ + 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]) + def eq(self, value): """Assignment. diff --git a/nmigen/test/test_hdl_ast.py b/nmigen/test/test_hdl_ast.py index 26e506a..f642f20 100644 --- a/nmigen/test/test_hdl_ast.py +++ b/nmigen/test/test_hdl_ast.py @@ -205,6 +205,27 @@ class ValueTestCase(FHDLTestCase): msg="Cannot index value with 'str'"): Const(31)["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))") + + 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(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))") + + 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): diff --git a/nmigen/test/test_sim.py b/nmigen/test/test_sim.py index 7996bec..444522e 100644 --- a/nmigen/test/test_sim.py +++ b/nmigen/test/test_sim.py @@ -301,6 +301,57 @@ class SimulatorUnitTestCase(FHDLTestCase): for i in range(10): self.assertStatement(stmt, [C(i)], C(0)) + def test_rotate_left(self): + stmt = lambda y, a: y.eq(a.rotate_left(1)) + self.assertStatement(stmt, [C(0b1)], C(0b1)) + self.assertStatement(stmt, [C(0b1001000)], C(0b0010001)) + stmt = lambda y, a: y.eq(a.rotate_left(5)) + self.assertStatement(stmt, [C(0b1000000)], C(0b0010000)) + self.assertStatement(stmt, [C(0b1000001)], C(0b0110000)) + stmt = lambda y, a: y.eq(a.rotate_left(7)) + self.assertStatement(stmt, [C(0b1000000)], C(0b1000000)) + self.assertStatement(stmt, [C(0b1000001)], C(0b1000001)) + stmt = lambda y, a: y.eq(a.rotate_left(9)) + self.assertStatement(stmt, [C(0b1000000)], C(0b0000010)) + self.assertStatement(stmt, [C(0b1000001)], C(0b0000110)) + stmt = lambda y, a: y.eq(a.rotate_left(-1)) + self.assertStatement(stmt, [C(0b1)], C(0b1)) + self.assertStatement(stmt, [C(0b1001000)], C(0b0100100)) + stmt = lambda y, a: y.eq(a.rotate_left(-5)) + self.assertStatement(stmt, [C(0b1000000)], C(0b0000010)) + self.assertStatement(stmt, [C(0b1000001)], C(0b0000110)) + stmt = lambda y, a: y.eq(a.rotate_left(-7)) + self.assertStatement(stmt, [C(0b1000000)], C(0b1000000)) + self.assertStatement(stmt, [C(0b1000001)], C(0b1000001)) + stmt = lambda y, a: y.eq(a.rotate_left(-9)) + self.assertStatement(stmt, [C(0b1000000)], C(0b0010000)) + self.assertStatement(stmt, [C(0b1000001)], C(0b0110000)) + + def test_rotate_right(self): + stmt = lambda y, a: y.eq(a.rotate_right(1)) + self.assertStatement(stmt, [C(0b1)], C(0b1)) + self.assertStatement(stmt, [C(0b1001000)], C(0b0100100)) + stmt = lambda y, a: y.eq(a.rotate_right(5)) + self.assertStatement(stmt, [C(0b1000000)], C(0b0000010)) + self.assertStatement(stmt, [C(0b1000001)], C(0b0000110)) + stmt = lambda y, a: y.eq(a.rotate_right(7)) + self.assertStatement(stmt, [C(0b1000000)], C(0b1000000)) + self.assertStatement(stmt, [C(0b1000001)], C(0b1000001)) + stmt = lambda y, a: y.eq(a.rotate_right(9)) + self.assertStatement(stmt, [C(0b1000000)], C(0b0010000)) + self.assertStatement(stmt, [C(0b1000001)], C(0b0110000)) + stmt = lambda y, a: y.eq(a.rotate_right(-1)) + self.assertStatement(stmt, [C(0b1)], C(0b1)) + self.assertStatement(stmt, [C(0b1001000)], C(0b0010001)) + stmt = lambda y, a: y.eq(a.rotate_right(-5)) + self.assertStatement(stmt, [C(0b1000000)], C(0b0010000)) + self.assertStatement(stmt, [C(0b1000001)], C(0b0110000)) + stmt = lambda y, a: y.eq(a.rotate_right(-7)) + self.assertStatement(stmt, [C(0b1000000)], C(0b1000000)) + self.assertStatement(stmt, [C(0b1000001)], C(0b1000001)) + stmt = lambda y, a: y.eq(a.rotate_right(-9)) + self.assertStatement(stmt, [C(0b1000000)], C(0b0000010)) + self.assertStatement(stmt, [C(0b1000001)], C(0b0000110)) class SimulatorIntegrationTestCase(FHDLTestCase): @contextmanager