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: 9fe27a15ad0f
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: 7ff4c6ce4314
Choose a head ref
  • 2 commits
  • 5 files changed
  • 1 contributor

Commits on Oct 11, 2019

  1. hdl.ast: Value.{wrap→cast}

    Preparation for #225.
    whitequark committed Oct 11, 2019
    Copy the full SHA
    d72d4a5 View commit details
  2. hdl.ast: simplify enum handling.

    whitequark committed Oct 11, 2019
    Copy the full SHA
    7ff4c6c View commit details
Showing with 61 additions and 57 deletions.
  1. +4 −4 nmigen/compat/fhdl/structure.py
  2. +40 −36 nmigen/hdl/ast.py
  3. +3 −3 nmigen/hdl/dsl.py
  4. +12 −12 nmigen/test/test_hdl_ast.py
  5. +2 −2 nmigen/test/test_sim.py
8 changes: 4 additions & 4 deletions nmigen/compat/fhdl/structure.py
Original file line number Diff line number Diff line change
@@ -13,9 +13,9 @@
"SPECIAL_INPUT", "SPECIAL_OUTPUT", "SPECIAL_INOUT"]


@deprecated("instead of `wrap`, use `Value.wrap`")
@deprecated("instead of `wrap`, use `Value.cast`")
def wrap(v):
return Value.wrap(v)
return Value.cast(v)


@extend(_Slice)
@@ -52,14 +52,14 @@ def choices(self):
class If(ast.Switch):
@deprecated("instead of `If(cond, ...)`, use `with m.If(cond): ...`")
def __init__(self, cond, *stmts):
cond = Value.wrap(cond)
cond = Value.cast(cond)
if len(cond) != 1:
cond = cond.bool()
super().__init__(cond, {("1",): ast.Statement.wrap(stmts)})

@deprecated("instead of `.Elif(cond, ...)`, use `with m.Elif(cond): ...`")
def Elif(self, cond, *stmts):
cond = Value.wrap(cond)
cond = Value.cast(cond)
if len(cond) != 1:
cond = cond.bool()
self.cases = OrderedDict((("-" + k,), v) for (k,), v in self.cases.items())
76 changes: 40 additions & 36 deletions nmigen/hdl/ast.py
Original file line number Diff line number Diff line change
@@ -40,24 +40,28 @@ def _enum_shape(enum_type):
return (width, signed)


def _enum_to_bits(enum_value):
width, signed = _enum_shape(type(enum_value))
return format(enum_value.value & ((1 << width) - 1), "b").rjust(width, "0")


class Value(metaclass=ABCMeta):
@staticmethod
def wrap(obj):
"""Ensures that the passed object is an nMigen value. Booleans and integers
are automatically wrapped into ``Const``."""
def cast(obj):
"""Converts ``obj`` to an nMigen value.
Booleans and integers are wrapped into a :class:`Const`. Enumerations whose members are
all integers are converted to a :class:`Const` with a shape that fits every member.
"""
if isinstance(obj, Value):
return obj
elif isinstance(obj, (bool, int)):
return Const(obj)
elif isinstance(obj, Enum):
return Const(obj.value, _enum_shape(type(obj)))
else:
raise TypeError("Object '{!r}' is not an nMigen value".format(obj))
raise TypeError("Object {!r} is not an nMigen value".format(obj))

# TODO(nmigen-0.2): remove this
@classmethod
@deprecated("instead of `Value.wrap`, use `Value.cast`")
def wrap(cls, obj):
return cls.cast(obj)

def __init__(self, *, src_loc_at=0):
super().__init__()
@@ -93,14 +97,14 @@ def __check_divisor(self):
# 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 = Value.cast(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 = Value.cast(other)
other.__check_divisor()
return Operator("//", [self, other])
def __rfloordiv__(self, other):
@@ -291,14 +295,14 @@ def matches(self, *patterns):
.format(pattern, len(self)),
SyntaxWarning, stacklevel=3)
continue
if isinstance(pattern, int):
matches.append(self == pattern)
elif isinstance(pattern, (str, Enum)):
if isinstance(pattern, Enum):
pattern = _enum_to_bits(pattern)
if isinstance(pattern, str):
mask = int(pattern.replace("0", "1").replace("-", "0"), 2)
pattern = int(pattern.replace("-", "0"), 2)
matches.append((self & mask) == pattern)
elif isinstance(pattern, int):
matches.append(self == pattern)
elif isinstance(pattern, Enum):
matches.append(self == pattern.value)
else:
assert False
if not matches:
@@ -452,7 +456,7 @@ class Operator(Value):
def __init__(self, op, operands, *, src_loc_at=0):
super().__init__(src_loc_at=1 + src_loc_at)
self.op = op
self.operands = [Value.wrap(o) for o in operands]
self.operands = [Value.cast(o) for o in operands]

