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: 3a8685c3523c
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: b70340c0daf6
Choose a head ref
  • 3 commits
  • 5 files changed
  • 1 contributor

Commits on Dec 15, 2018

  1. fhdl.ast: refactor Operator.shape(). NFC.

    whitequark committed Dec 15, 2018
    Copy the full SHA
    46f5add View commit details
  2. fhdl.ast, back.pysim: implement shifts.

    whitequark committed Dec 15, 2018
    Copy the full SHA
    db4600d View commit details
  3. pyback.sim: test Slice, Cat, Repl.

    whitequark committed Dec 15, 2018
    Copy the full SHA
    b70340c View commit details
Showing with 119 additions and 57 deletions.
  1. +13 −5 nmigen/back/pysim.py
  2. +2 −2 nmigen/back/rtlil.py
  3. +60 −50 nmigen/fhdl/ast.py
  4. +19 −0 nmigen/test/test_fhdl_ast.py
  5. +25 −0 nmigen/test/test_sim.py
18 changes: 13 additions & 5 deletions nmigen/back/pysim.py
Original file line number Diff line number Diff line change
@@ -75,15 +75,23 @@ def on_Operator(self, value):
elif len(value.operands) == 2:
lhs, rhs = map(self, value.operands)
if value.op == "+":
return lambda state: normalize(lhs(state) + rhs(state), shape)
return lambda state: normalize(lhs(state) + rhs(state), shape)
if value.op == "-":
return lambda state: normalize(lhs(state) - rhs(state), shape)
return lambda state: normalize(lhs(state) - rhs(state), shape)
if value.op == "&":
return lambda state: normalize(lhs(state) & rhs(state), shape)
return lambda state: normalize(lhs(state) & rhs(state), shape)
if value.op == "|":
return lambda state: normalize(lhs(state) | rhs(state), shape)
return lambda state: normalize(lhs(state) | rhs(state), shape)
if value.op == "^":
return lambda state: normalize(lhs(state) ^ rhs(state), shape)
return lambda state: normalize(lhs(state) ^ rhs(state), shape)
if value.op == "<<":
def sshl(lhs, rhs):
return lhs << rhs if rhs >= 0 else lhs >> -rhs
return lambda state: normalize(sshl(lhs(state), rhs(state)), shape)
if value.op == ">>":
def sshr(lhs, rhs):
return lhs >> rhs if rhs >= 0 else lhs << -rhs
return lambda state: normalize(sshr(lhs(state), rhs(state)), shape)
if value.op == "==":
return lambda state: normalize(lhs(state) == rhs(state), shape)
if value.op == "!=":
4 changes: 2 additions & 2 deletions nmigen/back/rtlil.py
Original file line number Diff line number Diff line change
@@ -206,8 +206,8 @@ class _ValueTransformer(xfrm.ValueTransformer):
(2, "/"): "$div",
(2, "%"): "$mod",
(2, "**"): "$pow",
(2, "<<<"): "$sshl",
(2, ">>>"): "$sshr",
(2, "<<"): "$sshl",
(2, ">>"): "$sshr",
(2, "&"): "$and",
(2, "^"): "$xor",
(2, "|"): "$or",
110 changes: 60 additions & 50 deletions nmigen/fhdl/ast.py
Original file line number Diff line number Diff line change
@@ -75,13 +75,13 @@ def __div__(self, other):
def __rdiv__(self, other):
return Operator("/", [other, self])
def __lshift__(self, other):
return Operator("<<<", [self, other])
return Operator("<<", [self, other])
def __rlshift__(self, other):
return Operator("<<<", [other, self])
return Operator("<<", [other, self])
def __rshift__(self, other):
return Operator(">>>", [self, other])
return Operator(">>", [self, other])
def __rrshift__(self, other):
return Operator(">>>", [other, self])
return Operator(">>", [other, self])
def __and__(self, other):
return Operator("&", [self, other])
def __rand__(self, other):
@@ -259,61 +259,71 @@ def __init__(self, op, operands, src_loc_at=0):
self.operands = [Value.wrap(o) for o in operands]

