11import bisect
22
3+ from nmigen .utils import bits_for
4+
35
46__all__ = ["MemoryMap" ]
57
@@ -84,15 +86,50 @@ def __init__(self, *, addr_width, data_width, alignment=0):
8486 raise ValueError ("Alignment must be a non-negative integer, not {!r}"
8587 .format (alignment ))
8688
87- self .addr_width = addr_width
88- self .data_width = data_width
89- self .alignment = alignment
89+ self ._addr_width = addr_width
90+ self ._data_width = data_width
91+ self ._alignment = alignment
92+
93+ self ._ranges = _RangeMap ()
94+ self ._resources = dict ()
95+ self ._windows = dict ()
96+
97+ self ._next_addr = 0
98+ self ._frozen = False
99+
100+ @property
101+ def addr_width (self ):
102+ return self ._addr_width
90103
91- self ._ranges = _RangeMap ()
92- self ._resources = dict ()
93- self ._windows = dict ()
104+ @addr_width .setter
105+ def addr_width (self , addr_width ):
106+ if self ._frozen :
107+ raise ValueError ("Memory map has been frozen. Address width cannot be extended "
108+ "further" )
109+ if not isinstance (addr_width , int ) or addr_width <= 0 :
110+ raise ValueError ("Address width must be a positive integer, not {!r}"
111+ .format (addr_width ))
112+ if addr_width < self ._addr_width :
113+ raise ValueError ("Address width {!r} must not be less than its previous value {!r}, "
114+ "because resources that were previously added may not fit anymore"
115+ .format (addr_width , self ._addr_width ))
116+ self ._addr_width = addr_width
94117
95- self ._next_addr = 0
118+ @property
119+ def data_width (self ):
120+ return self ._data_width
121+
122+ @property
123+ def alignment (self ):
124+ return self ._alignment
125+
126+ def freeze (self ):
127+ """Freeze the memory map.
128+
129+ Once frozen, the memory map cannot be extended anymore. Resources and windows may
130+ still be added, as long as they fit within the address space bounds.
131+ """
132+ self ._frozen = True
96133
97134 @staticmethod
98135 def _align_up (value , alignment ):
@@ -119,7 +156,7 @@ def align_to(self, alignment):
119156 self ._next_addr = self ._align_up (self ._next_addr , max (alignment , self .alignment ))
120157 return self ._next_addr
121158
122- def _compute_addr_range (self , addr , size , step = 1 , * , alignment ):
159+ def _compute_addr_range (self , addr , size , step = 1 , * , alignment , extend ):
123160 if addr is not None :
124161 if not isinstance (addr , int ) or addr < 0 :
125162 raise ValueError ("Address must be a non-negative integer, not {!r}"
@@ -137,9 +174,12 @@ def _compute_addr_range(self, addr, size, step=1, *, alignment):
137174 size = self ._align_up (size , alignment )
138175
139176 if addr > (1 << self .addr_width ) or addr + size > (1 << self .addr_width ):
140- raise ValueError ("Address range {:#x}..{:#x} out of bounds for memory map spanning "
141- "range {:#x}..{:#x} ({} address bits)"
142- .format (addr , addr + size , 0 , 1 << self .addr_width , self .addr_width ))
177+ if extend :
178+ self .addr_width = bits_for (addr + size )
179+ else :
180+ raise ValueError ("Address range {:#x}..{:#x} out of bounds for memory map spanning "
181+ "range {:#x}..{:#x} ({} address bits)"
182+ .format (addr , addr + size , 0 , 1 << self .addr_width , self .addr_width ))
143183
144184 addr_range = range (addr , addr + size , step )
145185 overlaps = self ._ranges .overlaps (addr_range )
@@ -159,7 +199,7 @@ def _compute_addr_range(self, addr, size, step=1, *, alignment):
159199
160200 return addr_range
161201
162- def add_resource (self , resource , * , size , addr = None , alignment = None ):
202+ def add_resource (self , resource , * , size , addr = None , alignment = None , extend = False ):
163203 """Add a resource.
164204
165205 A resource is any device on the bus that is a destination for bus transactions, e.g.
@@ -178,6 +218,9 @@ def add_resource(self, resource, *, size, addr=None, alignment=None):
178218 ``2 ** max(alignment, self.alignment)``.
179219 alignment : int or None
180220 Alignment of the resource. If not specified, the memory map alignment is used.
221+ extend: bool
222+ Allow memory map extension. If ``True``, the upper bound of the address space is
223+ raised as needed, in order to fit a resource that would otherwise be out of bounds.
181224
182225 Return value
183226 ------------
@@ -201,7 +244,7 @@ def add_resource(self, resource, *, size, addr=None, alignment=None):
201244 else :
202245 alignment = self .alignment
203246
204- addr_range = self ._compute_addr_range (addr , size , alignment = alignment )
247+ addr_range = self ._compute_addr_range (addr , size , alignment = alignment , extend = extend )
205248 self ._ranges .insert (addr_range , resource )
206249 self ._resources [resource ] = addr_range
207250 self ._next_addr = addr_range .stop
@@ -219,7 +262,7 @@ def resources(self):
219262 for resource , resource_range in self ._resources .items ():
220263 yield resource , (resource_range .start , resource_range .stop )
221264
222- def add_window (self , window , * , addr = None , sparse = None ):
265+ def add_window (self , window , * , addr = None , sparse = None , extend = False ):
223266 """Add a window.
224267
225268 A window is a device on a bus that provides access to a different bus, i.e. a bus bridge.
@@ -248,6 +291,9 @@ def add_window(self, window, *, addr=None, sparse=None):
248291 sparse : bool or None
249292 Address translation type. Ignored if the datapath widths of both memory maps are
250293 equal; must be specified otherwise.
294+ extend : bool
295+ Allow memory map extension. If ``True``, the upper bound of the address space is
296+ raised as needed, in order to fit a window that would otherwise be out of bounds.
251297
252298 Return value
253299 ------------
@@ -288,6 +334,8 @@ def add_window(self, window, *, addr=None, sparse=None):
288334 "data width {}"
289335 .format (self .data_width , window .data_width ))
290336
337+ window .freeze ()
338+
291339 if not sparse :
292340 ratio = self .data_width // window .data_width
293341 else :
@@ -300,7 +348,8 @@ def add_window(self, window, *, addr=None, sparse=None):
300348 # a window can still be aligned using align_to().
301349 alignment = max (self .alignment , window .addr_width // ratio )
302350
303- addr_range = self ._compute_addr_range (addr , size , ratio , alignment = alignment )
351+ addr_range = self ._compute_addr_range (addr , size , ratio , alignment = alignment ,
352+ extend = extend )
304353 self ._ranges .insert (addr_range , window )
305354 self ._windows [window ] = addr_range
306355 self ._next_addr = addr_range .stop
0 commit comments