Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: m-labs/nmigen
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 751ae33fe1a9
Choose a base ref
...
head repository: m-labs/nmigen
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: de34728bf874
Choose a head ref
  • 1 commit
  • 2 files changed
  • 1 contributor

Commits on Oct 4, 2019

  1. hdl.ast: prohibit signed divisors.

    See #238.
    whitequark committed Oct 4, 2019
    Copy the full SHA
    de34728 View commit details
Showing with 37 additions and 6 deletions.
  1. +18 −4 nmigen/hdl/ast.py
  2. +19 −2 nmigen/test/test_hdl_ast.py
22 changes: 18 additions & 4 deletions nmigen/hdl/ast.py
Original file line number Diff line number Diff line change
@@ -79,18 +79,34 @@ def __sub__(self, other):
return Operator("-", [self, other])
def __rsub__(self, other):
return Operator("-", [other, self])

def __mul__(self, other):
return Operator("*", [self, other])
def __rmul__(self, other):
return Operator("*", [other, self])

def __check_divisor(self):
width, signed = self.shape()
if signed:
# Python's division semantics and Verilog's division semantics differ for negative
# divisors (Python uses div/mod, Verilog uses quo/rem); for now, avoid the issue
# completely by prohibiting such division operations.
raise NotImplementedError("Division by a signed value is not supported")
def __mod__(self, other):
other = Value.wrap(other)
other.__check_divisor()
return Operator("%", [self, other])
def __rmod__(self, other):
self.__check_divisor()
return Operator("%", [other, self])
def __floordiv__(self, other):
other = Value.wrap(other)
other.__check_divisor()
return Operator("//", [self, other])
def __rfloordiv__(self, other):
self.__check_divisor()
return Operator("//", [other, self])

def __lshift__(self, other):
return Operator("<<", [self, other])
def __rlshift__(self, other):
@@ -475,10 +491,8 @@ def shape(self):
return width + 1, signed
if self.op == "*":
return a_width + b_width, a_signed or b_signed
if self.op == "//":
# division by -1 can overflow
return a_width + b_signed, a_signed or b_signed
if self.op == "%":
if self.op in ("//", "%"):
assert not b_signed
return a_width, a_signed
if self.op in ("<", "<=", "==", "!=", ">", ">="):
return 1, False
21 changes: 19 additions & 2 deletions nmigen/test/test_hdl_ast.py
Original file line number Diff line number Diff line change
@@ -181,17 +181,34 @@ def test_mul(self):
v5 = 10 * Const(0, 4)
self.assertEqual(v5.shape(), (8, False))

def test_mod(self):
v1 = Const(0, (4, False)) % Const(0, (6, False))
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))
v5 = 10 % Const(0, 4)
self.assertEqual(v5.shape(), (4, False))

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))

def test_floordiv(self):
v1 = Const(0, (4, False)) // Const(0, (6, False))
self.assertEqual(repr(v1), "(// (const 4'd0) (const 6'd0))")
self.assertEqual(v1.shape(), (4, False))
v2 = Const(0, (4, True)) // Const(0, (6, True))
self.assertEqual(v2.shape(), (5, True))
v3 = Const(0, (4, True)) // Const(0, (4, False))
self.assertEqual(v3.shape(), (4, True))
v5 = 10 // Const(0, 4)
self.assertEqual(v5.shape(), (4, False))

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))

def test_and(self):
v1 = Const(0, (4, False)) & Const(0, (6, False))
self.assertEqual(repr(v1), "(& (const 4'd0) (const 6'd0))")