Signal: allow to use integral Enum for reset value.
[nmigen.git] / nmigen / hdl / ast.py
1 from abc import ABCMeta, abstractmethod
2 import builtins
3 import traceback
4 import warnings
5 import typing
6 from collections import OrderedDict
7 from collections.abc import Iterable, MutableMapping, MutableSet, MutableSequence
8 from enum import Enum
9
10 from .. import tracer
11 from .._utils 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", "Assign", "Assert", "Assume", "Cover", "Switch",
22 "ValueKey", "ValueDict", "ValueSet", "SignalKey", "SignalDict",
23 "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 # TODO(nmigen-0.2): remove this
128 @classmethod
129 @deprecated("instead of `Value.wrap`, use `Value.cast`")
130 def wrap(cls, obj):
131 return cls.cast(obj)
132
133 def __init__(self, *, src_loc_at=0):
134 super().__init__()
135 self.src_loc = tracer.get_src_loc(1 + src_loc_at)
136
137 def __bool__(self):
138 raise TypeError("Attempted to convert nMigen value to boolean")
139
140 def __invert__(self):
141 return Operator("~", [self])
142 def __neg__(self):
143 return Operator("-", [self])
144
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])
153
154 def __mul__(self, other):
155 return Operator("*", [self, other])
156 def __rmul__(self, other):
157 return Operator("*", [other, self])
158
159 def __check_divisor(self):
160 width, signed = self.shape()
161 if signed:
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])
180
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])
201
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])
214
215 def __len__(self):
216 return self.shape().width
217
218 def __getitem__(self, key):
219 n = len(self)
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))
223 if key < 0:
224 key += n
225 return Slice(self, key, key + 1)
226 elif isinstance(key, slice):
227 start, stop, step = key.indices(n)
228 if step != 1:
229 return Cat(self[i] for i in range(start, stop, step))
230 return Slice(self, start, stop)
231 else:
232 raise TypeError("Cannot index value with {}".format(repr(key)))
233
234 def bool(self):
235 """Conversion to boolean.
236
237 Returns
238 -------
239 Value, out
240 ``1`` if any bits are set, ``0`` otherwise.
241 """
242 return Operator("b", [self])
243
244 def any(self):
245 """Check if any bits are ``1``.
246
247 Returns
248 -------
249 Value, out
250 ``1`` if any bits are set, ``0`` otherwise.
251 """
252 return Operator("r|", [self])
253
254 def all(self):
255 """Check if all bits are ``1``.
256
257 Returns
258 -------
259 Value, out
260 ``1`` if all bits are set, ``0`` otherwise.
261 """
262 return Operator("r&", [self])
263
264 def xor(self):
265 """Compute pairwise exclusive-or of every bit.
266
267 Returns
268 -------
269 Value, out
270 ``1`` if an odd number of bits are set, ``0`` if an even number of bits are set.
271 """
272 return Operator("r^", [self])
273
274 def implies(premise, conclusion):
275 """Implication.
276
277 Returns
278 -------
279 Value, out
280 ``0`` if ``premise`` is true and ``conclusion`` is not, ``1`` otherwise.
281 """
282 return ~premise | conclusion
283
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)
288
289 def bit_select(self, offset, width):
290 """Part-select with bit granularity.
291
292 Selects a constant width but variable offset part of a ``Value``, such that successive
293 parts overlap by all but 1 bit.
294
295 Parameters
296 ----------
297 offset : Value, in
298 Index of first selected bit.
299 width : int
300 Number of selected bits.
301
302 Returns
303 -------
304 Part, out
305 Selected part of the ``Value``
306 """
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)
311
312 def word_select(self, offset, width):
313 """Part-select with word granularity.
314
315 Selects a constant width but variable offset part of a ``Value``, such that successive
316 parts do not overlap.
317
318 Parameters
319 ----------
320 offset : Value, in
321 Index of first selected word.
322 width : int
323 Number of selected bits.
324
325 Returns
326 -------
327 Part, out
328 Selected part of the ``Value``
329 """
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)
334
335 def matches(self, *patterns):
336 """Pattern matching.
337
338 Matches against a set of patterns, which may be integers or bit strings, recognizing
339 the same grammar as ``Case()``.
340
341 Parameters
342 ----------
343 patterns : int or str
344 Patterns to match against.
345
346 Returns
347 -------
348 Value, out
349 ``1`` if any pattern matches the value, ``0`` otherwise.
350 """
351 matches = []
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, "
355 "not {!r}"
356 .format(pattern))
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) "
359 "bits"
360 .format(pattern))
361 if isinstance(pattern, str) and len(pattern) != len(self):
362 raise SyntaxError("Match pattern '{}' must have the same width as match value "
363 "(which is {})"
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)
370 continue
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)
379 else:
380 assert False
381 if not matches:
382 return Const(0)
383 elif len(matches) == 1:
384 return matches[0]
385 else:
386 return Cat(*matches).any()
387
388 def eq(self, value):
389 """Assignment.
390
391 Parameters
392 ----------
393 value : Value, in
394 Value to be assigned.
395
396 Returns
397 -------
398 Assign
399 Assignment statement that can be used in combinatorial or synchronous context.
400 """
401 return Assign(self, value, src_loc_at=1)
402
403 @abstractmethod
404 def shape(self):
405 """Bit width and signedness of a value.
406
407 Returns
408 -------
409 Shape
410 See :class:`Shape`.
411
412 Examples
413 --------
414 >>> Signal(8).shape()
415 Shape(width=8, signed=False)
416 >>> Const(0xaa).shape()
417 Shape(width=8, signed=False)
418 """
419 pass # :nocov:
420
421 def _lhs_signals(self):
422 raise TypeError("Value {!r} cannot be used in assignments".format(self))
423
424 @abstractmethod
425 def _rhs_signals(self):
426 pass # :nocov:
427
428 def _as_const(self):
429 raise TypeError("Value {!r} cannot be evaluated as constant".format(self))
430
431 __hash__ = None
432
433
434 @final
435 class Const(Value):
436 """A constant, literal integer value.
437
438 Parameters
439 ----------
440 value : int
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``.
445
446 Attributes
447 ----------
448 width : int
449 signed : bool
450 """
451 src_loc = None
452
453 @staticmethod
454 def normalize(value, shape):
455 width, signed = shape
456 mask = (1 << width) - 1
457 value &= mask
458 if signed and value >> (width - 1):
459 value |= ~mask
460 return value
461
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)
465 if shape is None:
466 shape = Shape(bits_for(self.value), signed=self.value < 0)
467 elif isinstance(shape, int):
468 shape = Shape(shape, signed=self.value < 0)
469 else:
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)
473
474 # TODO(nmigen-0.2): move this to nmigen.compat and make it a deprecated extension
475 @property
476 @deprecated("instead of `const.nbits`, use `const.width`")
477 def nbits(self):
478 return self.width
479
480 def shape(self):
481 return Shape(self.width, self.signed)
482
483 def _rhs_signals(self):
484 return ValueSet()
485
486 def _as_const(self):
487 return self.value
488
489 def __repr__(self):
490 return "(const {}'{}d{})".format(self.width, "s" if self.signed else "", self.value)
491
492
493 C = Const # shorthand
494
495
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}"
502 .format(self.width))
503
504 def shape(self):
505 return Shape(self.width, self.signed)
506
507 def _rhs_signals(self):
508 return ValueSet()
509
510
511 @final
512 class AnyConst(AnyValue):
513 def __repr__(self):
514 return "(anyconst {}'{})".format(self.nbits, "s" if self.signed else "")
515
516
517 @final
518 class AnySeq(AnyValue):
519 def __repr__(self):
520 return "(anyseq {}'{})".format(self.nbits, "s" if self.signed else "")
521
522
523 @final
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]
529
530 # TODO(nmigen-0.2): move this to nmigen.compat and make it a deprecated extension
531 @property
532 @deprecated("instead of `.op`, use `.operator`")
533 def op(self):
534 return self.operator
535
536 def shape(self):
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)
549 else:
550 # first signed, second operand unsigned (add sign bit)
551 return Shape(max(a_bits, b_bits + 1), True)
552
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 ("//", "%"):
570 assert not b_signed
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 == "<<":
577 if b_signed:
578 extra = 2 ** (b_width - 1) - 1
579 else:
580 extra = 2 ** (b_width) - 1
581 return Shape(a_width + extra, a_signed)
582 if self.operator == ">>":
583 if b_signed:
584 extra = 2 ** (b_width - 1)
585 else:
586 extra = 0
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:
594
595 def _rhs_signals(self):
596 return union(op._rhs_signals() for op in self.operands)
597
598 def __repr__(self):
599 return "({} {})".format(self.operator, " ".join(map(repr, self.operands)))
600
601
602 def Mux(sel, val1, val0):
603 """Choose between two values.
604
605 Parameters
606 ----------
607 sel : Value, in
608 Selector.
609 val1 : Value, in
610 val0 : Value, in
611 Input values.
612
613 Returns
614 -------
615 Value, out
616 Output ``Value``. If ``sel`` is asserted, the Mux returns ``val1``, else ``val0``.
617 """
618 sel = Value.cast(sel)
619 if len(sel) != 1:
620 sel = sel.bool()
621 return Operator("m", [sel, val1, val0])
622
623
624 @final
625 class Slice(Value):
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))
631
632 n = len(value)
633 if start not in range(-(n+1), n+1):
634 raise IndexError("Cannot start slice {} bits into {}-bit value".format(start, n))
635 if start < 0:
636 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))
639 if stop < 0:
640 stop += n
641 if start > stop:
642 raise IndexError("Slice start {} must be less than slice stop {}".format(start, stop))
643
644 super().__init__(src_loc_at=src_loc_at)
645 self.value = Value.cast(value)
646 self.start = start
647 self.stop = stop
648
649 # TODO(nmigen-0.2): remove this
650 @property
651 @deprecated("instead of `slice.end`, use `slice.stop`")
652 def end(self):
653 return self.stop
654
655 def shape(self):
656 return Shape(self.stop - self.start)
657
658 def _lhs_signals(self):
659 return self.value._lhs_signals()
660
661 def _rhs_signals(self):
662 return self.value._rhs_signals()
663
664 def __repr__(self):
665 return "(slice {} {}:{})".format(repr(self.value), self.start, self.stop)
666
667
668 @final
669 class Part(Value):
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))
675
676 super().__init__(src_loc_at=src_loc_at)
677 self.value = value
678 self.offset = Value.cast(offset)
679 self.width = width
680 self.stride = stride
681
682 def shape(self):
683 return Shape(self.width)
684
685 def _lhs_signals(self):
686 return self.value._lhs_signals()
687
688 def _rhs_signals(self):
689 return self.value._rhs_signals() | self.offset._rhs_signals()
690
691 def __repr__(self):
692 return "(part {} {} {} {})".format(repr(self.value), repr(self.offset),
693 self.width, self.stride)
694
695
696 @final
697 class Cat(Value):
698 """Concatenate values.
699
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::
708
709 len(Cat(args)) == sum(len(arg) for arg in args)
710
711 Parameters
712 ----------
713 *args : Values or iterables of Values, inout
714 ``Value`` s to be concatenated.
715
716 Returns
717 -------
718 Value, inout
719 Resulting ``Value`` obtained by concatentation.
720 """
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)]
724
725 def shape(self):
726 return Shape(sum(len(part) for part in self.parts))
727
728 def _lhs_signals(self):
729 return union((part._lhs_signals() for part in self.parts), start=ValueSet())
730
731 def _rhs_signals(self):
732 return union((part._rhs_signals() for part in self.parts), start=ValueSet())
733
734 def _as_const(self):
735 value = 0
736 for part in reversed(self.parts):
737 value <<= len(part)
738 value |= part._as_const()
739 return value
740
741 def __repr__(self):
742 return "(cat {})".format(" ".join(map(repr, self.parts)))
743
744
745 @final
746 class Repl(Value):
747 """Replicate a value
748
749 An input value is replicated (repeated) several times
750 to be used on the RHS of assignments::
751
752 len(Repl(s, n)) == len(s) * n
753
754 Parameters
755 ----------
756 value : Value, in
757 Input value to be replicated.
758 count : int
759 Number of replications.
760
761 Returns
762 -------
763 Repl, out
764 Replicated value.
765 """
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}"
769 .format(count))
770
771 super().__init__(src_loc_at=src_loc_at)
772 self.value = Value.cast(value)
773 self.count = count
774
775 def shape(self):
776 return Shape(len(self.value) * self.count)
777
778 def _rhs_signals(self):
779 return self.value._rhs_signals()
780
781 def __repr__(self):
782 return "(repl {!r} {})".format(self.value, self.count)
783
784
785 @final
786 class Signal(Value, DUID):
787 """A varying integer value.
788
789 Parameters
790 ----------
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.
795 name : str
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
799 sequences.
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.
806 reset_less : bool
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``.
810 min : int or None
811 max : int or None
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).
815 attrs : dict
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
821 the enumeration.
822
823 Attributes
824 ----------
825 width : int
826 signed : bool
827 name : str
828 reset : int
829 reset_less : bool
830 attrs : dict
831 """
832
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)
836
837 if isinstance(reset, Enum):
838 reset = reset.value
839 if not isinstance(reset, int):
840 raise TypeError("Reset value has to be an int or an integral Enum")
841
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)
848
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")
852
853 if shape is None:
854 if min is None:
855 min = 0
856 if max is None:
857 max = 2
858 max -= 1 # make both bounds inclusive
859 if min > max:
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
863 if min == max:
864 self.width = 0
865 else:
866 self.width = builtins.max(bits_for(min, self.signed),
867 bits_for(max, self.signed))
868
869 else:
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)
873
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 "
877 "only has {} bits"
878 .format(reset, reset_width, self.width),
879 SyntaxWarning, stacklevel=2 + src_loc_at)
880
881 self.reset = int(reset)
882 self.reset_less = bool(reset_less)
883
884 self.attrs = OrderedDict(() if attrs is None else attrs)
885
886 if decoder is None and isinstance(shape, type) and issubclass(shape, Enum):
887 decoder = shape
888 if isinstance(decoder, type) and issubclass(decoder, Enum):
889 def enum_decoder(value):
890 try:
891 return "{0.name:}/{0.value:}".format(decoder(value))
892 except ValueError:
893 return str(value)
894 self.decoder = enum_decoder
895 else:
896 self.decoder = decoder
897
898 @classmethod
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)
902
903 @classmethod
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)
909
910 @classmethod
911 def like(cls, other, *, name=None, name_suffix=None, src_loc_at=0, **kwargs):
912 """Create Signal based on another.
913
914 Parameters
915 ----------
916 other : Value
917 Object to base this Signal on.
918 """
919 if name is not None:
920 new_name = str(name)
921 elif name_suffix is not None:
922 new_name = other.name + str(name_suffix)
923 else:
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)
929 kw.update(kwargs)
930 return cls(**kw, src_loc_at=1 + src_loc_at)
931
932 # TODO(nmigen-0.2): move this to nmigen.compat and make it a deprecated extension
933 @property
934 @deprecated("instead of `signal.nbits`, use `signal.width`")
935 def nbits(self):
936 return self.width
937
938 @nbits.setter
939 @deprecated("instead of `signal.nbits = x`, use `signal.width = x`")
940 def nbits(self, value):
941 self.width = value
942
943 def shape(self):
944 return Shape(self.width, self.signed)
945
946 def _lhs_signals(self):
947 return ValueSet((self,))
948
949 def _rhs_signals(self):
950 return ValueSet((self,))
951
952 def __repr__(self):
953 return "(sig {})".format(self.name)
954
955
956 @final
957 class ClockSignal(Value):
958 """Clock signal for a clock domain.
959
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.
963
964 Parameters
965 ----------
966 domain : str
967 Clock domain to obtain a clock signal for. Defaults to ``"sync"``.
968 """
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))
973 if domain == "comb":
974 raise ValueError("Domain '{}' does not have a clock".format(domain))
975 self.domain = domain
976
977 def shape(self):
978 return Shape(1)
979
980 def _lhs_signals(self):
981 return ValueSet((self,))
982
983 def _rhs_signals(self):
984 raise NotImplementedError("ClockSignal must be lowered to a concrete signal") # :nocov:
985
986 def __repr__(self):
987 return "(clk {})".format(self.domain)
988
989
990 @final
991 class ResetSignal(Value):
992 """Reset signal for a clock domain.
993
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.
997
998 Parameters
999 ----------
1000 domain : str
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.
1004 """
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
1013
1014 def shape(self):
1015 return Shape(1)
1016
1017 def _lhs_signals(self):
1018 return ValueSet((self,))
1019
1020 def _rhs_signals(self):
1021 raise NotImplementedError("ResetSignal must be lowered to a concrete signal") # :nocov:
1022
1023 def __repr__(self):
1024 return "(rst {})".format(self.domain)
1025
1026
1027 class Array(MutableSequence):
1028 """Addressable multiplexer.
1029
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
1031 in a proxy.
1032
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.
1038
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.
1043
1044 Examples
1045 --------
1046
1047 Simple array::
1048
1049 gpios = Array(Signal() for _ in range(10))
1050 with m.If(bus.we):
1051 m.d.sync += gpios[bus.addr].eq(bus.w_data)
1052 with m.Else():
1053 m.d.sync += bus.r_data.eq(gpios[bus.addr])
1054
1055 Multidimensional array::
1056
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)
1060 r = Signal(8)
1061 m.d.comb += r.eq(mult[a][b])
1062
1063 Array of records::
1064
1065 layout = [
1066 ("r_data", 16),
1067 ("r_en", 1),
1068 ]
1069 buses = Array(Record(layout) for busno in range(4))
1070 master = Record(layout)
1071 m.d.comb += [
1072 buses[sel].r_en.eq(master.r_en),
1073 master.r_data.eq(buses[sel].r_data),
1074 ]
1075 """
1076 def __init__(self, iterable=()):
1077 self._inner = list(iterable)
1078 self._proxy_at = None
1079 self._mutable = True
1080
1081 def __getitem__(self, index):
1082 if isinstance(index, Value):
1083 if self._mutable:
1084 self._proxy_at = tracer.get_src_loc()
1085 self._mutable = False
1086 return ArrayProxy(self, index)
1087 else:
1088 return self._inner[index]
1089
1090 def __len__(self):
1091 return len(self._inner)
1092
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))
1097
1098 def __setitem__(self, index, value):
1099 self._check_mutability()
1100 self._inner[index] = value
1101
1102 def __delitem__(self, index):
1103 self._check_mutability()
1104 del self._inner[index]
1105
1106 def insert(self, index, value):
1107 self._check_mutability()
1108 self._inner.insert(index, value)
1109
1110 def __repr__(self):
1111 return "(array{} [{}])".format(" mutable" if self._mutable else "",
1112 ", ".join(map(repr, self._inner)))
1113
1114
1115 @final
1116 class ArrayProxy(Value):
1117 def __init__(self, elems, index, *, src_loc_at=0):
1118 super().__init__(src_loc_at=1 + src_loc_at)
1119 self.elems = elems
1120 self.index = Value.cast(index)
1121
1122 def __getattr__(self, attr):
1123 return ArrayProxy([getattr(elem, attr) for elem in self.elems], self.index)
1124
1125 def __getitem__(self, index):
1126 return ArrayProxy([ elem[index] for elem in self.elems], self.index)
1127
1128 def _iter_as_values(self):
1129 return (Value.cast(elem) for elem in self.elems)
1130
1131 def shape(self):
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)
1137
1138 def _lhs_signals(self):
1139 signals = union((elem._lhs_signals() for elem in self._iter_as_values()), start=ValueSet())
1140 return signals
1141
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
1145
1146 def __repr__(self):
1147 return "(proxy (array [{}]) {!r})".format(", ".join(map(repr, self.elems)), self.index)
1148
1149
1150 class UserValue(Value):
1151 """Value with custom lowering.
1152
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.
1157
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.
1162
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> +=``.
1169 """
1170 def __init__(self, *, src_loc_at=0):
1171 super().__init__(src_loc_at=1 + src_loc_at)
1172 self.__lowered = None
1173
1174 @abstractmethod
1175 def lower(self):
1176 """Conversion to a concrete representation."""
1177 pass # :nocov:
1178
1179 def _lazy_lower(self):
1180 if self.__lowered is None:
1181 self.__lowered = Value.cast(self.lower())
1182 return self.__lowered
1183
1184 def shape(self):
1185 return self._lazy_lower().shape()
1186
1187 def _lhs_signals(self):
1188 return self._lazy_lower()._lhs_signals()
1189
1190 def _rhs_signals(self):
1191 return self._lazy_lower()._rhs_signals()
1192
1193
1194 @final
1195 class Sample(Value):
1196 """Value from the past.
1197
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.
1201 """
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))
1210 if self.clocks < 0:
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))
1216
1217 def shape(self):
1218 return self.value.shape()
1219
1220 def _rhs_signals(self):
1221 return ValueSet((self,))
1222
1223 def __repr__(self):
1224 return "(sample {!r} @ {}[{}])".format(
1225 self.value, "<default>" if self.domain is None else self.domain, self.clocks)
1226
1227
1228 def Past(expr, clocks=1, domain=None):
1229 return Sample(expr, clocks, domain)
1230
1231
1232 def Stable(expr, clocks=0, domain=None):
1233 return Sample(expr, clocks + 1, domain) == Sample(expr, clocks, domain)
1234
1235
1236 def Rose(expr, clocks=0, domain=None):
1237 return ~Sample(expr, clocks + 1, domain) & Sample(expr, clocks, domain)
1238
1239
1240 def Fell(expr, clocks=0, domain=None):
1241 return Sample(expr, clocks + 1, domain) & ~Sample(expr, clocks, domain)
1242
1243
1244 @final
1245 class Initial(Value):
1246 """Start indicator, for model checking.
1247
1248 An ``Initial`` signal is ``1`` at the first cycle of model checking, and ``0`` at any other.
1249 """
1250 def __init__(self, *, src_loc_at=0):
1251 super().__init__(src_loc_at=1 + src_loc_at)
1252
1253 def shape(self):
1254 return Shape(1)
1255
1256 def _rhs_signals(self):
1257 return ValueSet((self,))
1258
1259 def __repr__(self):
1260 return "(initial)"
1261
1262
1263 class _StatementList(list):
1264 def __repr__(self):
1265 return "({})".format(" ".join(map(repr, self)))
1266
1267
1268 class Statement:
1269 def __init__(self, *, src_loc_at=0):
1270 self.src_loc = tracer.get_src_loc(1 + src_loc_at)
1271
1272 # TODO(nmigen-0.2): remove this
1273 @classmethod
1274 @deprecated("instead of `Statement.wrap`, use `Statement.cast`")
1275 def wrap(cls, obj):
1276 return cls.cast(obj)
1277
1278 @staticmethod
1279 def cast(obj):
1280 if isinstance(obj, Iterable):
1281 return _StatementList(sum((Statement.cast(e) for e in obj), []))
1282 else:
1283 if isinstance(obj, Statement):
1284 return _StatementList([obj])
1285 else:
1286 raise TypeError("Object {!r} is not an nMigen statement".format(obj))
1287
1288
1289 @final
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)
1295
1296 def _lhs_signals(self):
1297 return self.lhs._lhs_signals()
1298
1299 def _rhs_signals(self):
1300 return self.lhs._rhs_signals() | self.rhs._rhs_signals()
1301
1302 def __repr__(self):
1303 return "(eq {!r} {!r})".format(self.lhs, self.rhs)
1304
1305
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
1311 self._en = _en
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
1315 if _en is None:
1316 self._en = Signal(reset_less=True, name="${}$en".format(self._kind))
1317 self._en.src_loc = self.src_loc
1318
1319 def _lhs_signals(self):
1320 return ValueSet((self._en, self._check))
1321
1322 def _rhs_signals(self):
1323 return self.test._rhs_signals()
1324
1325 def __repr__(self):
1326 return "({} {!r})".format(self._kind, self.test)
1327
1328
1329 @final
1330 class Assert(Property):
1331 _kind = "assert"
1332
1333
1334 @final
1335 class Assume(Property):
1336 _kind = "assume"
1337
1338
1339 @final
1340 class Cover(Property):
1341 _kind = "cover"
1342
1343
1344 # @final
1345 class Switch(Statement):
1346 def __init__(self, test, cases, *, src_loc=None, src_loc_at=0, case_src_locs={}):
1347 if src_loc is None:
1348 super().__init__(src_loc_at=src_loc_at)
1349 else:
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 = {}
1356
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...)
1361 keys = orig_keys
1362 if keys is None:
1363 keys = ()
1364 if not isinstance(keys, tuple):
1365 keys = (keys,)
1366 # Map: 2 -> "0010"; "0010" -> "0010"
1367 new_keys = ()
1368 for key in keys:
1369 if isinstance(key, str):
1370 pass
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")
1375 else:
1376 raise TypeError("Object {!r} cannot be used as a switch key"
1377 .format(key))
1378 assert len(key) == len(self.test)
1379 new_keys = (*new_keys, key)
1380 if not isinstance(stmts, Iterable):
1381 stmts = [stmts]
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]
1385
1386 def _lhs_signals(self):
1387 signals = union((s._lhs_signals() for ss in self.cases.values() for s in ss),
1388 start=ValueSet())
1389 return signals
1390
1391 def _rhs_signals(self):
1392 signals = union((s._rhs_signals() for ss in self.cases.values() for s in ss),
1393 start=ValueSet())
1394 return self.test._rhs_signals() | signals
1395
1396 def __repr__(self):
1397 def case_repr(keys, stmts):
1398 stmts_repr = " ".join(map(repr, stmts))
1399 if keys == ():
1400 return "(default {})".format(stmts_repr)
1401 elif len(keys) == 1:
1402 return "(case {} {})".format(keys[0], stmts_repr)
1403 else:
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))
1407
1408
1409 class _MappedKeyCollection(metaclass=ABCMeta):
1410 @abstractmethod
1411 def _map_key(self, key):
1412 pass # :nocov:
1413
1414 @abstractmethod
1415 def _unmap_key(self, key):
1416 pass # :nocov:
1417
1418
1419 class _MappedKeyDict(MutableMapping, _MappedKeyCollection):
1420 def __init__(self, pairs=()):
1421 self._storage = OrderedDict()
1422 for key, value in pairs:
1423 self[key] = value
1424
1425 def __getitem__(self, key):
1426 key = None if key is None else self._map_key(key)
1427 return self._storage[key]
1428
1429 def __setitem__(self, key, value):
1430 key = None if key is None else self._map_key(key)
1431 self._storage[key] = value
1432
1433 def __delitem__(self, key):
1434 key = None if key is None else self._map_key(key)
1435 del self._storage[key]
1436
1437 def __iter__(self):
1438 for key in self._storage:
1439 if key is None:
1440 yield None
1441 else:
1442 yield self._unmap_key(key)
1443
1444 def __eq__(self, other):
1445 if not isinstance(other, type(self)):
1446 return False
1447 if len(self) != len(other):
1448 return False
1449 for ak, bk in zip(sorted(self._storage), sorted(other._storage)):
1450 if ak != bk:
1451 return False
1452 if self._storage[ak] != other._storage[bk]:
1453 return False
1454 return True
1455
1456 def __len__(self):
1457 return len(self._storage)
1458
1459 def __repr__(self):
1460 pairs = ["({!r}, {!r})".format(k, v) for k, v in self.items()]
1461 return "{}.{}([{}])".format(type(self).__module__, type(self).__name__,
1462 ", ".join(pairs))
1463
1464
1465 class _MappedKeySet(MutableSet, _MappedKeyCollection):
1466 def __init__(self, elements=()):
1467 self._storage = OrderedDict()
1468 for elem in elements:
1469 self.add(elem)
1470
1471 def add(self, value):
1472 self._storage[self._map_key(value)] = None
1473
1474 def update(self, values):
1475 for value in values:
1476 self.add(value)
1477
1478 def discard(self, value):
1479 if value in self:
1480 del self._storage[self._map_key(value)]
1481
1482 def __contains__(self, value):
1483 return self._map_key(value) in self._storage
1484
1485 def __iter__(self):
1486 for key in [k for k in self._storage]:
1487 yield self._unmap_key(key)
1488
1489 def __len__(self):
1490 return len(self._storage)
1491
1492 def __repr__(self):
1493 return "{}.{}({})".format(type(self).__module__, type(self).__name__,
1494 ", ".join(repr(x) for x in self))
1495
1496
1497 class ValueKey:
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):
1522 self._hash = 0
1523 else: # :nocov:
1524 raise TypeError("Object {!r} cannot be used as a key in value collections"
1525 .format(self.value))
1526
1527 def __hash__(self):
1528 return self._hash
1529
1530 def __eq__(self, other):
1531 if type(other) is not ValueKey:
1532 return False
1533 if type(self.value) is not type(other.value):
1534 return False
1535
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):
1570 return True
1571 else: # :nocov:
1572 raise TypeError("Object {!r} cannot be used as a key in value collections"
1573 .format(self.value))
1574
1575 def __lt__(self, other):
1576 if not isinstance(other, ValueKey):
1577 return False
1578 if type(self.value) != type(other.value):
1579 return False
1580
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)
1589 else: # :nocov:
1590 raise TypeError("Object {!r} cannot be used as a key in value collections")
1591
1592 def __repr__(self):
1593 return "<{}.ValueKey {!r}>".format(__name__, self.value)
1594
1595
1596 class ValueDict(_MappedKeyDict):
1597 _map_key = ValueKey
1598 _unmap_key = lambda self, key: key.value
1599
1600
1601 class ValueSet(_MappedKeySet):
1602 _map_key = ValueKey
1603 _unmap_key = lambda self, key: key.value
1604
1605
1606 class SignalKey:
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)
1615 else:
1616 raise TypeError("Object {!r} is not an nMigen signal".format(signal))
1617
1618 def __hash__(self):
1619 return hash(self._intern)
1620
1621 def __eq__(self, other):
1622 if type(other) is not SignalKey:
1623 return False
1624 return self._intern == other._intern
1625
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
1630
1631 def __repr__(self):
1632 return "<{}.SignalKey {!r}>".format(__name__, self.signal)
1633
1634
1635 class SignalDict(_MappedKeyDict):
1636 _map_key = SignalKey
1637 _unmap_key = lambda self, key: key.signal
1638
1639
1640 class SignalSet(_MappedKeySet):
1641 _map_key = SignalKey
1642 _unmap_key = lambda self, key: key.signal