Skip to content

Commit 0f02fc9

Browse files
committed
csr.bus: rewrite using the MemoryMap abstraction.
1 parent 02ef4bb commit 0f02fc9

File tree

2 files changed

+31
-70
lines changed

2 files changed

+31
-70
lines changed

nmigen_soc/csr/bus.py

Lines changed: 26 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import enum
22
from nmigen import *
33

4+
from ..memory import MemoryMap
5+
46

57
__all__ = ["Element", "Interface", "Decoder"]
68

@@ -73,6 +75,9 @@ def __init__(self, width, access, *, name=None, src_loc_at=0):
7375
]
7476
super().__init__(layout, name=name, src_loc_at=1)
7577

78+
# FIXME: get rid of this
79+
__hash__ = object.__hash__
80+
7681

7782
class Interface(Record):
7883
"""CPU-side CSR interface.
@@ -189,61 +194,27 @@ class Decoder(Elaboratable):
189194
CSR bus providing access to registers.
190195
"""
191196
def __init__(self, *, addr_width, data_width, alignment=0):
192-
self.bus = Interface(addr_width=addr_width, data_width=data_width)
197+
self.bus = Interface(addr_width=addr_width, data_width=data_width)
198+
self._map = MemoryMap(addr_width=addr_width, data_width=data_width, alignment=alignment)
193199

194-
if not isinstance(alignment, int) or alignment < 0:
195-
raise ValueError("Alignment must be a non-negative integer, not {!r}"
196-
.format(alignment))
197-
self.alignment = alignment
200+
def align_to(self, alignment):
201+
"""Align the implicit address of the next register.
198202
199-
self._next_addr = 0
200-
self._elements = dict()
203+
See :meth:`MemoryMap.align_to` for details.
204+
"""
205+
return self._map.align_to(alignment)
201206

202-
def add(self, element):
207+
def add(self, element, *, addr=None, alignment=None):
203208
"""Add a register.
204209
205-
Arguments
206-
---------
207-
element : :class:`Element`
208-
Interface of the register.
209-
210-
Return value
211-
------------
212-
An ``(addr, size)`` tuple, where ``addr`` is the address assigned to the first chunk of
213-
the register, and ``size`` is the amount of chunks it takes, which may be greater than
214-
``element.size // self.data_width`` due to alignment.
210+
See :meth:`MemoryMap.add_resource` for details.
215211
"""
216212
if not isinstance(element, Element):
217213
raise TypeError("Element must be an instance of csr.Element, not {!r}"
218214
.format(element))
219215

220-
addr = self.align_to(self.alignment)
221-
self._next_addr += (element.width + self.bus.data_width - 1) // self.bus.data_width
222-
size = self.align_to(self.alignment) - addr
223-
self._elements[addr] = element, size
224-
return addr, size
225-
226-
def align_to(self, alignment):
227-
"""Align the next register explicitly.
228-
229-
Arguments
230-
---------
231-
alignment : int
232-
Register alignment. The address assigned to the next register will be a multiple of
233-
``2 ** alignment`` or ``2 ** self.alignment``, whichever is greater.
234-
235-
Return value
236-
------------
237-
Address of the next register.
238-
"""
239-
if not isinstance(alignment, int) or alignment < 0:
240-
raise ValueError("Alignment must be a non-negative integer, not {!r}"
241-
.format(alignment))
242-
243-
align_chunks = 1 << alignment
244-
if self._next_addr % align_chunks != 0:
245-
self._next_addr += align_chunks - (self._next_addr % align_chunks)
246-
return self._next_addr
216+
size = (element.width + self.bus.data_width - 1) // self.bus.data_width
217+
return self._map.add_resource(element, size=size, addr=addr, alignment=alignment)
247218

248219
def elaborate(self, platform):
249220
m = Module()
@@ -255,7 +226,7 @@ def elaborate(self, platform):
255226
# 2-AND or 2-OR gates.
256227
r_data_fanin = 0
257228

