1 from abc
import ABCMeta
, abstractmethod
6 from collections
import OrderedDict
7 from collections
.abc
import Iterable
, MutableMapping
, MutableSet
, MutableSequence
11 from .._utils
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", "Assign", "Assert", "Assume", "Cover", "Switch",
22 "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.
62 def cast(obj
, *, src_loc_at
=0):
63 if isinstance(obj
, Shape
):
65 if isinstance(obj
, int):
67 if isinstance(obj
, tuple):
69 warnings
.warn("instead of `{tuple}`, use `{constructor}({width})`"
70 .format(constructor
="signed" if signed
else "unsigned", width
=width
,
72 DeprecationWarning, stacklevel
=2 + src_loc_at
)
73 return Shape(width
, signed
)
74 if isinstance(obj
, range):
76 return Shape(0, obj
.start
< 0)
77 signed
= obj
.start
< 0 or (obj
.stop
- obj
.step
) < 0
78 width
= max(bits_for(obj
.start
, signed
),
79 bits_for(obj
.stop
- obj
.step
, signed
))
80 return Shape(width
, signed
)
81 if isinstance(obj
, type) and issubclass(obj
, Enum
):
82 min_value
= min(member
.value
for member
in obj
)
83 max_value
= max(member
.value
for member
in obj
)
84 if not isinstance(min_value
, int) or not isinstance(max_value
, int):
85 raise TypeError("Only enumerations with integer values can be used "
87 signed
= min_value
< 0 or max_value
< 0
88 width
= max(bits_for(min_value
, signed
), bits_for(max_value
, signed
))
89 return Shape(width
, signed
)
90 raise TypeError("Object {!r} cannot be used as value shape".format(obj
))
93 # TODO: use dataclasses instead of this hack
94 def _Shape___init__(self
, width
=1, signed
=False):
95 if not isinstance(width
, int) or width
< 0:
96 raise TypeError("Width must be a non-negative integer, not {!r}"
98 Shape
.__init
__ = _Shape___init__
102 """Shorthand for ``Shape(width, signed=False)``."""
103 return Shape(width
, signed
=False)
107 """Shorthand for ``Shape(width, signed=True)``."""
108 return Shape(width
, signed
=True)
111 class Value(metaclass
=ABCMeta
):
114 """Converts ``obj`` to an nMigen value.
116 Booleans and integers are wrapped into a :class:`Const`. Enumerations whose members are
117 all integers are converted to a :class:`Const` with a shape that fits every member.
119 if isinstance(obj
, Value
):
121 if isinstance(obj
, int):
123 if isinstance(obj
, Enum
):
124 return Const(obj
.value
, Shape
.cast(type(obj
)))
125 raise TypeError("Object {!r} cannot be converted to an nMigen value".format(obj
))
127 # TODO(nmigen-0.2): remove this
129 @deprecated("instead of `Value.wrap`, use `Value.cast`")
133 def __init__(self
, *, src_loc_at
=0):
135 self
.src_loc
= tracer
.get_src_loc(1 + src_loc_at
)
138 raise TypeError("Attempted to convert nMigen value to boolean")
140 def __invert__(self
):
141 return Operator("~", [self
])
143 return Operator("-", [self
])
145 def __add__(self
, other
):
146 return Operator("+", [self
, other
])
147 def __radd__(self
, other
):
148 return Operator("+", [other
, self
])
149 def __sub__(self
, other
):
150 return Operator("-", [self
, other
])
151 def __rsub__(self
, other
):
152 return Operator("-", [other
, self
])
154 def __mul__(self
, other
):
155 return Operator("*", [self
, other
])
156 def __rmul__(self
, other
):
157 return Operator("*", [other
, self
])
159 def __check_divisor(self
):
160 width
, signed
= self
.shape()
162 # Python's division semantics and Verilog's division semantics differ for negative
163 # divisors (Python uses div/mod, Verilog uses quo/rem); for now, avoid the issue
164 # completely by prohibiting such division operations.
165 raise NotImplementedError("Division by a signed value is not supported")
166 def __mod__(self
, other
):
167 other
= Value
.cast(other
)
168 other
.__check
_divisor
()
169 return Operator("%", [self
, other
])
170 def __rmod__(self
, other
):
171 self
.__check
_divisor
()
172 return Operator("%", [other
, self
])
173 def __floordiv__(self
, other
):
174 other
= Value
.cast(other
)
175 other
.__check
_divisor
()
176 return Operator("//", [self
, other
])
177 def __rfloordiv__(self
, other
):
178 self
.__check
_divisor
()
179 return Operator("//", [other
, self
])
181 def __lshift__(self
, other
):
182 return Operator("<<", [self
, other
])
183 def __rlshift__(self
, other
):
184 return Operator("<<", [other
, self
])
185 def __rshift__(self
, other
):
186 return Operator(">>", [self
, other
])
187 def __rrshift__(self
, other
):
188 return Operator(">>", [other
, self
])
189 def __and__(self
, other
):
190 return Operator("&", [self
, other
])
191 def __rand__(self
, other
):
192 return Operator("&", [other
, self
])
193 def __xor__(self
, other
):
194 return Operator("^", [self
, other
])
195 def __rxor__(self
, other
):
196 return Operator("^", [other
, self
])
197 def __or__(self
, other
):
198 return Operator("|", [self
, other
])
199 def __ror__(self
, other
):
200 return Operator("|", [other
, self
])
202 def __eq__(self
, other
):
203 return Operator("==", [self
, other
])
204 def __ne__(self
, other
):
205 return Operator("!=", [self
, other
])
206 def __lt__(self
, other
):
207 return Operator("<", [self
, other
])
208 def __le__(self
, other
):
209 return Operator("<=", [self
, other
])
210 def __gt__(self
, other
):
211 return Operator(">", [self
, other
])
212 def __ge__(self
, other
):
213 return Operator(">=", [self
, other
])
216 return self
.shape().width
218 def __getitem__(self
, key
):
220 if isinstance(key
, int):
221 if key
not in range(-n
, n
):
222 raise IndexError("Cannot index {} bits into {}-bit value".format(key
, n
))
225 return Slice(self
, key
, key
+ 1)
226 elif isinstance(key
, slice):
227 start
, stop
, step
= key
.indices(n
)
229 return Cat(self
[i
] for i
in range(start
, stop
, step
))
230 return Slice(self
, start
, stop
)
232 raise TypeError("Cannot index value with {}".format(repr(key
)))
235 """Conversion to boolean.
240 ``1`` if any bits are set, ``0`` otherwise.
242 return Operator("b", [self
])
245 """Check if any bits are ``1``.
250 ``1`` if any bits are set, ``0`` otherwise.
252 return Operator("r|", [self
])
255 """Check if all bits are ``1``.
260 ``1`` if all bits are set, ``0`` otherwise.
262 return Operator("r&", [self
])
265 """Compute pairwise exclusive-or of every bit.
270 ``1`` if an odd number of bits are set, ``0`` if an even number of bits are set.
272 return Operator("r^", [self
])
274 def implies(premise
, conclusion
):
280 ``0`` if ``premise`` is true and ``conclusion`` is not, ``1`` otherwise.
282 return ~premise | conclusion
284 # TODO(nmigen-0.2): move this to nmigen.compat and make it a deprecated extension
285 @deprecated("instead of `.part`, use `.bit_select`")
286 def part(self
, offset
, width
):
287 return Part(self
, offset
, width
, src_loc_at
=1)
289 def bit_select(self
, offset
, width
):
290 """Part-select with bit granularity.
292 Selects a constant width but variable offset part of a ``Value``, such that successive
293 parts overlap by all but 1 bit.
298 Index of first selected bit.
300 Number of selected bits.
305 Selected part of the ``Value``
307 offset
= Value
.cast(offset
)
308 if type(offset
) is Const
and isinstance(width
, int):
309 return self
[offset
.value
:offset
.value
+ width
]
310 return Part(self
, offset
, width
, stride
=1, src_loc_at
=1)
312 def word_select(self
, offset
, width
):
313 """Part-select with word granularity.
315 Selects a constant width but variable offset part of a ``Value``, such that successive
316 parts do not overlap.
321 Index of first selected word.
323 Number of selected bits.
328 Selected part of the ``Value``
330 offset
= Value
.cast(offset
)
331 if type(offset
) is Const
and isinstance(width
, int):
332 return self
[offset
.value
* width
:(offset
.value
+ 1) * width
]
333 return Part(self
, offset
, width
, stride
=width
, src_loc_at
=1)
335 def matches(self
, *patterns
):
338 Matches against a set of patterns, which may be integers or bit strings, recognizing
339 the same grammar as ``Case()``.
343 patterns : int or str
344 Patterns to match against.
349 ``1`` if any pattern matches the value, ``0`` otherwise.
352 for pattern
in patterns
:
353 if not isinstance(pattern
, (int, str, Enum
)):
354 raise SyntaxError("Match pattern must be an integer, a string, or an enumeration, "
357 if isinstance(pattern
, str) and any(bit
not in "01-" for bit
in pattern
):
358 raise SyntaxError("Match pattern '{}' must consist of 0, 1, and - (don't care) "
361 if isinstance(pattern
, str) and len(pattern
) != len(self
):
362 raise SyntaxError("Match pattern '{}' must have the same width as match value "
364 .format(pattern
, len(self
)))
365 if isinstance(pattern
, int) and bits_for(pattern
) > len(self
):
366 warnings
.warn("Match pattern '{:b}' is wider than match value "
367 "(which has width {}); comparison will never be true"
368 .format(pattern
, len(self
)),
369 SyntaxWarning, stacklevel
=3)
371 if isinstance(pattern
, str):
372 mask
= int(pattern
.replace("0", "1").replace("-", "0"), 2)
373 pattern
= int(pattern
.replace("-", "0"), 2)
374 matches
.append((self
& mask
) == pattern
)
375 elif isinstance(pattern
, int):
376 matches
.append(self
== pattern
)
377 elif isinstance(pattern
, Enum
):
378 matches
.append(self
== pattern
.value
)
383 elif len(matches
) == 1:
386 return Cat(*matches
).any()
394 Value to be assigned.
399 Assignment statement that can be used in combinatorial or synchronous context.
401 return Assign(self
, value
, src_loc_at
=1)
405 """Bit width and signedness of a value.
414 >>> Signal(8).shape()
415 Shape(width=8, signed=False)
416 >>> Const(0xaa).shape()
417 Shape(width=8, signed=False)
421 def _lhs_signals(self
):
422 raise TypeError("Value {!r} cannot be used in assignments".format(self
))
425 def _rhs_signals(self
):
429 raise TypeError("Value {!r} cannot be evaluated as constant".format(self
))
436 """A constant, literal integer value.
441 shape : int or tuple or None
442 Either an integer ``width`` or a tuple ``(width, signed)`` specifying the number of bits
443 in this constant and whether it is signed (can represent negative values).
444 ``shape`` defaults to the minimum possible width and signedness of ``value``.
454 def normalize(value
, shape
):
455 width
, signed
= shape
456 mask
= (1 << width
) - 1
458 if signed
and value
>> (width
- 1):
462 def __init__(self
, value
, shape
=None, *, src_loc_at
=0):
463 # We deliberately do not call Value.__init__ here.
464 self
.value
= int(value
)
466 shape
= Shape(bits_for(self
.value
), signed
=self
.value
< 0)
467 elif isinstance(shape
, int):
468 shape
= Shape(shape
, signed
=self
.value
< 0)
470 shape
= Shape
.cast(shape
, src_loc_at
=1 + src_loc_at
)
471 self
.width
, self
.signed
= shape
472 self
.value
= self
.normalize(self
.value
, shape
)
474 # TODO(nmigen-0.2): move this to nmigen.compat and make it a deprecated extension
476 @deprecated("instead of `const.nbits`, use `const.width`")
481 return Shape(self
.width
, self
.signed
)
483 def _rhs_signals(self
):
490 return "(const {}'{}d{})".format(self
.width
, "s" if self
.signed
else "", self
.value
)
493 C
= Const
# shorthand
496 class AnyValue(Value
, DUID
):
497 def __init__(self
, shape
, *, src_loc_at
=0):
498 super().__init
__(src_loc_at
=src_loc_at
)
499 self
.width
, self
.signed
= Shape
.cast(shape
, src_loc_at
=1 + src_loc_at
)
500 if not isinstance(self
.width
, int) or self
.width
< 0:
501 raise TypeError("Width must be a non-negative integer, not {!r}"
505 return Shape(self
.width
, self
.signed
)
507 def _rhs_signals(self
):
512 class AnyConst(AnyValue
):
514 return "(anyconst {}'{})".format(self
.nbits
, "s" if self
.signed
else "")
518 class AnySeq(AnyValue
):
520 return "(anyseq {}'{})".format(self
.nbits
, "s" if self
.signed
else "")
524 class Operator(Value
):
525 def __init__(self
, operator
, operands
, *, src_loc_at
=0):
526 super().__init
__(src_loc_at
=1 + src_loc_at
)
527 self
.operator
= operator
528 self
.operands
= [Value
.cast(op
) for op
in operands
]
530 # TODO(nmigen-0.2): move this to nmigen.compat and make it a deprecated extension
532 @deprecated("instead of `.op`, use `.operator`")
537 def _bitwise_binary_shape(a_shape
, b_shape
):
538 a_bits
, a_sign
= a_shape
539 b_bits
, b_sign
= b_shape
540 if not a_sign
and not b_sign
:
541 # both operands unsigned
542 return Shape(max(a_bits
, b_bits
), False)
543 elif a_sign
and b_sign
:
544 # both operands signed
545 return Shape(max(a_bits
, b_bits
), True)
546 elif not a_sign
and b_sign
:
547 # first operand unsigned (add sign bit), second operand signed
548 return Shape(max(a_bits
+ 1, b_bits
), True)
550 # first signed, second operand unsigned (add sign bit)
551 return Shape(max(a_bits
, b_bits
+ 1), True)
553 op_shapes
= list(map(lambda x
: x
.shape(), self
.operands
))
554 if len(op_shapes
) == 1:
555 (a_width
, a_signed
), = op_shapes
556 if self
.operator
in ("+", "~"):
557 return Shape(a_width
, a_signed
)
558 if self
.operator
== "-":
559 return Shape(a_width
+ 1, True)
560 if self
.operator
in ("b", "r|", "r&", "r^"):
561 return Shape(1, False)
562 elif len(op_shapes
) == 2:
563 (a_width
, a_signed
), (b_width
, b_signed
) = op_shapes
564 if self
.operator
in ("+", "-"):
565 width
, signed
= _bitwise_binary_shape(*op_shapes
)
566 return Shape(width
+ 1, signed
)
567 if self
.operator
== "*":
568 return Shape(a_width
+ b_width
, a_signed
or b_signed
)
569 if self
.operator
in ("//", "%"):
571 return Shape(a_width
, a_signed
)
572 if self
.operator
in ("<", "<=", "==", "!=", ">", ">="):
573 return Shape(1, False)
574 if self
.operator
in ("&", "^", "|"):
575 return _bitwise_binary_shape(*op_shapes
)
576 if self
.operator
== "<<":
578 extra
= 2 ** (b_width
- 1) - 1
580 extra
= 2 ** (b_width
) - 1
581 return Shape(a_width
+ extra
, a_signed
)
582 if self
.operator
== ">>":
584 extra
= 2 ** (b_width
- 1)
587 return Shape(a_width
+ extra
, a_signed
)
588 elif len(op_shapes
) == 3:
589 if self
.operator
== "m":
590 s_shape
, a_shape
, b_shape
= op_shapes
591 return _bitwise_binary_shape(a_shape
, b_shape
)
592 raise NotImplementedError("Operator {}/{} not implemented"
593 .format(self
.operator
, len(op_shapes
))) # :nocov:
595 def _rhs_signals(self
):
596 return union(op
._rhs
_signals
() for op
in self
.operands
)
599 return "({} {})".format(self
.operator
, " ".join(map(repr, self
.operands
)))
602 def Mux(sel
, val1
, val0
):
603 """Choose between two values.
616 Output ``Value``. If ``sel`` is asserted, the Mux returns ``val1``, else ``val0``.
618 sel
= Value
.cast(sel
)
621 return Operator("m", [sel
, val1
, val0
])
626 def __init__(self
, value
, start
, stop
, *, src_loc_at
=0):
627 if not isinstance(start
, int):
628 raise TypeError("Slice start must be an integer, not {!r}".format(start
))
629 if not isinstance(stop
, int):
630 raise TypeError("Slice stop must be an integer, not {!r}".format(stop
))
633 if start
not in range(-(n
+1), n
+1):
634 raise IndexError("Cannot start slice {} bits into {}-bit value".format(start
, n
))
637 if stop
not in range(-(n
+1), n
+1):
638 raise IndexError("Cannot stop slice {} bits into {}-bit value".format(stop
, n
))
642 raise IndexError("Slice start {} must be less than slice stop {}".format(start
, stop
))
644 super().__init
__(src_loc_at
=src_loc_at
)
645 self
.value
= Value
.cast(value
)
649 # TODO(nmigen-0.2): remove this
651 @deprecated("instead of `slice.end`, use `slice.stop`")
656 return Shape(self
.stop
- self
.start
)
658 def _lhs_signals(self
):
659 return self
.value
._lhs
_signals
()
661 def _rhs_signals(self
):
662 return self
.value
._rhs
_signals
()
665 return "(slice {} {}:{})".format(repr(self
.value
), self
.start
, self
.stop
)
670 def __init__(self
, value
, offset
, width
, stride
=1, *, src_loc_at
=0):
671 if not isinstance(width
, int) or width
< 0:
672 raise TypeError("Part width must be a non-negative integer, not {!r}".format(width
))
673 if not isinstance(stride
, int) or stride
<= 0:
674 raise TypeError("Part stride must be a positive integer, not {!r}".format(stride
))
676 super().__init
__(src_loc_at
=src_loc_at
)
678 self
.offset
= Value
.cast(offset
)
683 return Shape(self
.width
)
685 def _lhs_signals(self
):
686 return self
.value
._lhs
_signals
()
688 def _rhs_signals(self
):
689 return self
.value
._rhs
_signals
() | self
.offset
._rhs
_signals
()
692 return "(part {} {} {} {})".format(repr(self
.value
), repr(self
.offset
),
693 self
.width
, self
.stride
)
698 """Concatenate values.
700 Form a compound ``Value`` from several smaller ones by concatenation.
701 The first argument occupies the lower bits of the result.
702 The return value can be used on either side of an assignment, that
703 is, the concatenated value can be used as an argument on the RHS or
704 as a target on the LHS. If it is used on the LHS, it must solely
705 consist of ``Signal`` s, slices of ``Signal`` s, and other concatenations
706 meeting these properties. The bit length of the return value is the sum of
707 the bit lengths of the arguments::
709 len(Cat(args)) == sum(len(arg) for arg in args)
713 *args : Values or iterables of Values, inout
714 ``Value`` s to be concatenated.
719 Resulting ``Value`` obtained by concatentation.
721 def __init__(self
, *args
, src_loc_at
=0):
722 super().__init
__(src_loc_at
=src_loc_at
)
723 self
.parts
= [Value
.cast(v
) for v
in flatten(args
)]
726 return Shape(sum(len(part
) for part
in self
.parts
))
728 def _lhs_signals(self
):
729 return union((part
._lhs
_signals
() for part
in self
.parts
), start
=ValueSet())
731 def _rhs_signals(self
):
732 return union((part
._rhs
_signals
() for part
in self
.parts
), start
=ValueSet())
736 for part
in reversed(self
.parts
):
738 value |
= part
._as
_const
()
742 return "(cat {})".format(" ".join(map(repr, self
.parts
)))
749 An input value is replicated (repeated) several times
750 to be used on the RHS of assignments::
752 len(Repl(s, n)) == len(s) * n
757 Input value to be replicated.
759 Number of replications.
766 def __init__(self
, value
, count
, *, src_loc_at
=0):
767 if not isinstance(count
, int) or count
< 0:
768 raise TypeError("Replication count must be a non-negative integer, not {!r}"
771 super().__init
__(src_loc_at
=src_loc_at
)
772 self
.value
= Value
.cast(value
)
776 return Shape(len(self
.value
) * self
.count
)
778 def _rhs_signals(self
):
779 return self
.value
._rhs
_signals
()
782 return "(repl {!r} {})".format(self
.value
, self
.count
)
786 class Signal(Value
, DUID
):
787 """A varying integer value.
791 shape : int or tuple or None
792 Either an integer ``width`` or a tuple ``(width, signed)`` specifying the number of bits
793 in this ``Signal`` and whether it is signed (can represent negative values).
794 ``shape`` defaults to 1-bit and non-signed.
796 Name hint for this signal. If ``None`` (default) the name is inferred from the variable
797 name this ``Signal`` is assigned to. Name collisions are automatically resolved by
798 prepending names of objects that contain this ``Signal`` and by appending integer
800 reset : int or integral Enum
801 Reset (synchronous) or default (combinatorial) value.
802 When this ``Signal`` is assigned to in synchronous context and the corresponding clock
803 domain is reset, the ``Signal`` assumes the given value. When this ``Signal`` is unassigned
804 in combinatorial context (due to conditional assignments not being taken), the ``Signal``
805 assumes its ``reset`` value. Defaults to 0.
807 If ``True``, do not generate reset logic for this ``Signal`` in synchronous statements.
808 The ``reset`` value is only used as a combinatorial default or as the initial value.
809 Defaults to ``False``.
812 If ``shape`` is ``None``, the signal bit width and signedness are
813 determined by the integer range given by ``min`` (inclusive,
814 defaults to 0) and ``max`` (exclusive, defaults to 2).
816 Dictionary of synthesis attributes.
817 decoder : function or Enum
818 A function converting integer signal values to human-readable strings (e.g. FSM state
819 names). If an ``Enum`` subclass is passed, it is concisely decoded using format string
820 ``"{0.name:}/{0.value:}"``, or a number if the signal value is not a member of
833 def __init__(self
, shape
=None, *, name
=None, reset
=0, reset_less
=False, min=None, max=None,
834 attrs
=None, decoder
=None, src_loc_at
=0):
835 super().__init
__(src_loc_at
=src_loc_at
)
837 if isinstance(reset
, Enum
):
839 if not isinstance(reset
, int):
840 raise TypeError("Reset value has to be an int or an integral Enum")
842 # TODO(nmigen-0.2): move this to nmigen.compat and make it a deprecated extension
843 if min is not None or max is not None:
844 warnings
.warn("instead of `Signal(min={min}, max={max})`, "
845 "use `Signal(range({min}, {max}))`"
846 .format(min=min or 0, max=max or 2),
847 DeprecationWarning, stacklevel
=2 + src_loc_at
)
849 if name
is not None and not isinstance(name
, str):
850 raise TypeError("Name must be a string, not {!r}".format(name
))
851 self
.name
= name
or tracer
.get_var_name(depth
=2 + src_loc_at
, default
="$signal")
858 max -= 1 # make both bounds inclusive
860 raise ValueError("Lower bound {} should be less or equal to higher bound {}"
861 .format(min, max + 1))
862 self
.signed
= min < 0 or max < 0
866 self
.width
= builtins
.max(bits_for(min, self
.signed
),
867 bits_for(max, self
.signed
))
870 if not (min is None and max is None):
871 raise ValueError("Only one of bits/signedness or bounds may be specified")
872 self
.width
, self
.signed
= Shape
.cast(shape
, src_loc_at
=1 + src_loc_at
)
874 reset_width
= bits_for(reset
, self
.signed
)
875 if reset
!= 0 and reset_width
> self
.width
:
876 warnings
.warn("Reset value {!r} requires {} bits to represent, but the signal "
878 .format(reset
, reset_width
, self
.width
),
879 SyntaxWarning, stacklevel
=2 + src_loc_at
)
881 self
.reset
= int(reset
)
882 self
.reset_less
= bool(reset_less
)
884 self
.attrs
= OrderedDict(() if attrs
is None else attrs
)
886 if decoder
is None and isinstance(shape
, type) and issubclass(shape
, Enum
):
888 if isinstance(decoder
, type) and issubclass(decoder
, Enum
):
889 def enum_decoder(value
):
891 return "{0.name:}/{0.value:}".format(decoder(value
))
894 self
.decoder
= enum_decoder
896 self
.decoder
= decoder
899 @deprecated("instead of `Signal.range(...)`, use `Signal(range(...))`")
900 def range(cls
, *args
, src_loc_at
=0, **kwargs
):
901 return cls(range(*args
), src_loc_at
=2 + src_loc_at
, **kwargs
)
904 @deprecated("instead of `Signal.enum(...)`, use `Signal(...)`")
905 def enum(cls
, enum_type
, *, src_loc_at
=0, **kwargs
):
906 if not issubclass(enum_type
, Enum
):
907 raise TypeError("Type {!r} is not an enumeration")
908 return cls(enum_type
, src_loc_at
=2 + src_loc_at
, **kwargs
)
911 def like(cls
, other
, *, name
=None, name_suffix
=None, src_loc_at
=0, **kwargs
):
912 """Create Signal based on another.
917 Object to base this Signal on.
921 elif name_suffix
is not None:
922 new_name
= other
.name
+ str(name_suffix
)
924 new_name
= tracer
.get_var_name(depth
=2 + src_loc_at
, default
="$like")
925 kw
= dict(shape
=cls
.cast(other
).shape(), name
=new_name
)
926 if isinstance(other
, cls
):
927 kw
.update(reset
=other
.reset
, reset_less
=other
.reset_less
,
928 attrs
=other
.attrs
, decoder
=other
.decoder
)
930 return cls(**kw
, src_loc_at
=1 + src_loc_at
)
932 # TODO(nmigen-0.2): move this to nmigen.compat and make it a deprecated extension
934 @deprecated("instead of `signal.nbits`, use `signal.width`")
939 @deprecated("instead of `signal.nbits = x`, use `signal.width = x`")
940 def nbits(self
, value
):
944 return Shape(self
.width
, self
.signed
)
946 def _lhs_signals(self
):
947 return ValueSet((self
,))
949 def _rhs_signals(self
):
950 return ValueSet((self
,))
953 return "(sig {})".format(self
.name
)
957 class ClockSignal(Value
):
958 """Clock signal for a clock domain.
960 Any ``ClockSignal`` is equivalent to ``cd.clk`` for a clock domain with the corresponding name.
961 All of these signals ultimately refer to the same signal, but they can be manipulated
962 independently of the clock domain, even before the clock domain is created.
967 Clock domain to obtain a clock signal for. Defaults to ``"sync"``.
969 def __init__(self
, domain
="sync", *, src_loc_at
=0):
970 super().__init
__(src_loc_at
=src_loc_at
)
971 if not isinstance(domain
, str):
972 raise TypeError("Clock domain name must be a string, not {!r}".format(domain
))
974 raise ValueError("Domain '{}' does not have a clock".format(domain
))
980 def _lhs_signals(self
):
981 return ValueSet((self
,))
983 def _rhs_signals(self
):
984 raise NotImplementedError("ClockSignal must be lowered to a concrete signal") # :nocov:
987 return "(clk {})".format(self
.domain
)
991 class ResetSignal(Value
):
992 """Reset signal for a clock domain.
994 Any ``ResetSignal`` is equivalent to ``cd.rst`` for a clock domain with the corresponding name.
995 All of these signals ultimately refer to the same signal, but they can be manipulated
996 independently of the clock domain, even before the clock domain is created.
1001 Clock domain to obtain a reset signal for. Defaults to ``"sync"``.
1002 allow_reset_less : bool
1003 If the clock domain is reset-less, act as a constant ``0`` instead of reporting an error.
1005 def __init__(self
, domain
="sync", allow_reset_less
=False, *, src_loc_at
=0):
1006 super().__init
__(src_loc_at
=src_loc_at
)
1007 if not isinstance(domain
, str):
1008 raise TypeError("Clock domain name must be a string, not {!r}".format(domain
))
1009 if domain
== "comb":
1010 raise ValueError("Domain '{}' does not have a reset".format(domain
))
1011 self
.domain
= domain
1012 self
.allow_reset_less
= allow_reset_less
1017 def _lhs_signals(self
):
1018 return ValueSet((self
,))
1020 def _rhs_signals(self
):
1021 raise NotImplementedError("ResetSignal must be lowered to a concrete signal") # :nocov:
1024 return "(rst {})".format(self
.domain
)
1027 class Array(MutableSequence
):
1028 """Addressable multiplexer.
1030 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
1033 The array proxy can be used as an ordinary ``Value``, i.e. participate in calculations and
1034 assignments, provided that all elements of the array are values. The array proxy also supports
1035 attribute access and further indexing, each returning another array proxy; this means that
1036 the results of indexing into arrays, arrays of records, and arrays of arrays can all
1037 be used as first-class values.
1039 It is an error to change an array or any of its elements after an array proxy was created.
1040 Changing the array directly will raise an exception. However, it is not possible to detect
1041 the elements being modified; if an element's attribute or element is modified after the proxy
1042 for it has been created, the proxy will refer to stale data.
1049 gpios = Array(Signal() for _ in range(10))
1051 m.d.sync += gpios[bus.addr].eq(bus.w_data)
1053 m.d.sync += bus.r_data.eq(gpios[bus.addr])
1055 Multidimensional array::
1057 mult = Array(Array(x * y for y in range(10)) for x in range(10))
1058 a = Signal.range(10)
1059 b = Signal.range(10)
1061 m.d.comb += r.eq(mult[a][b])
1069 buses = Array(Record(layout) for busno in range(4))
1070 master = Record(layout)
1072 buses[sel].r_en.eq(master.r_en),
1073 master.r_data.eq(buses[sel].r_data),
1076 def __init__(self
, iterable
=()):
1077 self
._inner
= list(iterable
)
1078 self
._proxy
_at
= None
1079 self
._mutable
= True
1081 def __getitem__(self
, index
):
1082 if isinstance(index
, Value
):
1084 self
._proxy
_at
= tracer
.get_src_loc()
1085 self
._mutable
= False
1086 return ArrayProxy(self
, index
)
1088 return self
._inner
[index
]
1091 return len(self
._inner
)
1093 def _check_mutability(self
):
1094 if not self
._mutable
:
1095 raise ValueError("Array can no longer be mutated after it was indexed with a value "
1096 "at {}:{}".format(*self
._proxy
_at
))
1098 def __setitem__(self
, index
, value
):
1099 self
._check
_mutability
()
1100 self
._inner
[index
] = value
1102 def __delitem__(self
, index
):
1103 self
._check
_mutability
()
1104 del self
._inner
[index
]
1106 def insert(self
, index
, value
):
1107 self
._check
_mutability
()
1108 self
._inner
.insert(index
, value
)
1111 return "(array{} [{}])".format(" mutable" if self
._mutable
else "",
1112 ", ".join(map(repr, self
._inner
)))
1116 class ArrayProxy(Value
):
1117 def __init__(self
, elems
, index
, *, src_loc_at
=0):
1118 super().__init
__(src_loc_at
=1 + src_loc_at
)
1120 self
.index
= Value
.cast(index
)
1122 def __getattr__(self
, attr
):
1123 return ArrayProxy([getattr(elem
, attr
) for elem
in self
.elems
], self
.index
)
1125 def __getitem__(self
, index
):
1126 return ArrayProxy([ elem
[index
] for elem
in self
.elems
], self
.index
)
1128 def _iter_as_values(self
):
1129 return (Value
.cast(elem
) for elem
in self
.elems
)
1132 width
, signed
= 0, False
1133 for elem_width
, elem_signed
in (elem
.shape() for elem
in self
._iter
_as
_values
()):
1134 width
= max(width
, elem_width
+ elem_signed
)
1135 signed
= max(signed
, elem_signed
)
1136 return Shape(width
, signed
)
1138 def _lhs_signals(self
):
1139 signals
= union((elem
._lhs
_signals
() for elem
in self
._iter
_as
_values
()), start
=ValueSet())
1142 def _rhs_signals(self
):
1143 signals
= union((elem
._rhs
_signals
() for elem
in self
._iter
_as
_values
()), start
=ValueSet())
1144 return self
.index
._rhs
_signals
() | signals
1147 return "(proxy (array [{}]) {!r})".format(", ".join(map(repr, self
.elems
)), self
.index
)
1150 class UserValue(Value
):
1151 """Value with custom lowering.
1153 A ``UserValue`` is a value whose precise representation does not have to be immediately known,
1154 which is useful in certain metaprogramming scenarios. Instead of providing fixed semantics
1155 upfront, it is kept abstract for as long as possible, only being lowered to a concrete nMigen
1156 value when required.
1158 Note that the ``lower`` method will only be called once; this is necessary to ensure that
1159 nMigen's view of representation of all values stays internally consistent. If the class
1160 deriving from ``UserValue`` is mutable, then it must ensure that after ``lower`` is called,
1161 it is not mutated in a way that changes its representation.
1163 The following is an incomplete list of actions that, when applied to an ``UserValue`` directly
1164 or indirectly, will cause it to be lowered, provided as an illustrative reference:
1165 * Querying the shape using ``.shape()`` or ``len()``;
1166 * Creating a similarly shaped signal using ``Signal.like``;
1167 * Indexing or iterating through individual bits;
1168 * Adding an assignment to the value to a ``Module`` using ``m.d.<domain> +=``.
1170 def __init__(self
, *, src_loc_at
=0):
1171 super().__init
__(src_loc_at
=1 + src_loc_at
)
1172 self
.__lowered
= None
1176 """Conversion to a concrete representation."""
1179 def _lazy_lower(self
):
1180 if self
.__lowered
is None:
1181 self
.__lowered
= Value
.cast(self
.lower())
1182 return self
.__lowered
1185 return self
._lazy
_lower
().shape()
1187 def _lhs_signals(self
):
1188 return self
._lazy
_lower
()._lhs
_signals
()
1190 def _rhs_signals(self
):
1191 return self
._lazy
_lower
()._rhs
_signals
()
1195 class Sample(Value
):
1196 """Value from the past.
1198 A ``Sample`` of an expression is equal to the value of the expression ``clocks`` clock edges
1199 of the ``domain`` clock back. If that moment is before the beginning of time, it is equal
1200 to the value of the expression calculated as if each signal had its reset value.
1202 def __init__(self
, expr
, clocks
, domain
, *, src_loc_at
=0):
1203 super().__init
__(src_loc_at
=1 + src_loc_at
)
1204 self
.value
= Value
.cast(expr
)
1205 self
.clocks
= int(clocks
)
1206 self
.domain
= domain
1207 if not isinstance(self
.value
, (Const
, Signal
, ClockSignal
, ResetSignal
, Initial
)):
1208 raise TypeError("Sampled value must be a signal or a constant, not {!r}"
1209 .format(self
.value
))
1211 raise ValueError("Cannot sample a value {} cycles in the future"
1212 .format(-self
.clocks
))
1213 if not (self
.domain
is None or isinstance(self
.domain
, str)):
1214 raise TypeError("Domain name must be a string or None, not {!r}"
1215 .format(self
.domain
))
1218 return self
.value
.shape()
1220 def _rhs_signals(self
):
1221 return ValueSet((self
,))
1224 return "(sample {!r} @ {}[{}])".format(
1225 self
.value
, "<default>" if self
.domain
is None else self
.domain
, self
.clocks
)
1228 def Past(expr
, clocks
=1, domain
=None):
1229 return Sample(expr
, clocks
, domain
)
1232 def Stable(expr
, clocks
=0, domain
=None):
1233 return Sample(expr
, clocks
+ 1, domain
) == Sample(expr
, clocks
, domain
)
1236 def Rose(expr
, clocks
=0, domain
=None):
1237 return ~
Sample(expr
, clocks
+ 1, domain
) & Sample(expr
, clocks
, domain
)
1240 def Fell(expr
, clocks
=0, domain
=None):
1241 return Sample(expr
, clocks
+ 1, domain
) & ~
Sample(expr
, clocks
, domain
)
1245 class Initial(Value
):
1246 """Start indicator, for model checking.
1248 An ``Initial`` signal is ``1`` at the first cycle of model checking, and ``0`` at any other.
1250 def __init__(self
, *, src_loc_at
=0):
1251 super().__init
__(src_loc_at
=1 + src_loc_at
)
1256 def _rhs_signals(self
):
1257 return ValueSet((self
,))
1263 class _StatementList(list):
1265 return "({})".format(" ".join(map(repr, self
)))
1269 def __init__(self
, *, src_loc_at
=0):
1270 self
.src_loc
= tracer
.get_src_loc(1 + src_loc_at
)
1272 # TODO(nmigen-0.2): remove this
1274 @deprecated("instead of `Statement.wrap`, use `Statement.cast`")
1276 return cls
.cast(obj
)
1280 if isinstance(obj
, Iterable
):
1281 return _StatementList(sum((Statement
.cast(e
) for e
in obj
), []))
1283 if isinstance(obj
, Statement
):
1284 return _StatementList([obj
])
1286 raise TypeError("Object {!r} is not an nMigen statement".format(obj
))
1290 class Assign(Statement
):
1291 def __init__(self
, lhs
, rhs
, *, src_loc_at
=0):
1292 super().__init
__(src_loc_at
=src_loc_at
)
1293 self
.lhs
= Value
.cast(lhs
)
1294 self
.rhs
= Value
.cast(rhs
)
1296 def _lhs_signals(self
):
1297 return self
.lhs
._lhs
_signals
()
1299 def _rhs_signals(self
):
1300 return self
.lhs
._rhs
_signals
() | self
.rhs
._rhs
_signals
()
1303 return "(eq {!r} {!r})".format(self
.lhs
, self
.rhs
)
1306 class Property(Statement
):
1307 def __init__(self
, test
, *, _check
=None, _en
=None, src_loc_at
=0):
1308 super().__init
__(src_loc_at
=src_loc_at
)
1309 self
.test
= Value
.cast(test
)
1310 self
._check
= _check
1312 if self
._check
is None:
1313 self
._check
= Signal(reset_less
=True, name
="${}$check".format(self
._kind
))
1314 self
._check
.src_loc
= self
.src_loc
1316 self
._en
= Signal(reset_less
=True, name
="${}$en".format(self
._kind
))
1317 self
._en
.src_loc
= self
.src_loc
1319 def _lhs_signals(self
):
1320 return ValueSet((self
._en
, self
._check
))
1322 def _rhs_signals(self
):
1323 return self
.test
._rhs
_signals
()
1326 return "({} {!r})".format(self
._kind
, self
.test
)
1330 class Assert(Property
):
1335 class Assume(Property
):
1340 class Cover(Property
):
1345 class Switch(Statement
):
1346 def __init__(self
, test
, cases
, *, src_loc
=None, src_loc_at
=0, case_src_locs
={}):
1348 super().__init
__(src_loc_at
=src_loc_at
)
1350 # Switch is a bit special in terms of location tracking because it is usually created
1351 # long after the control has left the statement that directly caused its creation.
1352 self
.src_loc
= src_loc
1353 # Switch is also a bit special in that its parts also have location information. It can't
1354 # be automatically traced, so whatever constructs a Switch may optionally provide it.
1355 self
.case_src_locs
= {}
1357 self
.test
= Value
.cast(test
)
1358 self
.cases
= OrderedDict()
1359 for orig_keys
, stmts
in cases
.items():
1360 # Map: None -> (); key -> (key,); (key...) -> (key...)
1364 if not isinstance(keys
, tuple):
1366 # Map: 2 -> "0010"; "0010" -> "0010"
1369 if isinstance(key
, str):
1371 elif isinstance(key
, int):
1372 key
= format(key
, "b").rjust(len(self
.test
), "0")
1373 elif isinstance(key
, Enum
):
1374 key
= format(key
.value
, "b").rjust(len(self
.test
), "0")
1376 raise TypeError("Object {!r} cannot be used as a switch key"
1378 assert len(key
) == len(self
.test
)
1379 new_keys
= (*new_keys
, key
)
1380 if not isinstance(stmts
, Iterable
):
1382 self
.cases
[new_keys
] = Statement
.cast(stmts
)
1383 if orig_keys
in case_src_locs
:
1384 self
.case_src_locs
[new_keys
] = case_src_locs
[orig_keys
]
1386 def _lhs_signals(self
):
1387 signals
= union((s
._lhs
_signals
() for ss
in self
.cases
.values() for s
in ss
),
1391 def _rhs_signals(self
):
1392 signals
= union((s
._rhs
_signals
() for ss
in self
.cases
.values() for s
in ss
),
1394 return self
.test
._rhs
_signals
() | signals
1397 def case_repr(keys
, stmts
):
1398 stmts_repr
= " ".join(map(repr, stmts
))
1400 return "(default {})".format(stmts_repr
)
1401 elif len(keys
) == 1:
1402 return "(case {} {})".format(keys
[0], stmts_repr
)
1404 return "(case ({}) {})".format(" ".join(keys
), stmts_repr
)
1405 case_reprs
= [case_repr(keys
, stmts
) for keys
, stmts
in self
.cases
.items()]
1406 return "(switch {!r} {})".format(self
.test
, " ".join(case_reprs
))
1409 class _MappedKeyCollection(metaclass
=ABCMeta
):
1411 def _map_key(self
, key
):
1415 def _unmap_key(self
, key
):
1419 class _MappedKeyDict(MutableMapping
, _MappedKeyCollection
):
1420 def __init__(self
, pairs
=()):
1421 self
._storage
= OrderedDict()
1422 for key
, value
in pairs
:
1425 def __getitem__(self
, key
):
1426 key
= None if key
is None else self
._map
_key
(key
)
1427 return self
._storage
[key
]
1429 def __setitem__(self
, key
, value
):
1430 key
= None if key
is None else self
._map
_key
(key
)
1431 self
._storage
[key
] = value
1433 def __delitem__(self
, key
):
1434 key
= None if key
is None else self
._map
_key
(key
)
1435 del self
._storage
[key
]
1438 for key
in self
._storage
:
1442 yield self
._unmap
_key
(key
)
1444 def __eq__(self
, other
):
1445 if not isinstance(other
, type(self
)):
1447 if len(self
) != len(other
):
1449 for ak
, bk
in zip(sorted(self
._storage
), sorted(other
._storage
)):
1452 if self
._storage
[ak
] != other
._storage
[bk
]:
1457 return len(self
._storage
)
1460 pairs
= ["({!r}, {!r})".format(k
, v
) for k
, v
in self
.items()]
1461 return "{}.{}([{}])".format(type(self
).__module
__, type(self
).__name
__,
1465 class _MappedKeySet(MutableSet
, _MappedKeyCollection
):
1466 def __init__(self
, elements
=()):
1467 self
._storage
= OrderedDict()
1468 for elem
in elements
:
1471 def add(self
, value
):
1472 self
._storage
[self
._map
_key
(value
)] = None
1474 def update(self
, values
):
1475 for value
in values
:
1478 def discard(self
, value
):
1480 del self
._storage
[self
._map
_key
(value
)]
1482 def __contains__(self
, value
):
1483 return self
._map
_key
(value
) in self
._storage
1486 for key
in [k
for k
in self
._storage
]:
1487 yield self
._unmap
_key
(key
)
1490 return len(self
._storage
)
1493 return "{}.{}({})".format(type(self
).__module
__, type(self
).__name
__,
1494 ", ".join(repr(x
) for x
in self
))
1498 def __init__(self
, value
):
1499 self
.value
= Value
.cast(value
)
1500 if isinstance(self
.value
, Const
):
1501 self
._hash
= hash(self
.value
.value
)
1502 elif isinstance(self
.value
, (Signal
, AnyValue
)):
1503 self
._hash
= hash(self
.value
.duid
)
1504 elif isinstance(self
.value
, (ClockSignal
, ResetSignal
)):
1505 self
._hash
= hash(self
.value
.domain
)
1506 elif isinstance(self
.value
, Operator
):
1507 self
._hash
= hash((self
.value
.operator
,
1508 tuple(ValueKey(o
) for o
in self
.value
.operands
)))
1509 elif isinstance(self
.value
, Slice
):
1510 self
._hash
= hash((ValueKey(self
.value
.value
), self
.value
.start
, self
.value
.end
))
1511 elif isinstance(self
.value
, Part
):
1512 self
._hash
= hash((ValueKey(self
.value
.value
), ValueKey(self
.value
.offset
),
1513 self
.value
.width
, self
.value
.stride
))
1514 elif isinstance(self
.value
, Cat
):
1515 self
._hash
= hash(tuple(ValueKey(o
) for o
in self
.value
.parts
))
1516 elif isinstance(self
.value
, ArrayProxy
):
1517 self
._hash
= hash((ValueKey(self
.value
.index
),
1518 tuple(ValueKey(e
) for e
in self
.value
._iter
_as
_values
())))
1519 elif isinstance(self
.value
, Sample
):
1520 self
._hash
= hash((ValueKey(self
.value
.value
), self
.value
.clocks
, self
.value
.domain
))
1521 elif isinstance(self
.value
, Initial
):
1524 raise TypeError("Object {!r} cannot be used as a key in value collections"
1525 .format(self
.value
))
1530 def __eq__(self
, other
):
1531 if type(other
) is not ValueKey
:
1533 if type(self
.value
) is not type(other
.value
):
1536 if isinstance(self
.value
, Const
):
1537 return self
.value
.value
== other
.value
.value
1538 elif isinstance(self
.value
, (Signal
, AnyValue
)):
1539 return self
.value
is other
.value
1540 elif isinstance(self
.value
, (ClockSignal
, ResetSignal
)):
1541 return self
.value
.domain
== other
.value
.domain
1542 elif isinstance(self
.value
, Operator
):
1543 return (self
.value
.operator
== other
.value
.operator
and
1544 len(self
.value
.operands
) == len(other
.value
.operands
) and
1545 all(ValueKey(a
) == ValueKey(b
)
1546 for a
, b
in zip(self
.value
.operands
, other
.value
.operands
)))
1547 elif isinstance(self
.value
, Slice
):
1548 return (ValueKey(self
.value
.value
) == ValueKey(other
.value
.value
) and
1549 self
.value
.start
== other
.value
.start
and
1550 self
.value
.end
== other
.value
.end
)
1551 elif isinstance(self
.value
, Part
):
1552 return (ValueKey(self
.value
.value
) == ValueKey(other
.value
.value
) and
1553 ValueKey(self
.value
.offset
) == ValueKey(other
.value
.offset
) and
1554 self
.value
.width
== other
.value
.width
and
1555 self
.value
.stride
== other
.value
.stride
)
1556 elif isinstance(self
.value
, Cat
):
1557 return all(ValueKey(a
) == ValueKey(b
)
1558 for a
, b
in zip(self
.value
.parts
, other
.value
.parts
))
1559 elif isinstance(self
.value
, ArrayProxy
):
1560 return (ValueKey(self
.value
.index
) == ValueKey(other
.value
.index
) and
1561 len(self
.value
.elems
) == len(other
.value
.elems
) and
1562 all(ValueKey(a
) == ValueKey(b
)
1563 for a
, b
in zip(self
.value
._iter
_as
_values
(),
1564 other
.value
._iter
_as
_values
())))
1565 elif isinstance(self
.value
, Sample
):
1566 return (ValueKey(self
.value
.value
) == ValueKey(other
.value
.value
) and
1567 self
.value
.clocks
== other
.value
.clocks
and
1568 self
.value
.domain
== self
.value
.domain
)
1569 elif isinstance(self
.value
, Initial
):
1572 raise TypeError("Object {!r} cannot be used as a key in value collections"
1573 .format(self
.value
))
1575 def __lt__(self
, other
):
1576 if not isinstance(other
, ValueKey
):
1578 if type(self
.value
) != type(other
.value
):
1581 if isinstance(self
.value
, Const
):
1582 return self
.value
< other
.value
1583 elif isinstance(self
.value
, (Signal
, AnyValue
)):
1584 return self
.value
.duid
< other
.value
.duid
1585 elif isinstance(self
.value
, Slice
):
1586 return (ValueKey(self
.value
.value
) < ValueKey(other
.value
.value
) and
1587 self
.value
.start
< other
.value
.start
and
1588 self
.value
.end
< other
.value
.end
)
1590 raise TypeError("Object {!r} cannot be used as a key in value collections")
1593 return "<{}.ValueKey {!r}>".format(__name__
, self
.value
)
1596 class ValueDict(_MappedKeyDict
):
1598 _unmap_key
= lambda self
, key
: key
.value
1601 class ValueSet(_MappedKeySet
):
1603 _unmap_key
= lambda self
, key
: key
.value
1607 def __init__(self
, signal
):
1608 self
.signal
= signal
1609 if type(signal
) is Signal
:
1610 self
._intern
= (0, signal
.duid
)
1611 elif type(signal
) is ClockSignal
:
1612 self
._intern
= (1, signal
.domain
)
1613 elif type(signal
) is ResetSignal
:
1614 self
._intern
= (2, signal
.domain
)
1616 raise TypeError("Object {!r} is not an nMigen signal".format(signal
))
1619 return hash(self
._intern
)
1621 def __eq__(self
, other
):
1622 if type(other
) is not SignalKey
:
1624 return self
._intern
== other
._intern
1626 def __lt__(self
, other
):
1627 if type(other
) is not SignalKey
:
1628 raise TypeError("Object {!r} cannot be compared to a SignalKey".format(signal
))
1629 return self
._intern
< other
._intern
1632 return "<{}.SignalKey {!r}>".format(__name__
, self
.signal
)
1635 class SignalDict(_MappedKeyDict
):
1636 _map_key
= SignalKey
1637 _unmap_key
= lambda self
, key
: key
.signal
1640 class SignalSet(_MappedKeySet
):
1641 _map_key
= SignalKey
1642 _unmap_key
= lambda self
, key
: key
.signal