@staticmethod
def _bitwise_binary_shape(a_shape, b_shape):
@@ -540,7 +544,7 @@ def Mux(sel, val1, val0):
Value, out
Output ``Value``. If ``sel`` is asserted, the Mux returns ``val1``, else ``val0``.
"""
sel = Value.wrap(sel)
sel = Value.cast(sel)
if len(sel) != 1:
sel = sel.bool()
return Operator("m", [sel, val1, val0])
@@ -567,7 +571,7 @@ def __init__(self, value, start, end, *, src_loc_at=0):
raise IndexError("Slice start {} must be less than slice end {}".format(start, end))

super().__init__(src_loc_at=src_loc_at)
self.value = Value.wrap(value)
self.value = Value.cast(value)
self.start = start
self.end = end

@@ -594,7 +598,7 @@ def __init__(self, value, offset, width, stride=1, *, src_loc_at=0):

super().__init__(src_loc_at=src_loc_at)
self.value = value
self.offset = Value.wrap(offset)
self.offset = Value.cast(offset)
self.width = width
self.stride = stride

@@ -639,7 +643,7 @@ class Cat(Value):
"""
def __init__(self, *args, src_loc_at=0):
super().__init__(src_loc_at=src_loc_at)
self.parts = [Value.wrap(v) for v in flatten(args)]
self.parts = [Value.cast(v) for v in flatten(args)]

def shape(self):
return sum(len(part) for part in self.parts), False
@@ -688,7 +692,7 @@ def __init__(self, value, count, *, src_loc_at=0):
.format(count))

super().__init__(src_loc_at=src_loc_at)
self.value = Value.wrap(value)
self.value = Value.cast(value)
self.count = count

def shape(self):
@@ -857,7 +861,7 @@ def like(cls, other, *, name=None, name_suffix=None, src_loc_at=0, **kwargs):
new_name = other.name + str(name_suffix)
else:
new_name = tracer.get_var_name(depth=2 + src_loc_at, default="$like")
kw = dict(shape=cls.wrap(other).shape(), name=new_name)
kw = dict(shape=cls.cast(other).shape(), name=new_name)
if isinstance(other, cls):
kw.update(reset=other.reset, reset_less=other.reset_less,
attrs=other.attrs, decoder=other.decoder)
@@ -1052,7 +1056,7 @@ class ArrayProxy(Value):
def __init__(self, elems, index, *, src_loc_at=0):
super().__init__(src_loc_at=1 + src_loc_at)
self.elems = elems
self.index = Value.wrap(index)
self.index = Value.cast(index)

def __getattr__(self, attr):
return ArrayProxy([getattr(elem, attr) for elem in self.elems], self.index)
@@ -1061,7 +1065,7 @@ def __getitem__(self, index):
return ArrayProxy([ elem[index] for elem in self.elems], self.index)

def _iter_as_values(self):
return (Value.wrap(elem) for elem in self.elems)
return (Value.cast(elem) for elem in self.elems)

def shape(self):
width, signed = 0, False
@@ -1113,7 +1117,7 @@ def lower(self):

def _lazy_lower(self):
if self.__lowered is None:
self.__lowered = Value.wrap(self.lower())
self.__lowered = Value.cast(self.lower())
return self.__lowered