@staticmethod
def _bitwise_binary_shape(a, b):
if not a[1] and not b[1]:
def _bitwise_binary_shape(a_shape, b_shape):
a_bits, a_sign = a_shape
b_bits, b_sign = b_shape
if not a_sign and not b_sign:
# both operands unsigned
return max(a[0], b[0]), False
elif a[1] and b[1]:
return max(a_bits, b_bits), False
elif a_sign and b_sign:
# both operands signed
return max(a[0], b[0]), True
elif not a[1] and b[1]:
return max(a_bits, b_bits), True
elif not a_sign and b_sign:
# first operand unsigned (add sign bit), second operand signed
return max(a[0] + 1, b[0]), True
return max(a_bits + 1, b_bits), True
else:
# first signed, second operand unsigned (add sign bit)
return max(a[0], b[0] + 1), True
return max(a_bits, b_bits + 1), True

def shape(self):
obs = list(map(lambda x: x.shape(), self.operands))
if self.op == "+" or self.op == "-":
if len(obs) == 1:
if self.op == "-" and not obs[0][1]:
return obs[0][0] + 1, True
op_shapes = list(map(lambda x: x.shape(), self.operands))
if len(op_shapes) == 1:
(a_bits, a_sign), = op_shapes
if self.op in ("+", "~"):
return a_bits, a_sign
if self.op == "-":
if not a_sign:
return a_bits + 1, True
else:
return obs[0]
n, s = self._bitwise_binary_shape(*obs)
return n + 1, s
elif self.op == "*":
if not obs[0][1] and not obs[1][1]:
# both operands unsigned
return obs[0][0] + obs[1][0], False
elif obs[0][1] and obs[1][1]:
# both operands signed
return obs[0][0] + obs[1][0] - 1, True
else:
return a_bits, a_sign
if self.op == "b":
return 1, False
elif len(op_shapes) == 2:
(a_bits, a_sign), (b_bits, b_sign) = op_shapes
if self.op == "+" or self.op == "-":
bits, sign = self._bitwise_binary_shape(*op_shapes)
return bits + 1, sign
if self.op == "*":
if not a_sign and not b_sign:
# both operands unsigned
return a_bits + b_bits, False
if a_sign and b_sign:
# both operands signed
return a_bits + b_bits - 1, True
# one operand signed, the other unsigned (add sign bit)
return obs[0][0] + obs[1][0] + 1 - 1, True
elif self.op == "<<<":
if obs[1][1]:
extra = 2**(obs[1][0] - 1) - 1
else:
extra = 2**obs[1][0] - 1
return obs[0][0] + extra, obs[0][1]
elif self.op == ">>>":
if obs[1][1]:
extra = 2**(obs[1][0] - 1)
else:
extra = 0
return obs[0][0] + extra, obs[0][1]
elif self.op in ("&", "^", "|"):
return self._bitwise_binary_shape(*obs)
elif self.op in ("<", "<=", "==", "!=", ">", ">=", "b"):
return 1, False
elif self.op == "~":
return obs[0]
elif self.op == "m":
return self._bitwise_binary_shape(obs[1], obs[2])
raise NotImplementedError("Operator '{}' not implemented".format(self.op)) # :nocov:
return a_bits + b_bits + 1 - 1, True
if self.op in ("<", "<=", "==", "!=", ">", ">=", "b"):
return 1, False
if self.op in ("&", "^", "|"):
return self._bitwise_binary_shape(*op_shapes)
if self.op == "<<":
if b_sign:
extra = 2 ** (b_bits - 1) - 1
else:
extra = 2 ** (b_bits) - 1
return a_bits + extra, a_sign
if self.op == ">>":
if b_sign:
extra = 2 ** (b_bits - 1)
else:
extra = 0
return a_bits + extra, a_sign
elif len(op_shapes) == 3:
if self.op == "m":
s_shape, a_shape, b_shape = op_shapes
return self._bitwise_binary_shape(a_shape, b_shape)
raise NotImplementedError("Operator {}/{} not implemented"
.format(self.op, len(op_shapes))) # :nocov:

