From 7ea81f5f06be949ea3940152c8ce1ead3d0712b1 Mon Sep 17 00:00:00 2001 From: whitequark Date: Wed, 20 May 2020 03:18:33 +0000 Subject: [PATCH] hdl.ast: add const-shift operations. Also, clean up the rotate code a bit. Fixes #378. --- nmigen/hdl/ast.py | 72 +++++++++++++++++++++++++------- nmigen/test/test_hdl_ast.py | 83 +++++++++++++++++++++++++++++++++---- nmigen/test/test_sim.py | 12 ++++++ 3 files changed, 145 insertions(+), 22 deletions(-) diff --git a/nmigen/hdl/ast.py b/nmigen/hdl/ast.py index a7f77e2..39093e7 100644 --- a/nmigen/hdl/ast.py +++ b/nmigen/hdl/ast.py @@ -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. diff --git a/nmigen/test/test_hdl_ast.py b/nmigen/test/test_hdl_ast.py index 1b7dd58..88ad264 100644 --- a/nmigen/test/test_hdl_ast.py +++ b/nmigen/test/test_hdl_ast.py @@ -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)) diff --git a/nmigen/test/test_sim.py b/nmigen/test/test_sim.py index 444522e..a3a6913 100644 --- a/nmigen/test/test_sim.py +++ b/nmigen/test/test_sim.py @@ -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)) -- 2.30.2