hdl.{ast,dsl}: allow whitespace in bit patterns.
[nmigen.git] / nmigen / hdl / ast.py
1 from abc import ABCMeta, abstractmethod
2 import traceback
3 import warnings
4 import typing
5 from collections import OrderedDict
6 from collections.abc import Iterable, MutableMapping, MutableSet, MutableSequence
7 from enum import Enum
8
9 from .. import tracer
10 from .._utils import *
11 from .._unused import *
12
13
14 __all__ = [
15 "Shape", "signed", "unsigned",
16 "Value", "Const", "C", "AnyConst", "AnySeq", "Operator", "Mux", "Part", "Slice", "Cat", "Repl",
17 "Array", "ArrayProxy",
18 "Signal", "ClockSignal", "ResetSignal",
19 "UserValue",
20 "Sample", "Past", "Stable", "Rose", "Fell", "Initial",
21 "Statement", "Switch",
22 "Property", "Assign", "Assert", "Assume", "Cover",
23 "ValueKey", "ValueDict", "ValueSet", "SignalKey", "SignalDict", "SignalSet",
24 ]
25
26
27 class DUID:
28 """Deterministic Unique IDentifier."""
29 __next_uid = 0
30 def __init__(self):
31 self.duid = DUID.__next_uid
32 DUID.__next_uid += 1
33
34
35 class Shape(typing.NamedTuple):
36 """Bit width and signedness of a value.
37
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.
42
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.
50
51 Parameters
52 ----------
53 width : int
54 The number of bits in the representation, including the sign bit (if any).
55 signed : bool
56 If ``False``, the value is unsigned. If ``True``, the value is signed two's complement.
57 """
58 width: int = 1
59 signed: bool = False
60
61 @staticmethod
62 def cast(obj, *, src_loc_at=0):
63 if isinstance(obj, Shape):
64 return obj
65 if isinstance(obj, int):
66 return Shape(obj)
67 if isinstance(obj, tuple):
68 width, signed = obj
69 warnings.warn("instead of `{tuple}`, use `{constructor}({width})`"
70 .format(constructor="signed" if signed else "unsigned", width=width,
71 tuple=obj),
72 DeprecationWarning, stacklevel=2 + src_loc_at)
73 return Shape(width, signed)
74 if isinstance(obj, range):
75 if len(obj) == 0:
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 "
86 "as value shapes")
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))
91
92
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}"
97 .format(width))
98 Shape.__init__ = _Shape___init__
99
100
101 def unsigned(width):
102 """Shorthand for ``Shape(width, signed=False)``."""
103 return Shape(width, signed=False)
104
105
106 def signed(width):
107 """Shorthand for ``Shape(width, signed=True)``."""
108 return Shape(width, signed=True)
109
110
111 class Value(metaclass=ABCMeta):
112 @staticmethod
113 def cast(obj):
114 """Converts ``obj`` to an nMigen value.
115
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.
118 """
119 if isinstance(obj, Value):
120 return obj
121 if isinstance(obj, int):
122 return Const(obj)
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))
126
127 def __init__(self, *, src_loc_at=0):
128 super().__init__()
129 self.src_loc = tracer.get_src_loc(1 + src_loc_at)
130
131 def __bool__(self):
132 raise TypeError("Attempted to convert nMigen value to boolean")
133
134 def __invert__(self):
135 return Operator("~", [self])
136 def __neg__(self):
137 return Operator("-", [self])
138
139 def __add__(self, other):
140 return Operator("+", [self, other])
141 def __radd__(self, other):
142 return Operator("+", [other, self])
143 def __sub__(self, other):
144 return Operator("-", [self, other])
145 def __rsub__(self, other):
146 return Operator("-", [other, self])
147
148 def __mul__(self, other):
149 return Operator("*", [self, other])
150 def __rmul__(self, other):
151 return Operator("*", [other, self])
152
153 def __check_divisor(self):
154 width, signed = self.shape()
155 if signed:
156 # Python's division semantics and Verilog's division semantics differ for negative
157 # divisors (Python uses div/mod, Verilog uses quo/rem); for now, avoid the issue
158 # completely by prohibiting such division operations.
159 raise NotImplementedError("Division by a signed value is not supported")
160 def __mod__(self, other):
161 other = Value.cast(other)
162 other.__check_divisor()
163 return Operator("%", [self, other])
164 def __rmod__(self, other):
165 self.__check_divisor()
166 return Operator("%", [other, self])
167 def __floordiv__(self, other):
168 other = Value.cast(other)
169 other.__check_divisor()
170 return Operator("//", [self, other])
171 def __rfloordiv__(self, other):
172 self.__check_divisor()
173 return Operator("//", [other, self])
174
175 def __check_shamt(self):
176 width, signed = self.shape()
177 if signed:
178 # Neither Python nor HDLs implement shifts by negative values; prohibit any shifts
179 # by a signed value to make sure the shift amount can always be interpreted as
180 # an unsigned value.
181 raise NotImplementedError("Shift by a signed value is not supported")
182 def __lshift__(self, other):
183 other = Value.cast(other)
184 other.__check_shamt()
185 return Operator("<<", [self, other])
186 def __rlshift__(self, other):
187 self.__check_shamt()
188 return Operator("<<", [other, self])
189 def __rshift__(self, other):
190 other = Value.cast(other)
191 other.__check_shamt()
192 return Operator(">>", [self, other])
193 def __rrshift__(self, other):
194 self.__check_shamt()
195 return Operator(">>", [other, self])
196
197 def __and__(self, other):
198 return Operator("&", [self, other])
199 def __rand__(self, other):
200 return Operator("&", [other, self])
201 def __xor__(self, other):
202 return Operator("^", [self, other])
203 def __rxor__(self, other):
204 return Operator("^", [other, self])
205 def __or__(self, other):
206 return Operator("|", [self, other])
207 def __ror__(self, other):
208 return Operator("|", [other, self])
209
210 def __eq__(self, other):
211 return Operator("==", [self, other])
212 def __ne__(self, other):
213 return Operator("!=", [self, other])
214 def __lt__(self, other):
215 return Operator("<", [self, other])
216 def __le__(self, other):
217 return Operator("<=", [self, other])
218 def __gt__(self, other):
219 return Operator(">", [self, other])
220 def __ge__(self, other):
221 return Operator(">=", [self, other])
222
223 def __len__(self):
224 return self.shape().width
225
226 def __getitem__(self, key):
227 n = len(self)
228 if isinstance(key, int):
229 if key not in range(-n, n):
230 raise IndexError("Cannot index {} bits into {}-bit value".format(key, n))
231 if key < 0:
232 key += n
233 return Slice(self, key, key + 1)
234 elif isinstance(key, slice):
235 start, stop, step = key.indices(n)
236 if step != 1:
237 return Cat(self[i] for i in range(start, stop, step))
238 return Slice(self, start, stop)
239 else:
240 raise TypeError("Cannot index value with {}".format(repr(key)))
241
242 def bool(self):
243 """Conversion to boolean.
244
245 Returns
246 -------
247 Value, out
248 ``1`` if any bits are set, ``0`` otherwise.
249 """
250 return Operator("b", [self])
251
252 def any(self):
253 """Check if any bits are ``1``.
254
255 Returns
256 -------
257 Value, out
258 ``1`` if any bits are set, ``0`` otherwise.
259 """
260 return Operator("r|", [self])
261
262 def all(self):
263 """Check if all bits are ``1``.
264
265 Returns
266 -------
267 Value, out
268 ``1`` if all bits are set, ``0`` otherwise.
269 """
270 return Operator("r&", [self])
271
272 def xor(self):
273 """Compute pairwise exclusive-or of every bit.
274
275 Returns
276 -------
277 Value, out
278 ``1`` if an odd number of bits are set, ``0`` if an even number of bits are set.
279 """
280 return Operator("r^", [self])
281
282 def implies(premise, conclusion):
283 """Implication.
284
285 Returns
286 -------
287 Value, out
288 ``0`` if ``premise`` is true and ``conclusion`` is not, ``1`` otherwise.
289 """
290 return ~premise | conclusion
291
292 def bit_select(self, offset, width):
293 """Part-select with bit granularity.
294
295 Selects a constant width but variable offset part of a ``Value``, such that successive
296 parts overlap by all but 1 bit.
297
298 Parameters
299 ----------
300 offset : Value, in
301 Index of first selected bit.
302 width : int
303 Number of selected bits.
304
305 Returns
306 -------
307 Part, out
308 Selected part of the ``Value``
309 """
310 offset = Value.cast(offset)
311 if type(offset) is Const and isinstance(width, int):
312 return self[offset.value:offset.value + width]
313 return Part(self, offset, width, stride=1, src_loc_at=1)
314
315 def word_select(self, offset, width):
316 """Part-select with word granularity.
317
318 Selects a constant width but variable offset part of a ``Value``, such that successive
319 parts do not overlap.
320
321 Parameters
322 ----------
323 offset : Value, in
324 Index of first selected word.
325 width : int
326 Number of selected bits.
327
328 Returns
329 -------
330 Part, out
331 Selected part of the ``Value``
332 """
333 offset = Value.cast(offset)
334 if type(offset) is Const and isinstance(width, int):
335 return self[offset.value * width:(offset.value + 1) * width]
336 return Part(self, offset, width, stride=width, src_loc_at=1)
337
338 def matches(self, *patterns):
339 """Pattern matching.
340
341 Matches against a set of patterns, which may be integers or bit strings, recognizing
342 the same grammar as ``Case()``.
343
344 Parameters
345 ----------
346 patterns : int or str
347 Patterns to match against.
348
349 Returns
350 -------
351 Value, out
352 ``1`` if any pattern matches the value, ``0`` otherwise.
353 """
354 matches = []
355 for pattern in patterns:
356 if not isinstance(pattern, (int, str, Enum)):
357 raise SyntaxError("Match pattern must be an integer, a string, or an enumeration, "
358 "not {!r}"
359 .format(pattern))
360 if isinstance(pattern, str) and any(bit not in "01- \t" for bit in pattern):
361 raise SyntaxError("Match pattern '{}' must consist of 0, 1, and - (don't care) "
362 "bits, and may include whitespace"
363 .format(pattern))
364 if (isinstance(pattern, str) and
365 len("".join(pattern.split())) != len(self)):
366 raise SyntaxError("Match pattern '{}' must have the same width as match value "
367 "(which is {})"
368 .format(pattern, len(self)))
369 if isinstance(pattern, int) and bits_for(pattern) > len(self):
370 warnings.warn("Match pattern '{:b}' is wider than match value "
371 "(which has width {}); comparison will never be true"
372 .format(pattern, len(self)),
373 SyntaxWarning, stacklevel=3)
374 continue
375 if isinstance(pattern, str):
376 pattern = "".join(pattern.split()) # remove whitespace
377 mask = int(pattern.replace("0", "1").replace("-", "0"), 2)
378 pattern = int(pattern.replace("-", "0"), 2)
379 matches.append((self & mask) == pattern)
380 elif isinstance(pattern, int):
381 matches.append(self == pattern)
382 elif isinstance(pattern, Enum):
383 matches.append(self == pattern.value)
384 else:
385 assert False
386 if not matches:
387 return Const(0)
388 elif len(matches) == 1:
389 return matches[0]
390 else:
391 return Cat(*matches).any()
392
393 def eq(self, value):
394 """Assignment.
395
396 Parameters
397 ----------
398 value : Value, in
399 Value to be assigned.
400
401 Returns
402 -------
403 Assign
404 Assignment statement that can be used in combinatorial or synchronous context.
405 """
406 return Assign(self, value, src_loc_at=1)
407
408 @abstractmethod
409 def shape(self):
410 """Bit width and signedness of a value.
411
412 Returns
413 -------
414 Shape
415 See :class:`Shape`.
416
417 Examples
418 --------
419 >>> Signal(8).shape()
420 Shape(width=8, signed=False)
421 >>> Const(0xaa).shape()
422 Shape(width=8, signed=False)
423 """
424 pass # :nocov:
425
426 def _lhs_signals(self):
427 raise TypeError("Value {!r} cannot be used in assignments".format(self))
428
429 @abstractmethod
430 def _rhs_signals(self):
431 pass # :nocov:
432
433 def _as_const(self):
434 raise TypeError("Value {!r} cannot be evaluated as constant".format(self))
435
436 __hash__ = None
437
438
439 @final
440 class Const(Value):
441 """A constant, literal integer value.
442
443 Parameters
444 ----------
445 value : int
446 shape : int or tuple or None
447 Either an integer ``width`` or a tuple ``(width, signed)`` specifying the number of bits
448 in this constant and whether it is signed (can represent negative values).
449 ``shape`` defaults to the minimum possible width and signedness of ``value``.
450
451 Attributes
452 ----------
453 width : int
454 signed : bool
455 """
456 src_loc = None
457
458 @staticmethod
459 def normalize(value, shape):
460 width, signed = shape
461 mask = (1 << width) - 1
462 value &= mask
463 if signed and value >> (width - 1):
464 value |= ~mask
465 return value
466
467 def __init__(self, value, shape=None, *, src_loc_at=0):
468 # We deliberately do not call Value.__init__ here.
469 self.value = int(value)
470 if shape is None:
471 shape = Shape(bits_for(self.value), signed=self.value < 0)
472 elif isinstance(shape, int):
473 shape = Shape(shape, signed=self.value < 0)
474 else:
475 shape = Shape.cast(shape, src_loc_at=1 + src_loc_at)
476 self.width, self.signed = shape
477 self.value = self.normalize(self.value, shape)
478
479 def shape(self):
480 return Shape(self.width, self.signed)
481
482 def _rhs_signals(self):
483 return ValueSet()
484
485 def _as_const(self):
486 return self.value
487
488 def __repr__(self):
489 return "(const {}'{}d{})".format(self.width, "s" if self.signed else "", self.value)
490
491
492 C = Const # shorthand
493
494
495 class AnyValue(Value, DUID):
496 def __init__(self, shape, *, src_loc_at=0):
497 super().__init__(src_loc_at=src_loc_at)
498 self.width, self.signed = Shape.cast(shape, src_loc_at=1 + src_loc_at)
499 if not isinstance(self.width, int) or self.width < 0:
500 raise TypeError("Width must be a non-negative integer, not {!r}"
501 .format(self.width))
502
503 def shape(self):
504 return Shape(self.width, self.signed)
505
506 def _rhs_signals(self):
507 return ValueSet()
508
509
510 @final
511 class AnyConst(AnyValue):
512 def __repr__(self):
513 return "(anyconst {}'{})".format(self.width, "s" if self.signed else "")
514
515
516 @final
517 class AnySeq(AnyValue):
518 def __repr__(self):
519 return "(anyseq {}'{})".format(self.width, "s" if self.signed else "")
520
521
522 @final
523 class Operator(Value):
524 def __init__(self, operator, operands, *, src_loc_at=0):
525 super().__init__(src_loc_at=1 + src_loc_at)
526 self.operator = operator
527 self.operands = [Value.cast(op) for op in operands]
528
529 def shape(self):
530 def _bitwise_binary_shape(a_shape, b_shape):
531 a_bits, a_sign = a_shape
532 b_bits, b_sign = b_shape
533 if not a_sign and not b_sign:
534 # both operands unsigned
535 return Shape(max(a_bits, b_bits), False)
536 elif a_sign and b_sign:
537 # both operands signed
538 return Shape(max(a_bits, b_bits), True)
539 elif not a_sign and b_sign:
540 # first operand unsigned (add sign bit), second operand signed
541 return Shape(max(a_bits + 1, b_bits), True)
542 else:
543 # first signed, second operand unsigned (add sign bit)
544 return Shape(max(a_bits, b_bits + 1), True)
545
546 op_shapes = list(map(lambda x: x.shape(), self.operands))
547 if len(op_shapes) == 1:
548 (a_width, a_signed), = op_shapes
549 if self.operator in ("+", "~"):
550 return Shape(a_width, a_signed)
551 if self.operator == "-":
552 return Shape(a_width + 1, True)
553 if self.operator in ("b", "r|", "r&", "r^"):
554 return Shape(1, False)
555 elif len(op_shapes) == 2:
556 (a_width, a_signed), (b_width, b_signed) = op_shapes
557 if self.operator in ("+", "-"):
558 width, signed = _bitwise_binary_shape(*op_shapes)
559 return Shape(width + 1, signed)
560 if self.operator == "*":
561 return Shape(a_width + b_width, a_signed or b_signed)
562 if self.operator in ("//", "%"):
563 assert not b_signed
564 return Shape(a_width, a_signed)
565 if self.operator in ("<", "<=", "==", "!=", ">", ">="):
566 return Shape(1, False)
567 if self.operator in ("&", "^", "|"):
568 return _bitwise_binary_shape(*op_shapes)
569 if self.operator == "<<":
570 if b_signed:
571 extra = 2 ** (b_width - 1) - 1
572 else:
573 extra = 2 ** (b_width) - 1
574 return Shape(a_width + extra, a_signed)
575 if self.operator == ">>":
576 if b_signed:
577 extra = 2 ** (b_width - 1)
578 else:
579 extra = 0
580 return Shape(a_width + extra, a_signed)
581 elif len(op_shapes) == 3:
582 if self.operator == "m":
583 s_shape, a_shape, b_shape = op_shapes
584 return _bitwise_binary_shape(a_shape, b_shape)
585 raise NotImplementedError("Operator {}/{} not implemented"
586 .format(self.operator, len(op_shapes))) # :nocov:
587
588 def _rhs_signals(self):
589 return union(op._rhs_signals() for op in self.operands)
590
591 def __repr__(self):
592 return "({} {})".format(self.operator, " ".join(map(repr, self.operands)))
593
594
595 def Mux(sel, val1, val0):
596 """Choose between two values.
597
598 Parameters
599 ----------
600 sel : Value, in
601 Selector.
602 val1 : Value, in
603 val0 : Value, in
604 Input values.
605
606 Returns
607 -------
608 Value, out
609 Output ``Value``. If ``sel`` is asserted, the Mux returns ``val1``, else ``val0``.
610 """
611 sel = Value.cast(sel)
612 if len(sel) != 1:
613 sel = sel.bool()
614 return Operator("m", [sel, val1, val0])
615
616
617 @final
618 class Slice(Value):
619 def __init__(self, value, start, stop, *, src_loc_at=0):
620 if not isinstance(start, int):
621 raise TypeError("Slice start must be an integer, not {!r}".format(start))
622 if not isinstance(stop, int):
623 raise TypeError("Slice stop must be an integer, not {!r}".format(stop))
624
625 n = len(value)
626 if start not in range(-(n+1), n+1):
627 raise IndexError("Cannot start slice {} bits into {}-bit value".format(start, n))
628 if start < 0:
629 start += n
630 if stop not in range(-(n+1), n+1):
631 raise IndexError("Cannot stop slice {} bits into {}-bit value".format(stop, n))
632 if stop < 0:
633 stop += n
634 if start > stop:
635 raise IndexError("Slice start {} must be less than slice stop {}".format(start, stop))
636
637 super().__init__(src_loc_at=src_loc_at)
638 self.value = Value.cast(value)
639 self.start = start
640 self.stop = stop
641
642 def shape(self):
643 return Shape(self.stop - self.start)
644
645 def _lhs_signals(self):
646 return self.value._lhs_signals()
647
648 def _rhs_signals(self):
649 return self.value._rhs_signals()
650
651 def __repr__(self):
652 return "(slice {} {}:{})".format(repr(self.value), self.start, self.stop)
653
654
655 @final
656 class Part(Value):
657 def __init__(self, value, offset, width, stride=1, *, src_loc_at=0):
658 if not isinstance(width, int) or width < 0:
659 raise TypeError("Part width must be a non-negative integer, not {!r}".format(width))
660 if not isinstance(stride, int) or stride <= 0:
661 raise TypeError("Part stride must be a positive integer, not {!r}".format(stride))
662
663 super().__init__(src_loc_at=src_loc_at)
664 self.value = value
665 self.offset = Value.cast(offset)
666 self.width = width
667 self.stride = stride
668
669 def shape(self):
670 return Shape(self.width)
671
672 def _lhs_signals(self):
673 return self.value._lhs_signals()
674
675 def _rhs_signals(self):
676 return self.value._rhs_signals() | self.offset._rhs_signals()
677
678 def __repr__(self):
679 return "(part {} {} {} {})".format(repr(self.value), repr(self.offset),
680 self.width, self.stride)
681
682
683 @final
684 class Cat(Value):
685 """Concatenate values.
686
687 Form a compound ``Value`` from several smaller ones by concatenation.
688 The first argument occupies the lower bits of the result.
689 The return value can be used on either side of an assignment, that
690 is, the concatenated value can be used as an argument on the RHS or
691 as a target on the LHS. If it is used on the LHS, it must solely
692 consist of ``Signal`` s, slices of ``Signal`` s, and other concatenations
693 meeting these properties. The bit length of the return value is the sum of
694 the bit lengths of the arguments::
695
696 len(Cat(args)) == sum(len(arg) for arg in args)
697
698 Parameters
699 ----------
700 *args : Values or iterables of Values, inout
701 ``Value`` s to be concatenated.
702
703 Returns
704 -------
705 Value, inout
706 Resulting ``Value`` obtained by concatentation.
707 """
708 def __init__(self, *args, src_loc_at=0):
709 super().__init__(src_loc_at=src_loc_at)
710 self.parts = [Value.cast(v) for v in flatten(args)]
711
712 def shape(self):
713 return Shape(sum(len(part) for part in self.parts))
714
715 def _lhs_signals(self):
716 return union((part._lhs_signals() for part in self.parts), start=ValueSet())
717
718 def _rhs_signals(self):
719 return union((part._rhs_signals() for part in self.parts), start=ValueSet())
720
721 def _as_const(self):
722 value = 0
723 for part in reversed(self.parts):
724 value <<= len(part)
725 value |= part._as_const()
726 return value
727
728 def __repr__(self):
729 return "(cat {})".format(" ".join(map(repr, self.parts)))
730
731
732 @final
733 class Repl(Value):
734 """Replicate a value
735
736 An input value is replicated (repeated) several times
737 to be used on the RHS of assignments::
738
739 len(Repl(s, n)) == len(s) * n
740
741 Parameters
742 ----------
743 value : Value, in
744 Input value to be replicated.
745 count : int
746 Number of replications.
747
748 Returns
749 -------
750 Repl, out
751 Replicated value.
752 """
753 def __init__(self, value, count, *, src_loc_at=0):
754 if not isinstance(count, int) or count < 0:
755 raise TypeError("Replication count must be a non-negative integer, not {!r}"
756 .format(count))
757
758 super().__init__(src_loc_at=src_loc_at)
759 self.value = Value.cast(value)
760 self.count = count
761
762 def shape(self):
763 return Shape(len(self.value) * self.count)
764
765 def _rhs_signals(self):
766 return self.value._rhs_signals()
767
768 def __repr__(self):
769 return "(repl {!r} {})".format(self.value, self.count)
770
771
772 # @final
773 class Signal(Value, DUID):
774 """A varying integer value.
775
776 Parameters
777 ----------
778 shape : ``Shape``-castable object or None
779 Specification for the number of bits in this ``Signal`` and its signedness (whether it
780 can represent negative values). See ``Shape.cast`` for details.
781 If not specified, ``shape`` defaults to 1-bit and non-signed.
782 name : str
783 Name hint for this signal. If ``None`` (default) the name is inferred from the variable
784 name this ``Signal`` is assigned to.
785 reset : int or integral Enum
786 Reset (synchronous) or default (combinatorial) value.
787 When this ``Signal`` is assigned to in synchronous context and the corresponding clock
788 domain is reset, the ``Signal`` assumes the given value. When this ``Signal`` is unassigned
789 in combinatorial context (due to conditional assignments not being taken), the ``Signal``
790 assumes its ``reset`` value. Defaults to 0.
791 reset_less : bool
792 If ``True``, do not generate reset logic for this ``Signal`` in synchronous statements.
793 The ``reset`` value is only used as a combinatorial default or as the initial value.
794 Defaults to ``False``.
795 attrs : dict
796 Dictionary of synthesis attributes.
797 decoder : function or Enum
798 A function converting integer signal values to human-readable strings (e.g. FSM state
799 names). If an ``Enum`` subclass is passed, it is concisely decoded using format string
800 ``"{0.name:}/{0.value:}"``, or a number if the signal value is not a member of
801 the enumeration.
802
803 Attributes
804 ----------
805 width : int
806 signed : bool
807 name : str
808 reset : int
809 reset_less : bool
810 attrs : dict
811 decoder : function
812 """
813
814 def __init__(self, shape=None, *, name=None, reset=0, reset_less=False,
815 attrs=None, decoder=None, src_loc_at=0):
816 super().__init__(src_loc_at=src_loc_at)
817
818 if name is not None and not isinstance(name, str):
819 raise TypeError("Name must be a string, not {!r}".format(name))
820 self.name = name or tracer.get_var_name(depth=2 + src_loc_at, default="$signal")
821
822 if shape is None:
823 shape = unsigned(1)
824 self.width, self.signed = Shape.cast(shape, src_loc_at=1 + src_loc_at)
825
826 if isinstance(reset, Enum):
827 reset = reset.value
828 if not isinstance(reset, int):
829 raise TypeError("Reset value has to be an int or an integral Enum")
830
831 reset_width = bits_for(reset, self.signed)
832 if reset != 0 and reset_width > self.width:
833 warnings.warn("Reset value {!r} requires {} bits to represent, but the signal "
834 "only has {} bits"
835 .format(reset, reset_width, self.width),
836 SyntaxWarning, stacklevel=2 + src_loc_at)
837
838 self.reset = reset
839 self.reset_less = bool(reset_less)
840
841 self.attrs = OrderedDict(() if attrs is None else attrs)
842
843 if decoder is None and isinstance(shape, type) and issubclass(shape, Enum):
844 decoder = shape
845 if isinstance(decoder, type) and issubclass(decoder, Enum):
846 def enum_decoder(value):
847 try:
848 return "{0.name:}/{0.value:}".format(decoder(value))
849 except ValueError:
850 return str(value)
851 self.decoder = enum_decoder
852 else:
853 self.decoder = decoder
854
855 # Not a @classmethod because nmigen.compat requires it.
856 @staticmethod
857 def like(other, *, name=None, name_suffix=None, src_loc_at=0, **kwargs):
858 """Create Signal based on another.
859
860 Parameters
861 ----------
862 other : Value
863 Object to base this Signal on.
864 """
865 if name is not None:
866 new_name = str(name)
867 elif name_suffix is not None:
868 new_name = other.name + str(name_suffix)
869 else:
870 new_name = tracer.get_var_name(depth=2 + src_loc_at, default="$like")
871 kw = dict(shape=Value.cast(other).shape(), name=new_name)
872 if isinstance(other, Signal):
873 kw.update(reset=other.reset, reset_less=other.reset_less,
874 attrs=other.attrs, decoder=other.decoder)
875 kw.update(kwargs)
876 return Signal(**kw, src_loc_at=1 + src_loc_at)
877
878 def shape(self):
879 return Shape(self.width, self.signed)
880
881 def _lhs_signals(self):
882 return ValueSet((self,))
883
884 def _rhs_signals(self):
885 return ValueSet((self,))
886
887 def __repr__(self):
888 return "(sig {})".format(self.name)
889
890
891 @final
892 class ClockSignal(Value):
893 """Clock signal for a clock domain.
894
895 Any ``ClockSignal`` is equivalent to ``cd.clk`` for a clock domain with the corresponding name.
896 All of these signals ultimately refer to the same signal, but they can be manipulated
897 independently of the clock domain, even before the clock domain is created.
898
899 Parameters
900 ----------
901 domain : str
902 Clock domain to obtain a clock signal for. Defaults to ``"sync"``.
903 """
904 def __init__(self, domain="sync", *, src_loc_at=0):
905 super().__init__(src_loc_at=src_loc_at)
906 if not isinstance(domain, str):
907 raise TypeError("Clock domain name must be a string, not {!r}".format(domain))
908 if domain == "comb":
909 raise ValueError("Domain '{}' does not have a clock".format(domain))
910 self.domain = domain
911
912 def shape(self):
913 return Shape(1)
914
915 def _lhs_signals(self):
916 return ValueSet((self,))
917
918 def _rhs_signals(self):
919 raise NotImplementedError("ClockSignal must be lowered to a concrete signal") # :nocov:
920
921 def __repr__(self):
922 return "(clk {})".format(self.domain)
923
924
925 @final
926 class ResetSignal(Value):
927 """Reset signal for a clock domain.
928
929 Any ``ResetSignal`` is equivalent to ``cd.rst`` for a clock domain with the corresponding name.
930 All of these signals ultimately refer to the same signal, but they can be manipulated
931 independently of the clock domain, even before the clock domain is created.
932
933 Parameters
934 ----------
935 domain : str
936 Clock domain to obtain a reset signal for. Defaults to ``"sync"``.
937 allow_reset_less : bool
938 If the clock domain is reset-less, act as a constant ``0`` instead of reporting an error.
939 """
940 def __init__(self, domain="sync", allow_reset_less=False, *, src_loc_at=0):
941 super().__init__(src_loc_at=src_loc_at)
942 if not isinstance(domain, str):
943 raise TypeError("Clock domain name must be a string, not {!r}".format(domain))
944 if domain == "comb":
945 raise ValueError("Domain '{}' does not have a reset".format(domain))
946 self.domain = domain
947 self.allow_reset_less = allow_reset_less
948
949 def shape(self):
950 return Shape(1)
951
952 def _lhs_signals(self):
953 return ValueSet((self,))
954
955 def _rhs_signals(self):
956 raise NotImplementedError("ResetSignal must be lowered to a concrete signal") # :nocov:
957
958 def __repr__(self):
959 return "(rst {})".format(self.domain)
960
961
962 class Array(MutableSequence):
963 """Addressable multiplexer.
964
965 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
966 in a proxy.
967
968 The array proxy can be used as an ordinary ``Value``, i.e. participate in calculations and
969 assignments, provided that all elements of the array are values. The array proxy also supports
970 attribute access and further indexing, each returning another array proxy; this means that
971 the results of indexing into arrays, arrays of records, and arrays of arrays can all
972 be used as first-class values.
973
974 It is an error to change an array or any of its elements after an array proxy was created.
975 Changing the array directly will raise an exception. However, it is not possible to detect
976 the elements being modified; if an element's attribute or element is modified after the proxy
977 for it has been created, the proxy will refer to stale data.
978
979 Examples
980 --------
981
982 Simple array::
983
984 gpios = Array(Signal() for _ in range(10))
985 with m.If(bus.we):
986 m.d.sync += gpios[bus.addr].eq(bus.w_data)
987 with m.Else():
988 m.d.sync += bus.r_data.eq(gpios[bus.addr])
989
990 Multidimensional array::
991
992 mult = Array(Array(x * y for y in range(10)) for x in range(10))
993 a = Signal.range(10)
994 b = Signal.range(10)
995 r = Signal(8)
996 m.d.comb += r.eq(mult[a][b])
997
998 Array of records::
999
1000 layout = [
1001 ("r_data", 16),
1002 ("r_en", 1),
1003 ]
1004 buses = Array(Record(layout) for busno in range(4))
1005 master = Record(layout)
1006 m.d.comb += [
1007 buses[sel].r_en.eq(master.r_en),
1008 master.r_data.eq(buses[sel].r_data),
1009 ]
1010 """
1011 def __init__(self, iterable=()):
1012 self._inner = list(iterable)
1013 self._proxy_at = None
1014 self._mutable = True
1015
1016 def __getitem__(self, index):
1017 if isinstance(index, Value):
1018 if self._mutable:
1019 self._proxy_at = tracer.get_src_loc()
1020 self._mutable = False
1021 return ArrayProxy(self, index)
1022 else:
1023 return self._inner[index]
1024
1025 def __len__(self):
1026 return len(self._inner)
1027
1028 def _check_mutability(self):
1029 if not self._mutable:
1030 raise ValueError("Array can no longer be mutated after it was indexed with a value "
1031 "at {}:{}".format(*self._proxy_at))
1032
1033 def __setitem__(self, index, value):
1034 self._check_mutability()
1035 self._inner[index] = value
1036
1037 def __delitem__(self, index):
1038 self._check_mutability()
1039 del self._inner[index]
1040
1041 def insert(self, index, value):
1042 self._check_mutability()
1043 self._inner.insert(index, value)
1044
1045 def __repr__(self):
1046 return "(array{} [{}])".format(" mutable" if self._mutable else "",
1047 ", ".join(map(repr, self._inner)))
1048
1049
1050 @final
1051 class ArrayProxy(Value):
1052 def __init__(self, elems, index, *, src_loc_at=0):
1053 super().__init__(src_loc_at=1 + src_loc_at)
1054 self.elems = elems
1055 self.index = Value.cast(index)
1056
1057 def __getattr__(self, attr):
1058 return ArrayProxy([getattr(elem, attr) for elem in self.elems], self.index)
1059
1060 def __getitem__(self, index):
1061 return ArrayProxy([ elem[index] for elem in self.elems], self.index)
1062
1063 def _iter_as_values(self):
1064 return (Value.cast(elem) for elem in self.elems)
1065
1066 def shape(self):
1067 width, signed = 0, False
1068 for elem_width, elem_signed in (elem.shape() for elem in self._iter_as_values()):
1069 width = max(width, elem_width + elem_signed)
1070 signed = max(signed, elem_signed)
1071 return Shape(width, signed)
1072
1073 def _lhs_signals(self):
1074 signals = union((elem._lhs_signals() for elem in self._iter_as_values()), start=ValueSet())
1075 return signals
1076
1077 def _rhs_signals(self):
1078 signals = union((elem._rhs_signals() for elem in self._iter_as_values()), start=ValueSet())
1079 return self.index._rhs_signals() | signals
1080
1081 def __repr__(self):
1082 return "(proxy (array [{}]) {!r})".format(", ".join(map(repr, self.elems)), self.index)
1083
1084
1085 class UserValue(Value):
1086 """Value with custom lowering.
1087
1088 A ``UserValue`` is a value whose precise representation does not have to be immediately known,
1089 which is useful in certain metaprogramming scenarios. Instead of providing fixed semantics
1090 upfront, it is kept abstract for as long as possible, only being lowered to a concrete nMigen
1091 value when required.
1092
1093 Note that the ``lower`` method will only be called once; this is necessary to ensure that
1094 nMigen's view of representation of all values stays internally consistent. If the class
1095 deriving from ``UserValue`` is mutable, then it must ensure that after ``lower`` is called,
1096 it is not mutated in a way that changes its representation.
1097
1098 The following is an incomplete list of actions that, when applied to an ``UserValue`` directly
1099 or indirectly, will cause it to be lowered, provided as an illustrative reference:
1100 * Querying the shape using ``.shape()`` or ``len()``;
1101 * Creating a similarly shaped signal using ``Signal.like``;
1102 * Indexing or iterating through individual bits;
1103 * Adding an assignment to the value to a ``Module`` using ``m.d.<domain> +=``.
1104 """
1105 def __init__(self, *, src_loc_at=0):
1106 super().__init__(src_loc_at=1 + src_loc_at)
1107 self.__lowered = None
1108
1109 @abstractmethod
1110 def lower(self):
1111 """Conversion to a concrete representation."""
1112 pass # :nocov:
1113
1114 def _lazy_lower(self):
1115 if self.__lowered is None:
1116 self.__lowered = Value.cast(self.lower())
1117 return self.__lowered
1118
1119 def shape(self):
1120 return self._lazy_lower().shape()
1121
1122 def _lhs_signals(self):
1123 return self._lazy_lower()._lhs_signals()
1124
1125 def _rhs_signals(self):
1126 return self._lazy_lower()._rhs_signals()
1127
1128
1129 @final
1130 class Sample(Value):
1131 """Value from the past.
1132
1133 A ``Sample`` of an expression is equal to the value of the expression ``clocks`` clock edges
1134 of the ``domain`` clock back. If that moment is before the beginning of time, it is equal
1135 to the value of the expression calculated as if each signal had its reset value.
1136 """
1137 def __init__(self, expr, clocks, domain, *, src_loc_at=0):
1138 super().__init__(src_loc_at=1 + src_loc_at)
1139 self.value = Value.cast(expr)
1140 self.clocks = int(clocks)
1141 self.domain = domain
1142 if not isinstance(self.value, (Const, Signal, ClockSignal, ResetSignal, Initial)):
1143 raise TypeError("Sampled value must be a signal or a constant, not {!r}"
1144 .format(self.value))
1145 if self.clocks < 0:
1146 raise ValueError("Cannot sample a value {} cycles in the future"
1147 .format(-self.clocks))
1148 if not (self.domain is None or isinstance(self.domain, str)):
1149 raise TypeError("Domain name must be a string or None, not {!r}"
1150 .format(self.domain))
1151
1152 def shape(self):
1153 return self.value.shape()
1154
1155 def _rhs_signals(self):
1156 return ValueSet((self,))
1157
1158 def __repr__(self):
1159 return "(sample {!r} @ {}[{}])".format(
1160 self.value, "<default>" if self.domain is None else self.domain, self.clocks)
1161
1162
1163 def Past(expr, clocks=1, domain=None):
1164 return Sample(expr, clocks, domain)
1165
1166
1167 def Stable(expr, clocks=0, domain=None):
1168 return Sample(expr, clocks + 1, domain) == Sample(expr, clocks, domain)
1169
1170
1171 def Rose(expr, clocks=0, domain=None):
1172 return ~Sample(expr, clocks + 1, domain) & Sample(expr, clocks, domain)
1173
1174
1175 def Fell(expr, clocks=0, domain=None):
1176 return Sample(expr, clocks + 1, domain) & ~Sample(expr, clocks, domain)
1177
1178
1179 @final
1180 class Initial(Value):
1181 """Start indicator, for model checking.
1182
1183 An ``Initial`` signal is ``1`` at the first cycle of model checking, and ``0`` at any other.
1184 """
1185 def __init__(self, *, src_loc_at=0):
1186 super().__init__(src_loc_at=1 + src_loc_at)
1187
1188 def shape(self):
1189 return Shape(1)
1190
1191 def _rhs_signals(self):
1192 return ValueSet((self,))
1193
1194 def __repr__(self):
1195 return "(initial)"
1196
1197
1198 class _StatementList(list):
1199 def __repr__(self):
1200 return "({})".format(" ".join(map(repr, self)))
1201
1202
1203 class Statement:
1204 def __init__(self, *, src_loc_at=0):
1205 self.src_loc = tracer.get_src_loc(1 + src_loc_at)
1206
1207 @staticmethod
1208 def cast(obj):
1209 if isinstance(obj, Iterable):
1210 return _StatementList(sum((Statement.cast(e) for e in obj), []))
1211 else:
1212 if isinstance(obj, Statement):
1213 return _StatementList([obj])
1214 else:
1215 raise TypeError("Object {!r} is not an nMigen statement".format(obj))
1216
1217
1218 @final
1219 class Assign(Statement):
1220 def __init__(self, lhs, rhs, *, src_loc_at=0):
1221 super().__init__(src_loc_at=src_loc_at)
1222 self.lhs = Value.cast(lhs)
1223 self.rhs = Value.cast(rhs)
1224
1225 def _lhs_signals(self):
1226 return self.lhs._lhs_signals()
1227
1228 def _rhs_signals(self):
1229 return self.lhs._rhs_signals() | self.rhs._rhs_signals()
1230
1231 def __repr__(self):
1232 return "(eq {!r} {!r})".format(self.lhs, self.rhs)
1233
1234
1235 class UnusedProperty(UnusedMustUse):
1236 pass
1237
1238
1239 class Property(Statement, MustUse):
1240 _MustUse__warning = UnusedProperty
1241
1242 def __init__(self, test, *, _check=None, _en=None, src_loc_at=0):
1243 super().__init__(src_loc_at=src_loc_at)
1244 self.test = Value.cast(test)
1245 self._check = _check
1246 self._en = _en
1247 if self._check is None:
1248 self._check = Signal(reset_less=True, name="${}$check".format(self._kind))
1249 self._check.src_loc = self.src_loc
1250 if _en is None:
1251 self._en = Signal(reset_less=True, name="${}$en".format(self._kind))
1252 self._en.src_loc = self.src_loc
1253
1254 def _lhs_signals(self):
1255 return ValueSet((self._en, self._check))
1256
1257 def _rhs_signals(self):
1258 return self.test._rhs_signals()
1259
1260 def __repr__(self):
1261 return "({} {!r})".format(self._kind, self.test)
1262
1263
1264 @final
1265 class Assert(Property):
1266 _kind = "assert"
1267
1268
1269 @final
1270 class Assume(Property):
1271 _kind = "assume"
1272
1273
1274 @final
1275 class Cover(Property):
1276 _kind = "cover"
1277
1278
1279 # @final
1280 class Switch(Statement):
1281 def __init__(self, test, cases, *, src_loc=None, src_loc_at=0, case_src_locs={}):
1282 if src_loc is None:
1283 super().__init__(src_loc_at=src_loc_at)
1284 else:
1285 # Switch is a bit special in terms of location tracking because it is usually created
1286 # long after the control has left the statement that directly caused its creation.
1287 self.src_loc = src_loc
1288 # Switch is also a bit special in that its parts also have location information. It can't
1289 # be automatically traced, so whatever constructs a Switch may optionally provide it.
1290 self.case_src_locs = {}
1291
1292 self.test = Value.cast(test)
1293 self.cases = OrderedDict()
1294 for orig_keys, stmts in cases.items():
1295 # Map: None -> (); key -> (key,); (key...) -> (key...)
1296 keys = orig_keys
1297 if keys is None:
1298 keys = ()
1299 if not isinstance(keys, tuple):
1300 keys = (keys,)
1301 # Map: 2 -> "0010"; "0010" -> "0010"
1302 new_keys = ()
1303 for key in keys:
1304 if isinstance(key, str):
1305 key = "".join(key.split()) # remove whitespace
1306 elif isinstance(key, int):
1307 key = format(key, "b").rjust(len(self.test), "0")
1308 elif isinstance(key, Enum):
1309 key = format(key.value, "b").rjust(len(self.test), "0")
1310 else:
1311 raise TypeError("Object {!r} cannot be used as a switch key"
1312 .format(key))
1313 assert len(key) == len(self.test)
1314 new_keys = (*new_keys, key)
1315 if not isinstance(stmts, Iterable):
1316 stmts = [stmts]
1317 self.cases[new_keys] = Statement.cast(stmts)
1318 if orig_keys in case_src_locs:
1319 self.case_src_locs[new_keys] = case_src_locs[orig_keys]
1320
1321 def _lhs_signals(self):
1322 signals = union((s._lhs_signals() for ss in self.cases.values() for s in ss),
1323 start=ValueSet())
1324 return signals
1325
1326 def _rhs_signals(self):
1327 signals = union((s._rhs_signals() for ss in self.cases.values() for s in ss),
1328 start=ValueSet())
1329 return self.test._rhs_signals() | signals
1330
1331 def __repr__(self):
1332 def case_repr(keys, stmts):
1333 stmts_repr = " ".join(map(repr, stmts))
1334 if keys == ():
1335 return "(default {})".format(stmts_repr)
1336 elif len(keys) == 1:
1337 return "(case {} {})".format(keys[0], stmts_repr)
1338 else:
1339 return "(case ({}) {})".format(" ".join(keys), stmts_repr)
1340 case_reprs = [case_repr(keys, stmts) for keys, stmts in self.cases.items()]
1341 return "(switch {!r} {})".format(self.test, " ".join(case_reprs))
1342
1343
1344 class _MappedKeyCollection(metaclass=ABCMeta):
1345 @abstractmethod
1346 def _map_key(self, key):
1347 pass # :nocov:
1348
1349 @abstractmethod
1350 def _unmap_key(self, key):
1351 pass # :nocov:
1352
1353
1354 class _MappedKeyDict(MutableMapping, _MappedKeyCollection):
1355 def __init__(self, pairs=()):
1356 self._storage = OrderedDict()
1357 for key, value in pairs:
1358 self[key] = value
1359
1360 def __getitem__(self, key):
1361 key = None if key is None else self._map_key(key)
1362 return self._storage[key]
1363
1364 def __setitem__(self, key, value):
1365 key = None if key is None else self._map_key(key)
1366 self._storage[key] = value
1367
1368 def __delitem__(self, key):
1369 key = None if key is None else self._map_key(key)
1370 del self._storage[key]
1371
1372 def __iter__(self):
1373 for key in self._storage:
1374 if key is None:
1375 yield None
1376 else:
1377 yield self._unmap_key(key)
1378
1379 def __eq__(self, other):
1380 if not isinstance(other, type(self)):
1381 return False
1382 if len(self) != len(other):
1383 return False
1384 for ak, bk in zip(sorted(self._storage), sorted(other._storage)):
1385 if ak != bk:
1386 return False
1387 if self._storage[ak] != other._storage[bk]:
1388 return False
1389 return True
1390
1391 def __len__(self):
1392 return len(self._storage)
1393
1394 def __repr__(self):
1395 pairs = ["({!r}, {!r})".format(k, v) for k, v in self.items()]
1396 return "{}.{}([{}])".format(type(self).__module__, type(self).__name__,
1397 ", ".join(pairs))
1398
1399
1400 class _MappedKeySet(MutableSet, _MappedKeyCollection):
1401 def __init__(self, elements=()):
1402 self._storage = OrderedDict()
1403 for elem in elements:
1404 self.add(elem)
1405
1406 def add(self, value):
1407 self._storage[self._map_key(value)] = None
1408
1409 def update(self, values):
1410 for value in values:
1411 self.add(value)
1412
1413 def discard(self, value):
1414 if value in self:
1415 del self._storage[self._map_key(value)]
1416
1417 def __contains__(self, value):
1418 return self._map_key(value) in self._storage
1419
1420 def __iter__(self):
1421 for key in [k for k in self._storage]:
1422 yield self._unmap_key(key)
1423
1424 def __len__(self):
1425 return len(self._storage)
1426
1427 def __repr__(self):
1428 return "{}.{}({})".format(type(self).__module__, type(self).__name__,
1429 ", ".join(repr(x) for x in self))
1430
1431
1432 class ValueKey:
1433 def __init__(self, value):
1434 self.value = Value.cast(value)
1435 if isinstance(self.value, Const):
1436 self._hash = hash(self.value.value)
1437 elif isinstance(self.value, (Signal, AnyValue)):
1438 self._hash = hash(self.value.duid)
1439 elif isinstance(self.value, (ClockSignal, ResetSignal)):
1440 self._hash = hash(self.value.domain)
1441 elif isinstance(self.value, Operator):
1442 self._hash = hash((self.value.operator,
1443 tuple(ValueKey(o) for o in self.value.operands)))
1444 elif isinstance(self.value, Slice):
1445 self._hash = hash((ValueKey(self.value.value), self.value.start, self.value.stop))
1446 elif isinstance(self.value, Part):
1447 self._hash = hash((ValueKey(self.value.value), ValueKey(self.value.offset),
1448 self.value.width, self.value.stride))
1449 elif isinstance(self.value, Cat):
1450 self._hash = hash(tuple(ValueKey(o) for o in self.value.parts))
1451 elif isinstance(self.value, ArrayProxy):
1452 self._hash = hash((ValueKey(self.value.index),
1453 tuple(ValueKey(e) for e in self.value._iter_as_values())))
1454 elif isinstance(self.value, Sample):
1455 self._hash = hash((ValueKey(self.value.value), self.value.clocks, self.value.domain))
1456 elif isinstance(self.value, Initial):
1457 self._hash = 0
1458 else: # :nocov:
1459 raise TypeError("Object {!r} cannot be used as a key in value collections"
1460 .format(self.value))
1461
1462 def __hash__(self):
1463 return self._hash
1464
1465 def __eq__(self, other):
1466 if type(other) is not ValueKey:
1467 return False
1468 if type(self.value) is not type(other.value):
1469 return False
1470
1471 if isinstance(self.value, Const):
1472 return self.value.value == other.value.value
1473 elif isinstance(self.value, (Signal, AnyValue)):
1474 return self.value is other.value
1475 elif isinstance(self.value, (ClockSignal, ResetSignal)):
1476 return self.value.domain == other.value.domain
1477 elif isinstance(self.value, Operator):
1478 return (self.value.operator == other.value.operator and
1479 len(self.value.operands) == len(other.value.operands) and
1480 all(ValueKey(a) == ValueKey(b)
1481 for a, b in zip(self.value.operands, other.value.operands)))
1482 elif isinstance(self.value, Slice):
1483 return (ValueKey(self.value.value) == ValueKey(other.value.value) and
1484 self.value.start == other.value.start and
1485 self.value.stop == other.value.stop)
1486 elif isinstance(self.value, Part):
1487 return (ValueKey(self.value.value) == ValueKey(other.value.value) and
1488 ValueKey(self.value.offset) == ValueKey(other.value.offset) and
1489 self.value.width == other.value.width and
1490 self.value.stride == other.value.stride)
1491 elif isinstance(self.value, Cat):
1492 return all(ValueKey(a) == ValueKey(b)
1493 for a, b in zip(self.value.parts, other.value.parts))
1494 elif isinstance(self.value, ArrayProxy):
1495 return (ValueKey(self.value.index) == ValueKey(other.value.index) and
1496 len(self.value.elems) == len(other.value.elems) and
1497 all(ValueKey(a) == ValueKey(b)
1498 for a, b in zip(self.value._iter_as_values(),
1499 other.value._iter_as_values())))
1500 elif isinstance(self.value, Sample):
1501 return (ValueKey(self.value.value) == ValueKey(other.value.value) and
1502 self.value.clocks == other.value.clocks and
1503 self.value.domain == self.value.domain)
1504 elif isinstance(self.value, Initial):
1505 return True
1506 else: # :nocov:
1507 raise TypeError("Object {!r} cannot be used as a key in value collections"
1508 .format(self.value))
1509
1510 def __lt__(self, other):
1511 if not isinstance(other, ValueKey):
1512 return False
1513 if type(self.value) != type(other.value):
1514 return False
1515
1516 if isinstance(self.value, Const):
1517 return self.value < other.value
1518 elif isinstance(self.value, (Signal, AnyValue)):
1519 return self.value.duid < other.value.duid
1520 elif isinstance(self.value, Slice):
1521 return (ValueKey(self.value.value) < ValueKey(other.value.value) and
1522 self.value.start < other.value.start and
1523 self.value.end < other.value.end)
1524 else: # :nocov:
1525 raise TypeError("Object {!r} cannot be used as a key in value collections")
1526
1527 def __repr__(self):
1528 return "<{}.ValueKey {!r}>".format(__name__, self.value)
1529
1530
1531 class ValueDict(_MappedKeyDict):
1532 _map_key = ValueKey
1533 _unmap_key = lambda self, key: key.value
1534
1535
1536 class ValueSet(_MappedKeySet):
1537 _map_key = ValueKey
1538 _unmap_key = lambda self, key: key.value
1539
1540
1541 class SignalKey:
1542 def __init__(self, signal):
1543 self.signal = signal
1544 if isinstance(signal, Signal):
1545 self._intern = (0, signal.duid)
1546 elif type(signal) is ClockSignal:
1547 self._intern = (1, signal.domain)
1548 elif type(signal) is ResetSignal:
1549 self._intern = (2, signal.domain)
1550 else:
1551 raise TypeError("Object {!r} is not an nMigen signal".format(signal))
1552
1553 def __hash__(self):
1554 return hash(self._intern)
1555
1556 def __eq__(self, other):
1557 if type(other) is not SignalKey:
1558 return False
1559 return self._intern == other._intern
1560
1561 def __lt__(self, other):
1562 if type(other) is not SignalKey:
1563 raise TypeError("Object {!r} cannot be compared to a SignalKey".format(signal))
1564 return self._intern < other._intern
1565
1566 def __repr__(self):
1567 return "<{}.SignalKey {!r}>".format(__name__, self.signal)
1568
1569
1570 class SignalDict(_MappedKeyDict):
1571 _map_key = SignalKey
1572 _unmap_key = lambda self, key: key.signal
1573
1574
1575 class SignalSet(_MappedKeySet):
1576 _map_key = SignalKey
1577 _unmap_key = lambda self, key: key.signal