1
1
import operator
2
2
from collections import OrderedDict
3
+ from collections .abc import MutableSequence
3
4
4
5
from .. import tracer
5
6
from ._ast import *
6
7
from ._ir import Elaboratable , Fragment
7
8
from ..utils import ceil_log2
8
- from .._utils import deprecated
9
+ from .._utils import deprecated , final
9
10
10
11
11
- __all__ = ["Memory" , "ReadPort" , "WritePort" , "DummyPort" ]
12
+ __all__ = ["MemoryData" , " Memory" , "ReadPort" , "WritePort" , "DummyPort" ]
12
13
13
14
14
- class MemoryIdentity : pass
15
+ @final
16
+ class FrozenError (Exception ):
17
+ """This exception is raised when ports are added to a :class:`Memory` or its
18
+ :attr:`~Memory.init` attribute is changed after it has been elaborated once.
19
+ """
20
+
21
+
22
+ @final
23
+ class MemoryData :
24
+ @final
25
+ class Init (MutableSequence ):
26
+ """Memory initialization data.
27
+
28
+ This is a special container used only for initial contents of memories. It is similar
29
+ to :class:`list`, but does not support inserting or deleting elements; its length is always
30
+ the same as the depth of the memory it belongs to.
31
+
32
+ If :py:`shape` is a :ref:`custom shape-castable object <lang-shapecustom>`, then:
33
+
34
+ * Each element must be convertible to :py:`shape` via :meth:`.ShapeCastable.const`, and
35
+ * Elements that are not explicitly initialized default to :py:`shape.const(None)`.
36
+
37
+ Otherwise (when :py:`shape` is a :class:`.Shape`):
38
+
39
+ * Each element must be an :class:`int`, and
40
+ * Elements that are not explicitly initialized default to :py:`0`.
41
+ """
42
+ def __init__ (self , elems , * , shape , depth ):
43
+ Shape .cast (shape )
44
+ if not isinstance (depth , int ) or depth < 0 :
45
+ raise TypeError ("Memory depth must be a non-negative integer, not {!r}"
46
+ .format (depth ))
47
+ self ._shape = shape
48
+ self ._depth = depth
49
+ self ._frozen = False
50
+
51
+ if isinstance (shape , ShapeCastable ):
52
+ self ._elems = [None ] * depth
53
+ self ._raw = [Const .cast (Const (None , shape )).value ] * depth
54
+ else :
55
+ self ._elems = [0 ] * depth
56
+ self ._raw = self ._elems # intentionally mutably aliased
57
+ elems = list (elems )
58
+ if len (elems ) > depth :
59
+ raise ValueError (f"Memory initialization value count exceeds memory depth ({ len (elems )} > { depth } )" )
60
+ for index , item in enumerate (elems ):
61
+ try :
62
+ self [index ] = item
63
+ except (TypeError , ValueError ) as e :
64
+ raise type (e )(f"Memory initialization value at address { index :x} : { e } " ) from None
65
+
66
+ @property
67
+ def shape (self ):
68
+ return self ._shape
69
+
70
+ def __getitem__ (self , index ):
71
+ return self ._elems [index ]
72
+
73
+ def __setitem__ (self , index , value ):
74
+ if self ._frozen :
75
+ raise FrozenError ("Cannot set 'init' on a memory that has already been elaborated" )
76
+
77
+ if isinstance (index , slice ):
78
+ indices = range (* index .indices (len (self ._elems )))
79
+ if len (value ) != len (indices ):
80
+ raise ValueError ("Changing length of Memory.init is not allowed" )
81
+ for actual_index , actual_value in zip (indices , value ):
82
+ self [actual_index ] = actual_value
83
+ else :
84
+ if isinstance (self ._shape , ShapeCastable ):
85
+ self ._raw [index ] = Const .cast (Const (value , self ._shape )).value
86
+ else :
87
+ value = operator .index (value )
88
+ # self._raw[index] assigned by the following line
89
+ self ._elems [index ] = value
90
+
91
+ def __delitem__ (self , index ):
92
+ raise TypeError ("Deleting elements from Memory.init is not allowed" )
93
+
94
+ def insert (self , index , value ):
95
+ """:meta private:"""
96
+ raise TypeError ("Inserting elements into Memory.init is not allowed" )
97
+
98
+ def __len__ (self ):
99
+ return self ._depth
100
+
101
+ def __repr__ (self ):
102
+ return f"MemoryData.Init({ self ._elems !r} , shape={ self ._shape !r} , depth={ self ._depth } )"
103
+
104
+
105
+ def __init__ (self , * , shape , depth , init , src_loc_at = 0 ):
106
+ # shape and depth validation is performed in MemoryData.Init()
107
+ self ._shape = shape
108
+ self ._depth = depth
109
+ self ._init = MemoryData .Init (init , shape = shape , depth = depth )
110
+ self .src_loc = tracer .get_src_loc (src_loc_at = src_loc_at )
111
+ self .name = tracer .get_var_name (depth = 2 + src_loc_at , default = "$memory" )
112
+ self ._frozen = False
113
+
114
+ def freeze (self ):
115
+ self ._frozen = True
116
+ self ._init ._frozen = True
117
+
118
+ @property
119
+ def shape (self ):
120
+ return self ._shape
121
+
122
+ @property
123
+ def depth (self ):
124
+ return self ._depth
125
+
126
+ @property
127
+ def init (self ):
128
+ return self ._init
129
+
130
+ @init .setter
131
+ def init (self , init ):
132
+ if self ._frozen :
133
+ raise FrozenError ("Cannot set 'init' on a memory that has already been elaborated" )
134
+ self ._init = MemoryData .Init (init , shape = self ._shape , depth = self ._depth )
135
+
136
+ def __repr__ (self ):
137
+ return f"(memory-data { self .name } )"
138
+
139
+ def __getitem__ (self , index ):
140
+ """Simulation only."""
141
+ return MemorySimRead (self , index )
15
142
16
143
17
144
class MemorySimRead :
18
- def __init__ (self , identity , addr ):
19
- assert isinstance (identity , MemoryIdentity )
20
- self ._identity = identity
145
+ def __init__ (self , memory , addr ):
146
+ assert isinstance (memory , MemoryData )
147
+ self ._memory = memory
21
148
self ._addr = Value .cast (addr )
22
149
23
150
def eq (self , value ):
24
- return MemorySimWrite (self ._identity , self ._addr , value )
151
+ return MemorySimWrite (self ._memory , self ._addr , value )
25
152
26
153
27
154
class MemorySimWrite :
28
- def __init__ (self , identity , addr , data ):
29
- assert isinstance (identity , MemoryIdentity )
30
- self ._identity = identity
155
+ def __init__ (self , memory , addr , data ):
156
+ assert isinstance (memory , MemoryData )
157
+ self ._memory = memory
31
158
self ._addr = Value .cast (addr )
32
159
self ._data = Value .cast (data )
33
160
@@ -66,26 +193,20 @@ def _granularity(self):
66
193
return len (self ._data ) // len (self ._en )
67
194
68
195
69
- def __init__ (self , * , identity , width , depth , init = None , attrs = None , src_loc = None ):
196
+ def __init__ (self , * , data , attrs = None , src_loc = None ):
70
197
super ().__init__ (src_loc = src_loc )
71
- assert isinstance (identity , MemoryIdentity )
72
- self ._identity = identity
73
- self ._width = operator .index (width )
74
- self ._depth = operator .index (depth )
75
- mask = (1 << self ._width ) - 1
76
- self ._init = tuple (item & mask for item in init ) if init is not None else ()
77
- assert len (self ._init ) <= self ._depth
78
- self ._init += (0 ,) * (self ._depth - len (self ._init ))
79
- for x in self ._init :
80
- assert isinstance (x , int )
198
+ assert isinstance (data , MemoryData )
199
+ data .freeze ()
200
+ self ._data = data
81
201
self ._attrs = attrs or {}
82
202
self ._read_ports : "list[MemoryInstance._ReadPort]" = []
83
203
self ._write_ports : "list[MemoryInstance._WritePort]" = []
84
204
85
205
def read_port (self , * , domain , addr , data , en , transparent_for ):
86
206
port = self ._ReadPort (domain = domain , addr = addr , data = data , en = en , transparent_for = transparent_for )
87
- assert len (port ._data ) == self ._width
88
- assert len (port ._addr ) == ceil_log2 (self ._depth )
207
+ shape = Shape .cast (self ._data .shape )
208
+ assert len (port ._data ) == shape .width
209
+ assert len (port ._addr ) == ceil_log2 (self ._data .depth )
89
210
for idx in port ._transparent_for :
90
211
assert isinstance (idx , int )
91
212
assert idx in range (len (self ._write_ports ))
@@ -96,8 +217,9 @@ def read_port(self, *, domain, addr, data, en, transparent_for):
96
217
97
218
def write_port (self , * , domain , addr , data , en ):
98
219
port = self ._WritePort (domain = domain , addr = addr , data = data , en = en )
99
- assert len (port ._data ) == self ._width
100
- assert len (port ._addr ) == ceil_log2 (self ._depth )
220
+ shape = Shape .cast (self ._data .shape )
221
+ assert len (port ._data ) == shape .width
222
+ assert len (port ._addr ) == ceil_log2 (self ._data .depth )
101
223
self ._write_ports .append (port )
102
224
return len (self ._write_ports ) - 1
103
225
@@ -145,28 +267,17 @@ def __init__(self, *, width, depth, init=None, name=None, attrs=None, simulate=T
145
267
self .depth = depth
146
268
self .attrs = OrderedDict (() if attrs is None else attrs )
147
269
148
- self .init = init
149
270
self ._read_ports = []
150
271
self ._write_ports = []
151
- self ._identity = MemoryIdentity ( )
272
+ self ._data = MemoryData ( shape = width , depth = depth , init = init or [] )
152
273
153
274
@property
154
275
def init (self ):
155
- return self ._init
276
+ return self ._data . init
156
277
157
278
@init .setter
158
279
def init (self , new_init ):
159
- self ._init = [] if new_init is None else list (new_init )
160
- if len (self .init ) > self .depth :
161
- raise ValueError ("Memory initialization value count exceed memory depth ({} > {})"
162
- .format (len (self .init ), self .depth ))
163
-
164
- try :
165
- for addr , val in enumerate (self ._init ):
166
- operator .index (val )
167
- except TypeError as e :
168
- raise TypeError ("Memory initialization value at address {:x}: {}"
169
- .format (addr , e )) from None
280
+ self ._data .init = new_init
170
281
171
282
def read_port (self , * , src_loc_at = 0 , ** kwargs ):
172
283
"""Get a read port.
@@ -202,10 +313,10 @@ def write_port(self, *, src_loc_at=0, **kwargs):
202
313
203
314
def __getitem__ (self , index ):
204
315
"""Simulation only."""
205
- return MemorySimRead (self ._identity , index )
316
+ return MemorySimRead (self ._data , index )
206
317
207
318
def elaborate (self , platform ):
208
- f = MemoryInstance (identity = self ._identity , width = self . width , depth = self . depth , init = self . init , attrs = self .attrs , src_loc = self .src_loc )
319
+ f = MemoryInstance (data = self ._data , attrs = self .attrs , src_loc = self .src_loc )
209
320
write_ports = {}
210
321
for port in self ._write_ports :
211
322
port ._MustUse__used = True
0 commit comments