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
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.
58 def __init__(self
, width
=1, signed
=False):
59 if not isinstance(width
, int) or width
< 0:
60 raise TypeError("Width must be a non-negative integer, not {!r}"
66 return iter((self
.width
, self
.signed
))
69 def cast(obj
, *, src_loc_at
=0):
70 if isinstance(obj
, Shape
):
72 if isinstance(obj
, int):
74 if isinstance(obj
, tuple):
76 warnings
.warn("instead of `{tuple}`, use `{constructor}({width})`"
77 .format(constructor
="signed" if signed
else "unsigned", width
=width
,
79 DeprecationWarning, stacklevel
=2 + src_loc_at
)
80 return Shape(width
, signed
)
81 if isinstance(obj
, range):
83 return Shape(0, obj
.start
< 0)
84 signed
= obj
.start
< 0 or (obj
.stop
- obj
.step
) < 0
85 width
= max(bits_for(obj
.start
, signed
),
86 bits_for(obj
.stop
- obj
.step
, signed
))
87 return Shape(width
, signed
)
88 if isinstance(obj
, type) and issubclass(obj
, Enum
):
89 min_value
= min(member
.value
for member
in obj
)
90 max_value
= max(member
.value
for member
in obj
)
91 if not isinstance(min_value
, int) or not isinstance(max_value
, int):
92 raise TypeError("Only enumerations with integer values can be used "
94 signed
= min_value
< 0 or max_value
< 0
95 width
= max(bits_for(min_value
, signed
), bits_for(max_value
, signed
))
96 return Shape(width
, signed
)
97 raise TypeError("Object {!r} cannot be used as value shape".format(obj
))
101 return "signed({})".format(self
.width
)
103 return "unsigned({})".format(self
.width
)
105 def __eq__(self
, other
):
106 if isinstance(other
, tuple) and len(other
) == 2:
107 width
, signed
= other
108 if isinstance(width
, int) and isinstance(signed
, bool):
109 return self
.width
== width
and self
.signed
== signed
111 raise TypeError("Shapes may be compared with other Shapes and (int, bool) tuples, "
114 if not isinstance(other
, Shape
):
115 raise TypeError("Shapes may be compared with other Shapes and (int, bool) tuples, "
118 return self
.width
== other
.width
and self
.signed
== other
.signed
122 """Shorthand for ``Shape(width, signed=False)``."""
123 return Shape(width
, signed
=False)
127 """Shorthand for ``Shape(width, signed=True)``."""
128 return Shape(width
, signed
=True)
131 class Value(metaclass
=ABCMeta
):
134 """Converts ``obj`` to an nMigen value.
136 Booleans and integers are wrapped into a :class:`Const`. Enumerations whose members are
137 all integers are converted to a :class:`Const` with a shape that fits every member.
139 if isinstance(obj
, Value
):
141 if isinstance(obj
, int):
143 if isinstance(obj
, Enum
):
144 return Const(obj
.value
, Shape
.cast(type(obj
)))
145 raise TypeError("Object {!r} cannot be converted to an nMigen value".format(obj
))
147 def __init__(self
, *, src_loc_at
=0):
149 self
.src_loc
= tracer
.get_src_loc(1 + src_loc_at
)
152 raise TypeError("Attempted to convert nMigen value to Python boolean")
154 def __invert__(self
):
155 return Operator("~", [self
])
157 return Operator("-", [self
])
159 def __add__(self
, other
):
160 return Operator("+", [self
, other
])
161 def __radd__(self
, other
):
162 return Operator("+", [other
, self
])
163 def __sub__(self
, other
):
164 return Operator("-", [self
, other
])
165 def __rsub__(self
, other
):
166 return Operator("-", [other
, self
])
168 def __mul__(self
, other
):
169 return Operator("*", [self
, other
])
170 def __rmul__(self
, other
):
171 return Operator("*", [other
, self
])
173 def __check_divisor(self
):
174 width
, signed
= self
.shape()
176 # Python's division semantics and Verilog's division semantics differ for negative
177 # divisors (Python uses div/mod, Verilog uses quo/rem); for now, avoid the issue
178 # completely by prohibiting such division operations.
179 raise NotImplementedError("Division by a signed value is not supported")
180 def __mod__(self
, other
):
181 other
= Value
.cast(other
)
182 other
.__check
_divisor
()
183 return Operator("%", [self
, other
])
184 def __rmod__(self
, other
):
185 self
.__check
_divisor
()
186 return Operator("%", [other
, self
])
187 def __floordiv__(self
, other
):
188 other
= Value
.cast(other
)
189 other
.__check
_divisor
()
190 return Operator("//", [self
, other
])
191 def __rfloordiv__(self
, other
):
192 self
.__check
_divisor
()
193 return Operator("//", [other
, self
])
195 def __check_shamt(self
):
196 width
, signed
= self
.shape()
198 # Neither Python nor HDLs implement shifts by negative values; prohibit any shifts
199 # by a signed value to make sure the shift amount can always be interpreted as
201 raise TypeError("Shift amount must be unsigned")
202 def __lshift__(self
, other
):
203 other
= Value
.cast(other
)
204 other
.__check
_shamt
()
205 return Operator("<<", [self
, other
])
206 def __rlshift__(self
, other
):
208 return Operator("<<", [other
, self
])
209 def __rshift__(self
, other
):
210 other
= Value
.cast(other
)
211 other
.__check
_shamt
()
212 return Operator(">>", [self
, other
])
213 def __rrshift__(self
, other
):
215 return Operator(">>", [other
, self
])
217 def __and__(self
, other
):
218 return Operator("&", [self
, other
])
219 def __rand__(self
, other
):
220 return Operator("&", [other
, self
])
221 def __xor__(self
, other
):
222 return Operator("^", [self
, other
])
223 def __rxor__(self
, other
):
224 return Operator("^", [other
, self
])
225 def __or__(self
, other
):
226 return Operator("|", [self
, other
])
227 def __ror__(self
, other
):
228 return Operator("|", [other
, self
])
230 def __eq__(self
, other
):
231 return Operator("==", [self
, other
])
232 def __ne__(self
, other
):
233 return Operator("!=", [self
, other
])
234 def __lt__(self
, other
):
235 return Operator("<", [self
, other
])
236 def __le__(self
, other
):
237 return Operator("<=", [self
, other
])
238 def __gt__(self
, other
):
239 return Operator(">", [self
, other
])
240 def __ge__(self
, other
):
241 return Operator(">=", [self
, other
])
244 width
, signed
= self
.shape()
246 return Mux(self
>= 0, self
, -self
)
251 return self
.shape().width
253 def __getitem__(self
, key
):
255 if isinstance(key
, int):
256 if key
not in range(-n
, n
):
257 raise IndexError("Cannot index {} bits into {}-bit value".format(key
, n
))
260 return Slice(self
, key
, key
+ 1)
261 elif isinstance(key
, slice):
262 start
, stop
, step
= key
.indices(n
)
264 return Cat(self
[i
] for i
in range(start
, stop
, step
))
265 return Slice(self
, start
, stop
)
267 raise TypeError("Cannot index value with {}".format(repr(key
)))
269 def as_unsigned(self
):
270 """Conversion to unsigned.
275 This ``Value`` reinterpreted as a unsigned integer.
277 return Operator("u", [self
])
280 """Conversion to signed.
285 This ``Value`` reinterpreted as a signed integer.
287 return Operator("s", [self
])
290 """Conversion to boolean.
295 ``1`` if any bits are set, ``0`` otherwise.
297 return Operator("b", [self
])
300 """Check if any bits are ``1``.
305 ``1`` if any bits are set, ``0`` otherwise.
307 return Operator("r|", [self
])
310 """Check if all bits are ``1``.
315 ``1`` if all bits are set, ``0`` otherwise.
317 return Operator("r&", [self
])
320 """Compute pairwise exclusive-or of every bit.
325 ``1`` if an odd number of bits are set, ``0`` if an even number of bits are set.
327 return Operator("r^", [self
])
329 def implies(premise
, conclusion
):
335 ``0`` if ``premise`` is true and ``conclusion`` is not, ``1`` otherwise.
337 return ~premise | conclusion
339 def bit_select(self
, offset
, width
):
340 """Part-select with bit granularity.
342 Selects a constant width but variable offset part of a ``Value``, such that successive
343 parts overlap by all but 1 bit.
348 Index of first selected bit.
350 Number of selected bits.
355 Selected part of the ``Value``
357 offset
= Value
.cast(offset
)
358 if type(offset
) is Const
and isinstance(width
, int):
359 return self
[offset
.value
:offset
.value
+ width
]
360 return Part(self
, offset
, width
, stride
=1, src_loc_at
=1)
362 def word_select(self
, offset
, width
):
363 """Part-select with word granularity.
365 Selects a constant width but variable offset part of a ``Value``, such that successive
366 parts do not overlap.
371 Index of first selected word.
373 Number of selected bits.
378 Selected part of the ``Value``
380 offset
= Value
.cast(offset
)
381 if type(offset
) is Const
and isinstance(width
, int):
382 return self
[offset
.value
* width
:(offset
.value
+ 1) * width
]
383 return Part(self
, offset
, width
, stride
=width
, src_loc_at
=1)
385 def matches(self
, *patterns
):
388 Matches against a set of patterns, which may be integers or bit strings, recognizing
389 the same grammar as ``Case()``.
393 patterns : int or str
394 Patterns to match against.
399 ``1`` if any pattern matches the value, ``0`` otherwise.
402 for pattern
in patterns
:
403 if not isinstance(pattern
, (int, str, Enum
)):
404 raise SyntaxError("Match pattern must be an integer, a string, or an enumeration, "
407 if isinstance(pattern
, str) and any(bit
not in "01- \t" for bit
in pattern
):
408 raise SyntaxError("Match pattern '{}' must consist of 0, 1, and - (don't care) "
409 "bits, and may include whitespace"
411 if (isinstance(pattern
, str) and
412 len("".join(pattern
.split())) != len(self
)):
413 raise SyntaxError("Match pattern '{}' must have the same width as match value "
415 .format(pattern
, len(self
)))
416 if isinstance(pattern
, int) and bits_for(pattern
) > len(self
):
417 warnings
.warn("Match pattern '{:b}' is wider than match value "
418 "(which has width {}); comparison will never be true"
419 .format(pattern
, len(self
)),
420 SyntaxWarning, stacklevel
=3)
422 if isinstance(pattern
, str):
423 pattern
= "".join(pattern
.split()) # remove whitespace
424 mask
= int(pattern
.replace("0", "1").replace("-", "0"), 2)
425 pattern
= int(pattern
.replace("-", "0"), 2)
426 matches
.append((self
& mask
) == pattern
)
427 elif isinstance(pattern
, int):
428 matches
.append(self
== pattern
)
429 elif isinstance(pattern
, Enum
):
430 matches
.append(self
== pattern
.value
)
435 elif len(matches
) == 1:
438 return Cat(*matches
).any()
440 def shift_left(self
, amount
):
441 """Shift left by constant amount.
451 If the amount is positive, the input shifted left. Otherwise, the input shifted right.
453 if not isinstance(amount
, int):
454 raise TypeError("Shift amount must be an integer, not {!r}".format(amount
))
456 return self
.shift_right(-amount
)
457 if self
.shape().signed
:
458 return Cat(Const(0, amount
), self
).as_signed()
460 return Cat(Const(0, amount
), self
) # unsigned
462 def shift_right(self
, amount
):
463 """Shift right by constant amount.
473 If the amount is positive, the input shifted right. Otherwise, the input shifted left.
475 if not isinstance(amount
, int):
476 raise TypeError("Shift amount must be an integer, not {!r}".format(amount
))
478 return self
.shift_left(-amount
)
479 if self
.shape().signed
:
480 return self
[amount
:].as_signed()
482 return self
[amount
:] # unsigned
484 def rotate_left(self
, amount
):
485 """Rotate left by constant amount.
495 If the amount is positive, the input rotated left. Otherwise, the input rotated right.
497 if not isinstance(amount
, int):
498 raise TypeError("Rotate amount must be an integer, not {!r}".format(amount
))
500 return Cat(self
[-amount
:], self
[:-amount
]) # meow :3
502 def rotate_right(self
, amount
):
503 """Rotate right by constant amount.
513 If the amount is positive, the input rotated right. Otherwise, the input rotated right.
515 if not isinstance(amount
, int):
516 raise TypeError("Rotate amount must be an integer, not {!r}".format(amount
))
518 return Cat(self
[amount
:], self
[:amount
])
526 Value to be assigned.
531 Assignment statement that can be used in combinatorial or synchronous context.
533 return Assign(self
, value
, src_loc_at
=1)
537 """Bit width and signedness of a value.
546 >>> Signal(8).shape()
547 Shape(width=8, signed=False)
548 >>> Const(0xaa).shape()
549 Shape(width=8, signed=False)
553 def _lhs_signals(self
):
554 raise TypeError("Value {!r} cannot be used in assignments".format(self
))
557 def _rhs_signals(self
):
561 raise TypeError("Value {!r} cannot be evaluated as constant".format(self
))
568 """A constant, literal integer value.
573 shape : int or tuple or None
574 Either an integer ``width`` or a tuple ``(width, signed)`` specifying the number of bits
575 in this constant and whether it is signed (can represent negative values).
576 ``shape`` defaults to the minimum possible width and signedness of ``value``.
586 def normalize(value
, shape
):
587 width
, signed
= shape
588 mask
= (1 << width
) - 1
590 if signed
and value
>> (width
- 1):
594 def __init__(self
, value
, shape
=None, *, src_loc_at
=0):
595 # We deliberately do not call Value.__init__ here.
596 self
.value
= int(value
)
598 shape
= Shape(bits_for(self
.value
), signed
=self
.value
< 0)
599 elif isinstance(shape
, int):
600 shape
= Shape(shape
, signed
=self
.value
< 0)
602 shape
= Shape
.cast(shape
, src_loc_at
=1 + src_loc_at
)
603 self
.width
, self
.signed
= shape
604 self
.value
= self
.normalize(self
.value
, shape
)
607 return Shape(self
.width
, self
.signed
)
609 def _rhs_signals(self
):
616 return "(const {}'{}d{})".format(self
.width
, "s" if self
.signed
else "", self
.value
)
619 C
= Const
# shorthand
622 class AnyValue(Value
, DUID
):
623 def __init__(self
, shape
, *, src_loc_at
=0):
624 super().__init
__(src_loc_at
=src_loc_at
)
625 self
.width
, self
.signed
= Shape
.cast(shape
, src_loc_at
=1 + src_loc_at
)
626 if not isinstance(self
.width
, int) or self
.width
< 0:
627 raise TypeError("Width must be a non-negative integer, not {!r}"
631 return Shape(self
.width
, self
.signed
)
633 def _rhs_signals(self
):
638 class AnyConst(AnyValue
):
640 return "(anyconst {}'{})".format(self
.width
, "s" if self
.signed
else "")
644 class AnySeq(AnyValue
):
646 return "(anyseq {}'{})".format(self
.width
, "s" if self
.signed
else "")
650 class Operator(Value
):
651 def __init__(self
, operator
, operands
, *, src_loc_at
=0):
652 super().__init
__(src_loc_at
=1 + src_loc_at
)
653 self
.operator
= operator
654 self
.operands
= [Value
.cast(op
) for op
in operands
]
657 def _bitwise_binary_shape(a_shape
, b_shape
):
658 a_bits
, a_sign
= a_shape
659 b_bits
, b_sign
= b_shape
660 if not a_sign
and not b_sign
:
661 # both operands unsigned
662 return Shape(max(a_bits
, b_bits
), False)
663 elif a_sign
and b_sign
:
664 # both operands signed
665 return Shape(max(a_bits
, b_bits
), True)
666 elif not a_sign
and b_sign
:
667 # first operand unsigned (add sign bit), second operand signed
668 return Shape(max(a_bits
+ 1, b_bits
), True)
670 # first signed, second operand unsigned (add sign bit)
671 return Shape(max(a_bits
, b_bits
+ 1), True)
673 op_shapes
= list(map(lambda x
: x
.shape(), self
.operands
))
674 if len(op_shapes
) == 1:
675 (a_width
, a_signed
), = op_shapes
676 if self
.operator
in ("+", "~"):
677 return Shape(a_width
, a_signed
)
678 if self
.operator
== "-":
679 return Shape(a_width
+ 1, True)
680 if self
.operator
in ("b", "r|", "r&", "r^"):
681 return Shape(1, False)
682 if self
.operator
== "u":
683 return Shape(a_width
, False)
684 if self
.operator
== "s":
685 return Shape(a_width
, True)
686 elif len(op_shapes
) == 2:
687 (a_width
, a_signed
), (b_width
, b_signed
) = op_shapes
688 if self
.operator
in ("+", "-"):
689 width
, signed
= _bitwise_binary_shape(*op_shapes
)
690 return Shape(width
+ 1, signed
)
691 if self
.operator
== "*":
692 return Shape(a_width
+ b_width
, a_signed
or b_signed
)
693 if self
.operator
in ("//", "%"):
695 return Shape(a_width
, a_signed
)
696 if self
.operator
in ("<", "<=", "==", "!=", ">", ">="):
697 return Shape(1, False)
698 if self
.operator
in ("&", "^", "|"):
699 return _bitwise_binary_shape(*op_shapes
)
700 if self
.operator
== "<<":
702 extra
= 2 ** (b_width
- 1) - 1
704 extra
= 2 ** (b_width
) - 1
705 return Shape(a_width
+ extra
, a_signed
)
706 if self
.operator
== ">>":
708 extra
= 2 ** (b_width
- 1)
711 return Shape(a_width
+ extra
, a_signed
)
712 elif len(op_shapes
) == 3:
713 if self
.operator
== "m":
714 s_shape
, a_shape
, b_shape
= op_shapes
715 return _bitwise_binary_shape(a_shape
, b_shape
)
716 raise NotImplementedError("Operator {}/{} not implemented"
717 .format(self
.operator
, len(op_shapes
))) # :nocov:
719 def _rhs_signals(self
):
720 return union(op
._rhs
_signals
() for op
in self
.operands
)
723 return "({} {})".format(self
.operator
, " ".join(map(repr, self
.operands
)))
726 def Mux(sel
, val1
, val0
):
727 """Choose between two values.
740 Output ``Value``. If ``sel`` is asserted, the Mux returns ``val1``, else ``val0``.
742 sel
= Value
.cast(sel
)
745 return Operator("m", [sel
, val1
, val0
])
750 def __init__(self
, value
, start
, stop
, *, src_loc_at
=0):
751 if not isinstance(start
, int):
752 raise TypeError("Slice start must be an integer, not {!r}".format(start
))
753 if not isinstance(stop
, int):
754 raise TypeError("Slice stop must be an integer, not {!r}".format(stop
))
757 if start
not in range(-(n
+1), n
+1):
758 raise IndexError("Cannot start slice {} bits into {}-bit value".format(start
, n
))
761 if stop
not in range(-(n
+1), n
+1):
762 raise IndexError("Cannot stop slice {} bits into {}-bit value".format(stop
, n
))
766 raise IndexError("Slice start {} must be less than slice stop {}".format(start
, stop
))
768 super().__init
__(src_loc_at
=src_loc_at
)
769 self
.value
= Value
.cast(value
)
774 return Shape(self
.stop
- self
.start
)
776 def _lhs_signals(self
):
777 return self
.value
._lhs
_signals
()
779 def _rhs_signals(self
):
780 return self
.value
._rhs
_signals
()
783 return "(slice {} {}:{})".format(repr(self
.value
), self
.start
, self
.stop
)
788 def __init__(self
, value
, offset
, width
, stride
=1, *, src_loc_at
=0):
789 if not isinstance(width
, int) or width
< 0:
790 raise TypeError("Part width must be a non-negative integer, not {!r}".format(width
))
791 if not isinstance(stride
, int) or stride
<= 0:
792 raise TypeError("Part stride must be a positive integer, not {!r}".format(stride
))
794 super().__init
__(src_loc_at
=src_loc_at
)
796 self
.offset
= Value
.cast(offset
)
801 return Shape(self
.width
)
803 def _lhs_signals(self
):
804 return self
.value
._lhs
_signals
()
806 def _rhs_signals(self
):
807 return self
.value
._rhs
_signals
() | self
.offset
._rhs
_signals
()
810 return "(part {} {} {} {})".format(repr(self
.value
), repr(self
.offset
),
811 self
.width
, self
.stride
)
816 """Concatenate values.
818 Form a compound ``Value`` from several smaller ones by concatenation.
819 The first argument occupies the lower bits of the result.
820 The return value can be used on either side of an assignment, that
821 is, the concatenated value can be used as an argument on the RHS or
822 as a target on the LHS. If it is used on the LHS, it must solely
823 consist of ``Signal`` s, slices of ``Signal`` s, and other concatenations
824 meeting these properties. The bit length of the return value is the sum of
825 the bit lengths of the arguments::
827 len(Cat(args)) == sum(len(arg) for arg in args)
831 *args : Values or iterables of Values, inout
832 ``Value`` s to be concatenated.
837 Resulting ``Value`` obtained by concatentation.
839 def __init__(self
, *args
, src_loc_at
=0):
840 super().__init
__(src_loc_at
=src_loc_at
)
841 self
.parts
= [Value
.cast(v
) for v
in flatten(args
)]
844 return Shape(sum(len(part
) for part
in self
.parts
))
846 def _lhs_signals(self
):
847 return union((part
._lhs
_signals
() for part
in self
.parts
), start
=SignalSet())
849 def _rhs_signals(self
):
850 return union((part
._rhs
_signals
() for part
in self
.parts
), start
=SignalSet())
854 for part
in reversed(self
.parts
):
856 value |
= part
._as
_const
()
860 return "(cat {})".format(" ".join(map(repr, self
.parts
)))
867 An input value is replicated (repeated) several times
868 to be used on the RHS of assignments::
870 len(Repl(s, n)) == len(s) * n
875 Input value to be replicated.
877 Number of replications.
884 def __init__(self
, value
, count
, *, src_loc_at
=0):
885 if not isinstance(count
, int) or count
< 0:
886 raise TypeError("Replication count must be a non-negative integer, not {!r}"
889 super().__init
__(src_loc_at
=src_loc_at
)
890 self
.value
= Value
.cast(value
)
894 return Shape(len(self
.value
) * self
.count
)
896 def _rhs_signals(self
):
897 return self
.value
._rhs
_signals
()
900 return "(repl {!r} {})".format(self
.value
, self
.count
)
904 class Signal(Value
, DUID
):
905 """A varying integer value.
909 shape : ``Shape``-castable object or None
910 Specification for the number of bits in this ``Signal`` and its signedness (whether it
911 can represent negative values). See ``Shape.cast`` for details.
912 If not specified, ``shape`` defaults to 1-bit and non-signed.
914 Name hint for this signal. If ``None`` (default) the name is inferred from the variable
915 name this ``Signal`` is assigned to.
916 reset : int or integral Enum
917 Reset (synchronous) or default (combinatorial) value.
918 When this ``Signal`` is assigned to in synchronous context and the corresponding clock
919 domain is reset, the ``Signal`` assumes the given value. When this ``Signal`` is unassigned
920 in combinatorial context (due to conditional assignments not being taken), the ``Signal``
921 assumes its ``reset`` value. Defaults to 0.
923 If ``True``, do not generate reset logic for this ``Signal`` in synchronous statements.
924 The ``reset`` value is only used as a combinatorial default or as the initial value.
925 Defaults to ``False``.
927 Dictionary of synthesis attributes.
928 decoder : function or Enum
929 A function converting integer signal values to human-readable strings (e.g. FSM state
930 names). If an ``Enum`` subclass is passed, it is concisely decoded using format string
931 ``"{0.name:}/{0.value:}"``, or a number if the signal value is not a member of
945 def __init__(self
, shape
=None, *, name
=None, reset
=0, reset_less
=False,
946 attrs
=None, decoder
=None, src_loc_at
=0):
947 super().__init
__(src_loc_at
=src_loc_at
)
949 if name
is not None and not isinstance(name
, str):
950 raise TypeError("Name must be a string, not {!r}".format(name
))
951 self
.name
= name
or tracer
.get_var_name(depth
=2 + src_loc_at
, default
="$signal")
955 self
.width
, self
.signed
= Shape
.cast(shape
, src_loc_at
=1 + src_loc_at
)
957 if isinstance(reset
, Enum
):
959 if not isinstance(reset
, int):
960 raise TypeError("Reset value has to be an int or an integral Enum")
962 reset_width
= bits_for(reset
, self
.signed
)
963 if reset
!= 0 and reset_width
> self
.width
:
964 warnings
.warn("Reset value {!r} requires {} bits to represent, but the signal "
966 .format(reset
, reset_width
, self
.width
),
967 SyntaxWarning, stacklevel
=2 + src_loc_at
)
970 self
.reset_less
= bool(reset_less
)
972 self
.attrs
= OrderedDict(() if attrs
is None else attrs
)
974 if decoder
is None and isinstance(shape
, type) and issubclass(shape
, Enum
):
976 if isinstance(decoder
, type) and issubclass(decoder
, Enum
):
977 def enum_decoder(value
):
979 return "{0.name:}/{0.value:}".format(decoder(value
))
982 self
.decoder
= enum_decoder
983 self
._enum
_class
= decoder
985 self
.decoder
= decoder
986 self
._enum
_class
= None
988 # Not a @classmethod because nmigen.compat requires it.
990 def like(other
, *, name
=None, name_suffix
=None, src_loc_at
=0, **kwargs
):
991 """Create Signal based on another.
996 Object to base this Signal on.
1000 elif name_suffix
is not None:
1001 new_name
= other
.name
+ str(name_suffix
)
1003 new_name
= tracer
.get_var_name(depth
=2 + src_loc_at
, default
="$like")
1004 kw
= dict(shape
=Value
.cast(other
).shape(), name
=new_name
)
1005 if isinstance(other
, Signal
):
1006 kw
.update(reset
=other
.reset
, reset_less
=other
.reset_less
,
1007 attrs
=other
.attrs
, decoder
=other
.decoder
)
1009 return Signal(**kw
, src_loc_at
=1 + src_loc_at
)
1012 return Shape(self
.width
, self
.signed
)
1014 def _lhs_signals(self
):
1015 return SignalSet((self
,))
1017 def _rhs_signals(self
):
1018 return SignalSet((self
,))
1021 return "(sig {})".format(self
.name
)
1025 class ClockSignal(Value
):
1026 """Clock signal for a clock domain.
1028 Any ``ClockSignal`` is equivalent to ``cd.clk`` for a clock domain with the corresponding name.
1029 All of these signals ultimately refer to the same signal, but they can be manipulated
1030 independently of the clock domain, even before the clock domain is created.
1035 Clock domain to obtain a clock signal for. Defaults to ``"sync"``.
1037 def __init__(self
, domain
="sync", *, src_loc_at
=0):
1038 super().__init
__(src_loc_at
=src_loc_at
)
1039 if not isinstance(domain
, str):
1040 raise TypeError("Clock domain name must be a string, not {!r}".format(domain
))
1041 if domain
== "comb":
1042 raise ValueError("Domain '{}' does not have a clock".format(domain
))
1043 self
.domain
= domain
1048 def _lhs_signals(self
):
1049 return SignalSet((self
,))
1051 def _rhs_signals(self
):
1052 raise NotImplementedError("ClockSignal must be lowered to a concrete signal") # :nocov:
1055 return "(clk {})".format(self
.domain
)
1059 class ResetSignal(Value
):
1060 """Reset signal for a clock domain.
1062 Any ``ResetSignal`` is equivalent to ``cd.rst`` for a clock domain with the corresponding name.
1063 All of these signals ultimately refer to the same signal, but they can be manipulated
1064 independently of the clock domain, even before the clock domain is created.
1069 Clock domain to obtain a reset signal for. Defaults to ``"sync"``.
1070 allow_reset_less : bool
1071 If the clock domain is reset-less, act as a constant ``0`` instead of reporting an error.
1073 def __init__(self
, domain
="sync", allow_reset_less
=False, *, src_loc_at
=0):
1074 super().__init
__(src_loc_at
=src_loc_at
)
1075 if not isinstance(domain
, str):
1076 raise TypeError("Clock domain name must be a string, not {!r}".format(domain
))
1077 if domain
== "comb":
1078 raise ValueError("Domain '{}' does not have a reset".format(domain
))
1079 self
.domain
= domain
1080 self
.allow_reset_less
= allow_reset_less
1085 def _lhs_signals(self
):
1086 return SignalSet((self
,))
1088 def _rhs_signals(self
):
1089 raise NotImplementedError("ResetSignal must be lowered to a concrete signal") # :nocov:
1092 return "(rst {})".format(self
.domain
)
1095 class Array(MutableSequence
):
1096 """Addressable multiplexer.
1098 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
1101 The array proxy can be used as an ordinary ``Value``, i.e. participate in calculations and
1102 assignments, provided that all elements of the array are values. The array proxy also supports
1103 attribute access and further indexing, each returning another array proxy; this means that
1104 the results of indexing into arrays, arrays of records, and arrays of arrays can all
1105 be used as first-class values.
1107 It is an error to change an array or any of its elements after an array proxy was created.
1108 Changing the array directly will raise an exception. However, it is not possible to detect
1109 the elements being modified; if an element's attribute or element is modified after the proxy
1110 for it has been created, the proxy will refer to stale data.
1117 gpios = Array(Signal() for _ in range(10))
1119 m.d.sync += gpios[bus.addr].eq(bus.w_data)
1121 m.d.sync += bus.r_data.eq(gpios[bus.addr])
1123 Multidimensional array::
1125 mult = Array(Array(x * y for y in range(10)) for x in range(10))
1126 a = Signal.range(10)
1127 b = Signal.range(10)
1129 m.d.comb += r.eq(mult[a][b])
1137 buses = Array(Record(layout) for busno in range(4))
1138 master = Record(layout)
1140 buses[sel].r_en.eq(master.r_en),
1141 master.r_data.eq(buses[sel].r_data),
1144 def __init__(self
, iterable
=()):
1145 self
._inner
= list(iterable
)
1146 self
._proxy
_at
= None
1147 self
._mutable
= True
1149 def __getitem__(self
, index
):
1150 if isinstance(index
, Value
):
1152 self
._proxy
_at
= tracer
.get_src_loc()
1153 self
._mutable
= False
1154 return ArrayProxy(self
, index
)
1156 return self
._inner
[index
]
1159 return len(self
._inner
)
1161 def _check_mutability(self
):
1162 if not self
._mutable
:
1163 raise ValueError("Array can no longer be mutated after it was indexed with a value "
1164 "at {}:{}".format(*self
._proxy
_at
))
1166 def __setitem__(self
, index
, value
):
1167 self
._check
_mutability
()
1168 self
._inner
[index
] = value
1170 def __delitem__(self
, index
):
1171 self
._check
_mutability
()
1172 del self
._inner
[index
]
1174 def insert(self
, index
, value
):
1175 self
._check
_mutability
()
1176 self
._inner
.insert(index
, value
)
1179 return "(array{} [{}])".format(" mutable" if self
._mutable
else "",
1180 ", ".join(map(repr, self
._inner
)))
1184 class ArrayProxy(Value
):
1185 def __init__(self
, elems
, index
, *, src_loc_at
=0):
1186 super().__init
__(src_loc_at
=1 + src_loc_at
)
1188 self
.index
= Value
.cast(index
)
1190 def __getattr__(self
, attr
):
1191 return ArrayProxy([getattr(elem
, attr
) for elem
in self
.elems
], self
.index
)
1193 def __getitem__(self
, index
):
1194 return ArrayProxy([ elem
[index
] for elem
in self
.elems
], self
.index
)
1196 def _iter_as_values(self
):
1197 return (Value
.cast(elem
) for elem
in self
.elems
)
1200 width
, signed
= 0, False
1201 for elem_width
, elem_signed
in (elem
.shape() for elem
in self
._iter
_as
_values
()):
1202 width
= max(width
, elem_width
+ elem_signed
)
1203 signed
= max(signed
, elem_signed
)
1204 return Shape(width
, signed
)
1206 def _lhs_signals(self
):
1207 signals
= union((elem
._lhs
_signals
() for elem
in self
._iter
_as
_values
()),
1211 def _rhs_signals(self
):
1212 signals
= union((elem
._rhs
_signals
() for elem
in self
._iter
_as
_values
()),
1214 return self
.index
._rhs
_signals
() | signals
1217 return "(proxy (array [{}]) {!r})".format(", ".join(map(repr, self
.elems
)), self
.index
)
1220 class UserValue(Value
):
1221 """Value with custom lowering.
1223 A ``UserValue`` is a value whose precise representation does not have to be immediately known,
1224 which is useful in certain metaprogramming scenarios. Instead of providing fixed semantics
1225 upfront, it is kept abstract for as long as possible, only being lowered to a concrete nMigen
1226 value when required.
1228 Note that the ``lower`` method will only be called once; this is necessary to ensure that
1229 nMigen's view of representation of all values stays internally consistent. If the class
1230 deriving from ``UserValue`` is mutable, then it must ensure that after ``lower`` is called,
1231 it is not mutated in a way that changes its representation.
1233 The following is an incomplete list of actions that, when applied to an ``UserValue`` directly
1234 or indirectly, will cause it to be lowered, provided as an illustrative reference:
1235 * Querying the shape using ``.shape()`` or ``len()``;
1236 * Creating a similarly shaped signal using ``Signal.like``;
1237 * Indexing or iterating through individual bits;
1238 * Adding an assignment to the value to a ``Module`` using ``m.d.<domain> +=``.
1240 def __init__(self
, *, src_loc_at
=0):
1241 super().__init
__(src_loc_at
=1 + src_loc_at
)
1242 self
.__lowered
= None
1246 """Conversion to a concrete representation."""
1249 def _lazy_lower(self
):
1250 if self
.__lowered
is None:
1251 lowered
= self
.lower()
1252 if isinstance(lowered
, UserValue
):
1253 lowered
= lowered
._lazy
_lower
()
1254 self
.__lowered
= Value
.cast(lowered
)
1255 return self
.__lowered
1258 return self
._lazy
_lower
().shape()
1260 def _lhs_signals(self
):
1261 return self
._lazy
_lower
()._lhs
_signals
()
1263 def _rhs_signals(self
):
1264 return self
._lazy
_lower
()._rhs
_signals
()
1268 class Sample(Value
):
1269 """Value from the past.
1271 A ``Sample`` of an expression is equal to the value of the expression ``clocks`` clock edges
1272 of the ``domain`` clock back. If that moment is before the beginning of time, it is equal
1273 to the value of the expression calculated as if each signal had its reset value.
1275 def __init__(self
, expr
, clocks
, domain
, *, src_loc_at
=0):
1276 super().__init
__(src_loc_at
=1 + src_loc_at
)
1277 self
.value
= Value
.cast(expr
)
1278 self
.clocks
= int(clocks
)
1279 self
.domain
= domain
1280 if not isinstance(self
.value
, (Const
, Signal
, ClockSignal
, ResetSignal
, Initial
)):
1281 raise TypeError("Sampled value must be a signal or a constant, not {!r}"
1282 .format(self
.value
))
1284 raise ValueError("Cannot sample a value {} cycles in the future"
1285 .format(-self
.clocks
))
1286 if not (self
.domain
is None or isinstance(self
.domain
, str)):
1287 raise TypeError("Domain name must be a string or None, not {!r}"
1288 .format(self
.domain
))
1291 return self
.value
.shape()
1293 def _rhs_signals(self
):
1294 return SignalSet((self
,))
1297 return "(sample {!r} @ {}[{}])".format(
1298 self
.value
, "<default>" if self
.domain
is None else self
.domain
, self
.clocks
)
1301 def Past(expr
, clocks
=1, domain
=None):
1302 return Sample(expr
, clocks
, domain
)
1305 def Stable(expr
, clocks
=0, domain
=None):
1306 return Sample(expr
, clocks
+ 1, domain
) == Sample(expr
, clocks
, domain
)
1309 def Rose(expr
, clocks
=0, domain
=None):
1310 return ~
Sample(expr
, clocks
+ 1, domain
) & Sample(expr
, clocks
, domain
)
1313 def Fell(expr
, clocks
=0, domain
=None):
1314 return Sample(expr
, clocks
+ 1, domain
) & ~
Sample(expr
, clocks
, domain
)
1318 class Initial(Value
):
1319 """Start indicator, for model checking.
1321 An ``Initial`` signal is ``1`` at the first cycle of model checking, and ``0`` at any other.
1323 def __init__(self
, *, src_loc_at
=0):
1324 super().__init
__(src_loc_at
=src_loc_at
)
1329 def _rhs_signals(self
):
1330 return SignalSet((self
,))
1336 class _StatementList(list):
1338 return "({})".format(" ".join(map(repr, self
)))
1342 def __init__(self
, *, src_loc_at
=0):
1343 self
.src_loc
= tracer
.get_src_loc(1 + src_loc_at
)
1347 if isinstance(obj
, Iterable
):
1348 return _StatementList(sum((Statement
.cast(e
) for e
in obj
), []))
1350 if isinstance(obj
, Statement
):
1351 return _StatementList([obj
])
1353 raise TypeError("Object {!r} is not an nMigen statement".format(obj
))
1357 class Assign(Statement
):
1358 def __init__(self
, lhs
, rhs
, *, src_loc_at
=0):
1359 super().__init
__(src_loc_at
=src_loc_at
)
1360 self
.lhs
= Value
.cast(lhs
)
1361 self
.rhs
= Value
.cast(rhs
)
1363 def _lhs_signals(self
):
1364 return self
.lhs
._lhs
_signals
()
1366 def _rhs_signals(self
):
1367 return self
.lhs
._rhs
_signals
() | self
.rhs
._rhs
_signals
()
1370 return "(eq {!r} {!r})".format(self
.lhs
, self
.rhs
)
1373 class UnusedProperty(UnusedMustUse
):
1377 class Property(Statement
, MustUse
):
1378 _MustUse__warning
= UnusedProperty
1380 def __init__(self
, test
, *, _check
=None, _en
=None, src_loc_at
=0):
1381 super().__init
__(src_loc_at
=src_loc_at
)
1382 self
.test
= Value
.cast(test
)
1383 self
._check
= _check
1385 if self
._check
is None:
1386 self
._check
= Signal(reset_less
=True, name
="${}$check".format(self
._kind
))
1387 self
._check
.src_loc
= self
.src_loc
1389 self
._en
= Signal(reset_less
=True, name
="${}$en".format(self
._kind
))
1390 self
._en
.src_loc
= self
.src_loc
1392 def _lhs_signals(self
):
1393 return SignalSet((self
._en
, self
._check
))
1395 def _rhs_signals(self
):
1396 return self
.test
._rhs
_signals
()
1399 return "({} {!r})".format(self
._kind
, self
.test
)
1403 class Assert(Property
):
1408 class Assume(Property
):
1413 class Cover(Property
):
1418 class Switch(Statement
):
1419 def __init__(self
, test
, cases
, *, src_loc
=None, src_loc_at
=0, case_src_locs
={}):
1421 super().__init
__(src_loc_at
=src_loc_at
)
1423 # Switch is a bit special in terms of location tracking because it is usually created
1424 # long after the control has left the statement that directly caused its creation.
1425 self
.src_loc
= src_loc
1426 # Switch is also a bit special in that its parts also have location information. It can't
1427 # be automatically traced, so whatever constructs a Switch may optionally provide it.
1428 self
.case_src_locs
= {}
1430 self
.test
= Value
.cast(test
)
1431 self
.cases
= OrderedDict()
1432 for orig_keys
, stmts
in cases
.items():
1433 # Map: None -> (); key -> (key,); (key...) -> (key...)
1437 if not isinstance(keys
, tuple):
1439 # Map: 2 -> "0010"; "0010" -> "0010"
1442 if isinstance(key
, str):
1443 key
= "".join(key
.split()) # remove whitespace
1444 elif isinstance(key
, int):
1445 key
= format(key
, "b").rjust(len(self
.test
), "0")
1446 elif isinstance(key
, Enum
):
1447 key
= format(key
.value
, "b").rjust(len(self
.test
), "0")
1449 raise TypeError("Object {!r} cannot be used as a switch key"
1451 assert len(key
) == len(self
.test
)
1452 new_keys
= (*new_keys
, key
)
1453 if not isinstance(stmts
, Iterable
):
1455 self
.cases
[new_keys
] = Statement
.cast(stmts
)
1456 if orig_keys
in case_src_locs
:
1457 self
.case_src_locs
[new_keys
] = case_src_locs
[orig_keys
]
1459 def _lhs_signals(self
):
1460 signals
= union((s
._lhs
_signals
() for ss
in self
.cases
.values() for s
in ss
),
1464 def _rhs_signals(self
):
1465 signals
= union((s
._rhs
_signals
() for ss
in self
.cases
.values() for s
in ss
),
1467 return self
.test
._rhs
_signals
() | signals
1470 def case_repr(keys
, stmts
):
1471 stmts_repr
= " ".join(map(repr, stmts
))
1473 return "(default {})".format(stmts_repr
)
1474 elif len(keys
) == 1:
1475 return "(case {} {})".format(keys
[0], stmts_repr
)
1477 return "(case ({}) {})".format(" ".join(keys
), stmts_repr
)
1478 case_reprs
= [case_repr(keys
, stmts
) for keys
, stmts
in self
.cases
.items()]
1479 return "(switch {!r} {})".format(self
.test
, " ".join(case_reprs
))
1482 class _MappedKeyCollection(metaclass
=ABCMeta
):
1484 def _map_key(self
, key
):
1488 def _unmap_key(self
, key
):
1492 class _MappedKeyDict(MutableMapping
, _MappedKeyCollection
):
1493 def __init__(self
, pairs
=()):
1494 self
._storage
= OrderedDict()
1495 for key
, value
in pairs
:
1498 def __getitem__(self
, key
):
1499 key
= None if key
is None else self
._map
_key
(key
)
1500 return self
._storage
[key
]
1502 def __setitem__(self
, key
, value
):
1503 key
= None if key
is None else self
._map
_key
(key
)
1504 self
._storage
[key
] = value
1506 def __delitem__(self
, key
):
1507 key
= None if key
is None else self
._map
_key
(key
)
1508 del self
._storage
[key
]
1511 for key
in self
._storage
:
1515 yield self
._unmap
_key
(key
)
1517 def __eq__(self
, other
):
1518 if not isinstance(other
, type(self
)):
1520 if len(self
) != len(other
):
1522 for ak
, bk
in zip(sorted(self
._storage
), sorted(other
._storage
)):
1525 if self
._storage
[ak
] != other
._storage
[bk
]:
1530 return len(self
._storage
)
1533 pairs
= ["({!r}, {!r})".format(k
, v
) for k
, v
in self
.items()]
1534 return "{}.{}([{}])".format(type(self
).__module
__, type(self
).__name
__,
1538 class _MappedKeySet(MutableSet
, _MappedKeyCollection
):
1539 def __init__(self
, elements
=()):
1540 self
._storage
= OrderedDict()
1541 for elem
in elements
:
1544 def add(self
, value
):
1545 self
._storage
[self
._map
_key
(value
)] = None
1547 def update(self
, values
):
1548 for value
in values
:
1551 def discard(self
, value
):
1553 del self
._storage
[self
._map
_key
(value
)]
1555 def __contains__(self
, value
):
1556 return self
._map
_key
(value
) in self
._storage
1559 for key
in [k
for k
in self
._storage
]:
1560 yield self
._unmap
_key
(key
)
1563 return len(self
._storage
)
1566 return "{}.{}({})".format(type(self
).__module
__, type(self
).__name
__,
1567 ", ".join(repr(x
) for x
in self
))
1571 def __init__(self
, value
):
1572 self
.value
= Value
.cast(value
)
1573 if isinstance(self
.value
, Const
):
1574 self
._hash
= hash(self
.value
.value
)
1575 elif isinstance(self
.value
, (Signal
, AnyValue
)):
1576 self
._hash
= hash(self
.value
.duid
)
1577 elif isinstance(self
.value
, (ClockSignal
, ResetSignal
)):
1578 self
._hash
= hash(self
.value
.domain
)
1579 elif isinstance(self
.value
, Operator
):
1580 self
._hash
= hash((self
.value
.operator
,
1581 tuple(ValueKey(o
) for o
in self
.value
.operands
)))
1582 elif isinstance(self
.value
, Slice
):
1583 self
._hash
= hash((ValueKey(self
.value
.value
), self
.value
.start
, self
.value
.stop
))
1584 elif isinstance(self
.value
, Part
):
1585 self
._hash
= hash((ValueKey(self
.value
.value
), ValueKey(self
.value
.offset
),
1586 self
.value
.width
, self
.value
.stride
))
1587 elif isinstance(self
.value
, Cat
):
1588 self
._hash
= hash(tuple(ValueKey(o
) for o
in self
.value
.parts
))
1589 elif isinstance(self
.value
, ArrayProxy
):
1590 self
._hash
= hash((ValueKey(self
.value
.index
),
1591 tuple(ValueKey(e
) for e
in self
.value
._iter
_as
_values
())))
1592 elif isinstance(self
.value
, Sample
):
1593 self
._hash
= hash((ValueKey(self
.value
.value
), self
.value
.clocks
, self
.value
.domain
))
1594 elif isinstance(self
.value
, Initial
):
1597 raise TypeError("Object {!r} cannot be used as a key in value collections"
1598 .format(self
.value
))
1603 def __eq__(self
, other
):
1604 if type(other
) is not ValueKey
:
1606 if type(self
.value
) is not type(other
.value
):
1609 if isinstance(self
.value
, Const
):
1610 return self
.value
.value
== other
.value
.value
1611 elif isinstance(self
.value
, (Signal
, AnyValue
)):
1612 return self
.value
is other
.value
1613 elif isinstance(self
.value
, (ClockSignal
, ResetSignal
)):
1614 return self
.value
.domain
== other
.value
.domain
1615 elif isinstance(self
.value
, Operator
):
1616 return (self
.value
.operator
== other
.value
.operator
and
1617 len(self
.value
.operands
) == len(other
.value
.operands
) and
1618 all(ValueKey(a
) == ValueKey(b
)
1619 for a
, b
in zip(self
.value
.operands
, other
.value
.operands
)))
1620 elif isinstance(self
.value
, Slice
):
1621 return (ValueKey(self
.value
.value
) == ValueKey(other
.value
.value
) and
1622 self
.value
.start
== other
.value
.start
and
1623 self
.value
.stop
== other
.value
.stop
)
1624 elif isinstance(self
.value
, Part
):
1625 return (ValueKey(self
.value
.value
) == ValueKey(other
.value
.value
) and
1626 ValueKey(self
.value
.offset
) == ValueKey(other
.value
.offset
) and
1627 self
.value
.width
== other
.value
.width
and
1628 self
.value
.stride
== other
.value
.stride
)
1629 elif isinstance(self
.value
, Cat
):
1630 return all(ValueKey(a
) == ValueKey(b
)
1631 for a
, b
in zip(self
.value
.parts
, other
.value
.parts
))
1632 elif isinstance(self
.value
, ArrayProxy
):
1633 return (ValueKey(self
.value
.index
) == ValueKey(other
.value
.index
) and
1634 len(self
.value
.elems
) == len(other
.value
.elems
) and
1635 all(ValueKey(a
) == ValueKey(b
)
1636 for a
, b
in zip(self
.value
._iter
_as
_values
(),
1637 other
.value
._iter
_as
_values
())))
1638 elif isinstance(self
.value
, Sample
):
1639 return (ValueKey(self
.value
.value
) == ValueKey(other
.value
.value
) and
1640 self
.value
.clocks
== other
.value
.clocks
and
1641 self
.value
.domain
== self
.value
.domain
)
1642 elif isinstance(self
.value
, Initial
):
1645 raise TypeError("Object {!r} cannot be used as a key in value collections"
1646 .format(self
.value
))
1648 def __lt__(self
, other
):
1649 if not isinstance(other
, ValueKey
):
1651 if type(self
.value
) != type(other
.value
):
1654 if isinstance(self
.value
, Const
):
1655 return self
.value
< other
.value
1656 elif isinstance(self
.value
, (Signal
, AnyValue
)):
1657 return self
.value
.duid
< other
.value
.duid
1658 elif isinstance(self
.value
, Slice
):
1659 return (ValueKey(self
.value
.value
) < ValueKey(other
.value
.value
) and
1660 self
.value
.start
< other
.value
.start
and
1661 self
.value
.end
< other
.value
.end
)
1663 raise TypeError("Object {!r} cannot be used as a key in value collections")
1666 return "<{}.ValueKey {!r}>".format(__name__
, self
.value
)
1669 class ValueDict(_MappedKeyDict
):
1671 _unmap_key
= lambda self
, key
: key
.value
1674 class ValueSet(_MappedKeySet
):
1676 _unmap_key
= lambda self
, key
: key
.value
1680 def __init__(self
, signal
):
1681 self
.signal
= signal
1682 if isinstance(signal
, Signal
):
1683 self
._intern
= (0, signal
.duid
)
1684 elif type(signal
) is ClockSignal
:
1685 self
._intern
= (1, signal
.domain
)
1686 elif type(signal
) is ResetSignal
:
1687 self
._intern
= (2, signal
.domain
)
1689 raise TypeError("Object {!r} is not an nMigen signal".format(signal
))
1692 return hash(self
._intern
)
1694 def __eq__(self
, other
):
1695 if type(other
) is not SignalKey
:
1697 return self
._intern
== other
._intern
1699 def __lt__(self
, other
):
1700 if type(other
) is not SignalKey
:
1701 raise TypeError("Object {!r} cannot be compared to a SignalKey".format(signal
))
1702 return self
._intern
< other
._intern
1705 return "<{}.SignalKey {!r}>".format(__name__
, self
.signal
)
1708 class SignalDict(_MappedKeyDict
):
1709 _map_key
= SignalKey
1710 _unmap_key
= lambda self
, key
: key
.signal
1713 class SignalSet(_MappedKeySet
):
1714 _map_key
= SignalKey
1715 _unmap_key
= lambda self
, key
: key
.signal