55from ..hdl import MemoryIdentity , MemoryInstance , Shape , ShapeCastable , Const
66from ..hdl ._mem import MemorySimRead
77from ..utils import ceil_log2
8+ from .._utils import final
89from .. import tracer
910from . import wiring , data
1011
1112
1213__all__ = ["Memory" , "ReadPort" , "WritePort" ]
1314
1415
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+
1523class Memory (wiring .Component ):
1624 """Addressable array of rows.
1725
1826 This :ref:`component <wiring>` is used to construct a memory array by first specifying its
1927 dimensions and initial contents using the :py:`shape`, :py:`depth`, and :py:`init` parameters,
2028 and then adding memory ports using the :meth:`read_port` and :meth:`write_port` methods.
2129 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.
2332
2433 The :py:`init` parameter and assignment to the :py:`init` attribute have the same effect, with
2534 :class:`Memory.Init` converting elements of the iterable to match :py:`shape` and using
@@ -70,6 +79,7 @@ def __init__(self, elems, *, shape, depth):
7079 .format (depth ))
7180 self ._shape = shape
7281 self ._depth = depth
82+ self ._frozen = False
7383
7484 if isinstance (shape , ShapeCastable ):
7585 self ._elems = [None ] * depth
@@ -92,6 +102,9 @@ def __getitem__(self, index):
92102 return self ._elems [index ]
93103
94104 def __setitem__ (self , index , value ):
105+ if self ._frozen :
106+ raise FrozenError ("Cannot set 'init' on a memory that has already been elaborated" )
107+
95108 if isinstance (index , slice ):
96109 indices = range (* index .indices (len (self ._elems )))
97110 if len (value ) != len (indices ):
@@ -131,6 +144,7 @@ def __init__(self, *, shape, depth, init, attrs=None, src_loc_at=0):
131144 self ._identity = MemoryIdentity ()
132145 self ._read_ports : "list[ReadPort]" = []
133146 self ._write_ports : "list[WritePort]" = []
147+ self ._frozen = False
134148
135149 super ().__init__ (wiring .Signature ({}))
136150
@@ -148,6 +162,8 @@ def init(self):
148162
149163 @init .setter
150164 def init (self , init ):
165+ if self ._frozen :
166+ raise FrozenError ("Cannot set 'init' on a memory that has already been elaborated" )
151167 self ._init = Memory .Init (init , shape = self ._shape , depth = self ._depth )
152168
153169 @property
@@ -179,6 +195,8 @@ def read_port(self, *, domain="sync", transparent_for=(), src_loc_at=0):
179195 -------
180196 :class:`ReadPort`
181197 """
198+ if self ._frozen :
199+ raise FrozenError ("Cannot add a memory port to a memory that has already been elaborated" )
182200 signature = ReadPort .Signature (shape = self .shape , addr_width = ceil_log2 (self .depth ))
183201 return ReadPort (signature , memory = self , domain = domain , transparent_for = transparent_for ,
184202 src_loc_at = 1 + src_loc_at )
@@ -202,6 +220,8 @@ def write_port(self, *, domain="sync", granularity=None, src_loc_at=0):
202220 -------
203221 :class:`WritePort`
204222 """
223+ if self ._frozen :
224+ raise FrozenError ("Cannot add a memory port to a memory that has already been elaborated" )
205225 signature = WritePort .Signature (
206226 shape = self .shape , addr_width = ceil_log2 (self .depth ), granularity = granularity )
207227 return WritePort (signature , memory = self , domain = domain ,
@@ -224,6 +244,8 @@ def write_ports(self):
224244 return tuple (self ._write_ports )
225245
226246 def elaborate (self , platform ):
247+ self ._frozen = True
248+ self ._init ._frozen = True
227249 if hasattr (platform , "get_memory" ):
228250 return platform .get_memory (self )
229251
0 commit comments