def _rhs_signals(self):
return union(op._rhs_signals() for op in self.operands)
19 changes: 19 additions & 0 deletions nmigen/test/test_fhdl_ast.py
Original file line number Diff line number Diff line change
@@ -88,6 +88,11 @@ def test_hash(self):


class OperatorTestCase(FHDLTestCase):
def test_bool(self):
v = Const(0, 4).bool()
self.assertEqual(repr(v), "(b (const 4'd0))")
self.assertEqual(v.shape(), (1, False))

def test_invert(self):
v = ~Const(0, 4)
self.assertEqual(repr(v), "(~ (const 4'd0))")
@@ -177,6 +182,20 @@ def test_xor(self):
v5 = 10 ^ Const(0, 4)
self.assertEqual(v5.shape(), (4, False))

def test_shl(self):
v1 = Const(1, 4) << Const(4)
self.assertEqual(repr(v1), "(<< (const 4'd1) (const 3'd4))")
self.assertEqual(v1.shape(), (11, False))
v2 = Const(1, 4) << Const(-3)
self.assertEqual(v2.shape(), (7, False))

def test_shr(self):
v1 = Const(1, 4) >> Const(4)
self.assertEqual(repr(v1), "(>> (const 4'd1) (const 3'd4))")
self.assertEqual(v1.shape(), (4, False))
v2 = Const(1, 4) >> Const(-3)
self.assertEqual(v2.shape(), (8, False))

def test_lt(self):
v = Const(0, 4) < Const(0, 6)
self.assertEqual(repr(v), "(< (const 4'd0) (const 6'd0))")
25 changes: 25 additions & 0 deletions nmigen/test/test_sim.py
Original file line number Diff line number Diff line change
@@ -71,6 +71,18 @@ def test_xor(self):
stmt = lambda a, b: a ^ b
self.assertOperator(stmt, [C(0b1100, 4), C(0b1010, 4)], C(0b0110, 4))

def test_shl(self):
stmt = lambda a, b: a << b
self.assertOperator(stmt, [C(0b1001, 4), C(0)], C(0b1001, 5))
self.assertOperator(stmt, [C(0b1001, 4), C(3)], C(0b1001000, 7))
self.assertOperator(stmt, [C(0b1001, 4), C(-2)], C(0b10, 7))

def test_shr(self):
stmt = lambda a, b: a >> b
self.assertOperator(stmt, [C(0b1001, 4), C(0)], C(0b1001, 4))
self.assertOperator(stmt, [C(0b1001, 4), C(2)], C(0b10, 4))
self.assertOperator(stmt, [C(0b1001, 4), C(-2)], C(0b100100, 5))

def test_eq(self):
stmt = lambda a, b: a == b
self.assertOperator(stmt, [C(0, 4), C(0, 4)], C(1))
@@ -111,3 +123,16 @@ def test_mux(self):
stmt = lambda a, b, c: Mux(c, a, b)
self.assertOperator(stmt, [C(2, 4), C(3, 4), C(0)], C(3, 4))
self.assertOperator(stmt, [C(2, 4), C(3, 4), C(1)], C(2, 4))

def test_slice(self):
stmt1 = lambda a: a[2]
self.assertOperator(stmt1, [C(0b10110100, 8)], C(0b1, 1))
stmt2 = lambda a: a[2:4]
self.assertOperator(stmt2, [C(0b10110100, 8)], C(0b01, 2))

def test_cat(self):
self.assertOperator(Cat, [C(0b10, 2), C(0b01, 2)], C(0b0110, 4))

def test_repl(self):
stmt = lambda a: Repl(a, 3)
self.assertOperator(stmt, [C(0b10, 2)], C(0b101010, 6))