Skip to content

Commit 2dfd84c

Browse files
committed
memory: fix address translation of resources located in dense windows.
As a breaking change, MemoryMap.add_window(self, window, sparse=False) will fail if `self.data_width // window.data_width` isn't a power-of-2 or is greater than `2 ** window.alignment`.
1 parent 3380048 commit 2dfd84c

File tree

3 files changed

+64
-19
lines changed

3 files changed

+64
-19
lines changed

amaranth_soc/memory.py

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -441,22 +441,29 @@ def add_window(self, window, *, addr=None, sparse=None):
441441
----------
442442
:exc:`ValueError`
443443
If the memory map is frozen.
444-
:exc:`TypeError`
445-
If the added memory map is not a :class:`MemoryMap`.
446444
:exc:`ValueError`
447445
If the requested address and size, after alignment, would overlap with any resources or
448446
windows that have already been added, or would be out of bounds.
449447
:exc:`ValueError`
450-
If the added memory map has a wider datapath than this memory map.
448+
If ``window.data_width`` is wider than :attr:`data_width`.
449+
:exc:`ValueError`
450+
If the address translation mode is unspecified and ``window.data_width`` is different
451+
than :attr:`data_width`.
452+
:exc:`ValueError`
453+
If dense address translation is used and :attr:`data_width` is not an integer multiple
454+
of ``window.data_width``.
451455
:exc:`ValueError`
452-
If dense address translation is used and the datapath width of this memory map is not
453-
an integer multiple of the datapath of the added memory map.
456+
If dense address translation is used and the ratio of :attr:`data_width` to
457+
``window.data_width`` is not a power of 2.
454458
:exc:`ValueError`
455-
If the name of the added memory map conflicts with the name of other resources or
456-
windows present in this memory map.
459+
If dense address translation is used and the ratio of :attr:`data_width` to
460+
``window.data_width`` is lesser than 2 raised to the power of :attr:`alignment`.
457461
:exc:`ValueError`
458-
If the added memory map has no name, and the name of one of its resources or windows
459-
conflicts with the name of others present in this memory map.
462+
If the requested name would conflict with the name of other resources or windows that
463+
have already been added.
464+
:exc:`ValueError`
465+
If ``window`` is anonymous and the name of one of its resources or windows would
466+
conflict with the name of any resources or windows that have already been added.
460467
"""
461468
if not isinstance(window, MemoryMap):
462469
raise TypeError(f"Window must be a MemoryMap, not {window!r}")
@@ -494,6 +501,17 @@ def add_window(self, window, *, addr=None, sparse=None):
494501
ratio = self.data_width // window.data_width
495502
else:
496503
ratio = 1
504+
505+
if ratio & (ratio - 1) != 0:
506+
raise ValueError(f"Dense addressing cannot be used because the ratio {ratio} of the "
507+
f"memory map data width {self.data_width} to the window data width "
508+
f"{window.data_width} is not a power-of-2")
509+
if ratio > (1 << window.alignment):
510+
raise ValueError(f"Dense addressing cannot be used because the ratio {ratio} of the "
511+
f"memory map data width {self.data_width} to the window data width "
512+
f"{window.data_width} is greater than the window alignment "
513+
f"{1 << window.alignment}")
514+
497515
size = (1 << window.addr_width) // ratio
498516
# For resources, the alignment argument of add_resource() affects both address and size
499517
# of the resource; aligning only the address should be done using align_to(). For windows,
@@ -556,14 +574,18 @@ def window_patterns(self):
556574

557575
@staticmethod
558576
def _translate(resource_info, window, window_range):
577+
# When a resource is accessed through a dense window, its size and address on the wider
578+
# bus must be integer multiples of the number of addresses on the narrower bus that are
579+
# accessed for each transaction.
559580
assert (resource_info.end - resource_info.start) % window_range.step == 0
581+
assert resource_info.start % window_range.step == 0
560582
# Accessing a resource through a dense and then a sparse window results in very strange
561583
# layouts that cannot be easily represented, so reject those.
562584
assert window_range.step == 1 or resource_info.width == window.data_width
563585

564586
path = resource_info.path if window.name is None else (window.name, *resource_info.path)
565587
size = (resource_info.end - resource_info.start) // window_range.step
566-
start = resource_info.start + window_range.start
588+
start = (resource_info.start // window_range.step) + window_range.start
567589
width = resource_info.width * window_range.step
568590
return ResourceInfo(resource_info.resource, path, start, start + size, width)
569591

@@ -642,7 +664,7 @@ def decode_address(self, address):
642664
return assignment
643665
elif id(assignment) in self._windows:
644666
_, addr_range = self._windows[id(assignment)]
645-
return assignment.decode_address((address - addr_range.start) // addr_range.step)
667+
return assignment.decode_address((address - addr_range.start) * addr_range.step)
646668
else:
647669
assert False # :nocov:
648670

tests/test_memory.py

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -399,7 +399,7 @@ def test_add_window_sparse(self):
399399

400400
def test_add_window_dense(self):
401401
memory_map = MemoryMap(addr_width=16, data_width=32)
402-
self.assertEqual(memory_map.add_window(MemoryMap(addr_width=10, data_width=8),
402+
self.assertEqual(memory_map.add_window(MemoryMap(addr_width=10, data_width=8, alignment=2),
403403
sparse=False),
404404
(0, 0x100, 4))
405405

@@ -430,13 +430,28 @@ def test_add_window_wrong_no_mode(self):
430430
r"a window with data width 8 to a memory map with data width 16"):
431431
memory_map.add_window(MemoryMap(addr_width=10, data_width=8))
432432

433-
def test_add_window_wrong_ratio(self):
433+
def test_add_window_wrong_ratio_multiple(self):
434434
memory_map = MemoryMap(addr_width=16, data_width=16)
435435
with self.assertRaisesRegex(ValueError,
436436
r"Dense addressing cannot be used because the memory map data width "
437437
r"16 is not an integer multiple of window data width 7"):
438438
memory_map.add_window(MemoryMap(addr_width=10, data_width=7), sparse=False)
439439

440+
def test_add_window_wrong_ratio_pow2(self):
441+
memory_map = MemoryMap(addr_width=16, data_width=24)
442+
with self.assertRaisesRegex(ValueError,
443+
r"Dense addressing cannot be used because the ratio 3 of the memory map "
444+
r"data width 24 to the window data width 8 is not a power-of-2"):
445+
memory_map.add_window(MemoryMap(addr_width=10, data_width=8), sparse=False)
446+
447+
def test_add_window_wrong_ratio_alignment(self):
448+
memory_map = MemoryMap(addr_width=16, data_width=16)
449+
with self.assertRaisesRegex(ValueError,
450+
r"Dense addressing cannot be used because the ratio 2 of the memory map "
451+
r"data width 16 to the window data width 8 is greater than the window "
452+
r"alignment 1"):
453+
memory_map.add_window(MemoryMap(addr_width=10, data_width=8), sparse=False)
454+
440455
def test_add_window_wrong_out_of_bounds(self):
441456
memory_map = MemoryMap(addr_width=16, data_width=8)
442457
with self.assertRaisesRegex(ValueError,
@@ -489,9 +504,9 @@ def test_add_window_wrong_name_conflict_subordinate(self):
489504

490505
def test_iter_windows(self):
491506
memory_map = MemoryMap(addr_width=16, data_width=16)
492-
window_1 = MemoryMap(addr_width=10, data_width=8)
507+
window_1 = MemoryMap(addr_width=10, data_width=8, alignment=1)
493508
window_2 = MemoryMap(addr_width=12, data_width=16)
494-
window_3 = MemoryMap(addr_width=10, data_width=8)
509+
window_3 = MemoryMap(addr_width=10, data_width=8, alignment=1)
495510
memory_map.add_window(window_1, sparse=False)
496511
memory_map.add_window(window_2)
497512
memory_map.add_window(window_3, sparse=False, addr=0x400)
@@ -503,9 +518,9 @@ def test_iter_windows(self):
503518

504519
def test_iter_window_patterns(self):
505520
memory_map = MemoryMap(addr_width=16, data_width=16)
506-
window_1 = MemoryMap(addr_width=10, data_width=8)
521+
window_1 = MemoryMap(addr_width=10, data_width=8, alignment=1)
507522
window_2 = MemoryMap(addr_width=12, data_width=16)
508-
window_3 = MemoryMap(addr_width=10, data_width=8)
523+
window_3 = MemoryMap(addr_width=10, data_width=8, alignment=1)
509524
memory_map.add_window(window_1, sparse=False)
510525
memory_map.add_window(window_2)
511526
memory_map.add_window(window_3, sparse=False, addr=0x400)
@@ -555,9 +570,11 @@ def setUp(self):
555570
self.res5 = _MockResource("res5")
556571
self.win2.add_resource(self.res5, name=("name5",), size=16)
557572
self.root.add_window(self.win2, sparse=True)
558-
self.win3 = MemoryMap(addr_width=16, data_width=8, name="win3")
573+
self.win3 = MemoryMap(addr_width=16, data_width=8, alignment=2, name="win3")
559574
self.res6 = _MockResource("res6")
575+
self.res7 = _MockResource("res7")
560576
self.win3.add_resource(self.res6, name=("name6",), size=16)
577+
self.win3.add_resource(self.res7, name=("name7",), size=1)
561578
self.root.add_window(self.win3, sparse=False)
562579

563580
def test_iter_all_resources(self):
@@ -599,6 +616,12 @@ def test_iter_all_resources(self):
599616
self.assertEqual(res_info[5].end, 0x00040004)
600617
self.assertEqual(res_info[5].width, 32)
601618

619+
self.assertIs(res_info[6].resource, self.res7)
620+
self.assertEqual(res_info[6].path, ("win3", ("name7",)))
621+
self.assertEqual(res_info[6].start, 0x00040004)
622+
self.assertEqual(res_info[6].end, 0x00040005)
623+
self.assertEqual(res_info[6].width, 32)
624+
602625
def test_find_resource(self):
603626
for res_info in self.root.all_resources():
604627
other = self.root.find_resource(res_info.resource)

tests/test_wishbone_bus.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -326,7 +326,7 @@ def elaborate(self, platform):
326326
self.assertEqual(dut.add(loop_1.bus, addr=0x10000),
327327
(0x10000, 0x10100, 1))
328328
loop_2 = AddressLoopback(addr_width=6, data_width=32, granularity=8)
329-
loop_2.bus.memory_map = MemoryMap(addr_width=8, data_width=8)
329+
loop_2.bus.memory_map = MemoryMap(addr_width=8, data_width=8, alignment=1)
330330
self.assertEqual(dut.add(loop_2.bus, addr=0x20000),
331331
(0x20000, 0x20080, 2))
332332
loop_3 = AddressLoopback(addr_width=8, data_width=16, granularity=16)

0 commit comments

Comments
 (0)