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