From 27b47faf169908e6e5acdc4588b4caf58582e69d Mon Sep 17 00:00:00 2001 From: whitequark Date: Thu, 6 Feb 2020 18:27:55 +0000 Subject: [PATCH] hdl.ast: add Value.{as_signed,as_unsigned}. Before this commit, there was no way to do so besides creating and assigning an intermediate signal, which could not be extracted into a helper function due to Module statefulness. Fixes #292. --- nmigen/back/pysim.py | 3 +++ nmigen/back/rtlil.py | 4 ++++ nmigen/hdl/ast.py | 24 ++++++++++++++++++++++++ nmigen/test/test_hdl_ast.py | 10 ++++++++++ nmigen/test/test_sim.py | 10 ++++++++++ 5 files changed, 51 insertions(+) diff --git a/nmigen/back/pysim.py b/nmigen/back/pysim.py index 0187a07..9ae476f 100644 --- a/nmigen/back/pysim.py +++ b/nmigen/back/pysim.py @@ -426,6 +426,9 @@ class _RHSValueCompiler(_ValueCompiler): if value.operator == "r^": # Believe it or not, this is the fastest way to compute a sideways XOR in Python. return f"(format({mask(arg)}, 'b').count('1') % 2)" + if value.operator in ("u", "s"): + # These operators don't change the bit pattern, only its interpretation. + return self(arg) elif len(value.operands) == 2: lhs, rhs = value.operands lhs_mask = (1 << len(lhs)) - 1 diff --git a/nmigen/back/rtlil.py b/nmigen/back/rtlil.py index 0e5c93a..2075b90 100644 --- a/nmigen/back/rtlil.py +++ b/nmigen/back/rtlil.py @@ -454,6 +454,10 @@ class _RHSValueCompiler(_ValueCompiler): def on_Operator_unary(self, value): arg, = value.operands + if value.operator in ("u", "s"): + # These operators don't change the bit pattern, only its interpretation. + return self(arg) + arg_bits, arg_sign = arg.shape() res_bits, res_sign = value.shape() res = self.s.rtlil.wire(width=res_bits, src=src(value.src_loc)) diff --git a/nmigen/hdl/ast.py b/nmigen/hdl/ast.py index 96f1e30..7c0d74b 100644 --- a/nmigen/hdl/ast.py +++ b/nmigen/hdl/ast.py @@ -239,6 +239,26 @@ class Value(metaclass=ABCMeta): else: raise TypeError("Cannot index value with {}".format(repr(key))) + def as_unsigned(self): + """Conversion to unsigned. + + Returns + ------- + Value, out + This ``Value`` reinterpreted as a unsigned integer. + """ + return Operator("u", [self]) + + def as_signed(self): + """Conversion to signed. + + Returns + ------- + Value, out + This ``Value`` reinterpreted as a signed integer. + """ + return Operator("s", [self]) + def bool(self): """Conversion to boolean. @@ -552,6 +572,10 @@ class Operator(Value): return Shape(a_width + 1, True) if self.operator in ("b", "r|", "r&", "r^"): return Shape(1, False) + if self.operator == "u": + return Shape(a_width, False) + if self.operator == "s": + return Shape(a_width, True) elif len(op_shapes) == 2: (a_width, a_signed), (b_width, b_signed) = op_shapes if self.operator in ("+", "-"): diff --git a/nmigen/test/test_hdl_ast.py b/nmigen/test/test_hdl_ast.py index 2a40e8b..c0d26ee 100644 --- a/nmigen/test/test_hdl_ast.py +++ b/nmigen/test/test_hdl_ast.py @@ -246,6 +246,16 @@ class OperatorTestCase(FHDLTestCase): self.assertEqual(repr(v), "(~ (const 4'd0))") 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, unsigned(4)) self.assertEqual(repr(v1), "(- (const 4'd0))") diff --git a/nmigen/test/test_sim.py b/nmigen/test/test_sim.py index 7c00072..932d0f9 100644 --- a/nmigen/test/test_sim.py +++ b/nmigen/test/test_sim.py @@ -56,6 +56,16 @@ class SimulatorUnitTestCase(FHDLTestCase): self.assertStatement(stmt, [C(1, 4)], C(1)) self.assertStatement(stmt, [C(2, 4)], C(1)) + def test_as_unsigned(self): + stmt = lambda y, a, b: y.eq(a.as_unsigned() == b) + self.assertStatement(stmt, [C(0b01, signed(2)), C(0b0001, unsigned(4))], C(1)) + self.assertStatement(stmt, [C(0b11, signed(2)), C(0b0011, unsigned(4))], C(1)) + + def test_as_signed(self): + stmt = lambda y, a, b: y.eq(a.as_signed() == b) + self.assertStatement(stmt, [C(0b01, unsigned(2)), C(0b0001, signed(4))], C(1)) + self.assertStatement(stmt, [C(0b11, unsigned(2)), C(0b1111, signed(4))], C(1)) + def test_any(self): stmt = lambda y, a: y.eq(a.any()) self.assertStatement(stmt, [C(0b00, 2)], C(0)) -- 2.30.2