Skip to content

Commit f8f8982

Browse files
author
Jean-François Nguyen
authored
Add support for lazy Decoder/Multiplexer interface creation. (#7)
Before this commit, the bus interface of a Decoder or a Multiplexer would be created during __init__. A bus interface would always be tied to an underlying memory map. After this commit, the following changes are introduced: * The bus interface of a Decoder (or a Multiplexer) is lazily created upon request. This allows the record fields of the bus interface to be up-to-date with any address space extension that may have occured. * The memory_map attribute of a bus interface is no longer assigned during __init__. It is instead assigned externally at a later time. Decoupling the memory map from the bus interface allows a Decoder (or a Multiplexer) to first create the memory map, extend it, and then use it to create the bus interface, once requested. * Decoder.add(extend=True) (or Multiplexer.add) can be used to add a window (or resource) that would otherwise not fit inside its underlying memory map.
1 parent 987aeb0 commit f8f8982

File tree

5 files changed

+269
-62
lines changed

5 files changed

+269
-62
lines changed

nmigen_soc/csr/bus.py

+57-18
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,6 @@ class Interface(Record):
104104
Address width. At most ``(2 ** addr_width) * data_width`` register bits will be available.
105105
data_width : int
106106
Data width. Registers are accessed in ``data_width`` sized chunks.
107-
alignment : int
108-
Register and window alignment. See :class:`MemoryMap`.
109107
name : str
110108
Name of the underlying record.
111109
@@ -132,7 +130,7 @@ class Interface(Record):
132130
nothing.
133131
"""
134132

135-
def __init__(self, *, addr_width, data_width, alignment=0, name=None):
133+
def __init__(self, *, addr_width, data_width, name=None):
136134
if not isinstance(addr_width, int) or addr_width <= 0:
137135
raise ValueError("Address width must be a positive integer, not {!r}"
138136
.format(addr_width))
@@ -141,8 +139,7 @@ def __init__(self, *, addr_width, data_width, alignment=0, name=None):
141139
.format(data_width))
142140
self.addr_width = addr_width
143141
self.data_width = data_width
144-
self.memory_map = MemoryMap(addr_width=addr_width, data_width=data_width,
145-
alignment=alignment)
142+
self._map = None
146143

147144
super().__init__([
148145
("addr", addr_width),
@@ -152,6 +149,29 @@ def __init__(self, *, addr_width, data_width, alignment=0, name=None):
152149
("w_stb", 1),
153150
], name=name, src_loc_at=1)
154151

152+
@property
153+
def memory_map(self):
154+
if self._map is None:
155+
raise NotImplementedError("Bus interface {!r} does not have a memory map"
156+
.format(self))
157+
return self._map
158+
159+
@memory_map.setter
160+
def memory_map(self, memory_map):
161+
if not isinstance(memory_map, MemoryMap):
162+
raise TypeError("Memory map must be an instance of MemoryMap, not {!r}"
163+
.format(memory_map))
164+
if memory_map.addr_width != self.addr_width:
165+
raise ValueError("Memory map has address width {}, which is not the same as "
166+
"bus interface address width {}"
167+
.format(memory_map.addr_width, self.addr_width))
168+
if memory_map.data_width != self.data_width:
169+
raise ValueError("Memory map has data width {}, which is not the same as "
170+
"bus interface data width {}"
171+
.format(memory_map.data_width, self.data_width))
172+
memory_map.freeze()
173+
self._map = memory_map
174+
155175

156176
class Multiplexer(Elaboratable):
157177
"""CSR register multiplexer.
@@ -201,9 +221,18 @@ class Multiplexer(Elaboratable):
201221
CSR bus providing access to registers.
202222
"""
203223
def __init__(self, *, addr_width, data_width, alignment=0):
204-
self.bus = Interface(addr_width=addr_width, data_width=data_width, alignment=alignment,
205-
name="csr")
206-
self._map = self.bus.memory_map
224+
self._map = MemoryMap(addr_width=addr_width, data_width=data_width, alignment=alignment)
225+
self._bus = None
226+
227+
@property
228+
def bus(self):
229+
if self._bus is None:
230+
self._map.freeze()
231+
self._bus = Interface(addr_width=self._map.addr_width,
232+
data_width=self._map.data_width,
233+
name="csr")
234+
self._bus.memory_map = self._map
235+
return self._bus
207236

208237
def align_to(self, alignment):
209238
"""Align the implicit address of the next register.
@@ -212,7 +241,7 @@ def align_to(self, alignment):
212241
"""
213242
return self._map.align_to(alignment)
214243

215-
def add(self, element, *, addr=None, alignment=None):
244+
def add(self, element, *, addr=None, alignment=None, extend=False):
216245
"""Add a register.
217246
218247
See :meth:`MemoryMap.add_resource` for details.
@@ -221,8 +250,9 @@ def add(self, element, *, addr=None, alignment=None):
221250
raise TypeError("Element must be an instance of csr.Element, not {!r}"
222251
.format(element))
223252

224-
size = (element.width + self.bus.data_width - 1) // self.bus.data_width
225-
return self._map.add_resource(element, size=size, addr=addr, alignment=alignment)
253+
size = (element.width + self._map.data_width - 1) // self._map.data_width
254+
return self._map.add_resource(element, size=size, addr=addr, alignment=alignment,
255+
extend=extend)
226256

227257
def elaborate(self, platform):
228258
m = Module()
@@ -306,32 +336,41 @@ class Decoder(Elaboratable):
306336
CSR bus providing access to subordinate buses.
307337
"""
308338
def __init__(self, *, addr_width, data_width, alignment=0):
309-
self.bus = Interface(addr_width=addr_width, data_width=data_width, alignment=alignment,
310-
name="csr")
311-
self._map = self.bus.memory_map
339+
self._map = MemoryMap(addr_width=addr_width, data_width=data_width, alignment=alignment)
340+
self._bus = None
312341
self._subs = dict()
313342

343+
@property
344+
def bus(self):
345+
if self._bus is None:
346+
self._map.freeze()
347+
self._bus = Interface(addr_width=self._map.addr_width,
348+
data_width=self._map.data_width,
349+
name="csr")
350+
self._bus.memory_map = self._map
351+
return self._bus
352+
314353
def align_to(self, alignment):
315354
"""Align the implicit address of the next window.
316355
317356
See :meth:`MemoryMap.align_to` for details.
318357
"""
319358
return self._map.align_to(alignment)
320359

321-
def add(self, sub_bus, *, addr=None):
360+
def add(self, sub_bus, *, addr=None, extend=False):
322361
"""Add a window to a subordinate bus.
323362
324363
See :meth:`MemoryMap.add_resource` for details.
325364
"""
326365
if not isinstance(sub_bus, Interface):
327366
raise TypeError("Subordinate bus must be an instance of csr.Interface, not {!r}"
328367
.format(sub_bus))
329-
if sub_bus.data_width != self.bus.data_width:
368+
if sub_bus.data_width != self._map.data_width:
330369
raise ValueError("Subordinate bus has data width {}, which is not the same as "
331370
"decoder data width {}"
332-
.format(sub_bus.data_width, self.bus.data_width))
371+
.format(sub_bus.data_width, self._map.data_width))
333372
self._subs[sub_bus.memory_map] = sub_bus
334-
return self._map.add_window(sub_bus.memory_map, addr=addr)
373+
return self._map.add_window(sub_bus.memory_map, addr=addr, extend=extend)
335374

336375
def elaborate(self, platform):
337376
m = Module()

nmigen_soc/csr/wishbone.py

+4
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
from . import Interface as CSRInterface
55
from ..wishbone import Interface as WishboneInterface
6+
from ..memory import MemoryMap
67

78

89
__all__ = ["WishboneCSRBridge"]
@@ -49,6 +50,9 @@ def __init__(self, csr_bus, *, data_width=None):
4950
granularity=csr_bus.data_width,
5051
name="wb")
5152

53+
self.wb_bus.memory_map = MemoryMap(addr_width=csr_bus.addr_width,
54+
data_width=csr_bus.data_width)
55+
5256
# Since granularity of the Wishbone interface matches the data width of the CSR bus,
5357
# no width conversion is performed, even if the Wishbone data width is greater.
5458
self.wb_bus.memory_map.add_window(self.csr_bus.memory_map)

nmigen_soc/test/test_csr_bus.py

+69-4
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from nmigen.back.pysim import *
77

88
from ..csr.bus import *
9+
from ..memory import MemoryMap
910

1011

1112
class ElementTestCase(unittest.TestCase):
@@ -83,6 +84,41 @@ def test_wrong_data_width(self):
8384
r"Data width must be a positive integer, not -1"):
8485
Interface(addr_width=16, data_width=-1)
8586

87+
def test_get_map_wrong(self):
88+
iface = Interface(addr_width=16, data_width=8)
89+
with self.assertRaisesRegex(NotImplementedError,
90+
r"Bus interface \(rec iface addr r_data r_stb w_data w_stb\) does not "
91+
r"have a memory map"):
92+
iface.memory_map
93+
94+
def test_get_map_frozen(self):
95+
iface = Interface(addr_width=16, data_width=8)
96+
iface.memory_map = MemoryMap(addr_width=16, data_width=8)
97+
with self.assertRaisesRegex(ValueError,
98+
r"Memory map has been frozen. Address width cannot be extended "
99+
r"further"):
100+
iface.memory_map.addr_width = 24
101+
102+
def test_set_map_wrong(self):
103+
iface = Interface(addr_width=16, data_width=8)
104+
with self.assertRaisesRegex(TypeError,
105+
r"Memory map must be an instance of MemoryMap, not 'foo'"):
106+
iface.memory_map = "foo"
107+
108+
def test_set_map_wrong_addr_width(self):
109+
iface = Interface(addr_width=16, data_width=8)
110+
with self.assertRaisesRegex(ValueError,
111+
r"Memory map has address width 8, which is not the same as "
112+
r"bus interface address width 16"):
113+
iface.memory_map = MemoryMap(addr_width=8, data_width=8)
114+
115+
def test_set_map_wrong_data_width(self):
116+
iface = Interface(addr_width=16, data_width=8)
117+
with self.assertRaisesRegex(ValueError,
118+
r"Memory map has data width 16, which is not the same as "
119+
r"bus interface data width 8"):
120+
iface.memory_map = MemoryMap(addr_width=16, data_width=16)
121+
86122

87123
class MultiplexerTestCase(unittest.TestCase):
88124
def setUp(self):
@@ -110,6 +146,11 @@ def test_add_two(self):
110146
self.assertEqual(self.dut.add(Element(8, "rw")),
111147
(2, 3))
112148

149+
def test_add_extend(self):
150+
self.assertEqual(self.dut.add(Element(8, "rw"), addr=0x10000, extend=True),
151+
(0x10000, 0x10001))
152+
self.assertEqual(self.dut.bus.addr_width, 17)
153+
113154
def test_add_wrong(self):
114155
with self.assertRaisesRegex(TypeError,
115156
r"Element must be an instance of csr\.Element, not 'foo'"):
@@ -122,6 +163,12 @@ def test_align_to(self):
122163
self.assertEqual(self.dut.add(Element(8, "rw")),
123164
(4, 5))
124165

166+
def test_add_wrong_out_of_bounds(self):
167+
with self.assertRaisesRegex(ValueError,
168+
r"Address range 0x10000\.\.0x10001 out of bounds for memory map spanning "
169+
r"range 0x0\.\.0x10000 \(16 address bits\)"):
170+
self.dut.add(Element(8, "rw"), addr=0x10000)
171+
125172
def test_sim(self):
126173
bus = self.dut.bus
127174

@@ -263,11 +310,21 @@ def setUp(self):
263310
self.dut = Decoder(addr_width=16, data_width=8)
264311

265312
def test_align_to(self):
266-
self.assertEqual(self.dut.add(Interface(addr_width=10, data_width=8)),
267-
(0, 0x400, 1))
313+
sub_1 = Interface(addr_width=10, data_width=8)
314+
sub_1.memory_map = MemoryMap(addr_width=10, data_width=8)
315+
self.assertEqual(self.dut.add(sub_1), (0, 0x400, 1))
316+
268317
self.assertEqual(self.dut.align_to(12), 0x1000)
269-
self.assertEqual(self.dut.add(Interface(addr_width=10, data_width=8)),
270-
(0x1000, 0x1400, 1))
318+
319+
sub_2 = Interface(addr_width=10, data_width=8)
320+
sub_2.memory_map = MemoryMap(addr_width=10, data_width=8)
321+
self.assertEqual(self.dut.add(sub_2), (0x1000, 0x1400, 1))
322+
323+
def test_add_extend(self):
324+
iface = Interface(addr_width=17, data_width=8)
325+
iface.memory_map = MemoryMap(addr_width=17, data_width=8)
326+
self.assertEqual(self.dut.add(iface, extend=True), (0, 0x20000, 1))
327+
self.assertEqual(self.dut.bus.addr_width, 18)
271328

272329
def test_add_wrong_sub_bus(self):
273330
with self.assertRaisesRegex(TypeError,
@@ -283,6 +340,14 @@ def test_add_wrong_data_width(self):
283340
r"decoder data width 8"):
284341
self.dut.add(mux.bus)
285342

343+
def test_add_wrong_out_of_bounds(self):
344+
iface = Interface(addr_width=17, data_width=8)
345+
iface.memory_map = MemoryMap(addr_width=17, data_width=8)
346+
with self.assertRaisesRegex(ValueError,
347+
r"Address range 0x0\.\.0x20000 out of bounds for memory map spanning "
348+
r"range 0x0\.\.0x10000 \(16 address bits\)"):
349+
self.dut.add(iface)
350+
286351
def test_sim(self):
287352
mux_1 = Multiplexer(addr_width=10, data_width=8)
288353
self.dut.add(mux_1.bus)

0 commit comments

Comments
 (0)