2018-12-14 15:54:07 -07:00
|
|
|
from abc import ABCMeta, abstractmethod
|
2018-12-12 03:43:09 -07:00
|
|
|
import builtins
|
2018-12-13 04:35:20 -07:00
|
|
|
import traceback
|
2018-12-11 13:50:56 -07:00
|
|
|
from collections import OrderedDict
|
2018-12-15 10:16:22 -07:00
|
|
|
from collections.abc import Iterable, MutableMapping, MutableSet, MutableSequence
|
2018-12-11 13:50:56 -07:00
|
|
|
|
|
|
|
from .. import tracer
|
|
|
|
from ..tools import *
|
|
|
|
|
|
|
|
|
|
|
|
__all__ = [
|
2018-12-14 07:21:22 -07:00
|
|
|
"Value", "Const", "C", "Operator", "Mux", "Part", "Slice", "Cat", "Repl",
|
2018-12-15 10:16:22 -07:00
|
|
|
"Array", "ArrayProxy",
|
2018-12-11 13:50:56 -07:00
|
|
|
"Signal", "ClockSignal", "ResetSignal",
|
2018-12-13 21:33:06 -07:00
|
|
|
"Statement", "Assign", "Switch", "Delay", "Tick", "Passive",
|
2018-12-11 13:50:56 -07:00
|
|
|
"ValueKey", "ValueDict", "ValueSet",
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
class DUID:
|
|
|
|
"""Deterministic Unique IDentifier"""
|
|
|
|
__next_uid = 0
|
|
|
|
def __init__(self):
|
|
|
|
self.duid = DUID.__next_uid
|
|
|
|
DUID.__next_uid += 1
|
|
|
|
|
|
|
|
|
2018-12-14 15:54:07 -07:00
|
|
|
class Value(metaclass=ABCMeta):
|
2018-12-11 13:50:56 -07:00
|
|
|
@staticmethod
|
|
|
|
def wrap(obj):
|
|
|
|
"""Ensures that the passed object is a Migen value. Booleans and integers
|
|
|
|
are automatically wrapped into ``Const``."""
|
|
|
|
if isinstance(obj, Value):
|
|
|
|
return obj
|
|
|
|
elif isinstance(obj, (bool, int)):
|
|
|
|
return Const(obj)
|
|
|
|
else:
|
2018-12-15 02:31:58 -07:00
|
|
|
raise TypeError("Object '{!r}' is not a Migen value".format(obj))
|
2018-12-11 13:50:56 -07:00
|
|
|
|
2018-12-13 04:35:20 -07:00
|
|
|
def __init__(self, src_loc_at=0):
|
|
|
|
super().__init__()
|
|
|
|
|
2018-12-15 10:16:22 -07:00
|
|
|
tb = traceback.extract_stack(limit=3 + src_loc_at)
|
2018-12-13 04:35:20 -07:00
|
|
|
if len(tb) < src_loc_at:
|
|
|
|
self.src_loc = None
|
|
|
|
else:
|
|
|
|
self.src_loc = (tb[0].filename, tb[0].lineno)
|
|
|
|
|
2018-12-11 13:50:56 -07:00
|
|
|
def __bool__(self):
|
|
|
|
raise TypeError("Attempted to convert Migen value to boolean")
|
|
|
|
|
|
|
|
def __invert__(self):
|
|
|
|
return Operator("~", [self])
|
|
|
|
def __neg__(self):
|
|
|
|
return Operator("-", [self])
|
|
|
|
|
|
|
|
def __add__(self, other):
|
|
|
|
return Operator("+", [self, other])
|
|
|
|
def __radd__(self, other):
|
|
|
|
return Operator("+", [other, self])
|
|
|
|
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 __mod__(self, other):
|
|
|
|
return Operator("%", [self, other])
|
|
|
|
def __rmod__(self, other):
|
|
|
|
return Operator("%", [other, self])
|
|
|
|
def __div__(self, other):
|
|
|
|
return Operator("/", [self, other])
|
|
|
|
def __rdiv__(self, other):
|
|
|
|
return Operator("/", [other, self])
|
|
|
|
def __lshift__(self, other):
|
2018-12-15 02:58:30 -07:00
|
|
|
return Operator("<<", [self, other])
|
2018-12-11 13:50:56 -07:00
|
|
|
def __rlshift__(self, other):
|
2018-12-15 02:58:30 -07:00
|
|
|
return Operator("<<", [other, self])
|
2018-12-11 13:50:56 -07:00
|
|
|
def __rshift__(self, other):
|
2018-12-15 02:58:30 -07:00
|
|
|
return Operator(">>", [self, other])
|
2018-12-11 13:50:56 -07:00
|
|
|
def __rrshift__(self, other):
|
2018-12-15 02:58:30 -07:00
|
|
|
return Operator(">>", [other, self])
|
2018-12-11 13:50:56 -07:00
|
|
|
def __and__(self, other):
|
|
|
|
return Operator("&", [self, other])
|
|
|
|
def __rand__(self, other):
|
|
|
|
return Operator("&", [other, self])
|
|
|
|
def __xor__(self, other):
|
|
|
|
return Operator("^", [self, other])
|
|
|
|
def __rxor__(self, other):
|
|
|
|
return Operator("^", [other, self])
|
|
|
|
def __or__(self, other):
|
|
|
|
return Operator("|", [self, other])
|
|
|
|
def __ror__(self, other):
|
|
|
|
return Operator("|", [other, self])
|
|
|
|
|
|
|
|
def __eq__(self, other):
|
|
|
|
return Operator("==", [self, other])
|
|
|
|
def __ne__(self, other):
|
|
|
|
return Operator("!=", [self, other])
|
|
|
|
def __lt__(self, other):
|
|
|
|
return Operator("<", [self, other])
|
|
|
|
def __le__(self, other):
|
|
|
|
return Operator("<=", [self, other])
|
|
|
|
def __gt__(self, other):
|
|
|
|
return Operator(">", [self, other])
|
|
|
|
def __ge__(self, other):
|
|
|
|
return Operator(">=", [self, other])
|
|
|
|
|
|
|
|
def __len__(self):
|
2018-12-12 19:06:49 -07:00
|
|
|
return self.shape()[0]
|
2018-12-11 13:50:56 -07:00
|
|
|
|
|
|
|
def __getitem__(self, key):
|
|
|
|
n = len(self)
|
|
|
|
if isinstance(key, int):
|
|
|
|
if key not in range(-n, n):
|
|
|
|
raise IndexError("Cannot index {} bits into {}-bit value".format(key, n))
|
|
|
|
if key < 0:
|
|
|
|
key += n
|
|
|
|
return Slice(self, key, key + 1)
|
|
|
|
elif isinstance(key, slice):
|
|
|
|
start, stop, step = key.indices(n)
|
|
|
|
if step != 1:
|
|
|
|
return Cat(self[i] for i in range(start, stop, step))
|
|
|
|
return Slice(self, start, stop)
|
|
|
|
else:
|
|
|
|
raise TypeError("Cannot index value with {}".format(repr(key)))
|
|
|
|
|
|
|
|
def bool(self):
|
|
|
|
"""Conversion to boolean.
|
|
|
|
|
|
|
|
Returns
|
|
|
|
-------
|
|
|
|
Value, out
|
|
|
|
Output ``Value``. If any bits are set, returns ``1``, else ``0``.
|
|
|
|
"""
|
|
|
|
return Operator("b", [self])
|
|
|
|
|
|
|
|
def part(self, offset, width):
|
|
|
|
"""Indexed part-select.
|
|
|
|
|
|
|
|
Selects a constant width but variable offset part of a ``Value``.
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
----------
|
|
|
|
offset : Value, in
|
|
|
|
start point of the selected bits
|
|
|
|
width : int
|
|
|
|
number of selected bits
|
|
|
|
|
|
|
|
Returns
|
|
|
|
-------
|
|
|
|
Part, out
|
|
|
|
Selected part of the ``Value``
|
|
|
|
"""
|
|
|
|
return Part(self, offset, width)
|
|
|
|
|
|
|
|
def eq(self, value):
|
|
|
|
"""Assignment.
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
----------
|
|
|
|
value : Value, in
|
|
|
|
Value to be assigned.
|
|
|
|
|
|
|
|
Returns
|
|
|
|
-------
|
|
|
|
Assign
|
|
|
|
Assignment statement that can be used in combinatorial or synchronous context.
|
|
|
|
"""
|
|
|
|
return Assign(self, value)
|
|
|
|
|
2018-12-14 15:54:07 -07:00
|
|
|
@abstractmethod
|
2018-12-12 19:06:49 -07:00
|
|
|
def shape(self):
|
2018-12-11 13:50:56 -07:00
|
|
|
"""Bit length and signedness of a value.
|
|
|
|
|
|
|
|
Returns
|
|
|
|
-------
|
|
|
|
int, bool
|
|
|
|
Number of bits required to store `v` or available in `v`, followed by
|
|
|
|
whether `v` has a sign bit (included in the bit count).
|
|
|
|
|
|
|
|
Examples
|
|
|
|
--------
|
2018-12-12 19:06:49 -07:00
|
|
|
>>> Value.shape(Signal(8))
|
2018-12-11 13:50:56 -07:00
|
|
|
8, False
|
2018-12-12 19:06:49 -07:00
|
|
|
>>> Value.shape(C(0xaa))
|
2018-12-11 13:50:56 -07:00
|
|
|
8, False
|
|
|
|
"""
|
2018-12-14 15:54:07 -07:00
|
|
|
pass # :nocov:
|
2018-12-11 13:50:56 -07:00
|
|
|
|
|
|
|
def _lhs_signals(self):
|
|
|
|
raise TypeError("Value {!r} cannot be used in assignments".format(self))
|
|
|
|
|
2018-12-14 15:54:07 -07:00
|
|
|
@abstractmethod
|
2018-12-11 13:50:56 -07:00
|
|
|
def _rhs_signals(self):
|
2018-12-14 15:54:07 -07:00
|
|
|
pass # :nocov:
|
2018-12-11 13:50:56 -07:00
|
|
|
|
2018-12-14 15:54:07 -07:00
|
|
|
__hash__ = None
|
2018-12-11 13:50:56 -07:00
|
|
|
|
|
|
|
|
|
|
|
class Const(Value):
|
|
|
|
"""A constant, literal integer value.
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
----------
|
|
|
|
value : int
|
2018-12-12 19:06:49 -07:00
|
|
|
shape : int or tuple or None
|
2018-12-11 13:50:56 -07:00
|
|
|
Either an integer `bits` or a tuple `(bits, signed)`
|
|
|
|
specifying the number of bits in this `Const` and whether it is
|
2018-12-12 19:06:49 -07:00
|
|
|
signed (can represent negative values). `shape` defaults
|
2018-12-11 13:50:56 -07:00
|
|
|
to the minimum width and signedness of `value`.
|
|
|
|
|
|
|
|
Attributes
|
|
|
|
----------
|
|
|
|
nbits : int
|
|
|
|
signed : bool
|
|
|
|
"""
|
2018-12-13 11:00:05 -07:00
|
|
|
src_loc = None
|
|
|
|
|
2018-12-13 20:05:57 -07:00
|
|
|
@staticmethod
|
|
|
|
def normalize(value, shape):
|
|
|
|
nbits, signed = shape
|
|
|
|
mask = (1 << nbits) - 1
|
|
|
|
value &= mask
|
|
|
|
if signed and value >> (nbits - 1):
|
|
|
|
value |= ~mask
|
|
|
|
return value
|
|
|
|
|
2018-12-12 19:06:49 -07:00
|
|
|
def __init__(self, value, shape=None):
|
2018-12-11 13:50:56 -07:00
|
|
|
self.value = int(value)
|
2018-12-12 19:06:49 -07:00
|
|
|
if shape is None:
|
2018-12-13 11:00:05 -07:00
|
|
|
shape = bits_for(self.value), self.value < 0
|
2018-12-12 19:06:49 -07:00
|
|
|
if isinstance(shape, int):
|
|
|
|
shape = shape, self.value < 0
|
|
|
|
self.nbits, self.signed = shape
|
2018-12-11 13:50:56 -07:00
|
|
|
if not isinstance(self.nbits, int) or self.nbits < 0:
|
2018-12-15 02:19:26 -07:00
|
|
|
raise TypeError("Width must be a non-negative integer")
|
2018-12-13 20:05:57 -07:00
|
|
|
self.value = self.normalize(self.value, shape)
|
2018-12-13 11:00:05 -07:00
|
|
|
|
2018-12-12 19:06:49 -07:00
|
|
|
def shape(self):
|
2018-12-11 13:50:56 -07:00
|
|
|
return self.nbits, self.signed
|
|
|
|
|
|
|
|
def _rhs_signals(self):
|
|
|
|
return ValueSet()
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
return "(const {}'{}d{})".format(self.nbits, "s" if self.signed else "", self.value)
|
|
|
|
|
|
|
|
|
|
|
|
C = Const # shorthand
|
|
|
|
|
|
|
|
|
|
|
|
class Operator(Value):
|
2018-12-13 04:35:20 -07:00
|
|
|
def __init__(self, op, operands, src_loc_at=0):
|
|
|
|
super().__init__(src_loc_at=1 + src_loc_at)
|
2018-12-11 13:50:56 -07:00
|
|
|
self.op = op
|
|
|
|
self.operands = [Value.wrap(o) for o in operands]
|
|
|
|
|
|
|
|
@staticmethod
|
2018-12-15 02:46:20 -07:00
|
|
|
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:
|
2018-12-11 13:50:56 -07:00
|
|
|
# both operands unsigned
|
2018-12-15 02:46:20 -07:00
|
|
|
return max(a_bits, b_bits), False
|
|
|
|
elif a_sign and b_sign:
|
2018-12-11 13:50:56 -07:00
|
|
|
# both operands signed
|
2018-12-15 02:46:20 -07:00
|
|
|
return max(a_bits, b_bits), True
|
|
|
|
elif not a_sign and b_sign:
|
2018-12-11 13:50:56 -07:00
|
|
|
# first operand unsigned (add sign bit), second operand signed
|
2018-12-15 02:46:20 -07:00
|
|
|
return max(a_bits + 1, b_bits), True
|
2018-12-11 13:50:56 -07:00
|
|
|
else:
|
|
|
|
# first signed, second operand unsigned (add sign bit)
|
2018-12-15 02:46:20 -07:00
|
|
|
return max(a_bits, b_bits + 1), True
|
2018-12-11 13:50:56 -07:00
|
|
|
|
2018-12-12 19:06:49 -07:00
|
|
|
def shape(self):
|
2018-12-15 02:46:20 -07:00
|
|
|
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
|
2018-12-11 13:50:56 -07:00
|
|
|
else:
|
2018-12-15 02:46:20 -07:00
|
|
|
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
|
2018-12-11 13:50:56 -07:00
|
|
|
# one operand signed, the other unsigned (add sign bit)
|
2018-12-15 02:46:20 -07:00
|
|
|
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)
|
2018-12-15 02:58:30 -07:00
|
|
|
if self.op == "<<":
|
2018-12-15 02:46:20 -07:00
|
|
|
if b_sign:
|
2018-12-15 02:58:30 -07:00
|
|
|
extra = 2 ** (b_bits - 1) - 1
|
2018-12-15 02:46:20 -07:00
|
|
|
else:
|
2018-12-15 02:58:30 -07:00
|
|
|
extra = 2 ** (b_bits) - 1
|
2018-12-15 02:46:20 -07:00
|
|
|
return a_bits + extra, a_sign
|
2018-12-15 02:58:30 -07:00
|
|
|
if self.op == ">>":
|
2018-12-15 02:46:20 -07:00
|
|
|
if b_sign:
|
2018-12-15 02:58:30 -07:00
|
|
|
extra = 2 ** (b_bits - 1)
|
2018-12-15 02:46:20 -07:00
|
|
|
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:
|
2018-12-11 13:50:56 -07:00
|
|
|
|
|
|
|
def _rhs_signals(self):
|
|
|
|
return union(op._rhs_signals() for op in self.operands)
|
|
|
|
|
|
|
|
def __repr__(self):
|
2018-12-12 19:04:44 -07:00
|
|
|
return "({} {})".format(self.op, " ".join(map(repr, self.operands)))
|
2018-12-11 13:50:56 -07:00
|
|
|
|
|
|
|
|
|
|
|
def Mux(sel, val1, val0):
|
|
|
|
"""Choose between two values.
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
----------
|
|
|
|
sel : Value, in
|
|
|
|
Selector.
|
|
|
|
val1 : Value, in
|
|
|
|
val0 : Value, in
|
|
|
|
Input values.
|
|
|
|
|
|
|
|
Returns
|
|
|
|
-------
|
|
|
|
Value, out
|
|
|
|
Output ``Value``. If ``sel`` is asserted, the Mux returns ``val1``, else ``val0``.
|
|
|
|
"""
|
2018-12-13 04:35:20 -07:00
|
|
|
return Operator("m", [sel, val1, val0], src_loc_at=1)
|
2018-12-11 13:50:56 -07:00
|
|
|
|
|
|
|
|
|
|
|
class Slice(Value):
|
|
|
|
def __init__(self, value, start, end):
|
|
|
|
if not isinstance(start, int):
|
2018-12-15 02:31:58 -07:00
|
|
|
raise TypeError("Slice start must be an integer, not '{!r}'".format(start))
|
2018-12-11 13:50:56 -07:00
|
|
|
if not isinstance(end, int):
|
2018-12-15 02:31:58 -07:00
|
|
|
raise TypeError("Slice end must be an integer, not '{!r}'".format(end))
|
2018-12-11 13:50:56 -07:00
|
|
|
|
|
|
|
n = len(value)
|
|
|
|
if start not in range(-n, n):
|
|
|
|
raise IndexError("Cannot start slice {} bits into {}-bit value".format(start, n))
|
|
|
|
if start < 0:
|
|
|
|
start += n
|
|
|
|
if end not in range(-(n+1), n+1):
|
|
|
|
raise IndexError("Cannot end slice {} bits into {}-bit value".format(end, n))
|
|
|
|
if end < 0:
|
|
|
|
end += n
|
2018-12-13 11:00:05 -07:00
|
|
|
if start > end:
|
|
|
|
raise IndexError("Slice start {} must be less than slice end {}".format(start, end))
|
2018-12-11 13:50:56 -07:00
|
|
|
|
|
|
|
super().__init__()
|
|
|
|
self.value = Value.wrap(value)
|
|
|
|
self.start = start
|
|
|
|
self.end = end
|
|
|
|
|
2018-12-12 19:06:49 -07:00
|
|
|
def shape(self):
|
2018-12-11 13:50:56 -07:00
|
|
|
return self.end - self.start, False
|
|
|
|
|
|
|
|
def _lhs_signals(self):
|
|
|
|
return self.value._lhs_signals()
|
|
|
|
|
|
|
|
def _rhs_signals(self):
|
|
|
|
return self.value._rhs_signals()
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
return "(slice {} {}:{})".format(repr(self.value), self.start, self.end)
|
|
|
|
|
|
|
|
|
|
|
|
class Part(Value):
|
|
|
|
def __init__(self, value, offset, width):
|
|
|
|
if not isinstance(width, int) or width < 0:
|
2018-12-15 02:31:58 -07:00
|
|
|
raise TypeError("Part width must be a non-negative integer, not '{!r}'".format(width))
|
2018-12-11 13:50:56 -07:00
|
|
|
|
|
|
|
super().__init__()
|
|
|
|
self.value = value
|
|
|
|
self.offset = Value.wrap(offset)
|
|
|
|
self.width = width
|
|
|
|
|
2018-12-12 19:06:49 -07:00
|
|
|
def shape(self):
|
2018-12-11 13:50:56 -07:00
|
|
|
return self.width, False
|
|
|
|
|
|
|
|
def _lhs_signals(self):
|
|
|
|
return self.value._lhs_signals()
|
|
|
|
|
|
|
|
def _rhs_signals(self):
|
2018-12-15 13:58:06 -07:00
|
|
|
return self.value._rhs_signals() | self.offset._rhs_signals()
|
2018-12-11 13:50:56 -07:00
|
|
|
|
|
|
|
def __repr__(self):
|
2018-12-15 02:19:26 -07:00
|
|
|
return "(part {} {} {})".format(repr(self.value), repr(self.offset), self.width)
|
2018-12-11 13:50:56 -07:00
|
|
|
|
|
|
|
|
|
|
|
class Cat(Value):
|
|
|
|
"""Concatenate values.
|
|
|
|
|
|
|
|
Form a compound ``Value`` from several smaller ones by concatenation.
|
|
|
|
The first argument occupies the lower bits of the result.
|
|
|
|
The return value can be used on either side of an assignment, that
|
|
|
|
is, the concatenated value can be used as an argument on the RHS or
|
|
|
|
as a target on the LHS. If it is used on the LHS, it must solely
|
|
|
|
consist of ``Signal`` s, slices of ``Signal`` s, and other concatenations
|
|
|
|
meeting these properties. The bit length of the return value is the sum of
|
|
|
|
the bit lengths of the arguments::
|
|
|
|
|
|
|
|
len(Cat(args)) == sum(len(arg) for arg in args)
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
----------
|
|
|
|
*args : Values or iterables of Values, inout
|
|
|
|
``Value`` s to be concatenated.
|
|
|
|
|
|
|
|
Returns
|
|
|
|
-------
|
|
|
|
Value, inout
|
|
|
|
Resulting ``Value`` obtained by concatentation.
|
|
|
|
"""
|
|
|
|
def __init__(self, *args):
|
|
|
|
super().__init__()
|
|
|
|
self.operands = [Value.wrap(v) for v in flatten(args)]
|
|
|
|
|
2018-12-12 19:06:49 -07:00
|
|
|
def shape(self):
|
2018-12-11 13:50:56 -07:00
|
|
|
return sum(len(op) for op in self.operands), False
|
|
|
|
|
|
|
|
def _lhs_signals(self):
|
|
|
|
return union(op._lhs_signals() for op in self.operands)
|
|
|
|
|
|
|
|
def _rhs_signals(self):
|
|
|
|
return union(op._rhs_signals() for op in self.operands)
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
return "(cat {})".format(" ".join(map(repr, self.operands)))
|
|
|
|
|
|
|
|
|
|
|
|
class Repl(Value):
|
|
|
|
"""Replicate a value
|
|
|
|
|
|
|
|
An input value is replicated (repeated) several times
|
|
|
|
to be used on the RHS of assignments::
|
|
|
|
|
|
|
|
len(Repl(s, n)) == len(s) * n
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
----------
|
|
|
|
value : Value, in
|
|
|
|
Input value to be replicated.
|
|
|
|
count : int
|
|
|
|
Number of replications.
|
|
|
|
|
|
|
|
Returns
|
|
|
|
-------
|
|
|
|
Repl, out
|
|
|
|
Replicated value.
|
|
|
|
"""
|
|
|
|
def __init__(self, value, count):
|
|
|
|
if not isinstance(count, int) or count < 0:
|
2018-12-15 02:31:58 -07:00
|
|
|
raise TypeError("Replication count must be a non-negative integer, not '{!r}'"
|
2018-12-15 02:19:26 -07:00
|
|
|
.format(count))
|
2018-12-11 13:50:56 -07:00
|
|
|
|
|
|
|
super().__init__()
|
|
|
|
self.value = Value.wrap(value)
|
|
|
|
self.count = count
|
|
|
|
|
2018-12-12 19:06:49 -07:00
|
|
|
def shape(self):
|
2018-12-11 13:50:56 -07:00
|
|
|
return len(self.value) * self.count, False
|
|
|
|
|
|
|
|
def _rhs_signals(self):
|
2018-12-12 19:04:44 -07:00
|
|
|
return self.value._rhs_signals()
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
return "(repl {!r} {})".format(self.value, self.count)
|
2018-12-11 13:50:56 -07:00
|
|
|
|
|
|
|
|
|
|
|
class Signal(Value, DUID):
|
|
|
|
"""A varying integer value.
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
----------
|
2018-12-12 19:06:49 -07:00
|
|
|
shape : int or tuple or None
|
2018-12-11 13:50:56 -07:00
|
|
|
Either an integer ``bits`` or a tuple ``(bits, signed)`` specifying the number of bits
|
|
|
|
in this ``Signal`` and whether it is signed (can represent negative values).
|
2018-12-12 19:06:49 -07:00
|
|
|
``shape`` defaults to 1-bit and non-signed.
|
2018-12-11 13:50:56 -07:00
|
|
|
name : str
|
|
|
|
Name hint for this signal. If ``None`` (default) the name is inferred from the variable
|
|
|
|
name this ``Signal`` is assigned to. Name collisions are automatically resolved by
|
|
|
|
prepending names of objects that contain this ``Signal`` and by appending integer
|
|
|
|
sequences.
|
|
|
|
reset : int
|
|
|
|
Reset (synchronous) or default (combinatorial) value.
|
|
|
|
When this ``Signal`` is assigned to in synchronous context and the corresponding clock
|
|
|
|
domain is reset, the ``Signal`` assumes the given value. When this ``Signal`` is unassigned
|
|
|
|
in combinatorial context (due to conditional assignments not being taken), the ``Signal``
|
|
|
|
assumes its ``reset`` value. Defaults to 0.
|
2018-12-12 03:11:16 -07:00
|
|
|
reset_less : bool
|
|
|
|
If ``True``, do not generate reset logic for this ``Signal`` in synchronous statements.
|
|
|
|
The ``reset`` value is only used as a combinatorial default or as the initial value.
|
|
|
|
Defaults to ``False``.
|
2018-12-12 03:43:09 -07:00
|
|
|
min : int or None
|
|
|
|
max : int or None
|
2018-12-12 19:06:49 -07:00
|
|
|
If ``shape`` is ``None``, the signal bit width and signedness are
|
|
|
|
determined by the integer range given by ``min`` (inclusive,
|
|
|
|
defaults to 0) and ``max`` (exclusive, defaults to 2).
|
2018-12-12 04:02:13 -07:00
|
|
|
attrs : dict
|
|
|
|
Dictionary of synthesis attributes.
|
2018-12-14 02:02:29 -07:00
|
|
|
decoder : function
|
|
|
|
A function converting integer signal values to human-readable strings (e.g. FSM state
|
|
|
|
names).
|
2018-12-11 13:50:56 -07:00
|
|
|
|
|
|
|
Attributes
|
|
|
|
----------
|
|
|
|
nbits : int
|
|
|
|
signed : bool
|
|
|
|
name : str
|
|
|
|
reset : int
|
2018-12-12 04:02:13 -07:00
|
|
|
reset_less : bool
|
|
|
|
attrs : dict
|
2018-12-11 13:50:56 -07:00
|
|
|
"""
|
|
|
|
|
2018-12-12 19:06:49 -07:00
|
|
|
def __init__(self, shape=None, name=None, reset=0, reset_less=False, min=None, max=None,
|
2018-12-14 02:02:29 -07:00
|
|
|
attrs=None, decoder=None, src_loc_at=0):
|
2018-12-13 04:35:20 -07:00
|
|
|
super().__init__(src_loc_at=src_loc_at)
|
2018-12-11 13:50:56 -07:00
|
|
|
|
|
|
|
if name is None:
|
2018-12-12 03:08:56 -07:00
|
|
|
try:
|
2018-12-13 06:12:31 -07:00
|
|
|
name = tracer.get_var_name(depth=2 + src_loc_at)
|
2018-12-12 03:08:56 -07:00
|
|
|
except tracer.NameNotFound:
|
|
|
|
name = "$signal"
|
2018-12-11 13:50:56 -07:00
|
|
|
self.name = name
|
|
|
|
|
2018-12-12 19:06:49 -07:00
|
|
|
if shape is None:
|
2018-12-12 03:43:09 -07:00
|
|
|
if min is None:
|
|
|
|
min = 0
|
|
|
|
if max is None:
|
|
|
|
max = 2
|
|
|
|
max -= 1 # make both bounds inclusive
|
|
|
|
if not min < max:
|
2018-12-15 02:31:58 -07:00
|
|
|
raise ValueError("Lower bound {} should be less than higher bound {}"
|
2018-12-12 03:43:09 -07:00
|
|
|
.format(min, max))
|
|
|
|
self.signed = min < 0 or max < 0
|
|
|
|
self.nbits = builtins.max(bits_for(min, self.signed), bits_for(max, self.signed))
|
|
|
|
|
|
|
|
else:
|
2018-12-12 19:04:44 -07:00
|
|
|
if not (min is None and max is None):
|
|
|
|
raise ValueError("Only one of bits/signedness or bounds may be specified")
|
2018-12-12 19:06:49 -07:00
|
|
|
if isinstance(shape, int):
|
|
|
|
self.nbits, self.signed = shape, False
|
2018-12-12 19:04:44 -07:00
|
|
|
else:
|
2018-12-12 19:06:49 -07:00
|
|
|
self.nbits, self.signed = shape
|
2018-12-12 03:43:09 -07:00
|
|
|
|
2018-12-11 13:50:56 -07:00
|
|
|
if not isinstance(self.nbits, int) or self.nbits < 0:
|
2018-12-15 02:31:58 -07:00
|
|
|
raise TypeError("Width must be a non-negative integer, not '{!r}'".format(self.nbits))
|
2018-12-12 19:04:44 -07:00
|
|
|
self.reset = int(reset)
|
|
|
|
self.reset_less = bool(reset_less)
|
2018-12-11 13:50:56 -07:00
|
|
|
|
2018-12-12 04:02:13 -07:00
|
|
|
self.attrs = OrderedDict(() if attrs is None else attrs)
|
2018-12-14 02:02:29 -07:00
|
|
|
self.decoder = decoder
|
2018-12-12 04:02:13 -07:00
|
|
|
|
2018-12-12 07:43:03 -07:00
|
|
|
@classmethod
|
2018-12-13 04:35:20 -07:00
|
|
|
def like(cls, other, src_loc_at=0, **kwargs):
|
2018-12-12 07:43:03 -07:00
|
|
|
"""Create Signal based on another.
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
----------
|
|
|
|
other : Value
|
|
|
|
Object to base this Signal on.
|
|
|
|
"""
|
2018-12-13 06:12:31 -07:00
|
|
|
kw = dict(shape=cls.wrap(other).shape(),
|
|
|
|
name=tracer.get_var_name(depth=2 + src_loc_at))
|
2018-12-12 07:43:03 -07:00
|
|
|
if isinstance(other, cls):
|
2018-12-14 02:02:29 -07:00
|
|
|
kw.update(reset=other.reset, reset_less=other.reset_less,
|
|
|
|
attrs=other.attrs, decoder=other.decoder)
|
2018-12-12 07:43:03 -07:00
|
|
|
kw.update(kwargs)
|
2018-12-13 04:35:20 -07:00
|
|
|
return cls(**kw, src_loc_at=1 + src_loc_at)
|
2018-12-12 07:43:03 -07:00
|
|
|
|
2018-12-12 19:06:49 -07:00
|
|
|
def shape(self):
|
2018-12-11 13:50:56 -07:00
|
|
|
return self.nbits, self.signed
|
|
|
|
|
|
|
|
def _lhs_signals(self):
|
|
|
|
return ValueSet((self,))
|
|
|
|
|
|
|
|
def _rhs_signals(self):
|
|
|
|
return ValueSet((self,))
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
return "(sig {})".format(self.name)
|
|
|
|
|
|
|
|
|
|
|
|
class ClockSignal(Value):
|
2018-12-15 07:58:31 -07:00
|
|
|
"""Clock signal for a clock domain.
|
2018-12-11 13:50:56 -07:00
|
|
|
|
2018-12-15 07:58:31 -07:00
|
|
|
Any ``ClockSignal`` is equivalent to ``cd.clk`` for a clock domain with the corresponding name.
|
|
|
|
All of these signals ultimately refer to the same signal, but they can be manipulated
|
|
|
|
independently of the clock domain, even before the clock domain is created.
|
2018-12-11 13:50:56 -07:00
|
|
|
|
|
|
|
Parameters
|
|
|
|
----------
|
2018-12-12 19:04:44 -07:00
|
|
|
domain : str
|
2018-12-13 01:57:14 -07:00
|
|
|
Clock domain to obtain a clock signal for. Defaults to ``"sync"``.
|
2018-12-11 13:50:56 -07:00
|
|
|
"""
|
2018-12-12 19:04:44 -07:00
|
|
|
def __init__(self, domain="sync"):
|
2018-12-11 13:50:56 -07:00
|
|
|
super().__init__()
|
2018-12-12 19:04:44 -07:00
|
|
|
if not isinstance(domain, str):
|
2018-12-15 02:31:58 -07:00
|
|
|
raise TypeError("Clock domain name must be a string, not '{!r}'".format(domain))
|
2018-12-12 19:04:44 -07:00
|
|
|
self.domain = domain
|
2018-12-11 13:50:56 -07:00
|
|
|
|
2018-12-14 15:54:07 -07:00
|
|
|
def shape(self):
|
|
|
|
return 1, False
|
|
|
|
|
|
|
|
def _rhs_signals(self):
|
|
|
|
raise NotImplementedError("ClockSignal must be lowered to a concrete signal") # :nocov:
|
|
|
|
|
2018-12-11 13:50:56 -07:00
|
|
|
def __repr__(self):
|
2018-12-12 19:04:44 -07:00
|
|
|
return "(clk {})".format(self.domain)
|
2018-12-11 13:50:56 -07:00
|
|
|
|
|
|
|
|
|
|
|
class ResetSignal(Value):
|
2018-12-15 07:58:31 -07:00
|
|
|
"""Reset signal for a clock domain.
|
2018-12-11 13:50:56 -07:00
|
|
|
|
2018-12-15 07:58:31 -07:00
|
|
|
Any ``ResetSignal`` is equivalent to ``cd.rst`` for a clock domain with the corresponding name.
|
|
|
|
All of these signals ultimately refer to the same signal, but they can be manipulated
|
|
|
|
independently of the clock domain, even before the clock domain is created.
|
2018-12-11 13:50:56 -07:00
|
|
|
|
|
|
|
Parameters
|
|
|
|
----------
|
2018-12-12 19:04:44 -07:00
|
|
|
domain : str
|
2018-12-13 01:57:14 -07:00
|
|
|
Clock domain to obtain a reset signal for. Defaults to ``"sync"``.
|
2018-12-14 03:56:53 -07:00
|
|
|
allow_reset_less : bool
|
|
|
|
If the clock domain is reset-less, act as a constant ``0`` instead of reporting an error.
|
2018-12-11 13:50:56 -07:00
|
|
|
"""
|
2018-12-14 03:56:53 -07:00
|
|
|
def __init__(self, domain="sync", allow_reset_less=False):
|
2018-12-11 13:50:56 -07:00
|
|
|
super().__init__()
|
2018-12-12 19:04:44 -07:00
|
|
|
if not isinstance(domain, str):
|
2018-12-15 02:31:58 -07:00
|
|
|
raise TypeError("Clock domain name must be a string, not '{!r}'".format(domain))
|
2018-12-12 19:04:44 -07:00
|
|
|
self.domain = domain
|
2018-12-14 03:56:53 -07:00
|
|
|
self.allow_reset_less = allow_reset_less
|
2018-12-11 13:50:56 -07:00
|
|
|
|
2018-12-14 15:54:07 -07:00
|
|
|
def shape(self):
|
|
|
|
return 1, False
|
|
|
|
|
|
|
|
def _rhs_signals(self):
|
|
|
|
raise NotImplementedError("ResetSignal must be lowered to a concrete signal") # :nocov:
|
|
|
|
|
2018-12-11 13:50:56 -07:00
|
|
|
def __repr__(self):
|
2018-12-13 01:57:14 -07:00
|
|
|
return "(rst {})".format(self.domain)
|
2018-12-11 13:50:56 -07:00
|
|
|
|
|
|
|
|
2018-12-15 10:16:22 -07:00
|
|
|
class Array(MutableSequence):
|
|
|
|
"""Addressable multiplexer.
|
|
|
|
|
|
|
|
An array is similar to a ``list`` that can also be indexed by ``Value``s; indexing by an integer or a slice works the same as for Python lists, but indexing by a ``Value`` results
|
|
|
|
in a proxy.
|
|
|
|
|
|
|
|
The array proxy can be used as an ordinary ``Value``, i.e. participate in calculations and
|
|
|
|
assignments, provided that all elements of the array are values. The array proxy also supports
|
|
|
|
attribute access and further indexing, each returning another array proxy; this means that
|
|
|
|
the results of indexing into arrays, arrays of records, and arrays of arrays can all
|
|
|
|
be used as first-class values.
|
|
|
|
|
|
|
|
It is an error to change an array or any of its elements after an array proxy was created.
|
|
|
|
Changing the array directly will raise an exception. However, it is not possible to detect
|
|
|
|
the elements being modified; if an element's attribute or element is modified after the proxy
|
|
|
|
for it has been created, the proxy will refer to stale data.
|
|
|
|
|
|
|
|
Examples
|
|
|
|
--------
|
|
|
|
|
|
|
|
Simple array::
|
|
|
|
|
|
|
|
gpios = Array(Signal() for _ in range(10))
|
|
|
|
with m.If(bus.we):
|
|
|
|
m.d.sync += gpios[bus.adr].eq(bus.dat_w)
|
|
|
|
with m.Else():
|
|
|
|
m.d.sync += bus.dat_r.eq(gpios[bus.adr])
|
|
|
|
|
|
|
|
Multidimensional array::
|
|
|
|
|
|
|
|
mult = Array(Array(x * y for y in range(10)) for x in range(10))
|
|
|
|
a = Signal(max=10)
|
|
|
|
b = Signal(max=10)
|
|
|
|
r = Signal(8)
|
|
|
|
m.d.comb += r.eq(mult[a][b])
|
|
|
|
|
|
|
|
Array of records::
|
|
|
|
|
|
|
|
layout = [
|
|
|
|
("re", 1),
|
|
|
|
("dat_r", 16),
|
|
|
|
]
|
|
|
|
buses = Array(Record(layout) for busno in range(4))
|
|
|
|
master = Record(layout)
|
|
|
|
m.d.comb += [
|
|
|
|
buses[sel].re.eq(master.re),
|
|
|
|
master.dat_r.eq(buses[sel].dat_r),
|
|
|
|
]
|
|
|
|
"""
|
2018-12-16 03:38:25 -07:00
|
|
|
def __init__(self, iterable=()):
|
2018-12-15 10:16:22 -07:00
|
|
|
self._inner = list(iterable)
|
|
|
|
self._proxy_at = None
|
|
|
|
self._mutable = True
|
|
|
|
|
|
|
|
def __getitem__(self, index):
|
|
|
|
if isinstance(index, Value):
|
|
|
|
if self._mutable:
|
|
|
|
tb = traceback.extract_stack(limit=2)
|
|
|
|
self._proxy_at = (tb[0].filename, tb[0].lineno)
|
|
|
|
self._mutable = False
|
|
|
|
return ArrayProxy(self, index)
|
|
|
|
else:
|
|
|
|
return self._inner[index]
|
|
|
|
|
|
|
|
def __len__(self):
|
|
|
|
return len(self._inner)
|
|
|
|
|
|
|
|
def _check_mutability(self):
|
|
|
|
if not self._mutable:
|
|
|
|
raise ValueError("Array can no longer be mutated after it was indexed with a value "
|
|
|
|
"at {}:{}".format(*self._proxy_at))
|
|
|
|
|
|
|
|
def __setitem__(self, index, value):
|
|
|
|
self._check_mutability()
|
|
|
|
self._inner[index] = value
|
|
|
|
|
|
|
|
def __delitem__(self, index):
|
|
|
|
self._check_mutability()
|
|
|
|
del self._inner[index]
|
|
|
|
|
|
|
|
def insert(self, index, value):
|
|
|
|
self._check_mutability()
|
|
|
|
self._inner.insert(index, value)
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
return "(array{} [{}])".format(" mutable" if self._mutable else "",
|
|
|
|
", ".join(map(repr, self._inner)))
|
|
|
|
|
|
|
|
|
|
|
|
class ArrayProxy(Value):
|
|
|
|
def __init__(self, elems, index):
|
|
|
|
super().__init__(src_loc_at=1)
|
|
|
|
self.elems = elems
|
|
|
|
self.index = Value.wrap(index)
|
|
|
|
|
|
|
|
def __getattr__(self, attr):
|
|
|
|
return ArrayProxy([getattr(elem, attr) for elem in self.elems], self.index)
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
|
|
|
def shape(self):
|
|
|
|
bits, sign = 0, False
|
|
|
|
for elem_bits, elem_sign in (elem.shape() for elem in self._iter_as_values()):
|
|
|
|
bits = max(bits, elem_bits + elem_sign)
|
|
|
|
sign = max(sign, elem_sign)
|
|
|
|
return bits, sign
|
|
|
|
|
|
|
|
def _lhs_signals(self):
|
2018-12-15 12:37:36 -07:00
|
|
|
signals = union((elem._lhs_signals() for elem in self._iter_as_values()), start=ValueSet())
|
|
|
|
return signals
|
2018-12-15 10:16:22 -07:00
|
|
|
|
|
|
|
def _rhs_signals(self):
|
2018-12-15 12:37:36 -07:00
|
|
|
signals = union((elem._rhs_signals() for elem in self._iter_as_values()), start=ValueSet())
|
|
|
|
return self.index._rhs_signals() | signals
|
2018-12-15 10:16:22 -07:00
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
return "(proxy (array [{}]) {!r})".format(", ".join(map(repr, self.elems)), self.index)
|
|
|
|
|
|
|
|
|
2018-12-12 23:06:51 -07:00
|
|
|
class _StatementList(list):
|
|
|
|
def __repr__(self):
|
|
|
|
return "({})".format(" ".join(map(repr, self)))
|
|
|
|
|
|
|
|
|
2018-12-11 13:50:56 -07:00
|
|
|
class Statement:
|
|
|
|
@staticmethod
|
|
|
|
def wrap(obj):
|
|
|
|
if isinstance(obj, Iterable):
|
2018-12-12 23:06:51 -07:00
|
|
|
return _StatementList(sum((Statement.wrap(e) for e in obj), []))
|
2018-12-11 13:50:56 -07:00
|
|
|
else:
|
|
|
|
if isinstance(obj, Statement):
|
2018-12-12 23:06:51 -07:00
|
|
|
return _StatementList([obj])
|
2018-12-11 13:50:56 -07:00
|
|
|
else:
|
2018-12-15 02:31:58 -07:00
|
|
|
raise TypeError("Object '{!r}' is not a Migen statement".format(obj))
|
2018-12-11 13:50:56 -07:00
|
|
|
|
|
|
|
|
|
|
|
class Assign(Statement):
|
|
|
|
def __init__(self, lhs, rhs):
|
|
|
|
self.lhs = Value.wrap(lhs)
|
|
|
|
self.rhs = Value.wrap(rhs)
|
|
|
|
|
|
|
|
def _lhs_signals(self):
|
|
|
|
return self.lhs._lhs_signals()
|
|
|
|
|
|
|
|
def _rhs_signals(self):
|
2018-12-16 03:31:42 -07:00
|
|
|
return self.lhs._rhs_signals() | self.rhs._rhs_signals()
|
2018-12-11 13:50:56 -07:00
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
return "(eq {!r} {!r})".format(self.lhs, self.rhs)
|
|
|
|
|
|
|
|
|
|
|
|
class Switch(Statement):
|
|
|
|
def __init__(self, test, cases):
|
|
|
|
self.test = Value.wrap(test)
|
|
|
|
self.cases = OrderedDict()
|
|
|
|
for key, stmts in cases.items():
|
|
|
|
if isinstance(key, (bool, int)):
|
2018-12-14 09:07:25 -07:00
|
|
|
key = "{:0{}b}".format(key, len(self.test))
|
2018-12-11 13:50:56 -07:00
|
|
|
elif isinstance(key, str):
|
2018-12-14 09:07:25 -07:00
|
|
|
assert len(key) == len(self.test)
|
2018-12-11 13:50:56 -07:00
|
|
|
else:
|
2018-12-15 02:31:58 -07:00
|
|
|
raise TypeError("Object '{!r}' cannot be used as a switch key"
|
2018-12-14 15:54:07 -07:00
|
|
|
.format(key))
|
2018-12-11 13:50:56 -07:00
|
|
|
if not isinstance(stmts, Iterable):
|
|
|
|
stmts = [stmts]
|
|
|
|
self.cases[key] = Statement.wrap(stmts)
|
|
|
|
|
|
|
|
def _lhs_signals(self):
|
2018-12-15 10:16:22 -07:00
|
|
|
signals = union((s._lhs_signals() for ss in self.cases.values() for s in ss),
|
|
|
|
start=ValueSet())
|
2018-12-12 22:00:44 -07:00
|
|
|
return signals
|
2018-12-11 13:50:56 -07:00
|
|
|
|
|
|
|
def _rhs_signals(self):
|
2018-12-15 10:16:22 -07:00
|
|
|
signals = union((s._rhs_signals() for ss in self.cases.values() for s in ss),
|
|
|
|
start=ValueSet())
|
2018-12-11 13:50:56 -07:00
|
|
|
return self.test._rhs_signals() | signals
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
cases = ["(case {} {})".format(key, " ".join(map(repr, stmts)))
|
|
|
|
for key, stmts in self.cases.items()]
|
|
|
|
return "(switch {!r} {})".format(self.test, " ".join(cases))
|
|
|
|
|
|
|
|
|
2018-12-13 11:00:05 -07:00
|
|
|
class Delay(Statement):
|
2018-12-14 06:21:58 -07:00
|
|
|
def __init__(self, interval=None):
|
|
|
|
self.interval = None if interval is None else float(interval)
|
2018-12-13 11:00:05 -07:00
|
|
|
|
|
|
|
def _rhs_signals(self):
|
|
|
|
return ValueSet()
|
|
|
|
|
|
|
|
def __repr__(self):
|
2018-12-14 06:21:58 -07:00
|
|
|
if self.interval is None:
|
|
|
|
return "(delay ε)"
|
|
|
|
else:
|
|
|
|
return "(delay {:.3}us)".format(self.interval * 10e6)
|
2018-12-13 11:00:05 -07:00
|
|
|
|
|
|
|
|
2018-12-13 21:33:06 -07:00
|
|
|
class Tick(Statement):
|
|
|
|
def __init__(self, domain):
|
|
|
|
self.domain = str(domain)
|
|
|
|
|
|
|
|
def _rhs_signals(self):
|
|
|
|
return ValueSet()
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
return "(tick {})".format(self.domain)
|
|
|
|
|
|
|
|
|
2018-12-13 11:00:05 -07:00
|
|
|
class Passive(Statement):
|
|
|
|
def _rhs_signals(self):
|
|
|
|
return ValueSet()
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
return "(passive)"
|
|
|
|
|
|
|
|
|
2018-12-11 13:50:56 -07:00
|
|
|
class ValueKey:
|
|
|
|
def __init__(self, value):
|
|
|
|
self.value = Value.wrap(value)
|
|
|
|
|
|
|
|
def __hash__(self):
|
|
|
|
if isinstance(self.value, Const):
|
|
|
|
return hash(self.value)
|
|
|
|
elif isinstance(self.value, Signal):
|
|
|
|
return hash(id(self.value))
|
|
|
|
elif isinstance(self.value, Slice):
|
|
|
|
return hash((ValueKey(self.value.value), self.value.start, self.value.end))
|
2018-12-14 15:54:07 -07:00
|
|
|
else: # :nocov:
|
2018-12-15 02:31:58 -07:00
|
|
|
raise TypeError("Object '{!r}' cannot be used as a key in value collections")
|
2018-12-11 13:50:56 -07:00
|
|
|
|
|
|
|
def __eq__(self, other):
|
|
|
|
if not isinstance(other, ValueKey):
|
|
|
|
return False
|
|
|
|
if type(self.value) != type(other.value):
|
|
|
|
return False
|
|
|
|
|
|
|
|
if isinstance(self.value, Const):
|
|
|
|
return self.value == other.value
|
|
|
|
elif isinstance(self.value, Signal):
|
|
|
|
return id(self.value) == id(other.value)
|
|
|
|
elif isinstance(self.value, Slice):
|
|
|
|
return (ValueKey(self.value.value) == ValueKey(other.value.value) and
|
|
|
|
self.value.start == other.value.start and
|
|
|
|
self.value.end == other.value.end)
|
2018-12-14 15:54:07 -07:00
|
|
|
else: # :nocov:
|
2018-12-15 02:31:58 -07:00
|
|
|
raise TypeError("Object '{!r}' cannot be used as a key in value collections")
|
2018-12-11 13:50:56 -07:00
|
|
|
|
|
|
|
def __lt__(self, other):
|
|
|
|
if not isinstance(other, ValueKey):
|
|
|
|
return False
|
|
|
|
if type(self.value) != type(other.value):
|
|
|
|
return False
|
|
|
|
|
|
|
|
if isinstance(self.value, Const):
|
|
|
|
return self.value < other.value
|
|
|
|
elif isinstance(self.value, Signal):
|
|
|
|
return self.value.duid < other.value.duid
|
|
|
|
elif isinstance(self.value, Slice):
|
|
|
|
return (ValueKey(self.value.value) < ValueKey(other.value.value) and
|
|
|
|
self.value.start < other.value.start and
|
|
|
|
self.value.end < other.value.end)
|
2018-12-14 15:54:07 -07:00
|
|
|
else: # :nocov:
|
2018-12-15 02:31:58 -07:00
|
|
|
raise TypeError("Object '{!r}' cannot be used as a key in value collections")
|
2018-12-11 13:50:56 -07:00
|
|
|
|
2018-12-13 07:33:39 -07:00
|
|
|
def __repr__(self):
|
|
|
|
return "<{}.ValueKey {!r}>".format(__name__, self.value)
|
|
|
|
|
2018-12-11 13:50:56 -07:00
|
|
|
|
|
|
|
class ValueDict(MutableMapping):
|
|
|
|
def __init__(self, pairs=()):
|
|
|
|
self._inner = dict()
|
|
|
|
for key, value in pairs:
|
|
|
|
self[key] = value
|
|
|
|
|
|
|
|
def __getitem__(self, key):
|
|
|
|
key = None if key is None else ValueKey(key)
|
|
|
|
return self._inner[key]
|
|
|
|
|
|
|
|
def __setitem__(self, key, value):
|
|
|
|
key = None if key is None else ValueKey(key)
|
|
|
|
self._inner[key] = value
|
|
|
|
|
|
|
|
def __delitem__(self, key):
|
|
|
|
key = None if key is None else ValueKey(key)
|
|
|
|
del self._inner[key]
|
|
|
|
|
|
|
|
def __iter__(self):
|
|
|
|
return map(lambda x: None if x is None else x.value, sorted(self._inner))
|
|
|
|
|
2018-12-13 06:12:31 -07:00
|
|
|
def __eq__(self, other):
|
|
|
|
if not isinstance(other, ValueDict):
|
|
|
|
return False
|
|
|
|
if len(self) != len(other):
|
|
|
|
return False
|
|
|
|
for ak, bk in zip(self, other):
|
|
|
|
if ValueKey(ak) != ValueKey(bk):
|
|
|
|
return False
|
|
|
|
if self[ak] != other[bk]:
|
|
|
|
return False
|
|
|
|
return True
|
|
|
|
|
2018-12-11 13:50:56 -07:00
|
|
|
def __len__(self):
|
|
|
|
return len(self._inner)
|
|
|
|
|
2018-12-13 06:12:31 -07:00
|
|
|
def __repr__(self):
|
|
|
|
pairs = ["({!r}, {!r})".format(k, v) for k, v in self.items()]
|
|
|
|
return "ValueDict([{}])".format(", ".join(pairs))
|
|
|
|
|
2018-12-11 13:50:56 -07:00
|
|
|
|
|
|
|
class ValueSet(MutableSet):
|
|
|
|
def __init__(self, elements=()):
|
|
|
|
self._inner = set()
|
|
|
|
for elem in elements:
|
|
|
|
self.add(elem)
|
|
|
|
|
|
|
|
def add(self, value):
|
|
|
|
self._inner.add(ValueKey(value))
|
|
|
|
|
|
|
|
def update(self, values):
|
|
|
|
for value in values:
|
|
|
|
self.add(value)
|
|
|
|
|
|
|
|
def discard(self, value):
|
|
|
|
self._inner.discard(ValueKey(value))
|
|
|
|
|
|
|
|
def __contains__(self, value):
|
|
|
|
return ValueKey(value) in self._inner
|
|
|
|
|
|
|
|
def __iter__(self):
|
|
|
|
return map(lambda x: x.value, sorted(self._inner))
|
|
|
|
|
|
|
|
def __len__(self):
|
|
|
|
return len(self._inner)
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
return "ValueSet({})".format(", ".join(repr(x) for x in self))
|