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