1 from abc
import ABCMeta
, abstractmethod
5 from collections
import OrderedDict
6 from collections
.abc
import Iterable
, MutableMapping
, MutableSet
, MutableSequence
10 from .._utils
import *
11 from .._unused
import *
15 "Shape", "signed", "unsigned",
16 "Value", "Const", "C", "AnyConst", "AnySeq", "Operator", "Mux", "Part", "Slice", "Cat", "Repl",
17 "Array", "ArrayProxy",
18 "Signal", "ClockSignal", "ResetSignal",
20 "Sample", "Past", "Stable", "Rose", "Fell", "Initial",
21 "Statement", "Switch",
22 "Property", "Assign", "Assert", "Assume", "Cover",
23 "ValueKey", "ValueDict", "ValueSet", "SignalKey", "SignalDict", "SignalSet",
28 """Deterministic Unique IDentifier."""
31 self
.duid
= DUID
.__next
_uid
35 class Shape(typing
.NamedTuple
):
36 """Bit width and signedness of a value.
38 A ``Shape`` can be constructed using:
39 * explicit bit width and signedness;
40 * aliases :func:`signed` and :func:`unsigned`;
41 * casting from a variety of objects.
43 A ``Shape`` can be cast from:
44 * an integer, where the integer specifies the bit width;
45 * a range, where the result is wide enough to represent any element of the range, and is
46 signed if any element of the range is signed;
47 * an :class:`Enum` with all integer members or :class:`IntEnum`, where the result is wide
48 enough to represent any member of the enumeration, and is signed if any member of
49 the enumeration is signed.
54 The number of bits in the representation, including the sign bit (if any).
56 If ``False``, the value is unsigned. If ``True``, the value is signed two's complement.
62 def cast(obj
, *, src_loc_at
=0):
63 if isinstance(obj
, Shape
):
65 if isinstance(obj
, int):
67 if isinstance(obj
, tuple):
69 warnings
.warn("instead of `{tuple}`, use `{constructor}({width})`"
70 .format(constructor
="signed" if signed
else "unsigned", width
=width
,
72 DeprecationWarning, stacklevel
=2 + src_loc_at
)
73 return Shape(width
, signed
)
74 if isinstance(obj
, range):
76 return Shape(0, obj
.start
< 0)
77 signed
= obj
.start
< 0 or (obj
.stop
- obj
.step
) < 0
78 width
= max(bits_for(obj
.start
, signed
),
79 bits_for(obj
.stop
- obj
.step
, signed
))
80 return Shape(width
, signed
)
81 if isinstance(obj
, type) and issubclass(obj
, Enum
):
82 min_value
= min(member
.value
for member
in obj
)
83 max_value
= max(member
.value
for member
in obj
)
84 if not isinstance(min_value
, int) or not isinstance(max_value
, int):
85 raise TypeError("Only enumerations with integer values can be used "
87 signed
= min_value
< 0 or max_value
< 0
88 width
= max(bits_for(min_value
, signed
), bits_for(max_value
, signed
))
89 return Shape(width
, signed
)
90 raise TypeError("Object {!r} cannot be used as value shape".format(obj
))
94 return "signed({})".format(self
.width
)
96 return "unsigned({})".format(self
.width
)
99 # TODO: use dataclasses instead of this hack
100 def _Shape___init__(self
, width
=1, signed
=False):
101 if not isinstance(width
, int) or width
< 0:
102 raise TypeError("Width must be a non-negative integer, not {!r}"
104 Shape
.__init
__ = _Shape___init__
108 """Shorthand for ``Shape(width, signed=False)``."""
109 return Shape(width
, signed
=False)
113 """Shorthand for ``Shape(width, signed=True)``."""
114 return Shape(width
, signed
=True)
117 class Value(metaclass
=ABCMeta
):
120 """Converts ``obj`` to an nMigen value.
122 Booleans and integers are wrapped into a :class:`Const`. Enumerations whose members are
123 all integers are converted to a :class:`Const` with a shape that fits every member.
125 if isinstance(obj
, Value
):
127 if isinstance(obj
, int):
129 if isinstance(obj
, Enum
):
130 return Const(obj
.value
, Shape
.cast(type(obj
)))
131 raise TypeError("Object {!r} cannot be converted to an nMigen value".format(obj
))
133 def __init__(self
, *, src_loc_at
=0):
135 self
.src_loc
= tracer
.get_src_loc(1 + src_loc_at
)
138 raise TypeError("Attempted to convert nMigen value to boolean")
140 def __invert__(self
):
141 return Operator("~", [self
])
143 return Operator("-", [self
])
145 def __add__(self
, other
):
146 return Operator("+", [self
, other
])
147 def __radd__(self
, other
):
148 return Operator("+", [other
, self
])
149 def __sub__(self
, other
):
150 return Operator("-", [self
, other
])
151 def __rsub__(self
, other
):
152 return Operator("-", [other
, self
])
154 def __mul__(self
, other
):
155 return Operator("*", [self
, other
])
156 def __rmul__(self
, other
):
157 return Operator("*", [other
, self
])
159 def __check_divisor(self
):
160 width
, signed
= self
.shape()
162 # Python's division semantics and Verilog's division semantics differ for negative
163 # divisors (Python uses div/mod, Verilog uses quo/rem); for now, avoid the issue
164 # completely by prohibiting such division operations.
165 raise NotImplementedError("Division by a signed value is not supported")
166 def __mod__(self
, other
):
167 other
= Value
.cast(other
)
168 other
.__check
_divisor
()
169 return Operator("%", [self
, other
])
170 def __rmod__(self
, other
):
171 self
.__check
_divisor
()
172 return Operator("%", [other
, self
])
173 def __floordiv__(self
, other
):
174 other
= Value
.cast(other
)
175 other
.__check
_divisor
()
176 return Operator("//", [self
, other
])
177 def __rfloordiv__(self
, other
):
178 self
.__check
_divisor
()
179 return Operator("//", [other
, self
])
181 def __check_shamt(self
):
182 width
, signed
= self
.shape()
184 # Neither Python nor HDLs implement shifts by negative values; prohibit any shifts
185 # by a signed value to make sure the shift amount can always be interpreted as
187 raise NotImplementedError("Shift by a signed value is not supported")
188 def __lshift__(self
, other
):
189 other
= Value
.cast(other
)
190 other
.__check
_shamt
()
191 return Operator("<<", [self
, other
])
192 def __rlshift__(self
, other
):
194 return Operator("<<", [other
, self
])
195 def __rshift__(self
, other
):
196 other
= Value
.cast(other
)
197 other
.__check
_shamt
()
198 return Operator(">>", [self
, other
])
199 def __rrshift__(self
, other
):
201 return Operator(">>", [other
, self
])
203 def __and__(self
, other
):
204 return Operator("&", [self
, other
])
205 def __rand__(self
, other
):
206 return Operator("&", [other
, self
])
207 def __xor__(self
, other
):
208 return Operator("^", [self
, other
])
209 def __rxor__(self
, other
):
210 return Operator("^", [other
, self
])
211 def __or__(self
, other
):
212 return Operator("|", [self
, other
])
213 def __ror__(self
, other
):
214 return Operator("|", [other
, self
])
216 def __eq__(self
, other
):
217 return Operator("==", [self
, other
])
218 def __ne__(self
, other
):
219 return Operator("!=", [self
, other
])
220 def __lt__(self
, other
):
221 return Operator("<", [self
, other
])
222 def __le__(self
, other
):
223 return Operator("<=", [self
, other
])
224 def __gt__(self
, other
):
225 return Operator(">", [self
, other
])
226 def __ge__(self
, other
):
227 return Operator(">=", [self
, other
])
230 width
, signed
= self
.shape()
232 return Mux(self
>= 0, self
, -self
)
237 return self
.shape().width
239 def __getitem__(self
, key
):
241 if isinstance(key
, int):
242 if key
not in range(-n
, n
):
243 raise IndexError("Cannot index {} bits into {}-bit value".format(key
, n
))
246 return Slice(self
, key
, key
+ 1)
247 elif isinstance(key
, slice):
248 start
, stop
, step
= key
.indices(n
)
250 return Cat(self
[i
] for i
in range(start
, stop
, step
))
251 return Slice(self
, start
, stop
)
253 raise TypeError("Cannot index value with {}".format(repr(key
)))
255 def as_unsigned(self
):
256 """Conversion to unsigned.
261 This ``Value`` reinterpreted as a unsigned integer.
263 return Operator("u", [self
])
266 """Conversion to signed.
271 This ``Value`` reinterpreted as a signed integer.
273 return Operator("s", [self
])
276 """Conversion to boolean.
281 ``1`` if any bits are set, ``0`` otherwise.
283 return Operator("b", [self
])
286 """Check if any bits are ``1``.
291 ``1`` if any bits are set, ``0`` otherwise.
293 return Operator("r|", [self
])
296 """Check if all bits are ``1``.
301 ``1`` if all bits are set, ``0`` otherwise.
303 return Operator("r&", [self
])
306 """Compute pairwise exclusive-or of every bit.
311 ``1`` if an odd number of bits are set, ``0`` if an even number of bits are set.
313 return Operator("r^", [self
])
315 def implies(premise
, conclusion
):
321 ``0`` if ``premise`` is true and ``conclusion`` is not, ``1`` otherwise.
323 return ~premise | conclusion
325 def bit_select(self
, offset
, width
):
326 """Part-select with bit granularity.
328 Selects a constant width but variable offset part of a ``Value``, such that successive
329 parts overlap by all but 1 bit.
334 Index of first selected bit.
336 Number of selected bits.
341 Selected part of the ``Value``
343 offset
= Value
.cast(offset
)
344 if type(offset
) is Const
and isinstance(width
, int):
345 return self
[offset
.value
:offset
.value
+ width
]
346 return Part(self
, offset
, width
, stride
=1, src_loc_at
=1)
348 def word_select(self
, offset
, width
):
349 """Part-select with word granularity.
351 Selects a constant width but variable offset part of a ``Value``, such that successive
352 parts do not overlap.
357 Index of first selected word.
359 Number of selected bits.
364 Selected part of the ``Value``
366 offset
= Value
.cast(offset
)
367 if type(offset
) is Const
and isinstance(width
, int):
368 return self
[offset
.value
* width
:(offset
.value
+ 1) * width
]
369 return Part(self
, offset
, width
, stride
=width
, src_loc_at
=1)
371 def matches(self
, *patterns
):
374 Matches against a set of patterns, which may be integers or bit strings, recognizing
375 the same grammar as ``Case()``.
379 patterns : int or str
380 Patterns to match against.
385 ``1`` if any pattern matches the value, ``0`` otherwise.
388 for pattern
in patterns
:
389 if not isinstance(pattern
, (int, str, Enum
)):
390 raise SyntaxError("Match pattern must be an integer, a string, or an enumeration, "
393 if isinstance(pattern
, str) and any(bit
not in "01- \t" for bit
in pattern
):
394 raise SyntaxError("Match pattern '{}' must consist of 0, 1, and - (don't care) "
395 "bits, and may include whitespace"
397 if (isinstance(pattern
, str) and
398 len("".join(pattern
.split())) != len(self
)):
399 raise SyntaxError("Match pattern '{}' must have the same width as match value "
401 .format(pattern
, len(self
)))
402 if isinstance(pattern
, int) and bits_for(pattern
) > len(self
):
403 warnings
.warn("Match pattern '{:b}' is wider than match value "
404 "(which has width {}); comparison will never be true"
405 .format(pattern
, len(self
)),
406 SyntaxWarning, stacklevel
=3)
408 if isinstance(pattern
, str):
409 pattern
= "".join(pattern
.split()) # remove whitespace
410 mask
= int(pattern
.replace("0", "1").replace("-", "0"), 2)
411 pattern
= int(pattern
.replace("-", "0"), 2)
412 matches
.append((self
& mask
) == pattern
)
413 elif isinstance(pattern
, int):
414 matches
.append(self
== pattern
)
415 elif isinstance(pattern
, Enum
):
416 matches
.append(self
== pattern
.value
)
421 elif len(matches
) == 1:
424 return Cat(*matches
).any()
426 def rotate_left(self
, offset
):
427 """Rotate left by constant modulo 2**len(self).
437 The input rotated left by offset if offset is positive, else the input rotated right by -offset.
439 if not isinstance(offset
, int):
440 raise TypeError("Rotate amount must be an integer, not {!r}".format(offset
))
442 return Cat(self
[-offset
:], self
[:-offset
]) # meow :3
444 def rotate_right(self
, offset
):
445 """Rotate right by constant modulo 2**len(self).
455 The input rotated right by offset if offset is positive, else the input rotated left by -offset.
457 if not isinstance(offset
, int):
458 raise TypeError("Rotate amount must be an integer, not {!r}".format(offset
))
460 return Cat(self
[offset
:], self
[:offset
])
468 Value to be assigned.
473 Assignment statement that can be used in combinatorial or synchronous context.
475 return Assign(self
, value
, src_loc_at
=1)
479 """Bit width and signedness of a value.
488 >>> Signal(8).shape()
489 Shape(width=8, signed=False)
490 >>> Const(0xaa).shape()
491 Shape(width=8, signed=False)
495 def _lhs_signals(self
):
496 raise TypeError("Value {!r} cannot be used in assignments".format(self
))
499 def _rhs_signals(self
):
503 raise TypeError("Value {!r} cannot be evaluated as constant".format(self
))
510 """A constant, literal integer value.
515 shape : int or tuple or None
516 Either an integer ``width`` or a tuple ``(width, signed)`` specifying the number of bits
517 in this constant and whether it is signed (can represent negative values).
518 ``shape`` defaults to the minimum possible width and signedness of ``value``.
528 def normalize(value
, shape
):
529 width
, signed
= shape
530 mask
= (1 << width
) - 1
532 if signed
and value
>> (width
- 1):
536 def __init__(self
, value
, shape
=None, *, src_loc_at
=0):
537 # We deliberately do not call Value.__init__ here.
538 self
.value
= int(value
)
540 shape
= Shape(bits_for(self
.value
), signed
=self
.value
< 0)
541 elif isinstance(shape
, int):
542 shape
= Shape(shape
, signed
=self
.value
< 0)
544 shape
= Shape
.cast(shape
, src_loc_at
=1 + src_loc_at
)
545 self
.width
, self
.signed
= shape
546 self
.value
= self
.normalize(self
.value
, shape
)
549 return Shape(self
.width
, self
.signed
)
551 def _rhs_signals(self
):
558 return "(const {}'{}d{})".format(self
.width
, "s" if self
.signed
else "", self
.value
)
561 C
= Const
# shorthand
564 class AnyValue(Value
, DUID
):
565 def __init__(self
, shape
, *, src_loc_at
=0):
566 super().__init
__(src_loc_at
=src_loc_at
)
567 self
.width
, self
.signed
= Shape
.cast(shape
, src_loc_at
=1 + src_loc_at
)
568 if not isinstance(self
.width
, int) or self
.width
< 0:
569 raise TypeError("Width must be a non-negative integer, not {!r}"
573 return Shape(self
.width
, self
.signed
)
575 def _rhs_signals(self
):
580 class AnyConst(AnyValue
):
582 return "(anyconst {}'{})".format(self
.width
, "s" if self
.signed
else "")
586 class AnySeq(AnyValue
):
588 return "(anyseq {}'{})".format(self
.width
, "s" if self
.signed
else "")
592 class Operator(Value
):
593 def __init__(self
, operator
, operands
, *, src_loc_at
=0):
594 super().__init
__(src_loc_at
=1 + src_loc_at
)
595 self
.operator
= operator
596 self
.operands
= [Value
.cast(op
) for op
in operands
]
599 def _bitwise_binary_shape(a_shape
, b_shape
):
600 a_bits
, a_sign
= a_shape
601 b_bits
, b_sign
= b_shape
602 if not a_sign
and not b_sign
:
603 # both operands unsigned
604 return Shape(max(a_bits
, b_bits
), False)
605 elif a_sign
and b_sign
:
606 # both operands signed
607 return Shape(max(a_bits
, b_bits
), True)
608 elif not a_sign
and b_sign
:
609 # first operand unsigned (add sign bit), second operand signed
610 return Shape(max(a_bits
+ 1, b_bits
), True)
612 # first signed, second operand unsigned (add sign bit)
613 return Shape(max(a_bits
, b_bits
+ 1), True)
615 op_shapes
= list(map(lambda x
: x
.shape(), self
.operands
))
616 if len(op_shapes
) == 1:
617 (a_width
, a_signed
), = op_shapes
618 if self
.operator
in ("+", "~"):
619 return Shape(a_width
, a_signed
)
620 if self
.operator
== "-":
621 return Shape(a_width
+ 1, True)
622 if self
.operator
in ("b", "r|", "r&", "r^"):
623 return Shape(1, False)
624 if self
.operator
== "u":
625 return Shape(a_width
, False)
626 if self
.operator
== "s":
627 return Shape(a_width
, True)
628 elif len(op_shapes
) == 2:
629 (a_width
, a_signed
), (b_width
, b_signed
) = op_shapes
630 if self
.operator
in ("+", "-"):
631 width
, signed
= _bitwise_binary_shape(*op_shapes
)
632 return Shape(width
+ 1, signed
)
633 if self
.operator
== "*":
634 return Shape(a_width
+ b_width
, a_signed
or b_signed
)
635 if self
.operator
in ("//", "%"):
637 return Shape(a_width
, a_signed
)
638 if self
.operator
in ("<", "<=", "==", "!=", ">", ">="):
639 return Shape(1, False)
640 if self
.operator
in ("&", "^", "|"):
641 return _bitwise_binary_shape(*op_shapes
)
642 if self
.operator
== "<<":
644 extra
= 2 ** (b_width
- 1) - 1
646 extra
= 2 ** (b_width
) - 1
647 return Shape(a_width
+ extra
, a_signed
)
648 if self
.operator
== ">>":
650 extra
= 2 ** (b_width
- 1)
653 return Shape(a_width
+ extra
, a_signed
)
654 elif len(op_shapes
) == 3:
655 if self
.operator
== "m":
656 s_shape
, a_shape
, b_shape
= op_shapes
657 return _bitwise_binary_shape(a_shape
, b_shape
)
658 raise NotImplementedError("Operator {}/{} not implemented"
659 .format(self
.operator
, len(op_shapes
))) # :nocov:
661 def _rhs_signals(self
):
662 return union(op
._rhs
_signals
() for op
in self
.operands
)
665 return "({} {})".format(self
.operator
, " ".join(map(repr, self
.operands
)))
668 def Mux(sel
, val1
, val0
):
669 """Choose between two values.
682 Output ``Value``. If ``sel`` is asserted, the Mux returns ``val1``, else ``val0``.
684 sel
= Value
.cast(sel
)
687 return Operator("m", [sel
, val1
, val0
])
692 def __init__(self
, value
, start
, stop
, *, src_loc_at
=0):
693 if not isinstance(start
, int):
694 raise TypeError("Slice start must be an integer, not {!r}".format(start
))
695 if not isinstance(stop
, int):
696 raise TypeError("Slice stop must be an integer, not {!r}".format(stop
))
699 if start
not in range(-(n
+1), n
+1):
700 raise IndexError("Cannot start slice {} bits into {}-bit value".format(start
, n
))
703 if stop
not in range(-(n
+1), n
+1):
704 raise IndexError("Cannot stop slice {} bits into {}-bit value".format(stop
, n
))
708 raise IndexError("Slice start {} must be less than slice stop {}".format(start
, stop
))
710 super().__init
__(src_loc_at
=src_loc_at
)
711 self
.value
= Value
.cast(value
)
716 return Shape(self
.stop
- self
.start
)
718 def _lhs_signals(self
):
719 return self
.value
._lhs
_signals
()
721 def _rhs_signals(self
):
722 return self
.value
._rhs
_signals
()
725 return "(slice {} {}:{})".format(repr(self
.value
), self
.start
, self
.stop
)
730 def __init__(self
, value
, offset
, width
, stride
=1, *, src_loc_at
=0):
731 if not isinstance(width
, int) or width
< 0:
732 raise TypeError("Part width must be a non-negative integer, not {!r}".format(width
))
733 if not isinstance(stride
, int) or stride
<= 0:
734 raise TypeError("Part stride must be a positive integer, not {!r}".format(stride
))
736 super().__init
__(src_loc_at
=src_loc_at
)
738 self
.offset
= Value
.cast(offset
)
743 return Shape(self
.width
)
745 def _lhs_signals(self
):
746 return self
.value
._lhs
_signals
()
748 def _rhs_signals(self
):
749 return self
.value
._rhs
_signals
() | self
.offset
._rhs
_signals
()
752 return "(part {} {} {} {})".format(repr(self
.value
), repr(self
.offset
),
753 self
.width
, self
.stride
)
758 """Concatenate values.
760 Form a compound ``Value`` from several smaller ones by concatenation.
761 The first argument occupies the lower bits of the result.
762 The return value can be used on either side of an assignment, that
763 is, the concatenated value can be used as an argument on the RHS or
764 as a target on the LHS. If it is used on the LHS, it must solely
765 consist of ``Signal`` s, slices of ``Signal`` s, and other concatenations
766 meeting these properties. The bit length of the return value is the sum of
767 the bit lengths of the arguments::
769 len(Cat(args)) == sum(len(arg) for arg in args)
773 *args : Values or iterables of Values, inout
774 ``Value`` s to be concatenated.
779 Resulting ``Value`` obtained by concatentation.
781 def __init__(self
, *args
, src_loc_at
=0):
782 super().__init
__(src_loc_at
=src_loc_at
)
783 self
.parts
= [Value
.cast(v
) for v
in flatten(args
)]
786 return Shape(sum(len(part
) for part
in self
.parts
))
788 def _lhs_signals(self
):
789 return union((part
._lhs
_signals
() for part
in self
.parts
), start
=ValueSet())
791 def _rhs_signals(self
):
792 return union((part
._rhs
_signals
() for part
in self
.parts
), start
=ValueSet())
796 for part
in reversed(self
.parts
):
798 value |
= part
._as
_const
()
802 return "(cat {})".format(" ".join(map(repr, self
.parts
)))
809 An input value is replicated (repeated) several times
810 to be used on the RHS of assignments::
812 len(Repl(s, n)) == len(s) * n
817 Input value to be replicated.
819 Number of replications.
826 def __init__(self
, value
, count
, *, src_loc_at
=0):
827 if not isinstance(count
, int) or count
< 0:
828 raise TypeError("Replication count must be a non-negative integer, not {!r}"
831 super().__init
__(src_loc_at
=src_loc_at
)
832 self
.value
= Value
.cast(value
)
836 return Shape(len(self
.value
) * self
.count
)
838 def _rhs_signals(self
):
839 return self
.value
._rhs
_signals
()
842 return "(repl {!r} {})".format(self
.value
, self
.count
)
846 class Signal(Value
, DUID
):
847 """A varying integer value.
851 shape : ``Shape``-castable object or None
852 Specification for the number of bits in this ``Signal`` and its signedness (whether it
853 can represent negative values). See ``Shape.cast`` for details.
854 If not specified, ``shape`` defaults to 1-bit and non-signed.
856 Name hint for this signal. If ``None`` (default) the name is inferred from the variable
857 name this ``Signal`` is assigned to.
858 reset : int or integral Enum
859 Reset (synchronous) or default (combinatorial) value.
860 When this ``Signal`` is assigned to in synchronous context and the corresponding clock
861 domain is reset, the ``Signal`` assumes the given value. When this ``Signal`` is unassigned
862 in combinatorial context (due to conditional assignments not being taken), the ``Signal``
863 assumes its ``reset`` value. Defaults to 0.
865 If ``True``, do not generate reset logic for this ``Signal`` in synchronous statements.
866 The ``reset`` value is only used as a combinatorial default or as the initial value.
867 Defaults to ``False``.
869 Dictionary of synthesis attributes.
870 decoder : function or Enum
871 A function converting integer signal values to human-readable strings (e.g. FSM state
872 names). If an ``Enum`` subclass is passed, it is concisely decoded using format string
873 ``"{0.name:}/{0.value:}"``, or a number if the signal value is not a member of
887 def __init__(self
, shape
=None, *, name
=None, reset
=0, reset_less
=False,
888 attrs
=None, decoder
=None, src_loc_at
=0):
889 super().__init
__(src_loc_at
=src_loc_at
)
891 if name
is not None and not isinstance(name
, str):
892 raise TypeError("Name must be a string, not {!r}".format(name
))
893 self
.name
= name
or tracer
.get_var_name(depth
=2 + src_loc_at
, default
="$signal")
897 self
.width
, self
.signed
= Shape
.cast(shape
, src_loc_at
=1 + src_loc_at
)
899 if isinstance(reset
, Enum
):
901 if not isinstance(reset
, int):
902 raise TypeError("Reset value has to be an int or an integral Enum")
904 reset_width
= bits_for(reset
, self
.signed
)
905 if reset
!= 0 and reset_width
> self
.width
:
906 warnings
.warn("Reset value {!r} requires {} bits to represent, but the signal "
908 .format(reset
, reset_width
, self
.width
),
909 SyntaxWarning, stacklevel
=2 + src_loc_at
)
912 self
.reset_less
= bool(reset_less
)
914 self
.attrs
= OrderedDict(() if attrs
is None else attrs
)
916 if decoder
is None and isinstance(shape
, type) and issubclass(shape
, Enum
):
918 if isinstance(decoder
, type) and issubclass(decoder
, Enum
):
919 def enum_decoder(value
):
921 return "{0.name:}/{0.value:}".format(decoder(value
))
924 self
.decoder
= enum_decoder
925 self
._enum
_class
= decoder
927 self
.decoder
= decoder
928 self
._enum
_class
= None
930 # Not a @classmethod because nmigen.compat requires it.
932 def like(other
, *, name
=None, name_suffix
=None, src_loc_at
=0, **kwargs
):
933 """Create Signal based on another.
938 Object to base this Signal on.
942 elif name_suffix
is not None:
943 new_name
= other
.name
+ str(name_suffix
)
945 new_name
= tracer
.get_var_name(depth
=2 + src_loc_at
, default
="$like")
946 kw
= dict(shape
=Value
.cast(other
).shape(), name
=new_name
)
947 if isinstance(other
, Signal
):
948 kw
.update(reset
=other
.reset
, reset_less
=other
.reset_less
,
949 attrs
=other
.attrs
, decoder
=other
.decoder
)
951 return Signal(**kw
, src_loc_at
=1 + src_loc_at
)
954 return Shape(self
.width
, self
.signed
)
956 def _lhs_signals(self
):
957 return ValueSet((self
,))
959 def _rhs_signals(self
):
960 return ValueSet((self
,))
963 return "(sig {})".format(self
.name
)
967 class ClockSignal(Value
):
968 """Clock signal for a clock domain.
970 Any ``ClockSignal`` is equivalent to ``cd.clk`` for a clock domain with the corresponding name.
971 All of these signals ultimately refer to the same signal, but they can be manipulated
972 independently of the clock domain, even before the clock domain is created.
977 Clock domain to obtain a clock signal for. Defaults to ``"sync"``.
979 def __init__(self
, domain
="sync", *, src_loc_at
=0):
980 super().__init
__(src_loc_at
=src_loc_at
)
981 if not isinstance(domain
, str):
982 raise TypeError("Clock domain name must be a string, not {!r}".format(domain
))
984 raise ValueError("Domain '{}' does not have a clock".format(domain
))
990 def _lhs_signals(self
):
991 return ValueSet((self
,))
993 def _rhs_signals(self
):
994 raise NotImplementedError("ClockSignal must be lowered to a concrete signal") # :nocov:
997 return "(clk {})".format(self
.domain
)
1001 class ResetSignal(Value
):
1002 """Reset signal for a clock domain.
1004 Any ``ResetSignal`` is equivalent to ``cd.rst`` for a clock domain with the corresponding name.
1005 All of these signals ultimately refer to the same signal, but they can be manipulated
1006 independently of the clock domain, even before the clock domain is created.
1011 Clock domain to obtain a reset signal for. Defaults to ``"sync"``.
1012 allow_reset_less : bool
1013 If the clock domain is reset-less, act as a constant ``0`` instead of reporting an error.
1015 def __init__(self
, domain
="sync", allow_reset_less
=False, *, src_loc_at
=0):
1016 super().__init
__(src_loc_at
=src_loc_at
)
1017 if not isinstance(domain
, str):
1018 raise TypeError("Clock domain name must be a string, not {!r}".format(domain
))
1019 if domain
== "comb":
1020 raise ValueError("Domain '{}' does not have a reset".format(domain
))
1021 self
.domain
= domain
1022 self
.allow_reset_less
= allow_reset_less
1027 def _lhs_signals(self
):
1028 return ValueSet((self
,))
1030 def _rhs_signals(self
):
1031 raise NotImplementedError("ResetSignal must be lowered to a concrete signal") # :nocov:
1034 return "(rst {})".format(self
.domain
)
1037 class Array(MutableSequence
):
1038 """Addressable multiplexer.
1040 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
1043 The array proxy can be used as an ordinary ``Value``, i.e. participate in calculations and
1044 assignments, provided that all elements of the array are values. The array proxy also supports
1045 attribute access and further indexing, each returning another array proxy; this means that
1046 the results of indexing into arrays, arrays of records, and arrays of arrays can all
1047 be used as first-class values.
1049 It is an error to change an array or any of its elements after an array proxy was created.
1050 Changing the array directly will raise an exception. However, it is not possible to detect
1051 the elements being modified; if an element's attribute or element is modified after the proxy
1052 for it has been created, the proxy will refer to stale data.
1059 gpios = Array(Signal() for _ in range(10))
1061 m.d.sync += gpios[bus.addr].eq(bus.w_data)
1063 m.d.sync += bus.r_data.eq(gpios[bus.addr])
1065 Multidimensional array::
1067 mult = Array(Array(x * y for y in range(10)) for x in range(10))
1068 a = Signal.range(10)
1069 b = Signal.range(10)
1071 m.d.comb += r.eq(mult[a][b])
1079 buses = Array(Record(layout) for busno in range(4))
1080 master = Record(layout)
1082 buses[sel].r_en.eq(master.r_en),
1083 master.r_data.eq(buses[sel].r_data),
1086 def __init__(self
, iterable
=()):
1087 self
._inner
= list(iterable
)
1088 self
._proxy
_at
= None
1089 self
._mutable
= True
1091 def __getitem__(self
, index
):
1092 if isinstance(index
, Value
):
1094 self
._proxy
_at
= tracer
.get_src_loc()
1095 self
._mutable
= False
1096 return ArrayProxy(self
, index
)
1098 return self
._inner
[index
]
1101 return len(self
._inner
)
1103 def _check_mutability(self
):
1104 if not self
._mutable
:
1105 raise ValueError("Array can no longer be mutated after it was indexed with a value "
1106 "at {}:{}".format(*self
._proxy
_at
))
1108 def __setitem__(self
, index
, value
):
1109 self
._check
_mutability
()
1110 self
._inner
[index
] = value
1112 def __delitem__(self
, index
):
1113 self
._check
_mutability
()
1114 del self
._inner
[index
]
1116 def insert(self
, index
, value
):
1117 self
._check
_mutability
()
1118 self
._inner
.insert(index
, value
)
1121 return "(array{} [{}])".format(" mutable" if self
._mutable
else "",
1122 ", ".join(map(repr, self
._inner
)))
1126 class ArrayProxy(Value
):
1127 def __init__(self
, elems
, index
, *, src_loc_at
=0):
1128 super().__init
__(src_loc_at
=1 + src_loc_at
)
1130 self
.index
= Value
.cast(index
)
1132 def __getattr__(self
, attr
):
1133 return ArrayProxy([getattr(elem
, attr
) for elem
in self
.elems
], self
.index
)
1135 def __getitem__(self
, index
):
1136 return ArrayProxy([ elem
[index
] for elem
in self
.elems
], self
.index
)
1138 def _iter_as_values(self
):
1139 return (Value
.cast(elem
) for elem
in self
.elems
)
1142 width
, signed
= 0, False
1143 for elem_width
, elem_signed
in (elem
.shape() for elem
in self
._iter
_as
_values
()):
1144 width
= max(width
, elem_width
+ elem_signed
)
1145 signed
= max(signed
, elem_signed
)
1146 return Shape(width
, signed
)
1148 def _lhs_signals(self
):
1149 signals
= union((elem
._lhs
_signals
() for elem
in self
._iter
_as
_values
()), start
=ValueSet())
1152 def _rhs_signals(self
):
1153 signals
= union((elem
._rhs
_signals
() for elem
in self
._iter
_as
_values
()), start
=ValueSet())
1154 return self
.index
._rhs
_signals
() | signals
1157 return "(proxy (array [{}]) {!r})".format(", ".join(map(repr, self
.elems
)), self
.index
)
1160 class UserValue(Value
):
1161 """Value with custom lowering.
1163 A ``UserValue`` is a value whose precise representation does not have to be immediately known,
1164 which is useful in certain metaprogramming scenarios. Instead of providing fixed semantics
1165 upfront, it is kept abstract for as long as possible, only being lowered to a concrete nMigen
1166 value when required.
1168 Note that the ``lower`` method will only be called once; this is necessary to ensure that
1169 nMigen's view of representation of all values stays internally consistent. If the class
1170 deriving from ``UserValue`` is mutable, then it must ensure that after ``lower`` is called,
1171 it is not mutated in a way that changes its representation.
1173 The following is an incomplete list of actions that, when applied to an ``UserValue`` directly
1174 or indirectly, will cause it to be lowered, provided as an illustrative reference:
1175 * Querying the shape using ``.shape()`` or ``len()``;
1176 * Creating a similarly shaped signal using ``Signal.like``;
1177 * Indexing or iterating through individual bits;
1178 * Adding an assignment to the value to a ``Module`` using ``m.d.<domain> +=``.
1180 def __init__(self
, *, src_loc_at
=0):
1181 super().__init
__(src_loc_at
=1 + src_loc_at
)
1182 self
.__lowered
= None
1186 """Conversion to a concrete representation."""
1189 def _lazy_lower(self
):
1190 if self
.__lowered
is None:
1191 lowered
= self
.lower()
1192 if isinstance(lowered
, UserValue
):
1193 lowered
= lowered
._lazy
_lower
()
1194 self
.__lowered
= Value
.cast(lowered
)
1195 return self
.__lowered
1198 return self
._lazy
_lower
().shape()
1200 def _lhs_signals(self
):
1201 return self
._lazy
_lower
()._lhs
_signals
()
1203 def _rhs_signals(self
):
1204 return self
._lazy
_lower
()._rhs
_signals
()
1208 class Sample(Value
):
1209 """Value from the past.
1211 A ``Sample`` of an expression is equal to the value of the expression ``clocks`` clock edges
1212 of the ``domain`` clock back. If that moment is before the beginning of time, it is equal
1213 to the value of the expression calculated as if each signal had its reset value.
1215 def __init__(self
, expr
, clocks
, domain
, *, src_loc_at
=0):
1216 super().__init
__(src_loc_at
=1 + src_loc_at
)
1217 self
.value
= Value
.cast(expr
)
1218 self
.clocks
= int(clocks
)
1219 self
.domain
= domain
1220 if not isinstance(self
.value
, (Const
, Signal
, ClockSignal
, ResetSignal
, Initial
)):
1221 raise TypeError("Sampled value must be a signal or a constant, not {!r}"
1222 .format(self
.value
))
1224 raise ValueError("Cannot sample a value {} cycles in the future"
1225 .format(-self
.clocks
))
1226 if not (self
.domain
is None or isinstance(self
.domain
, str)):
1227 raise TypeError("Domain name must be a string or None, not {!r}"
1228 .format(self
.domain
))
1231 return self
.value
.shape()
1233 def _rhs_signals(self
):
1234 return ValueSet((self
,))
1237 return "(sample {!r} @ {}[{}])".format(
1238 self
.value
, "<default>" if self
.domain
is None else self
.domain
, self
.clocks
)
1241 def Past(expr
, clocks
=1, domain
=None):
1242 return Sample(expr
, clocks
, domain
)
1245 def Stable(expr
, clocks
=0, domain
=None):
1246 return Sample(expr
, clocks
+ 1, domain
) == Sample(expr
, clocks
, domain
)
1249 def Rose(expr
, clocks
=0, domain
=None):
1250 return ~
Sample(expr
, clocks
+ 1, domain
) & Sample(expr
, clocks
, domain
)
1253 def Fell(expr
, clocks
=0, domain
=None):
1254 return Sample(expr
, clocks
+ 1, domain
) & ~
Sample(expr
, clocks
, domain
)
1258 class Initial(Value
):
1259 """Start indicator, for model checking.
1261 An ``Initial`` signal is ``1`` at the first cycle of model checking, and ``0`` at any other.
1263 def __init__(self
, *, src_loc_at
=0):
1264 super().__init
__(src_loc_at
=src_loc_at
)
1269 def _rhs_signals(self
):
1270 return ValueSet((self
,))
1276 class _StatementList(list):
1278 return "({})".format(" ".join(map(repr, self
)))
1282 def __init__(self
, *, src_loc_at
=0):
1283 self
.src_loc
= tracer
.get_src_loc(1 + src_loc_at
)
1287 if isinstance(obj
, Iterable
):
1288 return _StatementList(sum((Statement
.cast(e
) for e
in obj
), []))
1290 if isinstance(obj
, Statement
):
1291 return _StatementList([obj
])
1293 raise TypeError("Object {!r} is not an nMigen statement".format(obj
))
1297 class Assign(Statement
):
1298 def __init__(self
, lhs
, rhs
, *, src_loc_at
=0):
1299 super().__init
__(src_loc_at
=src_loc_at
)
1300 self
.lhs
= Value
.cast(lhs
)
1301 self
.rhs
= Value
.cast(rhs
)
1303 def _lhs_signals(self
):
1304 return self
.lhs
._lhs
_signals
()
1306 def _rhs_signals(self
):
1307 return self
.lhs
._rhs
_signals
() | self
.rhs
._rhs
_signals
()
1310 return "(eq {!r} {!r})".format(self
.lhs
, self
.rhs
)
1313 class UnusedProperty(UnusedMustUse
):
1317 class Property(Statement
, MustUse
):
1318 _MustUse__warning
= UnusedProperty
1320 def __init__(self
, test
, *, _check
=None, _en
=None, src_loc_at
=0):
1321 super().__init
__(src_loc_at
=src_loc_at
)
1322 self
.test
= Value
.cast(test
)
1323 self
._check
= _check
1325 if self
._check
is None:
1326 self
._check
= Signal(reset_less
=True, name
="${}$check".format(self
._kind
))
1327 self
._check
.src_loc
= self
.src_loc
1329 self
._en
= Signal(reset_less
=True, name
="${}$en".format(self
._kind
))
1330 self
._en
.src_loc
= self
.src_loc
1332 def _lhs_signals(self
):
1333 return ValueSet((self
._en
, self
._check
))
1335 def _rhs_signals(self
):
1336 return self
.test
._rhs
_signals
()
1339 return "({} {!r})".format(self
._kind
, self
.test
)
1343 class Assert(Property
):
1348 class Assume(Property
):
1353 class Cover(Property
):
1358 class Switch(Statement
):
1359 def __init__(self
, test
, cases
, *, src_loc
=None, src_loc_at
=0, case_src_locs
={}):
1361 super().__init
__(src_loc_at
=src_loc_at
)
1363 # Switch is a bit special in terms of location tracking because it is usually created
1364 # long after the control has left the statement that directly caused its creation.
1365 self
.src_loc
= src_loc
1366 # Switch is also a bit special in that its parts also have location information. It can't
1367 # be automatically traced, so whatever constructs a Switch may optionally provide it.
1368 self
.case_src_locs
= {}
1370 self
.test
= Value
.cast(test
)
1371 self
.cases
= OrderedDict()
1372 for orig_keys
, stmts
in cases
.items():
1373 # Map: None -> (); key -> (key,); (key...) -> (key...)
1377 if not isinstance(keys
, tuple):
1379 # Map: 2 -> "0010"; "0010" -> "0010"
1382 if isinstance(key
, str):
1383 key
= "".join(key
.split()) # remove whitespace
1384 elif isinstance(key
, int):
1385 key
= format(key
, "b").rjust(len(self
.test
), "0")
1386 elif isinstance(key
, Enum
):
1387 key
= format(key
.value
, "b").rjust(len(self
.test
), "0")
1389 raise TypeError("Object {!r} cannot be used as a switch key"
1391 assert len(key
) == len(self
.test
)
1392 new_keys
= (*new_keys
, key
)
1393 if not isinstance(stmts
, Iterable
):
1395 self
.cases
[new_keys
] = Statement
.cast(stmts
)
1396 if orig_keys
in case_src_locs
:
1397 self
.case_src_locs
[new_keys
] = case_src_locs
[orig_keys
]
1399 def _lhs_signals(self
):
1400 signals
= union((s
._lhs
_signals
() for ss
in self
.cases
.values() for s
in ss
),
1404 def _rhs_signals(self
):
1405 signals
= union((s
._rhs
_signals
() for ss
in self
.cases
.values() for s
in ss
),
1407 return self
.test
._rhs
_signals
() | signals
1410 def case_repr(keys
, stmts
):
1411 stmts_repr
= " ".join(map(repr, stmts
))
1413 return "(default {})".format(stmts_repr
)
1414 elif len(keys
) == 1:
1415 return "(case {} {})".format(keys
[0], stmts_repr
)
1417 return "(case ({}) {})".format(" ".join(keys
), stmts_repr
)
1418 case_reprs
= [case_repr(keys
, stmts
) for keys
, stmts
in self
.cases
.items()]
1419 return "(switch {!r} {})".format(self
.test
, " ".join(case_reprs
))
1422 class _MappedKeyCollection(metaclass
=ABCMeta
):
1424 def _map_key(self
, key
):
1428 def _unmap_key(self
, key
):
1432 class _MappedKeyDict(MutableMapping
, _MappedKeyCollection
):
1433 def __init__(self
, pairs
=()):
1434 self
._storage
= OrderedDict()
1435 for key
, value
in pairs
:
1438 def __getitem__(self
, key
):
1439 key
= None if key
is None else self
._map
_key
(key
)
1440 return self
._storage
[key
]
1442 def __setitem__(self
, key
, value
):
1443 key
= None if key
is None else self
._map
_key
(key
)
1444 self
._storage
[key
] = value
1446 def __delitem__(self
, key
):
1447 key
= None if key
is None else self
._map
_key
(key
)
1448 del self
._storage
[key
]
1451 for key
in self
._storage
:
1455 yield self
._unmap
_key
(key
)
1457 def __eq__(self
, other
):
1458 if not isinstance(other
, type(self
)):
1460 if len(self
) != len(other
):
1462 for ak
, bk
in zip(sorted(self
._storage
), sorted(other
._storage
)):
1465 if self
._storage
[ak
] != other
._storage
[bk
]:
1470 return len(self
._storage
)
1473 pairs
= ["({!r}, {!r})".format(k
, v
) for k
, v
in self
.items()]
1474 return "{}.{}([{}])".format(type(self
).__module
__, type(self
).__name
__,
1478 class _MappedKeySet(MutableSet
, _MappedKeyCollection
):
1479 def __init__(self
, elements
=()):
1480 self
._storage
= OrderedDict()
1481 for elem
in elements
:
1484 def add(self
, value
):
1485 self
._storage
[self
._map
_key
(value
)] = None
1487 def update(self
, values
):
1488 for value
in values
:
1491 def discard(self
, value
):
1493 del self
._storage
[self
._map
_key
(value
)]
1495 def __contains__(self
, value
):
1496 return self
._map
_key
(value
) in self
._storage
1499 for key
in [k
for k
in self
._storage
]:
1500 yield self
._unmap
_key
(key
)
1503 return len(self
._storage
)
1506 return "{}.{}({})".format(type(self
).__module
__, type(self
).__name
__,
1507 ", ".join(repr(x
) for x
in self
))
1511 def __init__(self
, value
):
1512 self
.value
= Value
.cast(value
)
1513 if isinstance(self
.value
, Const
):
1514 self
._hash
= hash(self
.value
.value
)
1515 elif isinstance(self
.value
, (Signal
, AnyValue
)):
1516 self
._hash
= hash(self
.value
.duid
)
1517 elif isinstance(self
.value
, (ClockSignal
, ResetSignal
)):
1518 self
._hash
= hash(self
.value
.domain
)
1519 elif isinstance(self
.value
, Operator
):
1520 self
._hash
= hash((self
.value
.operator
,
1521 tuple(ValueKey(o
) for o
in self
.value
.operands
)))
1522 elif isinstance(self
.value
, Slice
):
1523 self
._hash
= hash((ValueKey(self
.value
.value
), self
.value
.start
, self
.value
.stop
))
1524 elif isinstance(self
.value
, Part
):
1525 self
._hash
= hash((ValueKey(self
.value
.value
), ValueKey(self
.value
.offset
),
1526 self
.value
.width
, self
.value
.stride
))
1527 elif isinstance(self
.value
, Cat
):
1528 self
._hash
= hash(tuple(ValueKey(o
) for o
in self
.value
.parts
))
1529 elif isinstance(self
.value
, ArrayProxy
):
1530 self
._hash
= hash((ValueKey(self
.value
.index
),
1531 tuple(ValueKey(e
) for e
in self
.value
._iter
_as
_values
())))
1532 elif isinstance(self
.value
, Sample
):
1533 self
._hash
= hash((ValueKey(self
.value
.value
), self
.value
.clocks
, self
.value
.domain
))
1534 elif isinstance(self
.value
, Initial
):
1537 raise TypeError("Object {!r} cannot be used as a key in value collections"
1538 .format(self
.value
))
1543 def __eq__(self
, other
):
1544 if type(other
) is not ValueKey
:
1546 if type(self
.value
) is not type(other
.value
):
1549 if isinstance(self
.value
, Const
):
1550 return self
.value
.value
== other
.value
.value
1551 elif isinstance(self
.value
, (Signal
, AnyValue
)):
1552 return self
.value
is other
.value
1553 elif isinstance(self
.value
, (ClockSignal
, ResetSignal
)):
1554 return self
.value
.domain
== other
.value
.domain
1555 elif isinstance(self
.value
, Operator
):
1556 return (self
.value
.operator
== other
.value
.operator
and
1557 len(self
.value
.operands
) == len(other
.value
.operands
) and
1558 all(ValueKey(a
) == ValueKey(b
)
1559 for a
, b
in zip(self
.value
.operands
, other
.value
.operands
)))
1560 elif isinstance(self
.value
, Slice
):
1561 return (ValueKey(self
.value
.value
) == ValueKey(other
.value
.value
) and
1562 self
.value
.start
== other
.value
.start
and
1563 self
.value
.stop
== other
.value
.stop
)
1564 elif isinstance(self
.value
, Part
):
1565 return (ValueKey(self
.value
.value
) == ValueKey(other
.value
.value
) and
1566 ValueKey(self
.value
.offset
) == ValueKey(other
.value
.offset
) and
1567 self
.value
.width
== other
.value
.width
and
1568 self
.value
.stride
== other
.value
.stride
)
1569 elif isinstance(self
.value
, Cat
):
1570 return all(ValueKey(a
) == ValueKey(b
)
1571 for a
, b
in zip(self
.value
.parts
, other
.value
.parts
))
1572 elif isinstance(self
.value
, ArrayProxy
):
1573 return (ValueKey(self
.value
.index
) == ValueKey(other
.value
.index
) and
1574 len(self
.value
.elems
) == len(other
.value
.elems
) and
1575 all(ValueKey(a
) == ValueKey(b
)
1576 for a
, b
in zip(self
.value
._iter
_as
_values
(),
1577 other
.value
._iter
_as
_values
())))
1578 elif isinstance(self
.value
, Sample
):
1579 return (ValueKey(self
.value
.value
) == ValueKey(other
.value
.value
) and
1580 self
.value
.clocks
== other
.value
.clocks
and
1581 self
.value
.domain
== self
.value
.domain
)
1582 elif isinstance(self
.value
, Initial
):
1585 raise TypeError("Object {!r} cannot be used as a key in value collections"
1586 .format(self
.value
))
1588 def __lt__(self
, other
):
1589 if not isinstance(other
, ValueKey
):
1591 if type(self
.value
) != type(other
.value
):
1594 if isinstance(self
.value
, Const
):
1595 return self
.value
< other
.value
1596 elif isinstance(self
.value
, (Signal
, AnyValue
)):
1597 return self
.value
.duid
< other
.value
.duid
1598 elif isinstance(self
.value
, Slice
):
1599 return (ValueKey(self
.value
.value
) < ValueKey(other
.value
.value
) and
1600 self
.value
.start
< other
.value
.start
and
1601 self
.value
.end
< other
.value
.end
)
1603 raise TypeError("Object {!r} cannot be used as a key in value collections")
1606 return "<{}.ValueKey {!r}>".format(__name__
, self
.value
)
1609 class ValueDict(_MappedKeyDict
):
1611 _unmap_key
= lambda self
, key
: key
.value
1614 class ValueSet(_MappedKeySet
):
1616 _unmap_key
= lambda self
, key
: key
.value
1620 def __init__(self
, signal
):
1621 self
.signal
= signal
1622 if isinstance(signal
, Signal
):
1623 self
._intern
= (0, signal
.duid
)
1624 elif type(signal
) is ClockSignal
:
1625 self
._intern
= (1, signal
.domain
)
1626 elif type(signal
) is ResetSignal
:
1627 self
._intern
= (2, signal
.domain
)
1629 raise TypeError("Object {!r} is not an nMigen signal".format(signal
))
1632 return hash(self
._intern
)
1634 def __eq__(self
, other
):
1635 if type(other
) is not SignalKey
:
1637 return self
._intern
== other
._intern
1639 def __lt__(self
, other
):
1640 if type(other
) is not SignalKey
:
1641 raise TypeError("Object {!r} cannot be compared to a SignalKey".format(signal
))
1642 return self
._intern
< other
._intern
1645 return "<{}.SignalKey {!r}>".format(__name__
, self
.signal
)
1648 class SignalDict(_MappedKeyDict
):
1649 _map_key
= SignalKey
1650 _unmap_key
= lambda self
, key
: key
.signal
1653 class SignalSet(_MappedKeySet
):
1654 _map_key
= SignalKey
1655 _unmap_key
= lambda self
, key
: key
.signal