@@ -74,8 +74,10 @@ class MemoryMap:
7474 Range alignment. Each added resource and window will be placed at an address that is
7575 a multiple of ``2 ** alignment``, and its size will be rounded up to be a multiple of
7676 ``2 ** alignment``.
77+ name : str
78+ Name of the address range. Optional.
7779 """
78- def __init__ (self , * , addr_width , data_width , alignment = 0 ):
80+ def __init__ (self , * , addr_width , data_width , alignment = 0 , name = None ):
7981 if not isinstance (addr_width , int ) or addr_width <= 0 :
8082 raise ValueError ("Address width must be a positive integer, not {!r}"
8183 .format (addr_width ))
@@ -85,14 +87,18 @@ def __init__(self, *, addr_width, data_width, alignment=0):
8587 if not isinstance (alignment , int ) or alignment < 0 :
8688 raise ValueError ("Alignment must be a non-negative integer, not {!r}"
8789 .format (alignment ))
90+ if name is not None and not (isinstance (name , str ) and name ):
91+ raise ValueError ("Name must be a non-empty string, not {!r}" .format (name ))
8892
8993 self ._addr_width = addr_width
9094 self ._data_width = data_width
9195 self ._alignment = alignment
96+ self ._name = name
9297
9398 self ._ranges = _RangeMap ()
9499 self ._resources = dict ()
95100 self ._windows = dict ()
101+ self ._namespace = dict ()
96102
97103 self ._next_addr = 0
98104 self ._frozen = False
@@ -123,11 +129,15 @@ def data_width(self):
123129 def alignment (self ):
124130 return self ._alignment
125131
132+ @property
133+ def name (self ):
134+ return self ._name
135+
126136 def freeze (self ):
127137 """Freeze the memory map.
128138
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 .
139+ Once the memory map is frozen, its visible state becomes immutable . Resources and windows
140+ cannot be added anymore, and its address width cannot be extended further .
131141 """
132142 self ._frozen = True
133143
@@ -187,7 +197,7 @@ def _compute_addr_range(self, addr, size, step=1, *, alignment, extend):
187197 overlap_descrs = []
188198 for overlap in overlaps :
189199 if id (overlap ) in self ._resources :
190- _ , resource_range = self ._resources [id (overlap )]
200+ _ , _ , resource_range = self ._resources [id (overlap )]
191201 overlap_descrs .append ("resource {!r} at {:#x}..{:#x}"
192202 .format (overlap , resource_range .start , resource_range .stop ))
193203 if id (overlap ) in self ._windows :
@@ -199,7 +209,7 @@ def _compute_addr_range(self, addr, size, step=1, *, alignment, extend):
199209
200210 return addr_range
201211
202- def add_resource (self , resource , * , size , addr = None , alignment = None , extend = False ):
212+ def add_resource (self , resource , * , name , size , addr = None , alignment = None , extend = False ):
203213 """Add a resource.
204214
205215 A resource is any device on the bus that is a destination for bus transactions, e.g.
@@ -209,6 +219,9 @@ def add_resource(self, resource, *, size, addr=None, alignment=None, extend=Fals
209219 ---------
210220 resource : object
211221 Arbitrary object representing a resource.
222+ name : str
223+ Name of the resource. It must not collide with the name of other resources or windows
224+ present in this memory map.
212225 addr : int or None
213226 Address of the resource. If ``None``, the implicit next address will be used.
214227 Otherwise, the exact specified address (which must be a multiple of
@@ -228,14 +241,28 @@ def add_resource(self, resource, *, size, addr=None, alignment=None, extend=Fals
228241
229242 Exceptions
230243 ----------
231- Raises :exn:`ValueError` if the requested address and size, after alignment, would overlap
232- with any resources or windows that have already been added, or would be out of bounds.
244+ Raises :exn:`ValueError` if one of the following occurs:
245+
246+ - this memory map is frozen;
247+ - the requested address and size, after alignment, would overlap with any resources or
248+ windows that have already been added, or would be out of bounds;
249+ - the resource has already been added to this memory map;
250+ - the name of the resource is already present in the namespace of this memory map;
233251 """
252+ if self ._frozen :
253+ raise ValueError ("Memory map has been frozen. Cannot add resource {!r}"
254+ .format (resource ))
255+
234256 if id (resource ) in self ._resources :
235- _ , addr_range = self ._resources [id (resource )]
257+ _ , _ , addr_range = self ._resources [id (resource )]
236258 raise ValueError ("Resource {!r} is already added at address range {:#x}..{:#x}"
237259 .format (resource , addr_range .start , addr_range .stop ))
238260
261+ if not isinstance (name , str ) or not name :
262+ raise TypeError ("Name must be a non-empty string, not {!r}" .format (name ))
263+ if name in self ._namespace :
264+ raise ValueError ("Name {} is already used by {!r}" .format (name , self ._namespace [name ]))
265+
239266 if alignment is not None :
240267 if not isinstance (alignment , int ) or alignment < 0 :
241268 raise ValueError ("Alignment must be a non-negative integer, not {!r}"
@@ -246,7 +273,8 @@ def add_resource(self, resource, *, size, addr=None, alignment=None, extend=Fals
246273
247274 addr_range = self ._compute_addr_range (addr , size , alignment = alignment , extend = extend )
248275 self ._ranges .insert (addr_range , resource )
249- self ._resources [id (resource )] = resource , addr_range
276+ self ._resources [id (resource )] = resource , name , addr_range
277+ self ._namespace [name ] = resource
250278 self ._next_addr = addr_range .stop
251279 return addr_range .start , addr_range .stop
252280
@@ -257,10 +285,11 @@ def resources(self):
257285
258286 Yield values
259287 ------------
260- A tuple ``resource, (start, end)`` describing the address range assigned to the resource.
288+ A tuple ``resource, name, (start, end)`` describing the address range assigned to the
289+ resource.
261290 """
262- for resource , resource_range in self ._resources .values ():
263- yield resource , (resource_range .start , resource_range .stop )
291+ for resource , resource_name , resource_range in self ._resources .values ():
292+ yield resource , resource_name , (resource_range .start , resource_range .stop )
264293
265294 def add_window (self , window , * , addr = None , sparse = None , extend = False ):
266295 """Add a window.
@@ -283,7 +312,8 @@ def add_window(self, window, *, addr=None, sparse=None, extend=False):
283312 Arguments
284313 ---------
285314 window : :class:`MemoryMap`
286- A memory map describing the layout of the window.
315+ A memory map describing the layout of the window. It is frozen as a side-effect of
316+ being added to this memory map.
287317 addr : int or None
288318 Address of the window. If ``None``, the implicit next address will be used after
289319 aligning it to ``2 ** window.addr_width``. Otherwise, the exact specified address
@@ -304,15 +334,26 @@ def add_window(self, window, *, addr=None, sparse=None, extend=False):
304334
305335 Exceptions
306336 ----------
307- Raises :exn:`ValueError` if the requested address and size, after alignment, would overlap
308- with any resources or windows that have already been added, or would be out of bounds;
309- if the added memory map has wider datapath than this memory map; if dense address
310- translation is used and the datapath width of this memory map is not an integer multiple
311- of the datapath width of the added memory map.
337+ Raises :exn:`ValueError` if one of the following occurs:
338+
339+ - this memory map is frozen;
340+ - the requested address and size, after alignment, would overlap with any resources or
341+ windows that have already been added, or would be out of bounds;
342+ - the added memory map has a wider datapath than this memory map;
343+ - dense address translation is used and the datapath width of this memory map is not an
344+ integer multiple of the datapath of the added memory map;
345+ - the name of the added memory map is already present in the namespace of this memory map;
346+ - the added memory map has no name, and the name of one of its subordinate resources or
347+ windows is already present in the namespace of this memory map;
312348 """
313349 if not isinstance (window , MemoryMap ):
314350 raise TypeError ("Window must be a MemoryMap, not {!r}"
315351 .format (window ))
352+
353+ if self ._frozen :
354+ raise ValueError ("Memory map has been frozen. Cannot add window {!r}"
355+ .format (window ))
356+
316357 if id (window ) in self ._windows :
317358 _ , addr_range = self ._windows [id (window )]
318359 raise ValueError ("Window {!r} is already added at address range {:#x}..{:#x}"
@@ -334,7 +375,17 @@ def add_window(self, window, *, addr=None, sparse=None, extend=False):
334375 "data width {}"
335376 .format (self .data_width , window .data_width ))
336377
337- window .freeze ()
378+ if window .name is None :
379+ name_conflicts = sorted (self ._namespace .keys () & window ._namespace .keys ())
380+ if name_conflicts :
381+ name_conflict_descrs = ["{} is used by {!r}" .format (name , self ._namespace [name ])
382+ for name in name_conflicts ]
383+ raise ValueError ("The following names are already used: {}"
384+ .format ("; " .join (name_conflict_descrs )))
385+ else :
386+ if window .name in self ._namespace :
387+ raise ValueError ("Name {} is already used by {!r}"
388+ .format (window .name , self ._namespace [window .name ]))
338389
339390 if not sparse :
340391 ratio = self .data_width // window .data_width
@@ -350,8 +401,13 @@ def add_window(self, window, *, addr=None, sparse=None, extend=False):
350401
351402 addr_range = self ._compute_addr_range (addr , size , ratio , alignment = alignment ,
352403 extend = extend )
404+ window .freeze ()
353405 self ._ranges .insert (addr_range , window )
354406 self ._windows [id (window )] = window , addr_range
407+ if window .name is None :
408+ self ._namespace .update (window ._namespace )
409+ else :
410+ self ._namespace [window .name ] = window
355411 self ._next_addr = addr_range .stop
356412 return addr_range .start , addr_range .stop , addr_range .step
357413
@@ -449,7 +505,7 @@ def find_resource(self, resource):
449505 Raises :exn:`KeyError` if the resource is not found.
450506 """
451507 if id (resource ) in self ._resources :
452- _ , resource_range = self ._resources [id (resource )]
508+ _ , _ , resource_range = self ._resources [id (resource )]
453509 return resource_range .start , resource_range .stop , self .data_width
454510
455511 for window , window_range in self ._windows .values ():
0 commit comments