def shape(self):
@@ -1136,7 +1140,7 @@ class Sample(Value):
"""
def __init__(self, expr, clocks, domain, *, src_loc_at=0):
super().__init__(src_loc_at=1 + src_loc_at)
self.value = Value.wrap(expr)
self.value = Value.cast(expr)
self.clocks = int(clocks)
self.domain = domain
if not isinstance(self.value, (Const, Signal, ClockSignal, ResetSignal, Initial)):
@@ -1219,8 +1223,8 @@ def wrap(obj):
class Assign(Statement):
def __init__(self, lhs, rhs, *, src_loc_at=0):
super().__init__(src_loc_at=src_loc_at)
self.lhs = Value.wrap(lhs)
self.rhs = Value.wrap(rhs)
self.lhs = Value.cast(lhs)
self.rhs = Value.cast(rhs)

def _lhs_signals(self):
return self.lhs._lhs_signals()
@@ -1235,7 +1239,7 @@ def __repr__(self):
class Property(Statement):
def __init__(self, test, *, _check=None, _en=None, src_loc_at=0):
super().__init__(src_loc_at=src_loc_at)
self.test = Value.wrap(test)
self.test = Value.cast(test)
self._check = _check
self._en = _en
if self._check is None:
@@ -1283,7 +1287,7 @@ def __init__(self, test, cases, *, src_loc=None, src_loc_at=0, case_src_locs={})
# be automatically traced, so whatever constructs a Switch may optionally provide it.
self.case_src_locs = {}

self.test = Value.wrap(test)
self.test = Value.cast(test)
self.cases = OrderedDict()
for orig_keys, stmts in cases.items():
# Map: None -> (); key -> (key,); (key...) -> (key...)
@@ -1295,12 +1299,12 @@ def __init__(self, test, cases, *, src_loc=None, src_loc_at=0, case_src_locs={})
# Map: 2 -> "0010"; "0010" -> "0010"
new_keys = ()
for key in keys:
if isinstance(key, (bool, int)):
key = "{:0{}b}".format(key, len(self.test))
elif isinstance(key, str):
if isinstance(key, str):
pass
elif isinstance(key, int):
key = format(key, "b").rjust(len(self.test), "0")
elif isinstance(key, Enum):
key = _enum_to_bits(key)
key = format(key.value, "b").rjust(len(self.test), "0")
else:
raise TypeError("Object '{!r}' cannot be used as a switch key"
.format(key))
@@ -1466,7 +1470,7 @@ def __repr__(self):

class ValueKey:
def __init__(self, value):
self.value = Value.wrap(value)
self.value = Value.cast(value)
if isinstance(self.value, Const):
self._hash = hash(self.value.value)
elif isinstance(self.value, (Signal, AnyValue)):
6 changes: 3 additions & 3 deletions nmigen/hdl/dsl.py
Original file line number Diff line number Diff line change
@@ -172,7 +172,7 @@ def _set_ctrl(self, name, data):
return data

def _check_signed_cond(self, cond):
cond = Value.wrap(cond)
cond = Value.cast(cond)
width, signed = cond.shape()
if signed:
warnings.warn("Signed values in If/Elif conditions usually result from inverting "
@@ -249,7 +249,7 @@ def Else(self):
def Switch(self, test):
self._check_context("Switch", context=None)
switch_data = self._set_ctrl("Switch", {
"test": Value.wrap(test),
"test": Value.cast(test),
"cases": OrderedDict(),
"src_loc": tracer.get_src_loc(src_loc_at=1),
"case_src_locs": {},
@@ -383,7 +383,7 @@ def _pop_ctrl(self):
tests, cases = [], OrderedDict()
for if_test, if_case in zip(if_tests + [None], if_bodies):
if if_test is not None:
if_test = Value.wrap(if_test)
if_test = Value.cast(if_test)
if len(if_test) != 1:
if_test = if_test.bool()
tests.append(if_test)
24 changes: 12 additions & 12 deletions nmigen/test/test_hdl_ast.py
Original file line number Diff line number Diff line change
@@ -23,27 +23,27 @@ class StringEnum(Enum):


class ValueTestCase(FHDLTestCase):
def test_wrap(self):
self.assertIsInstance(Value.wrap(0), Const)
self.assertIsInstance(Value.wrap(True), Const)
def test_cast(self):
self.assertIsInstance(Value.cast(0), Const)
self.assertIsInstance(Value.cast(True), Const)
c = Const(0)
self.assertIs(Value.wrap(c), c)
self.assertIs(Value.cast(c), c)
with self.assertRaises(TypeError,
msg="Object ''str'' is not an nMigen value"):
Value.wrap("str")
msg="Object 'str' is not an nMigen value"):
Value.cast("str")

def test_wrap_enum(self):
e1 = Value.wrap(UnsignedEnum.FOO)
def test_cast_enum(self):
e1 = Value.cast(UnsignedEnum.FOO)
self.assertIsInstance(e1, Const)
self.assertEqual(e1.shape(), (2, False))
e2 = Value.wrap(SignedEnum.FOO)
e2 = Value.cast(SignedEnum.FOO)
self.assertIsInstance(e2, Const)
self.assertEqual(e2.shape(), (2, True))

def test_wrap_enum_wrong(self):
def test_cast_enum_wrong(self):
with self.assertRaises(TypeError,
msg="Only enumerations with integer values can be converted to nMigen values"):
Value.wrap(StringEnum.FOO)
Value.cast(StringEnum.FOO)

def test_bool(self):
with self.assertRaises(TypeError,
@@ -346,7 +346,7 @@ def test_matches(self):
def test_matches_enum(self):
s = Signal.enum(SignedEnum)
self.assertRepr(s.matches(SignedEnum.FOO), """
(== (& (sig s) (const 2'd3)) (const 2'd3))
(== (sig s) (const 1'sd-1))
""")

def test_matches_width_wrong(self):
4 changes: 2 additions & 2 deletions nmigen/test/test_sim.py
Original file line number Diff line number Diff line change
@@ -13,8 +13,8 @@

class SimulatorUnitTestCase(FHDLTestCase):
def assertStatement(self, stmt, inputs, output, reset=0):
inputs = [Value.wrap(i) for i in inputs]
output = Value.wrap(output)
inputs = [Value.cast(i) for i in inputs]
output = Value.cast(output)

isigs = [Signal(i.shape(), name=n) for i, n in zip(inputs, "abcd")]
osig = Signal(output.shape(), name="y", reset=reset)