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