Skip to content

Commit 9a7232b

Browse files
Jean-François Nguyenjfng
authored andcommitted
memory: provide a naming mechanism for memory map resources and windows
Each memory map now has a namespace for its resources and windows. Names are mandatory for resources, but optional for windows. The full name of a resource is a concatenation of its own name and the name of each named window behind which it is located. Breaking changes: * MemoryMap.add_resource() now takes a mandatory ``name`` parameter. * MemoryMap.resources() now returns a ``resource, name, (start, end)`` tuple. * Freezing a memory map now prevents the further addition of *any* resource or window, instead of only the ones that require an extension of the memory map address bits. Allowing these could prevent us from detecting name conflicts in some cases. Other changes: * The MemoryMap constructor now takes an optional ``name`` parameter, which is then visible as a property. * The constructors of csr.Multiplexer, csr.Decoder, WishboneCSRBridge and wishbone.Decoder now take an optional ``name`` parameter, which is passed to their underlying memory map.
1 parent 9d67a0a commit 9a7232b

File tree

9 files changed

+252
-118
lines changed

9 files changed

+252
-118
lines changed

nmigen_soc/csr/bus.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -212,14 +212,17 @@ class Multiplexer(Elaboratable):
212212
Data width. See :class:`Interface`.
213213
alignment : log2 of int
214214
Register alignment. See :class:`..memory.MemoryMap`.
215+
name : str
216+
Window name. Optional.
215217
216218
Attributes
217219
----------
218220
bus : :class:`Interface`
219221
CSR bus providing access to registers.
220222
"""
221-
def __init__(self, *, addr_width, data_width, alignment=0):
222-
self._map = MemoryMap(addr_width=addr_width, data_width=data_width, alignment=alignment)
223+
def __init__(self, *, addr_width, data_width, alignment=0, name=None):
224+
self._map = MemoryMap(addr_width=addr_width, data_width=data_width, alignment=alignment,
225+
name=name)
223226
self._bus = None
224227

225228
@property
@@ -250,7 +253,7 @@ def add(self, element, *, addr=None, alignment=None, extend=False):
250253

251254
size = (element.width + self._map.data_width - 1) // self._map.data_width
252255
return self._map.add_resource(element, size=size, addr=addr, alignment=alignment,
253-
extend=extend)
256+
extend=extend, name=element.name)
254257

255258
def elaborate(self, platform):
256259
m = Module()
@@ -262,7 +265,7 @@ def elaborate(self, platform):
262265
# 2-AND or 2-OR gates.
263266
r_data_fanin = 0
264267

265-
for elem, (elem_start, elem_end) in self._map.resources():
268+
for elem, _, (elem_start, elem_end) in self._map.resources():
266269
shadow = Signal(elem.width, name="{}__shadow".format(elem.name))
267270
if elem.access.readable():
268271
shadow_en = Signal(elem_end - elem_start, name="{}__shadow_en".format(elem.name))
@@ -327,14 +330,17 @@ class Decoder(Elaboratable):
327330
Data width. See :class:`Interface`.
328331
alignment : log2 of int
329332
Window alignment. See :class:`..memory.MemoryMap`.
333+
name : str
334+
Window name. Optional.
330335
331336
Attributes
332337
----------
333338
bus : :class:`Interface`
334339
CSR bus providing access to subordinate buses.
335340
"""
336-
def __init__(self, *, addr_width, data_width, alignment=0):
337-
self._map = MemoryMap(addr_width=addr_width, data_width=data_width, alignment=alignment)
341+
def __init__(self, *, addr_width, data_width, alignment=0, name=None):
342+
self._map = MemoryMap(addr_width=addr_width, data_width=data_width, alignment=alignment,
343+
name=name)
338344
self._bus = None
339345
self._subs = dict()
340346

nmigen_soc/csr/wishbone.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,15 @@ class WishboneCSRBridge(Elaboratable):
2727
CSR bus driven by the bridge.
2828
data_width : int or None
2929
Wishbone bus data width. If not specified, defaults to ``csr_bus.data_width``.
30+
name : str
31+
Window name. Optional.
3032
3133
Attributes
3234
----------
3335
wb_bus : :class:`..wishbone.Interface`
3436
Wishbone bus provided by the bridge.
3537
"""
36-
def __init__(self, csr_bus, *, data_width=None):
38+
def __init__(self, csr_bus, *, data_width=None, name=None):
3739
if not isinstance(csr_bus, CSRInterface):
3840
raise ValueError("CSR bus must be an instance of CSRInterface, not {!r}"
3941
.format(csr_bus))
@@ -50,12 +52,12 @@ def __init__(self, csr_bus, *, data_width=None):
5052
granularity=csr_bus.data_width,
5153
name="wb")
5254

53-
self.wb_bus.memory_map = MemoryMap(addr_width=csr_bus.addr_width,
54-
data_width=csr_bus.data_width)
55-
55+
wb_map = MemoryMap(addr_width=csr_bus.addr_width, data_width=csr_bus.data_width,
56+
name=name)
5657
# Since granularity of the Wishbone interface matches the data width of the CSR bus,
5758
# no width conversion is performed, even if the Wishbone data width is greater.
58-
self.wb_bus.memory_map.add_window(self.csr_bus.memory_map)
59+
wb_map.add_window(self.csr_bus.memory_map)
60+
self.wb_bus.memory_map = wb_map
5961

6062
def elaborate(self, platform):
6163
csr_bus = self.csr_bus

nmigen_soc/memory.py

Lines changed: 76 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)