1
1
from abc import ABCMeta , abstractmethod
2
+ import inspect
2
3
import warnings
3
4
import functools
4
5
from collections import OrderedDict
@@ -41,12 +42,16 @@ class ShapeCastable:
41
42
a richer description of the shape than what is supported by the core Amaranth language, yet
42
43
still be transparently used with it.
43
44
"""
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" ):
47
47
raise TypeError (f"Class '{ cls .__name__ } ' deriving from `ShapeCastable` must override "
48
48
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" )
50
55
51
56
52
57
class Shape :
@@ -572,8 +577,6 @@ def _lhs_signals(self):
572
577
def _rhs_signals (self ):
573
578
pass # :nocov:
574
579
575
- __hash__ = None
576
-
577
580
578
581
@final
579
582
class Const (Value ):
@@ -634,6 +637,13 @@ def __init__(self, value, shape=None, *, src_loc_at=0):
634
637
elif isinstance (shape , int ):
635
638
shape = Shape (shape , signed = self .value < 0 )
636
639
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 )
637
647
shape = Shape .cast (shape , src_loc_at = 1 + src_loc_at )
638
648
self .width = shape .width
639
649
self .signed = shape .signed
@@ -943,8 +953,16 @@ def __repr__(self):
943
953
return "(repl {!r} {})" .format (self .value , self .count )
944
954
945
955
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
+
946
964
# @final
947
- class Signal (Value , DUID ):
965
+ class Signal (Value , DUID , metaclass = _SignalMeta ):
948
966
"""A varying integer value.
949
967
950
968
Parameters
@@ -985,7 +1003,7 @@ class Signal(Value, DUID):
985
1003
decoder : function
986
1004
"""
987
1005
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 ,
989
1007
attrs = None , decoder = None , src_loc_at = 0 ):
990
1008
super ().__init__ (src_loc_at = src_loc_at )
991
1009
@@ -1001,21 +1019,50 @@ def __init__(self, shape=None, *, name=None, reset=0, reset_less=False,
1001
1019
self .width = shape .width
1002
1020
self .signed = shape .signed
1003
1021
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
1017
1056
self .reset_less = bool (reset_less )
1018
1057
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
+
1019
1066
self .attrs = OrderedDict (() if attrs is None else attrs )
1020
1067
1021
1068
if decoder is None and isinstance (orig_shape , type ) and issubclass (orig_shape , Enum ):
@@ -1297,15 +1344,13 @@ class ValueCastable:
1297
1344
from :class:`ValueCastable` is mutable, it is up to the user to ensure that it is not mutated
1298
1345
in a way that changes its representation after the first call to :meth:`as_value`.
1299
1346
"""
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" ):
1303
1349
raise TypeError (f"Class '{ cls .__name__ } ' deriving from `ValueCastable` must override "
1304
1350
"the `as_value` method" )
1305
- if not hasattr (self .as_value , "_ValueCastable__memoized" ):
1351
+ if not hasattr (cls .as_value , "_ValueCastable__memoized" ):
1306
1352
raise TypeError (f"Class '{ cls .__name__ } ' deriving from `ValueCastable` must decorate "
1307
1353
"the `as_value` method with the `ValueCastable.lowermethod` decorator" )
1308
- return self
1309
1354
1310
1355
@staticmethod
1311
1356
def lowermethod (func ):
0 commit comments