Skip to content

Commit 466536e

Browse files
wanda-phiwhitequark
authored andcommitted
hdl._ir: raise an error when an elaboratable is duplicated in hierarchy.
Fixes #1194.
1 parent 2cf9bbf commit 466536e

File tree

3 files changed

+29
-2
lines changed

3 files changed

+29
-2
lines changed

amaranth/hdl/_ir.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99

1010

1111
__all__ = [
12-
"UnusedElaboratable", "Elaboratable", "DriverConflict", "Fragment", "Instance",
13-
"IOBufferInstance", "PortDirection", "Design", "build_netlist",
12+
"UnusedElaboratable", "Elaboratable", "DuplicateElaboratable", "DriverConflict", "Fragment",
13+
"Instance", "IOBufferInstance", "PortDirection", "Design", "build_netlist",
1414
]
1515

1616

@@ -30,6 +30,10 @@ class DriverConflict(UserWarning):
3030
pass
3131

3232

33+
class DuplicateElaboratable(Exception):
34+
pass
35+
36+
3337
class Fragment:
3438
@staticmethod
3539
def get(obj, platform):
@@ -430,6 +434,7 @@ def __init__(self, fragment: Fragment, ports, *, hierarchy):
430434
self.hierarchy = hierarchy
431435
self.fragments: dict[Fragment, DesignFragmentInfo] = {}
432436
self.signal_lca = _ast.SignalDict()
437+
self.elaboratables: dict[Elaboratable, Fragment] = {}
433438
self._compute_fragment_depth_parent(fragment, None, 0)
434439
self._collect_used_signals(fragment)
435440
self._add_io_ports()
@@ -598,6 +603,15 @@ def _assign_names(self, fragment: Fragment, hierarchy: "tuple[str]"):
598603
frag_info = self.fragments[fragment]
599604
frag_info.name = hierarchy
600605

606+
if fragment.origins is not None:
607+
for origin in fragment.origins:
608+
if origin in self.elaboratables:
609+
other_hierarchy = self.fragments[self.elaboratables[origin]].name
610+
raise DuplicateElaboratable(f"Elaboratable {origin!r} is included twice "
611+
f"in the hierarchy, as {'.'.join(other_hierarchy)} "
612+
f"and {'.'.join(hierarchy)}")
613+
self.elaboratables[origin] = fragment
614+
601615
if fragment is self.fragment:
602616
# Reserve names for top-level ports. If equal to the signal name, let the signal share it.
603617
for name, conn, _dir in self.ports:

amaranth/hdl/_xfrm.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,7 @@ def on_fragment(self, fragment):
318318
else:
319319
new_fragment = Fragment(src_loc=fragment.src_loc)
320320
new_fragment.attrs = OrderedDict(fragment.attrs)
321+
new_fragment.origins = fragment.origins
321322
self.map_subfragments(fragment, new_fragment)
322323
self.map_domains(fragment, new_fragment)
323324
self.map_statements(fragment, new_fragment)

tests/test_hdl_ir.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,18 @@ def test_empty(self):
7777
self.assertEqual(list(f.iter_sync()), [])
7878

7979

80+
class DuplicateElaboratableTestCase(FHDLTestCase):
81+
def test_duplicate(self):
82+
sub = Module()
83+
m = Module()
84+
m.submodules.a = sub
85+
m.submodules.b = sub
86+
with self.assertRaisesRegex(DuplicateElaboratable,
87+
r"^Elaboratable .* is included twice in the hierarchy, as "
88+
r"top\.a and top\.b$"):
89+
Fragment.get(m, None).prepare()
90+
91+
8092
class FragmentPortsTestCase(FHDLTestCase):
8193
def setUp(self):
8294
self.s1 = Signal()

0 commit comments

Comments
 (0)