5
5
from ..hdl import MemoryIdentity , MemoryInstance , Shape , ShapeCastable , Const
6
6
from ..hdl ._mem import MemorySimRead
7
7
from ..utils import ceil_log2
8
+ from .._utils import final
8
9
from .. import tracer
9
10
from . import wiring , data
10
11
11
12
12
13
__all__ = ["Memory" , "ReadPort" , "WritePort" ]
13
14
14
15
16
+ @final
17
+ class FrozenError (Exception ):
18
+ """This exception is raised when ports are added to a :class:`Memory` or its
19
+ :attr:`~Memory.init` attribute is changed after it has been elaborated once.
20
+ """
21
+
22
+
15
23
class Memory (wiring .Component ):
16
24
"""Addressable array of rows.
17
25
18
26
This :ref:`component <wiring>` is used to construct a memory array by first specifying its
19
27
dimensions and initial contents using the :py:`shape`, :py:`depth`, and :py:`init` parameters,
20
28
and then adding memory ports using the :meth:`read_port` and :meth:`write_port` methods.
21
29
Because it is mutable, it should be created and used locally within
22
- the :ref:`elaborate <lang-elaboration>` method.
30
+ the :ref:`elaborate <lang-elaboration>` method. It is an error to add ports to or change
31
+ initial contents of a memory after it has been elaborated.
23
32
24
33
The :py:`init` parameter and assignment to the :py:`init` attribute have the same effect, with
25
34
:class:`Memory.Init` converting elements of the iterable to match :py:`shape` and using
@@ -70,6 +79,7 @@ def __init__(self, elems, *, shape, depth):
70
79
.format (depth ))
71
80
self ._shape = shape
72
81
self ._depth = depth
82
+ self ._frozen = False
73
83
74
84
if isinstance (shape , ShapeCastable ):
75
85
self ._elems = [None ] * depth
@@ -92,6 +102,9 @@ def __getitem__(self, index):
92
102
return self ._elems [index ]
93
103
94
104
def __setitem__ (self , index , value ):
105
+ if self ._frozen :
106
+ raise FrozenError ("Cannot set 'init' on a memory that has already been elaborated" )
107
+
95
108
if isinstance (index , slice ):
96
109
indices = range (* index .indices (len (self ._elems )))
97
110
if len (value ) != len (indices ):
@@ -131,6 +144,7 @@ def __init__(self, *, shape, depth, init, attrs=None, src_loc_at=0):
131
144
self ._identity = MemoryIdentity ()
132
145
self ._read_ports : "list[ReadPort]" = []
133
146
self ._write_ports : "list[WritePort]" = []
147
+ self ._frozen = False
134
148
135
149
super ().__init__ (wiring .Signature ({}))
136
150
@@ -148,6 +162,8 @@ def init(self):
148
162
149
163
@init .setter
150
164
def init (self , init ):
165
+ if self ._frozen :
166
+ raise FrozenError ("Cannot set 'init' on a memory that has already been elaborated" )
151
167
self ._init = Memory .Init (init , shape = self ._shape , depth = self ._depth )
152
168
153
169
@property
@@ -179,6 +195,8 @@ def read_port(self, *, domain="sync", transparent_for=(), src_loc_at=0):
179
195
-------
180
196
:class:`ReadPort`
181
197
"""
198
+ if self ._frozen :
199
+ raise FrozenError ("Cannot add a memory port to a memory that has already been elaborated" )
182
200
signature = ReadPort .Signature (shape = self .shape , addr_width = ceil_log2 (self .depth ))
183
201
return ReadPort (signature , memory = self , domain = domain , transparent_for = transparent_for ,
184
202
src_loc_at = 1 + src_loc_at )
@@ -202,6 +220,8 @@ def write_port(self, *, domain="sync", granularity=None, src_loc_at=0):
202
220
-------
203
221
:class:`WritePort`
204
222
"""
223
+ if self ._frozen :
224
+ raise FrozenError ("Cannot add a memory port to a memory that has already been elaborated" )
205
225
signature = WritePort .Signature (
206
226
shape = self .shape , addr_width = ceil_log2 (self .depth ), granularity = granularity )
207
227
return WritePort (signature , memory = self , domain = domain ,
@@ -224,6 +244,8 @@ def write_ports(self):
224
244
return tuple (self ._write_ports )
225
245
226
246
def elaborate (self , platform ):
247
+ self ._frozen = True
248
+ self ._init ._frozen = True
227
249
if hasattr (platform , "get_memory" ):
228
250
return platform .get_memory (self )
229
251
0 commit comments