11from abc import ABCMeta , abstractmethod
2+ import inspect
23import warnings
34import functools
45from collections import OrderedDict
@@ -41,12 +42,16 @@ class ShapeCastable:
4142 a richer description of the shape than what is supported by the core Amaranth language, yet
4243 still be transparently used with it.
4344 """
44- def __new__ (cls , * args , ** kwargs ):
45- self = super ().__new__ (cls )
46- if not hasattr (self , "as_shape" ):
45+ def __init_subclass__ (cls , ** kwargs ):
46+ if not hasattr (cls , "as_shape" ):
4747 raise TypeError (f"Class '{ cls .__name__ } ' deriving from `ShapeCastable` must override "
4848 f"the `as_shape` method" )
49- return self
49+ if not (hasattr (cls , "__call__" ) and inspect .isfunction (cls .__call__ )):
50+ raise TypeError (f"Class '{ cls .__name__ } ' deriving from `ShapeCastable` must override "
51+ f"the `__call__` method" )
52+ if not hasattr (cls , "const" ):
53+ raise TypeError (f"Class '{ cls .__name__ } ' deriving from `ShapeCastable` must override "
54+ f"the `const` method" )
5055
5156
5257class Shape :
@@ -572,8 +577,6 @@ def _lhs_signals(self):
572577 def _rhs_signals (self ):
573578 pass # :nocov:
574579
575- __hash__ = None
576-
577580
578581@final
579582class Const (Value ):
@@ -634,6 +637,13 @@ def __init__(self, value, shape=None, *, src_loc_at=0):
634637 elif isinstance (shape , int ):
635638 shape = Shape (shape , signed = self .value < 0 )
636639 else :
640+ if isinstance (shape , range ) and self .value == shape .stop :
641+ warnings .warn (
642+ message = "Value {!r} equals the non-inclusive end of the constant "
643+ "shape {!r}; this is likely an off-by-one error"
644+ .format (self .value , shape ),
645+ category = SyntaxWarning ,
646+ stacklevel = 2 )
637647 shape = Shape .cast (shape , src_loc_at = 1 + src_loc_at )
638648 self .width = shape .width
639649 self .signed = shape .signed
@@ -943,8 +953,16 @@ def __repr__(self):
943953 return "(repl {!r} {})" .format (self .value , self .count )
944954
945955
956+ class _SignalMeta (ABCMeta ):
957+ def __call__ (cls , shape = None , src_loc_at = 0 , ** kwargs ):
958+ signal = super ().__call__ (shape , ** kwargs , src_loc_at = src_loc_at + 1 )
959+ if isinstance (shape , ShapeCastable ):
960+ return shape (signal )
961+ return signal
962+
963+
946964# @final
947- class Signal (Value , DUID ):
965+ class Signal (Value , DUID , metaclass = _SignalMeta ):
948966 """A varying integer value.
949967
950968 Parameters
@@ -985,7 +1003,7 @@ class Signal(Value, DUID):
9851003 decoder : function
9861004 """
9871005
988- def __init__ (self , shape = None , * , name = None , reset = 0 , reset_less = False ,
1006+ def __init__ (self , shape = None , * , name = None , reset = None , reset_less = False ,
9891007 attrs = None , decoder = None , src_loc_at = 0 ):
9901008 super ().__init__ (src_loc_at = src_loc_at )
9911009
@@ -1001,21 +1019,50 @@ def __init__(self, shape=None, *, name=None, reset=0, reset_less=False,
10011019 self .width = shape .width
10021020 self .signed = shape .signed
10031021
1004- if isinstance (reset , Enum ):
1005- reset = reset .value
1006- if not isinstance (reset , int ):
1007- raise TypeError ("Reset value has to be an int or an integral Enum" )
1008-
1009- reset_width = bits_for (reset , self .signed )
1010- if reset != 0 and reset_width > self .width :
1011- warnings .warn ("Reset value {!r} requires {} bits to represent, but the signal "
1012- "only has {} bits"
1013- .format (reset , reset_width , self .width ),
1014- SyntaxWarning , stacklevel = 2 + src_loc_at )
1015-
1016- self .reset = reset
1022+ orig_reset = reset
1023+ if isinstance (orig_shape , ShapeCastable ):
1024+ try :
1025+ reset = Const .cast (orig_shape .const (reset ))
1026+ except Exception :
1027+ raise TypeError ("Reset value must be a constant initializer of {!r}"
1028+ .format (orig_shape ))
1029+ if reset .shape () != Shape .cast (orig_shape ):
1030+ raise ValueError ("Constant returned by {!r}.const() must have the shape that "
1031+ "it casts to, {!r}, and not {!r}"
1032+ .format (orig_shape , Shape .cast (orig_shape ),
1033+ reset .shape ()))
1034+ else :
1035+ try :
1036+ reset = Const .cast (reset or 0 )
1037+ except TypeError :
1038+ raise TypeError ("Reset value must be a constant-castable expression, not {!r}"
1039+ .format (orig_reset ))
1040+ if orig_reset not in (None , 0 , - 1 ): # Avoid false positives for all-zeroes and all-ones
1041+ if reset .shape ().signed and not self .signed :
1042+ warnings .warn (
1043+ message = "Reset value {!r} is signed, but the signal shape is {!r}"
1044+ .format (orig_reset , shape ),
1045+ category = SyntaxWarning ,
1046+ stacklevel = 2 )
1047+ elif (reset .shape ().width > self .width or
1048+ reset .shape ().width == self .width and
1049+ self .signed and not reset .shape ().signed ):
1050+ warnings .warn (
1051+ message = "Reset value {!r} will be truncated to the signal shape {!r}"
1052+ .format (orig_reset , shape ),
1053+ category = SyntaxWarning ,
1054+ stacklevel = 2 )
1055+ self .reset = reset .value
10171056 self .reset_less = bool (reset_less )
10181057
1058+ if isinstance (orig_shape , range ) and self .reset == orig_shape .stop :
1059+ warnings .warn (
1060+ message = "Reset value {!r} equals the non-inclusive end of the signal "
1061+ "shape {!r}; this is likely an off-by-one error"
1062+ .format (self .reset , orig_shape ),
1063+ category = SyntaxWarning ,
1064+ stacklevel = 2 )
1065+
10191066 self .attrs = OrderedDict (() if attrs is None else attrs )
10201067
10211068 if decoder is None and isinstance (orig_shape , type ) and issubclass (orig_shape , Enum ):
@@ -1297,15 +1344,13 @@ class ValueCastable:
12971344 from :class:`ValueCastable` is mutable, it is up to the user to ensure that it is not mutated
12981345 in a way that changes its representation after the first call to :meth:`as_value`.
12991346 """
1300- def __new__ (cls , * args , ** kwargs ):
1301- self = super ().__new__ (cls )
1302- if not hasattr (self , "as_value" ):
1347+ def __init_subclass__ (cls , ** kwargs ):
1348+ if not hasattr (cls , "as_value" ):
13031349 raise TypeError (f"Class '{ cls .__name__ } ' deriving from `ValueCastable` must override "
13041350 "the `as_value` method" )
1305- if not hasattr (self .as_value , "_ValueCastable__memoized" ):
1351+ if not hasattr (cls .as_value , "_ValueCastable__memoized" ):
13061352 raise TypeError (f"Class '{ cls .__name__ } ' deriving from `ValueCastable` must decorate "
13071353 "the `as_value` method with the `ValueCastable.lowermethod` decorator" )
1308- return self
13091354
13101355 @staticmethod
13111356 def lowermethod (func ):
0 commit comments