1 from abc
import ABCMeta
, abstractmethod
6 from collections
import OrderedDict
7 from collections
.abc
import Iterable
, MutableMapping
, MutableSet
, MutableSequence
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", "Assign", "Assert", "Assume", "Cover", "Switch", "Delay", "Tick",
22 "Passive", "ValueKey", "ValueDict", "ValueSet", "SignalKey", "SignalDict",
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.
63 if isinstance(obj
, int):
65 if isinstance(obj
, tuple):
67 if isinstance(obj
, range):
69 return Shape(0, obj
.start
< 0)
70 signed
= obj
.start
< 0 or (obj
.stop
- obj
.step
) < 0
71 width
= max(bits_for(obj
.start
, signed
),
72 bits_for(obj
.stop
- obj
.step
, signed
))
73 return Shape(width
, signed
)
74 if isinstance(obj
, type) and issubclass(obj
, Enum
):
75 min_value
= min(member
.value
for member
in obj
)
76 max_value
= max(member
.value
for member
in obj
)
77 if not isinstance(min_value
, int) or not isinstance(max_value
, int):
78 raise TypeError("Only enumerations with integer values can be used "
80 signed
= min_value
< 0 or max_value
< 0
81 width
= max(bits_for(min_value
, signed
), bits_for(max_value
, signed
))
82 return Shape(width
, signed
)
83 raise TypeError("Object {!r} cannot be used as value shape".format(obj
))
86 # TODO: use dataclasses instead of this hack
87 def _Shape___init__(self
, width
=1, signed
=False):
88 if not isinstance(width
, int) or width
< 0:
89 raise TypeError("Width must be a non-negative integer, not {!r}"
91 Shape
.__init
__ = _Shape___init__
95 """Shorthand for ``Shape(width, signed=False)``."""
96 return Shape(width
, signed
=False)
100 """Shorthand for ``Shape(width, signed=True)``."""
101 return Shape(width
, signed
=True)
104 class Value(metaclass
=ABCMeta
):
107 """Converts ``obj`` to an nMigen value.
109 Booleans and integers are wrapped into a :class:`Const`. Enumerations whose members are
110 all integers are converted to a :class:`Const` with a shape that fits every member.
112 if isinstance(obj
, Value
):
114 if isinstance(obj
, int):
116 if isinstance(obj
, Enum
):
117 return Const(obj
.value
, Shape
.cast(type(obj
)))
118 raise TypeError("Object {!r} cannot be converted to an nMigen value".format(obj
))
120 # TODO(nmigen-0.2): remove this
122 @deprecated("instead of `Value.wrap`, use `Value.cast`")
126 def __init__(self
, *, src_loc_at
=0):
128 self
.src_loc
= tracer
.get_src_loc(1 + src_loc_at
)
131 raise TypeError("Attempted to convert nMigen value to boolean")
133 def __invert__(self
):
134 return Operator("~", [self
])
136 return Operator("-", [self
])
138 def __add__(self
, other
):
139 return Operator("+", [self
, other
])
140 def __radd__(self
, other
):
141 return Operator("+", [other
, self
])
142 def __sub__(self
, other
):
143 return Operator("-", [self
, other
])
144 def __rsub__(self
, other
):
145 return Operator("-", [other
, self
])
147 def __mul__(self
, other
):
148 return Operator("*", [self
, other
])
149 def __rmul__(self
, other
):
150 return Operator("*", [other
, self
])
152 def __check_divisor(self
):
153 width
, signed
= self
.shape()
155 # Python's division semantics and Verilog's division semantics differ for negative
156 # divisors (Python uses div/mod, Verilog uses quo/rem); for now, avoid the issue
157 # completely by prohibiting such division operations.
158 raise NotImplementedError("Division by a signed value is not supported")
159 def __mod__(self
, other
):
160 other
= Value
.cast(other
)
161 other
.__check
_divisor
()
162 return Operator("%", [self
, other
])
163 def __rmod__(self
, other
):
164 self
.__check
_divisor
()
165 return Operator("%", [other
, self
])
166 def __floordiv__(self
, other
):
167 other
= Value
.cast(other
)
168 other
.__check
_divisor
()
169 return Operator("//", [self
, other
])
170 def __rfloordiv__(self
, other
):
171 self
.__check
_divisor
()
172 return Operator("//", [other
, self
])
174 def __lshift__(self
, other
):
175 return Operator("<<", [self
, other
])
176 def __rlshift__(self
, other
):
177 return Operator("<<", [other
, self
])
178 def __rshift__(self
, other
):
179 return Operator(">>", [self
, other
])
180 def __rrshift__(self
, other
):
181 return Operator(">>", [other
, self
])
182 def __and__(self
, other
):
183 return Operator("&", [self
, other
])
184 def __rand__(self
, other
):
185 return Operator("&", [other
, self
])
186 def __xor__(self
, other
):
187 return Operator("^", [self
, other
])
188 def __rxor__(self
, other
):
189 return Operator("^", [other
, self
])
190 def __or__(self
, other
):
191 return Operator("|", [self
, other
])
192 def __ror__(self
, other
):
193 return Operator("|", [other
, self
])
195 def __eq__(self
, other
):
196 return Operator("==", [self
, other
])
197 def __ne__(self
, other
):
198 return Operator("!=", [self
, other
])
199 def __lt__(self
, other
):
200 return Operator("<", [self
, other
])
201 def __le__(self
, other
):
202 return Operator("<=", [self
, other
])
203 def __gt__(self
, other
):
204 return Operator(">", [self
, other
])
205 def __ge__(self
, other
):
206 return Operator(">=", [self
, other
])
209 return self
.shape().width
211 def __getitem__(self
, key
):
213 if isinstance(key
, int):
214 if key
not in range(-n
, n
):
215 raise IndexError("Cannot index {} bits into {}-bit value".format(key
, n
))
218 return Slice(self
, key
, key
+ 1)
219 elif isinstance(key
, slice):
220 start
, stop
, step
= key
.indices(n
)
222 return Cat(self
[i
] for i
in range(start
, stop
, step
))
223 return Slice(self
, start
, stop
)
225 raise TypeError("Cannot index value with {}".format(repr(key
)))
228 """Conversion to boolean.
233 ``1`` if any bits are set, ``0`` otherwise.
235 return Operator("b", [self
])
238 """Check if any bits are ``1``.
243 ``1`` if any bits are set, ``0`` otherwise.
245 return Operator("r|", [self
])
248 """Check if all bits are ``1``.
253 ``1`` if all bits are set, ``0`` otherwise.
255 return Operator("r&", [self
])
258 """Compute pairwise exclusive-or of every bit.
263 ``1`` if an odd number of bits are set, ``0`` if an even number of bits are set.
265 return Operator("r^", [self
])
267 def implies(premise
, conclusion
):
273 ``0`` if ``premise`` is true and ``conclusion`` is not, ``1`` otherwise.
275 return ~premise | conclusion
277 # TODO(nmigen-0.2): move this to nmigen.compat and make it a deprecated extension
278 @deprecated("instead of `.part`, use `.bit_select`")
279 def part(self
, offset
, width
):
280 return Part(self
, offset
, width
, src_loc_at
=1)
282 def bit_select(self
, offset
, width
):
283 """Part-select with bit granularity.
285 Selects a constant width but variable offset part of a ``Value``, such that successive
286 parts overlap by all but 1 bit.
291 Index of first selected bit.
293 Number of selected bits.
298 Selected part of the ``Value``
300 return Part(self
, offset
, width
, stride
=1, src_loc_at
=1)
302 def word_select(self
, offset
, width
):
303 """Part-select with word granularity.
305 Selects a constant width but variable offset part of a ``Value``, such that successive
306 parts do not overlap.
311 Index of first selected word.
313 Number of selected bits.
318 Selected part of the ``Value``
320 return Part(self
, offset
, width
, stride
=width
, src_loc_at
=1)
322 def matches(self
, *patterns
):
325 Matches against a set of patterns, which may be integers or bit strings, recognizing
326 the same grammar as ``Case()``.
330 patterns : int or str
331 Patterns to match against.
336 ``1`` if any pattern matches the value, ``0`` otherwise.
339 for pattern
in patterns
:
340 if not isinstance(pattern
, (int, str, Enum
)):
341 raise SyntaxError("Match pattern must be an integer, a string, or an enumeration, "
344 if isinstance(pattern
, str) and any(bit
not in "01-" for bit
in pattern
):
345 raise SyntaxError("Match pattern '{}' must consist of 0, 1, and - (don't care) "
348 if isinstance(pattern
, str) and len(pattern
) != len(self
):
349 raise SyntaxError("Match pattern '{}' must have the same width as match value "
351 .format(pattern
, len(self
)))
352 if isinstance(pattern
, int) and bits_for(pattern
) > len(self
):
353 warnings
.warn("Match pattern '{:b}' is wider than match value "
354 "(which has width {}); comparison will never be true"
355 .format(pattern
, len(self
)),
356 SyntaxWarning, stacklevel
=3)
358 if isinstance(pattern
, str):
359 mask
= int(pattern
.replace("0", "1").replace("-", "0"), 2)
360 pattern
= int(pattern
.replace("-", "0"), 2)
361 matches
.append((self
& mask
) == pattern
)
362 elif isinstance(pattern
, int):
363 matches
.append(self
== pattern
)
364 elif isinstance(pattern
, Enum
):
365 matches
.append(self
== pattern
.value
)
370 elif len(matches
) == 1:
373 return Cat(*matches
).any()
381 Value to be assigned.
386 Assignment statement that can be used in combinatorial or synchronous context.
388 return Assign(self
, value
, src_loc_at
=1)
392 """Bit width and signedness of a value.
401 >>> Signal(8).shape()
402 Shape(width=8, signed=False)
403 >>> Const(0xaa).shape()
404 Shape(width=8, signed=False)
408 def _lhs_signals(self
):
409 raise TypeError("Value {!r} cannot be used in assignments".format(self
))
412 def _rhs_signals(self
):
416 raise TypeError("Value {!r} cannot be evaluated as constant".format(self
))
423 """A constant, literal integer value.
428 shape : int or tuple or None
429 Either an integer ``width`` or a tuple ``(width, signed)`` specifying the number of bits
430 in this constant and whether it is signed (can represent negative values).
431 ``shape`` defaults to the minimum possible width and signedness of ``value``.
441 def normalize(value
, shape
):
442 width
, signed
= shape
443 mask
= (1 << width
) - 1
445 if signed
and value
>> (width
- 1):
449 def __init__(self
, value
, shape
=None):
450 # We deliberately do not call Value.__init__ here.
451 self
.value
= int(value
)
453 shape
= Shape(bits_for(self
.value
), signed
=self
.value
< 0)
454 elif isinstance(shape
, int):
455 shape
= Shape(shape
, signed
=self
.value
< 0)
457 shape
= Shape
.cast(shape
)
458 self
.width
, self
.signed
= shape
459 self
.value
= self
.normalize(self
.value
, shape
)
461 # TODO(nmigen-0.2): move this to nmigen.compat and make it a deprecated extension
463 @deprecated("instead of `const.nbits`, use `const.width`")
468 return Shape(self
.width
, self
.signed
)
470 def _rhs_signals(self
):
477 return "(const {}'{}d{})".format(self
.width
, "s" if self
.signed
else "", self
.value
)
480 C
= Const
# shorthand
483 class AnyValue(Value
, DUID
):
484 def __init__(self
, shape
, *, src_loc_at
=0):
485 super().__init
__(src_loc_at
=src_loc_at
)
486 self
.width
, self
.signed
= Shape
.cast(shape
)
487 if not isinstance(self
.width
, int) or self
.width
< 0:
488 raise TypeError("Width must be a non-negative integer, not {!r}"
492 return Shape(self
.width
, self
.signed
)
494 def _rhs_signals(self
):
499 class AnyConst(AnyValue
):
501 return "(anyconst {}'{})".format(self
.nbits
, "s" if self
.signed
else "")
505 class AnySeq(AnyValue
):
507 return "(anyseq {}'{})".format(self
.nbits
, "s" if self
.signed
else "")
511 class Operator(Value
):
512 def __init__(self
, operator
, operands
, *, src_loc_at
=0):
513 super().__init
__(src_loc_at
=1 + src_loc_at
)
514 self
.operator
= operator
515 self
.operands
= [Value
.cast(op
) for op
in operands
]
517 # TODO(nmigen-0.2): move this to nmigen.compat and make it a deprecated extension
519 @deprecated("instead of `.op`, use `.operator`")
524 def _bitwise_binary_shape(a_shape
, b_shape
):
525 a_bits
, a_sign
= a_shape
526 b_bits
, b_sign
= b_shape
527 if not a_sign
and not b_sign
:
528 # both operands unsigned
529 return Shape(max(a_bits
, b_bits
), False)
530 elif a_sign
and b_sign
:
531 # both operands signed
532 return Shape(max(a_bits
, b_bits
), True)
533 elif not a_sign
and b_sign
:
534 # first operand unsigned (add sign bit), second operand signed
535 return Shape(max(a_bits
+ 1, b_bits
), True)
537 # first signed, second operand unsigned (add sign bit)
538 return Shape(max(a_bits
, b_bits
+ 1), True)
540 op_shapes
= list(map(lambda x
: x
.shape(), self
.operands
))
541 if len(op_shapes
) == 1:
542 (a_width
, a_signed
), = op_shapes
543 if self
.operator
in ("+", "~"):
544 return Shape(a_width
, a_signed
)
545 if self
.operator
== "-":
547 return Shape(a_width
+ 1, True)
549 return Shape(a_width
, a_signed
)
550 if self
.operator
in ("b", "r|", "r&", "r^"):
551 return Shape(1, False)
552 elif len(op_shapes
) == 2:
553 (a_width
, a_signed
), (b_width
, b_signed
) = op_shapes
554 if self
.operator
in ("+", "-"):
555 width
, signed
= _bitwise_binary_shape(*op_shapes
)
556 return Shape(width
+ 1, signed
)
557 if self
.operator
== "*":
558 return Shape(a_width
+ b_width
, a_signed
or b_signed
)
559 if self
.operator
in ("//", "%"):
561 return Shape(a_width
, a_signed
)
562 if self
.operator
in ("<", "<=", "==", "!=", ">", ">="):
563 return Shape(1, False)
564 if self
.operator
in ("&", "^", "|"):
565 return _bitwise_binary_shape(*op_shapes
)
566 if self
.operator
== "<<":
568 extra
= 2 ** (b_width
- 1) - 1
570 extra
= 2 ** (b_width
) - 1
571 return Shape(a_width
+ extra
, a_signed
)
572 if self
.operator
== ">>":
574 extra
= 2 ** (b_width
- 1)
577 return Shape(a_width
+ extra
, a_signed
)
578 elif len(op_shapes
) == 3:
579 if self
.operator
== "m":
580 s_shape
, a_shape
, b_shape
= op_shapes
581 return _bitwise_binary_shape(a_shape
, b_shape
)
582 raise NotImplementedError("Operator {}/{} not implemented"
583 .format(self
.operator
, len(op_shapes
))) # :nocov:
585 def _rhs_signals(self
):
586 return union(op
._rhs
_signals
() for op
in self
.operands
)
589 return "({} {})".format(self
.operator
, " ".join(map(repr, self
.operands
)))
592 def Mux(sel
, val1
, val0
):
593 """Choose between two values.
606 Output ``Value``. If ``sel`` is asserted, the Mux returns ``val1``, else ``val0``.
608 sel
= Value
.cast(sel
)
611 return Operator("m", [sel
, val1
, val0
])
616 def __init__(self
, value
, start
, end
, *, src_loc_at
=0):
617 if not isinstance(start
, int):
618 raise TypeError("Slice start must be an integer, not {!r}".format(start
))
619 if not isinstance(end
, int):
620 raise TypeError("Slice end must be an integer, not {!r}".format(end
))
623 if start
not in range(-(n
+1), n
+1):
624 raise IndexError("Cannot start slice {} bits into {}-bit value".format(start
, n
))
627 if end
not in range(-(n
+1), n
+1):
628 raise IndexError("Cannot end slice {} bits into {}-bit value".format(end
, n
))
632 raise IndexError("Slice start {} must be less than slice end {}".format(start
, end
))
634 super().__init
__(src_loc_at
=src_loc_at
)
635 self
.value
= Value
.cast(value
)
640 return Shape(self
.end
- self
.start
)
642 def _lhs_signals(self
):
643 return self
.value
._lhs
_signals
()
645 def _rhs_signals(self
):
646 return self
.value
._rhs
_signals
()
649 return "(slice {} {}:{})".format(repr(self
.value
), self
.start
, self
.end
)
654 def __init__(self
, value
, offset
, width
, stride
=1, *, src_loc_at
=0):
655 if not isinstance(width
, int) or width
< 0:
656 raise TypeError("Part width must be a non-negative integer, not {!r}".format(width
))
657 if not isinstance(stride
, int) or stride
<= 0:
658 raise TypeError("Part stride must be a positive integer, not {!r}".format(stride
))
660 super().__init
__(src_loc_at
=src_loc_at
)
662 self
.offset
= Value
.cast(offset
)
667 return Shape(self
.width
)
669 def _lhs_signals(self
):
670 return self
.value
._lhs
_signals
()
672 def _rhs_signals(self
):
673 return self
.value
._rhs
_signals
() | self
.offset
._rhs
_signals
()
676 return "(part {} {} {} {})".format(repr(self
.value
), repr(self
.offset
),
677 self
.width
, self
.stride
)
682 """Concatenate values.
684 Form a compound ``Value`` from several smaller ones by concatenation.
685 The first argument occupies the lower bits of the result.
686 The return value can be used on either side of an assignment, that
687 is, the concatenated value can be used as an argument on the RHS or
688 as a target on the LHS. If it is used on the LHS, it must solely
689 consist of ``Signal`` s, slices of ``Signal`` s, and other concatenations
690 meeting these properties. The bit length of the return value is the sum of
691 the bit lengths of the arguments::
693 len(Cat(args)) == sum(len(arg) for arg in args)
697 *args : Values or iterables of Values, inout
698 ``Value`` s to be concatenated.
703 Resulting ``Value`` obtained by concatentation.
705 def __init__(self
, *args
, src_loc_at
=0):
706 super().__init
__(src_loc_at
=src_loc_at
)
707 self
.parts
= [Value
.cast(v
) for v
in flatten(args
)]
710 return Shape(sum(len(part
) for part
in self
.parts
))
712 def _lhs_signals(self
):
713 return union((part
._lhs
_signals
() for part
in self
.parts
), start
=ValueSet())
715 def _rhs_signals(self
):
716 return union((part
._rhs
_signals
() for part
in self
.parts
), start
=ValueSet())
720 for part
in reversed(self
.parts
):
722 value |
= part
._as
_const
()
726 return "(cat {})".format(" ".join(map(repr, self
.parts
)))
733 An input value is replicated (repeated) several times
734 to be used on the RHS of assignments::
736 len(Repl(s, n)) == len(s) * n
741 Input value to be replicated.
743 Number of replications.
750 def __init__(self
, value
, count
, *, src_loc_at
=0):
751 if not isinstance(count
, int) or count
< 0:
752 raise TypeError("Replication count must be a non-negative integer, not {!r}"
755 super().__init
__(src_loc_at
=src_loc_at
)
756 self
.value
= Value
.cast(value
)
760 return Shape(len(self
.value
) * self
.count
)
762 def _rhs_signals(self
):
763 return self
.value
._rhs
_signals
()
766 return "(repl {!r} {})".format(self
.value
, self
.count
)
770 class Signal(Value
, DUID
):
771 """A varying integer value.
775 shape : int or tuple or None
776 Either an integer ``width`` or a tuple ``(width, signed)`` specifying the number of bits
777 in this ``Signal`` and whether it is signed (can represent negative values).
778 ``shape`` defaults to 1-bit and non-signed.
780 Name hint for this signal. If ``None`` (default) the name is inferred from the variable
781 name this ``Signal`` is assigned to. Name collisions are automatically resolved by
782 prepending names of objects that contain this ``Signal`` and by appending integer
785 Reset (synchronous) or default (combinatorial) value.
786 When this ``Signal`` is assigned to in synchronous context and the corresponding clock
787 domain is reset, the ``Signal`` assumes the given value. When this ``Signal`` is unassigned
788 in combinatorial context (due to conditional assignments not being taken), the ``Signal``
789 assumes its ``reset`` value. Defaults to 0.
791 If ``True``, do not generate reset logic for this ``Signal`` in synchronous statements.
792 The ``reset`` value is only used as a combinatorial default or as the initial value.
793 Defaults to ``False``.
796 If ``shape`` is ``None``, the signal bit width and signedness are
797 determined by the integer range given by ``min`` (inclusive,
798 defaults to 0) and ``max`` (exclusive, defaults to 2).
800 Dictionary of synthesis attributes.
801 decoder : function or Enum
802 A function converting integer signal values to human-readable strings (e.g. FSM state
803 names). If an ``Enum`` subclass is passed, it is concisely decoded using format string
804 ``"{0.name:}/{0.value:}"``, or a number if the signal value is not a member of
817 def __init__(self
, shape
=None, *, name
=None, reset
=0, reset_less
=False, min=None, max=None,
818 attrs
=None, decoder
=None, src_loc_at
=0):
819 super().__init
__(src_loc_at
=src_loc_at
)
821 # TODO(nmigen-0.2): move this to nmigen.compat and make it a deprecated extension
822 if min is not None or max is not None:
823 warnings
.warn("instead of `Signal(min={min}, max={max})`, "
824 "use `Signal(range({min}, {max}))`"
825 .format(min=min or 0, max=max or 2),
826 DeprecationWarning, stacklevel
=2 + src_loc_at
)
828 if name
is not None and not isinstance(name
, str):
829 raise TypeError("Name must be a string, not {!r}".format(name
))
830 self
.name
= name
or tracer
.get_var_name(depth
=2 + src_loc_at
, default
="$signal")
837 max -= 1 # make both bounds inclusive
839 raise ValueError("Lower bound {} should be less or equal to higher bound {}"
840 .format(min, max + 1))
841 self
.signed
= min < 0 or max < 0
845 self
.width
= builtins
.max(bits_for(min, self
.signed
),
846 bits_for(max, self
.signed
))
849 if not (min is None and max is None):
850 raise ValueError("Only one of bits/signedness or bounds may be specified")
851 self
.width
, self
.signed
= Shape
.cast(shape
)
853 reset_width
= bits_for(reset
, self
.signed
)
854 if reset
!= 0 and reset_width
> self
.width
:
855 warnings
.warn("Reset value {!r} requires {} bits to represent, but the signal "
857 .format(reset
, reset_width
, self
.width
),
858 SyntaxWarning, stacklevel
=2 + src_loc_at
)
860 self
.reset
= int(reset
)
861 self
.reset_less
= bool(reset_less
)
863 self
.attrs
= OrderedDict(() if attrs
is None else attrs
)
865 if decoder
is None and isinstance(shape
, type) and issubclass(shape
, Enum
):
867 if isinstance(decoder
, type) and issubclass(decoder
, Enum
):
868 def enum_decoder(value
):
870 return "{0.name:}/{0.value:}".format(decoder(value
))
873 self
.decoder
= enum_decoder
875 self
.decoder
= decoder
878 @deprecated("instead of `Signal.range(...)`, use `Signal(range(...))`")
879 def range(cls
, *args
, src_loc_at
=0, **kwargs
):
880 return cls(range(*args
), src_loc_at
=2 + src_loc_at
, **kwargs
)
883 @deprecated("instead of `Signal.enum(...)`, use `Signal(...)`")
884 def enum(cls
, enum_type
, *, src_loc_at
=0, **kwargs
):
885 if not issubclass(enum_type
, Enum
):
886 raise TypeError("Type {!r} is not an enumeration")
887 return cls(enum_type
, src_loc_at
=2 + src_loc_at
, **kwargs
)
890 def like(cls
, other
, *, name
=None, name_suffix
=None, src_loc_at
=0, **kwargs
):
891 """Create Signal based on another.
896 Object to base this Signal on.
900 elif name_suffix
is not None:
901 new_name
= other
.name
+ str(name_suffix
)
903 new_name
= tracer
.get_var_name(depth
=2 + src_loc_at
, default
="$like")
904 kw
= dict(shape
=cls
.cast(other
).shape(), name
=new_name
)
905 if isinstance(other
, cls
):
906 kw
.update(reset
=other
.reset
, reset_less
=other
.reset_less
,
907 attrs
=other
.attrs
, decoder
=other
.decoder
)
909 return cls(**kw
, src_loc_at
=1 + src_loc_at
)
911 # TODO(nmigen-0.2): move this to nmigen.compat and make it a deprecated extension
913 @deprecated("instead of `signal.nbits`, use `signal.width`")
918 @deprecated("instead of `signal.nbits = x`, use `signal.width = x`")
919 def nbits(self
, value
):
923 return Shape(self
.width
, self
.signed
)
925 def _lhs_signals(self
):
926 return ValueSet((self
,))
928 def _rhs_signals(self
):
929 return ValueSet((self
,))
932 return "(sig {})".format(self
.name
)
936 class ClockSignal(Value
):
937 """Clock signal for a clock domain.
939 Any ``ClockSignal`` is equivalent to ``cd.clk`` for a clock domain with the corresponding name.
940 All of these signals ultimately refer to the same signal, but they can be manipulated
941 independently of the clock domain, even before the clock domain is created.
946 Clock domain to obtain a clock signal for. Defaults to ``"sync"``.
948 def __init__(self
, domain
="sync", *, src_loc_at
=0):
949 super().__init
__(src_loc_at
=src_loc_at
)
950 if not isinstance(domain
, str):
951 raise TypeError("Clock domain name must be a string, not {!r}".format(domain
))
953 raise ValueError("Domain '{}' does not have a clock".format(domain
))
959 def _lhs_signals(self
):
960 return ValueSet((self
,))
962 def _rhs_signals(self
):
963 raise NotImplementedError("ClockSignal must be lowered to a concrete signal") # :nocov:
966 return "(clk {})".format(self
.domain
)
970 class ResetSignal(Value
):
971 """Reset signal for a clock domain.
973 Any ``ResetSignal`` is equivalent to ``cd.rst`` for a clock domain with the corresponding name.
974 All of these signals ultimately refer to the same signal, but they can be manipulated
975 independently of the clock domain, even before the clock domain is created.
980 Clock domain to obtain a reset signal for. Defaults to ``"sync"``.
981 allow_reset_less : bool
982 If the clock domain is reset-less, act as a constant ``0`` instead of reporting an error.
984 def __init__(self
, domain
="sync", allow_reset_less
=False, *, src_loc_at
=0):
985 super().__init
__(src_loc_at
=src_loc_at
)
986 if not isinstance(domain
, str):
987 raise TypeError("Clock domain name must be a string, not {!r}".format(domain
))
989 raise ValueError("Domain '{}' does not have a reset".format(domain
))
991 self
.allow_reset_less
= allow_reset_less
996 def _lhs_signals(self
):
997 return ValueSet((self
,))
999 def _rhs_signals(self
):
1000 raise NotImplementedError("ResetSignal must be lowered to a concrete signal") # :nocov:
1003 return "(rst {})".format(self
.domain
)
1006 class Array(MutableSequence
):
1007 """Addressable multiplexer.
1009 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
1012 The array proxy can be used as an ordinary ``Value``, i.e. participate in calculations and
1013 assignments, provided that all elements of the array are values. The array proxy also supports
1014 attribute access and further indexing, each returning another array proxy; this means that
1015 the results of indexing into arrays, arrays of records, and arrays of arrays can all
1016 be used as first-class values.
1018 It is an error to change an array or any of its elements after an array proxy was created.
1019 Changing the array directly will raise an exception. However, it is not possible to detect
1020 the elements being modified; if an element's attribute or element is modified after the proxy
1021 for it has been created, the proxy will refer to stale data.
1028 gpios = Array(Signal() for _ in range(10))
1030 m.d.sync += gpios[bus.addr].eq(bus.w_data)
1032 m.d.sync += bus.r_data.eq(gpios[bus.addr])
1034 Multidimensional array::
1036 mult = Array(Array(x * y for y in range(10)) for x in range(10))
1037 a = Signal.range(10)
1038 b = Signal.range(10)
1040 m.d.comb += r.eq(mult[a][b])
1048 buses = Array(Record(layout) for busno in range(4))
1049 master = Record(layout)
1051 buses[sel].r_en.eq(master.r_en),
1052 master.r_data.eq(buses[sel].r_data),
1055 def __init__(self
, iterable
=()):
1056 self
._inner
= list(iterable
)
1057 self
._proxy
_at
= None
1058 self
._mutable
= True
1060 def __getitem__(self
, index
):
1061 if isinstance(index
, Value
):
1063 self
._proxy
_at
= tracer
.get_src_loc()
1064 self
._mutable
= False
1065 return ArrayProxy(self
, index
)
1067 return self
._inner
[index
]
1070 return len(self
._inner
)
1072 def _check_mutability(self
):
1073 if not self
._mutable
:
1074 raise ValueError("Array can no longer be mutated after it was indexed with a value "
1075 "at {}:{}".format(*self
._proxy
_at
))
1077 def __setitem__(self
, index
, value
):
1078 self
._check
_mutability
()
1079 self
._inner
[index
] = value
1081 def __delitem__(self
, index
):
1082 self
._check
_mutability
()
1083 del self
._inner
[index
]
1085 def insert(self
, index
, value
):
1086 self
._check
_mutability
()
1087 self
._inner
.insert(index
, value
)
1090 return "(array{} [{}])".format(" mutable" if self
._mutable
else "",
1091 ", ".join(map(repr, self
._inner
)))
1095 class ArrayProxy(Value
):
1096 def __init__(self
, elems
, index
, *, src_loc_at
=0):
1097 super().__init
__(src_loc_at
=1 + src_loc_at
)
1099 self
.index
= Value
.cast(index
)
1101 def __getattr__(self
, attr
):
1102 return ArrayProxy([getattr(elem
, attr
) for elem
in self
.elems
], self
.index
)
1104 def __getitem__(self
, index
):
1105 return ArrayProxy([ elem
[index
] for elem
in self
.elems
], self
.index
)
1107 def _iter_as_values(self
):
1108 return (Value
.cast(elem
) for elem
in self
.elems
)
1111 width
, signed
= 0, False
1112 for elem_width
, elem_signed
in (elem
.shape() for elem
in self
._iter
_as
_values
()):
1113 width
= max(width
, elem_width
+ elem_signed
)
1114 signed
= max(signed
, elem_signed
)
1115 return Shape(width
, signed
)
1117 def _lhs_signals(self
):
1118 signals
= union((elem
._lhs
_signals
() for elem
in self
._iter
_as
_values
()), start
=ValueSet())
1121 def _rhs_signals(self
):
1122 signals
= union((elem
._rhs
_signals
() for elem
in self
._iter
_as
_values
()), start
=ValueSet())
1123 return self
.index
._rhs
_signals
() | signals
1126 return "(proxy (array [{}]) {!r})".format(", ".join(map(repr, self
.elems
)), self
.index
)
1129 class UserValue(Value
):
1130 """Value with custom lowering.
1132 A ``UserValue`` is a value whose precise representation does not have to be immediately known,
1133 which is useful in certain metaprogramming scenarios. Instead of providing fixed semantics
1134 upfront, it is kept abstract for as long as possible, only being lowered to a concrete nMigen
1135 value when required.
1137 Note that the ``lower`` method will only be called once; this is necessary to ensure that
1138 nMigen's view of representation of all values stays internally consistent. If the class
1139 deriving from ``UserValue`` is mutable, then it must ensure that after ``lower`` is called,
1140 it is not mutated in a way that changes its representation.
1142 The following is an incomplete list of actions that, when applied to an ``UserValue`` directly
1143 or indirectly, will cause it to be lowered, provided as an illustrative reference:
1144 * Querying the shape using ``.shape()`` or ``len()``;
1145 * Creating a similarly shaped signal using ``Signal.like``;
1146 * Indexing or iterating through individual bits;
1147 * Adding an assignment to the value to a ``Module`` using ``m.d.<domain> +=``.
1149 def __init__(self
, *, src_loc_at
=0):
1150 super().__init
__(src_loc_at
=1 + src_loc_at
)
1151 self
.__lowered
= None
1155 """Conversion to a concrete representation."""
1158 def _lazy_lower(self
):
1159 if self
.__lowered
is None:
1160 self
.__lowered
= Value
.cast(self
.lower())
1161 return self
.__lowered
1164 return self
._lazy
_lower
().shape()
1166 def _lhs_signals(self
):
1167 return self
._lazy
_lower
()._lhs
_signals
()
1169 def _rhs_signals(self
):
1170 return self
._lazy
_lower
()._rhs
_signals
()
1174 class Sample(Value
):
1175 """Value from the past.
1177 A ``Sample`` of an expression is equal to the value of the expression ``clocks`` clock edges
1178 of the ``domain`` clock back. If that moment is before the beginning of time, it is equal
1179 to the value of the expression calculated as if each signal had its reset value.
1181 def __init__(self
, expr
, clocks
, domain
, *, src_loc_at
=0):
1182 super().__init
__(src_loc_at
=1 + src_loc_at
)
1183 self
.value
= Value
.cast(expr
)
1184 self
.clocks
= int(clocks
)
1185 self
.domain
= domain
1186 if not isinstance(self
.value
, (Const
, Signal
, ClockSignal
, ResetSignal
, Initial
)):
1187 raise TypeError("Sampled value must be a signal or a constant, not {!r}"
1188 .format(self
.value
))
1190 raise ValueError("Cannot sample a value {} cycles in the future"
1191 .format(-self
.clocks
))
1192 if not (self
.domain
is None or isinstance(self
.domain
, str)):
1193 raise TypeError("Domain name must be a string or None, not {!r}"
1194 .format(self
.domain
))
1197 return self
.value
.shape()
1199 def _rhs_signals(self
):
1200 return ValueSet((self
,))
1203 return "(sample {!r} @ {}[{}])".format(
1204 self
.value
, "<default>" if self
.domain
is None else self
.domain
, self
.clocks
)
1207 def Past(expr
, clocks
=1, domain
=None):
1208 return Sample(expr
, clocks
, domain
)
1211 def Stable(expr
, clocks
=0, domain
=None):
1212 return Sample(expr
, clocks
+ 1, domain
) == Sample(expr
, clocks
, domain
)
1215 def Rose(expr
, clocks
=0, domain
=None):
1216 return ~
Sample(expr
, clocks
+ 1, domain
) & Sample(expr
, clocks
, domain
)
1219 def Fell(expr
, clocks
=0, domain
=None):
1220 return Sample(expr
, clocks
+ 1, domain
) & ~
Sample(expr
, clocks
, domain
)
1224 class Initial(Value
):
1225 """Start indicator, for model checking.
1227 An ``Initial`` signal is ``1`` at the first cycle of model checking, and ``0`` at any other.
1229 def __init__(self
, *, src_loc_at
=0):
1230 super().__init
__(src_loc_at
=1 + src_loc_at
)
1235 def _rhs_signals(self
):
1236 return ValueSet((self
,))
1242 class _StatementList(list):
1244 return "({})".format(" ".join(map(repr, self
)))
1248 def __init__(self
, *, src_loc_at
=0):
1249 self
.src_loc
= tracer
.get_src_loc(1 + src_loc_at
)
1253 if isinstance(obj
, Iterable
):
1254 return _StatementList(sum((Statement
.wrap(e
) for e
in obj
), []))
1256 if isinstance(obj
, Statement
):
1257 return _StatementList([obj
])
1259 raise TypeError("Object {!r} is not an nMigen statement".format(obj
))
1263 class Assign(Statement
):
1264 def __init__(self
, lhs
, rhs
, *, src_loc_at
=0):
1265 super().__init
__(src_loc_at
=src_loc_at
)
1266 self
.lhs
= Value
.cast(lhs
)
1267 self
.rhs
= Value
.cast(rhs
)
1269 def _lhs_signals(self
):
1270 return self
.lhs
._lhs
_signals
()
1272 def _rhs_signals(self
):
1273 return self
.lhs
._rhs
_signals
() | self
.rhs
._rhs
_signals
()
1276 return "(eq {!r} {!r})".format(self
.lhs
, self
.rhs
)
1279 class Property(Statement
):
1280 def __init__(self
, test
, *, _check
=None, _en
=None, src_loc_at
=0):
1281 super().__init
__(src_loc_at
=src_loc_at
)
1282 self
.test
= Value
.cast(test
)
1283 self
._check
= _check
1285 if self
._check
is None:
1286 self
._check
= Signal(reset_less
=True, name
="${}$check".format(self
._kind
))
1287 self
._check
.src_loc
= self
.src_loc
1289 self
._en
= Signal(reset_less
=True, name
="${}$en".format(self
._kind
))
1290 self
._en
.src_loc
= self
.src_loc
1292 def _lhs_signals(self
):
1293 return ValueSet((self
._en
, self
._check
))
1295 def _rhs_signals(self
):
1296 return self
.test
._rhs
_signals
()
1299 return "({} {!r})".format(self
._kind
, self
.test
)
1303 class Assert(Property
):
1308 class Assume(Property
):
1313 class Cover(Property
):
1318 class Switch(Statement
):
1319 def __init__(self
, test
, cases
, *, src_loc
=None, src_loc_at
=0, case_src_locs
={}):
1321 super().__init
__(src_loc_at
=src_loc_at
)
1323 # Switch is a bit special in terms of location tracking because it is usually created
1324 # long after the control has left the statement that directly caused its creation.
1325 self
.src_loc
= src_loc
1326 # Switch is also a bit special in that its parts also have location information. It can't
1327 # be automatically traced, so whatever constructs a Switch may optionally provide it.
1328 self
.case_src_locs
= {}
1330 self
.test
= Value
.cast(test
)
1331 self
.cases
= OrderedDict()
1332 for orig_keys
, stmts
in cases
.items():
1333 # Map: None -> (); key -> (key,); (key...) -> (key...)
1337 if not isinstance(keys
, tuple):
1339 # Map: 2 -> "0010"; "0010" -> "0010"
1342 if isinstance(key
, str):
1344 elif isinstance(key
, int):
1345 key
= format(key
, "b").rjust(len(self
.test
), "0")
1346 elif isinstance(key
, Enum
):
1347 key
= format(key
.value
, "b").rjust(len(self
.test
), "0")
1349 raise TypeError("Object {!r} cannot be used as a switch key"
1351 assert len(key
) == len(self
.test
)
1352 new_keys
= (*new_keys
, key
)
1353 if not isinstance(stmts
, Iterable
):
1355 self
.cases
[new_keys
] = Statement
.wrap(stmts
)
1356 if orig_keys
in case_src_locs
:
1357 self
.case_src_locs
[new_keys
] = case_src_locs
[orig_keys
]
1359 def _lhs_signals(self
):
1360 signals
= union((s
._lhs
_signals
() for ss
in self
.cases
.values() for s
in ss
),
1364 def _rhs_signals(self
):
1365 signals
= union((s
._rhs
_signals
() for ss
in self
.cases
.values() for s
in ss
),
1367 return self
.test
._rhs
_signals
() | signals
1370 def case_repr(keys
, stmts
):
1371 stmts_repr
= " ".join(map(repr, stmts
))
1373 return "(default {})".format(stmts_repr
)
1374 elif len(keys
) == 1:
1375 return "(case {} {})".format(keys
[0], stmts_repr
)
1377 return "(case ({}) {})".format(" ".join(keys
), stmts_repr
)
1378 case_reprs
= [case_repr(keys
, stmts
) for keys
, stmts
in self
.cases
.items()]
1379 return "(switch {!r} {})".format(self
.test
, " ".join(case_reprs
))
1383 class Delay(Statement
):
1384 def __init__(self
, interval
=None, *, src_loc_at
=0):
1385 super().__init
__(src_loc_at
=src_loc_at
)
1386 self
.interval
= None if interval
is None else float(interval
)
1388 def _rhs_signals(self
):
1392 if self
.interval
is None:
1395 return "(delay {:.3}us)".format(self
.interval
* 1e6
)
1399 class Tick(Statement
):
1400 def __init__(self
, domain
="sync", *, src_loc_at
=0):
1401 super().__init
__(src_loc_at
=src_loc_at
)
1402 self
.domain
= str(domain
)
1404 def _rhs_signals(self
):
1408 return "(tick {})".format(self
.domain
)
1412 class Passive(Statement
):
1413 def __init__(self
, *, src_loc_at
=0):
1414 super().__init
__(src_loc_at
=src_loc_at
)
1416 def _rhs_signals(self
):
1423 class _MappedKeyCollection(metaclass
=ABCMeta
):
1425 def _map_key(self
, key
):
1429 def _unmap_key(self
, key
):
1433 class _MappedKeyDict(MutableMapping
, _MappedKeyCollection
):
1434 def __init__(self
, pairs
=()):
1435 self
._storage
= OrderedDict()
1436 for key
, value
in pairs
:
1439 def __getitem__(self
, key
):
1440 key
= None if key
is None else self
._map
_key
(key
)
1441 return self
._storage
[key
]
1443 def __setitem__(self
, key
, value
):
1444 key
= None if key
is None else self
._map
_key
(key
)
1445 self
._storage
[key
] = value
1447 def __delitem__(self
, key
):
1448 key
= None if key
is None else self
._map
_key
(key
)
1449 del self
._storage
[key
]
1452 for key
in self
._storage
:
1456 yield self
._unmap
_key
(key
)
1458 def __eq__(self
, other
):
1459 if not isinstance(other
, type(self
)):
1461 if len(self
) != len(other
):
1463 for ak
, bk
in zip(sorted(self
._storage
), sorted(other
._storage
)):
1466 if self
._storage
[ak
] != other
._storage
[bk
]:
1471 return len(self
._storage
)
1474 pairs
= ["({!r}, {!r})".format(k
, v
) for k
, v
in self
.items()]
1475 return "{}.{}([{}])".format(type(self
).__module
__, type(self
).__name
__,
1479 class _MappedKeySet(MutableSet
, _MappedKeyCollection
):
1480 def __init__(self
, elements
=()):
1481 self
._storage
= OrderedDict()
1482 for elem
in elements
:
1485 def add(self
, value
):
1486 self
._storage
[self
._map
_key
(value
)] = None
1488 def update(self
, values
):
1489 for value
in values
:
1492 def discard(self
, value
):
1494 del self
._storage
[self
._map
_key
(value
)]
1496 def __contains__(self
, value
):
1497 return self
._map
_key
(value
) in self
._storage
1500 for key
in [k
for k
in self
._storage
]:
1501 yield self
._unmap
_key
(key
)
1504 return len(self
._storage
)
1507 return "{}.{}({})".format(type(self
).__module
__, type(self
).__name
__,
1508 ", ".join(repr(x
) for x
in self
))
1512 def __init__(self
, value
):
1513 self
.value
= Value
.cast(value
)
1514 if isinstance(self
.value
, Const
):
1515 self
._hash
= hash(self
.value
.value
)
1516 elif isinstance(self
.value
, (Signal
, AnyValue
)):
1517 self
._hash
= hash(self
.value
.duid
)
1518 elif isinstance(self
.value
, (ClockSignal
, ResetSignal
)):
1519 self
._hash
= hash(self
.value
.domain
)
1520 elif isinstance(self
.value
, Operator
):
1521 self
._hash
= hash((self
.value
.operator
,
1522 tuple(ValueKey(o
) for o
in self
.value
.operands
)))
1523 elif isinstance(self
.value
, Slice
):
1524 self
._hash
= hash((ValueKey(self
.value
.value
), self
.value
.start
, self
.value
.end
))
1525 elif isinstance(self
.value
, Part
):
1526 self
._hash
= hash((ValueKey(self
.value
.value
), ValueKey(self
.value
.offset
),
1527 self
.value
.width
, self
.value
.stride
))
1528 elif isinstance(self
.value
, Cat
):
1529 self
._hash
= hash(tuple(ValueKey(o
) for o
in self
.value
.parts
))
1530 elif isinstance(self
.value
, ArrayProxy
):
1531 self
._hash
= hash((ValueKey(self
.value
.index
),
1532 tuple(ValueKey(e
) for e
in self
.value
._iter
_as
_values
())))
1533 elif isinstance(self
.value
, Sample
):
1534 self
._hash
= hash((ValueKey(self
.value
.value
), self
.value
.clocks
, self
.value
.domain
))
1535 elif isinstance(self
.value
, Initial
):
1538 raise TypeError("Object {!r} cannot be used as a key in value collections"
1539 .format(self
.value
))
1544 def __eq__(self
, other
):
1545 if type(other
) is not ValueKey
:
1547 if type(self
.value
) is not type(other
.value
):
1550 if isinstance(self
.value
, Const
):
1551 return self
.value
.value
== other
.value
.value
1552 elif isinstance(self
.value
, (Signal
, AnyValue
)):
1553 return self
.value
is other
.value
1554 elif isinstance(self
.value
, (ClockSignal
, ResetSignal
)):
1555 return self
.value
.domain
== other
.value
.domain
1556 elif isinstance(self
.value
, Operator
):
1557 return (self
.value
.operator
== other
.value
.operator
and
1558 len(self
.value
.operands
) == len(other
.value
.operands
) and
1559 all(ValueKey(a
) == ValueKey(b
)
1560 for a
, b
in zip(self
.value
.operands
, other
.value
.operands
)))
1561 elif isinstance(self
.value
, Slice
):
1562 return (ValueKey(self
.value
.value
) == ValueKey(other
.value
.value
) and
1563 self
.value
.start
== other
.value
.start
and
1564 self
.value
.end
== other
.value
.end
)
1565 elif isinstance(self
.value
, Part
):
1566 return (ValueKey(self
.value
.value
) == ValueKey(other
.value
.value
) and
1567 ValueKey(self
.value
.offset
) == ValueKey(other
.value
.offset
) and
1568 self
.value
.width
== other
.value
.width
and
1569 self
.value
.stride
== other
.value
.stride
)
1570 elif isinstance(self
.value
, Cat
):
1571 return all(ValueKey(a
) == ValueKey(b
)
1572 for a
, b
in zip(self
.value
.parts
, other
.value
.parts
))
1573 elif isinstance(self
.value
, ArrayProxy
):
1574 return (ValueKey(self
.value
.index
) == ValueKey(other
.value
.index
) and
1575 len(self
.value
.elems
) == len(other
.value
.elems
) and
1576 all(ValueKey(a
) == ValueKey(b
)
1577 for a
, b
in zip(self
.value
._iter
_as
_values
(),
1578 other
.value
._iter
_as
_values
())))
1579 elif isinstance(self
.value
, Sample
):
1580 return (ValueKey(self
.value
.value
) == ValueKey(other
.value
.value
) and
1581 self
.value
.clocks
== other
.value
.clocks
and
1582 self
.value
.domain
== self
.value
.domain
)
1583 elif isinstance(self
.value
, Initial
):
1586 raise TypeError("Object {!r} cannot be used as a key in value collections"
1587 .format(self
.value
))
1589 def __lt__(self
, other
):
1590 if not isinstance(other
, ValueKey
):
1592 if type(self
.value
) != type(other
.value
):
1595 if isinstance(self
.value
, Const
):
1596 return self
.value
< other
.value
1597 elif isinstance(self
.value
, (Signal
, AnyValue
)):
1598 return self
.value
.duid
< other
.value
.duid
1599 elif isinstance(self
.value
, Slice
):
1600 return (ValueKey(self
.value
.value
) < ValueKey(other
.value
.value
) and
1601 self
.value
.start
< other
.value
.start
and
1602 self
.value
.end
< other
.value
.end
)
1604 raise TypeError("Object {!r} cannot be used as a key in value collections")
1607 return "<{}.ValueKey {!r}>".format(__name__
, self
.value
)
1610 class ValueDict(_MappedKeyDict
):
1612 _unmap_key
= lambda self
, key
: key
.value
1615 class ValueSet(_MappedKeySet
):
1617 _unmap_key
= lambda self
, key
: key
.value
1621 def __init__(self
, signal
):
1622 self
.signal
= signal
1623 if type(signal
) is Signal
:
1624 self
._intern
= (0, signal
.duid
)
1625 elif type(signal
) is ClockSignal
:
1626 self
._intern
= (1, signal
.domain
)
1627 elif type(signal
) is ResetSignal
:
1628 self
._intern
= (2, signal
.domain
)
1630 raise TypeError("Object {!r} is not an nMigen signal".format(signal
))
1633 return hash(self
._intern
)
1635 def __eq__(self
, other
):
1636 if type(other
) is not SignalKey
:
1638 return self
._intern
== other
._intern
1640 def __lt__(self
, other
):
1641 if type(other
) is not SignalKey
:
1642 raise TypeError("Object {!r} cannot be compared to a SignalKey".format(signal
))
1643 return self
._intern
< other
._intern
1646 return "<{}.SignalKey {!r}>".format(__name__
, self
.signal
)
1649 class SignalDict(_MappedKeyDict
):
1650 _map_key
= SignalKey
1651 _unmap_key
= lambda self
, key
: key
.signal
1654 class SignalSet(_MappedKeySet
):
1655 _map_key
= SignalKey
1656 _unmap_key
= lambda self
, key
: key
.signal