258-
for elem_addr, (elem, elem_size) in self._elements.items():
229+
for elem, (elem_start, elem_end) in self._map.resources():
259230
shadow = Signal(elem.width, name="{}__shadow".format(elem.name))
260231
if elem.access.writable():
261232
m.d.comb += elem.w_data.eq(shadow)
@@ -265,28 +236,29 @@ def elaborate(self, platform):
265236
# carry chains for comparisons, even with a constant. (Register sizes don't have
266237
# to be powers of 2.)
267238
with m.Switch(self.bus.addr):
268-
for chunk_offset in range(elem_size):
269-
chunk_slice = slice(chunk_offset * self.bus.data_width,
270-
(chunk_offset + 1) * self.bus.data_width)
271-
with m.Case(elem_addr + chunk_offset):
239+
for chunk_offset, chunk_addr in enumerate(range(elem_start, elem_end)):
240+
with m.Case(chunk_addr):
241+
shadow_slice = shadow[chunk_offset * self.bus.data_width:
242+
(chunk_offset + 1) * self.bus.data_width]
243+
272244
if elem.access.readable():
273245
chunk_r_stb = Signal(self.bus.data_width,
274246
name="{}__r_stb_{}".format(elem.name, chunk_offset))
275-
r_data_fanin |= Mux(chunk_r_stb, shadow[chunk_slice], 0)
276-
if chunk_offset == 0:
247+
r_data_fanin |= Mux(chunk_r_stb, shadow_slice, 0)
248+
if chunk_addr == elem_start:
277249
m.d.comb += elem.r_stb.eq(self.bus.r_stb)
278250
with m.If(self.bus.r_stb):
279251
m.d.sync += shadow.eq(elem.r_data)
280252
# Delay by 1 cycle, allowing reads to be pipelined.
281253
m.d.sync += chunk_r_stb.eq(self.bus.r_stb)
282254

283255
if elem.access.writable():
284-
if chunk_offset == elem_size - 1:
256+
if chunk_addr == elem_end - 1:
285257
# Delay by 1 cycle, avoiding combinatorial paths through
286258
# the CSR bus and into CSR registers.
287259
m.d.sync += elem.w_stb.eq(self.bus.w_stb)
288260
with m.If(self.bus.w_stb):
289-
m.d.sync += shadow[chunk_slice].eq(self.bus.w_data)
261+
m.d.sync += shadow_slice.eq(self.bus.w_data)
290262

291263
with m.Default():
292264
m.d.sync += shadow.eq(0)

nmigen_soc/test/test_csr_bus.py

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -86,14 +86,6 @@ class DecoderTestCase(unittest.TestCase):
8686
def setUp(self):
8787
self.dut = Decoder(addr_width=16, data_width=8)
8888

89-
def test_alignment_wrong(self):
90-
with self.assertRaisesRegex(ValueError,
91-
r"Alignment must be a non-negative integer, not -1"):
92-
Decoder(addr_width=16, data_width=8, alignment=-1)
93-
94-
def test_attrs(self):
95-
self.assertEqual(self.dut.alignment, 0)
96-
9789
def test_add_4b(self):
9890
self.assertEqual(self.dut.add(Element(4, "rw")),
9991
(0, 1))
@@ -114,7 +106,7 @@ def test_add_two(self):
114106
self.assertEqual(self.dut.add(Element(16, "rw")),
115107
(0, 2))
116108
self.assertEqual(self.dut.add(Element(8, "rw")),
117-
(2, 1))
109+
(2, 3))
118110

119111
def test_add_wrong(self):
120112
with self.assertRaisesRegex(ValueError,
@@ -126,7 +118,7 @@ def test_align_to(self):
126118
(0, 1))
127119
self.assertEqual(self.dut.align_to(2), 4)
128120
self.assertEqual(self.dut.add(Element(8, "rw")),
129-
(4, 1))
121+
(4, 5))
130122

131123
def test_sim(self):
132124
bus = self.dut.bus
@@ -206,28 +198,25 @@ class DecoderAlignedTestCase(unittest.TestCase):
206198
def setUp(self):
207199
self.dut = Decoder(addr_width=16, data_width=8, alignment=2)
208200

209-
def test_attrs(self):
210-
self.assertEqual(self.dut.alignment, 2)
211-
212201
def test_add_two(self):
213202
self.assertEqual(self.dut.add(Element(8, "rw")),
214203
(0, 4))
215204
self.assertEqual(self.dut.add(Element(16, "rw")),
216-
(4, 4))
205+
(4, 8))
217206

218207
def test_over_align_to(self):
219208
self.assertEqual(self.dut.add(Element(8, "rw")),
220209
(0, 4))
221210
self.assertEqual(self.dut.align_to(3), 8)
222211
self.assertEqual(self.dut.add(Element(8, "rw")),
223-
(8, 4))
212+
(8, 12))
224213

225214
def test_under_align_to(self):
226215
self.assertEqual(self.dut.add(Element(8, "rw")),
227216
(0, 4))
228217
self.assertEqual(self.dut.align_to(1), 4)
229218
self.assertEqual(self.dut.add(Element(8, "rw")),
230-
(4, 4))
219+
(4, 8))
231220

232221
def test_sim(self):
233222
bus = self.dut.bus

0 commit comments

Comments
 (0)