Skip to content

Commit 723afcd

Browse files
committed
Add Node utilities for querying overlapping registers and fields
1 parent 94da299 commit 723afcd

File tree

6 files changed

+324
-9
lines changed

6 files changed

+324
-9
lines changed

src/systemrdl/component.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,19 @@ class Signal_PreStructuralElab(Signal):
387387
class Field(VectorComponent):
388388
original_def: Optional['Field']
389389

390+
def __init__(self) -> None:
391+
super().__init__()
392+
393+
# List of field inst names that validly overlap (ro + wo) with this
394+
# field. This is a list since it is theoretically possible for a wide
395+
# field to overlap with multiple narrow fields.
396+
self.overlaps_with_names: List[str] = []
397+
398+
def _copy_for_inst(self: 'Field', memo: Dict[int, Any], recursive: bool = False) -> 'Field':
399+
result = super()._copy_for_inst(memo, recursive)
400+
result.overlaps_with_names = copy(self.overlaps_with_names)
401+
return result
402+
390403
class Field_PreStructuralElab(Field):
391404
"""
392405
Alternately typed representation for type hinting prior to structural placement
@@ -414,6 +427,11 @@ def __init__(self) -> None:
414427
# so names are unambiguous
415428
self._alias_names: List[str] = []
416429

430+
# List of register inst names that validly overlap (ro + wo) with this
431+
# register. This is a list since it is theoretically possible for a wide
432+
# register to overlap with multiple narrow registers.
433+
self.overlaps_with_names: List[str] = []
434+
417435
#------------------------------
418436
# Alias Register
419437
#------------------------------
@@ -428,6 +446,7 @@ def _copy_for_inst(self: 'Reg', memo: Dict[int, Any], recursive: bool = False) -
428446
result = super()._copy_for_inst(memo, recursive)
429447
result.is_msb0_order = self.is_msb0_order
430448
result._alias_names = copy(self._alias_names)
449+
result.overlaps_with_names = copy(self.overlaps_with_names)
431450
result.is_alias = self.is_alias
432451
result.alias_primary_inst = deepcopy(self.alias_primary_inst, memo)
433452
return result

src/systemrdl/core/validate.py

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,11 @@ def enter_AddressableComponent(self, node: AddressableNode) -> None:
9696
):
9797
overlap_allowed = True
9898

99+
# Stash information about overlaps so that it can be queried in the API
100+
node.inst.overlaps_with_names.append(prev_addressable.inst_name)
101+
prev_addressable.inst.overlaps_with_names.append(node.inst_name)
102+
103+
99104
# Bridge addrmaps allow overlapping children
100105
if isinstance(node.parent, AddrmapNode) and node.parent.get_property('bridge'):
101106
overlap_allowed = True
@@ -367,14 +372,20 @@ def enter_Field(self, node: FieldNode) -> None:
367372
# Check if the overlap is allowed
368373
prev_f_sw = prev_field.get_property('sw')
369374

370-
if((prev_f_sw == rdltypes.AccessType.r)
371-
and (this_f_sw in (rdltypes.AccessType.w, rdltypes.AccessType.w1))
372-
):
373-
pass
374-
elif((this_f_sw == rdltypes.AccessType.r)
375-
and (prev_f_sw in (rdltypes.AccessType.w, rdltypes.AccessType.w1))
375+
if(
376+
(
377+
(prev_f_sw == rdltypes.AccessType.r)
378+
and (this_f_sw in {rdltypes.AccessType.w, rdltypes.AccessType.w1})
379+
)
380+
or (
381+
(this_f_sw == rdltypes.AccessType.r)
382+
and (prev_f_sw in {rdltypes.AccessType.w, rdltypes.AccessType.w1})
383+
)
376384
):
377-
pass
385+
# read-only + write-only overlap is OK
386+
# Stash information about overlaps so that it can be queried in the API
387+
node.inst.overlaps_with_names.append(prev_field.inst_name)
388+
prev_field.inst.overlaps_with_names.append(node.inst_name)
378389
else:
379390
self.msg.error(
380391
"Field '%s[%d:%d]' overlaps with field '%s[%d:%d]'"

src/systemrdl/node.py

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2005,6 +2005,31 @@ def aliases(self, skip_not_present: bool = True) -> List['FieldNode']:
20052005
fields.append(alias_field)
20062006
return fields
20072007

2008+
@property
2009+
def has_overlaps(self) -> bool:
2010+
"""
2011+
Returns True if this field overlaps with any other fields
2012+
that are present in the same register.
2013+
2014+
This is allowed if one is read-only and the other is write-only.
2015+
2016+
2017+
.. versionadded:: 1.31
2018+
"""
2019+
return bool(self.inst.overlaps_with_names)
2020+
2021+
@property
2022+
def overlapping_fields(self) -> List['FieldNode']:
2023+
"""
2024+
Returns a list of all other fields that overlap with this field.
2025+
"""
2026+
fields = []
2027+
for field_name in self.inst.overlaps_with_names:
2028+
field = self.parent.get_child_by_name(field_name)
2029+
assert isinstance(field, FieldNode)
2030+
fields.append(field)
2031+
return fields
2032+
20082033

20092034
#===============================================================================
20102035
class RegNode(AddressableNode):
@@ -2300,7 +2325,7 @@ def has_aliases(self) -> bool:
23002325

23012326
def aliases(self, skip_not_present: bool = True) -> List['RegNode']:
23022327
"""
2303-
Returns a listof all the registers that are aliases of this primary register
2328+
Returns a list of all the registers that are aliases of this primary register
23042329
23052330
Parameters
23062331
----------
@@ -2329,6 +2354,30 @@ def aliases(self, skip_not_present: bool = True) -> List['RegNode']:
23292354
regs.append(alias_reg)
23302355
return regs
23312356

2357+
@property
2358+
def has_overlaps(self) -> bool:
2359+
"""
2360+
Returns True if this register overlaps with any other registers
2361+
that are present in the design.
2362+
2363+
This is allowed if one is read-only and the other is write-only.
2364+
2365+
2366+
.. versionadded:: 1.31
2367+
"""
2368+
return bool(self.inst.overlaps_with_names)
2369+
2370+
@property
2371+
def overlapping_regs(self) -> List['RegNode']:
2372+
"""
2373+
Returns a list of all other registers that overlap with this register.
2374+
"""
2375+
regs = []
2376+
for reg_name in self.inst.overlaps_with_names:
2377+
reg = self.parent.get_child_by_name(reg_name)
2378+
assert isinstance(reg, RegNode)
2379+
regs.append(reg)
2380+
return regs
23322381

23332382

23342383

test/rdl_src/overlaps.rdl

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
addrmap top {
2+
field ro_field {sw = r; hw = w;};
3+
field wo_field {sw = w; hw = r;};
4+
//--------------------------------------------------------------------------
5+
// Overlapping registers
6+
//--------------------------------------------------------------------------
7+
// Simple overlap
8+
reg {
9+
ro_field f;
10+
} r0_ro @ 0x0;
11+
reg {
12+
wo_field f;
13+
} r0_wo @ 0x0;
14+
15+
// Offset overlap
16+
reg {
17+
ro_field f;
18+
} r1_ro @ 0x10;
19+
reg {
20+
wo_field f;
21+
} r1_wo @ 0x11;
22+
23+
// Wide + narrow multiple overlaps
24+
reg {
25+
regwidth = 32;
26+
ro_field f;
27+
} r2_ro @ 0x100;
28+
reg {
29+
regwidth = 8;
30+
wo_field f;
31+
} r2_wo_a @ 0x100,
32+
r2_wo_b @ 0x101,
33+
r2_wo_c @ 0x102,
34+
r2_wo_d @ 0x103;
35+
36+
// Narrow + wide multiple overlaps
37+
reg {
38+
regwidth = 8;
39+
ro_field f;
40+
} r3_ro_a @ 0x200,
41+
r3_ro_b @ 0x201,
42+
r3_ro_c @ 0x202,
43+
r3_ro_d @ 0x203;
44+
reg {
45+
regwidth = 32;
46+
wo_field f;
47+
} r3_wo @ 0x200;
48+
49+
50+
reg {
51+
ro_field f1;
52+
wo_field f2;
53+
} r4 @ 0x300;
54+
55+
//--------------------------------------------------------------------------
56+
// Overlapping fields
57+
//--------------------------------------------------------------------------
58+
59+
reg {
60+
ro_field f_ro[7:0];
61+
wo_field f_wo[7:0];
62+
} simple_overlap;
63+
64+
reg {
65+
ro_field f_ro[7:0];
66+
wo_field f_wo[12:4];
67+
} offset_overlap;
68+
69+
reg {
70+
ro_field f_ro[31:0];
71+
wo_field f_wo_a[7:0];
72+
wo_field f_wo_b[15:8];
73+
wo_field f_wo_c[23:16];
74+
wo_field f_wo_d[31:24];
75+
} wide_narrow_overlap;
76+
77+
reg {
78+
ro_field f_ro_a[7:0];
79+
ro_field f_ro_b[15:8];
80+
ro_field f_ro_c[23:16];
81+
ro_field f_ro_d[31:24];
82+
wo_field f_wo[31:0];
83+
} narrow_wide_overlap;
84+
};

test/test_overlaps.py

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
from systemrdl.node import RegNode, FieldNode
2+
3+
from unittest_utils import RDLSourceTestCase
4+
5+
class TestOverlaps(RDLSourceTestCase):
6+
7+
def test_reg_overlaps(self):
8+
root = self.compile(
9+
["rdl_src/overlaps.rdl"],
10+
"top"
11+
)
12+
top = root.top
13+
14+
r0_ro: RegNode = top.get_child_by_name("r0_ro")
15+
r0_wo: RegNode = top.get_child_by_name("r0_wo")
16+
r1_ro: RegNode = top.get_child_by_name("r1_ro")
17+
r1_wo: RegNode = top.get_child_by_name("r1_wo")
18+
r2_ro: RegNode = top.get_child_by_name("r2_ro")
19+
r2_wo_a: RegNode = top.get_child_by_name("r2_wo_a")
20+
r2_wo_b: RegNode = top.get_child_by_name("r2_wo_b")
21+
r2_wo_c: RegNode = top.get_child_by_name("r2_wo_c")
22+
r2_wo_d: RegNode = top.get_child_by_name("r2_wo_d")
23+
r3_ro_a: RegNode = top.get_child_by_name("r3_ro_a")
24+
r3_ro_b: RegNode = top.get_child_by_name("r3_ro_b")
25+
r3_ro_c: RegNode = top.get_child_by_name("r3_ro_c")
26+
r3_ro_d: RegNode = top.get_child_by_name("r3_ro_d")
27+
r3_wo: RegNode = top.get_child_by_name("r3_wo")
28+
r4: RegNode = top.get_child_by_name("r4")
29+
30+
self.assertTrue(r0_ro.has_overlaps)
31+
self.assertTrue(r0_wo.has_overlaps)
32+
self.assertListEqual(r0_ro.overlapping_regs, [r0_wo])
33+
self.assertListEqual(r0_wo.overlapping_regs, [r0_ro])
34+
35+
self.assertTrue(r1_ro.has_overlaps)
36+
self.assertTrue(r1_wo.has_overlaps)
37+
self.assertListEqual(r1_ro.overlapping_regs, [r1_wo])
38+
self.assertListEqual(r1_wo.overlapping_regs, [r1_ro])
39+
40+
self.assertTrue(r2_ro.has_overlaps)
41+
self.assertTrue(r2_wo_a.has_overlaps)
42+
self.assertTrue(r2_wo_b.has_overlaps)
43+
self.assertTrue(r2_wo_c.has_overlaps)
44+
self.assertTrue(r2_wo_d.has_overlaps)
45+
self.assertListEqual(r2_ro.overlapping_regs, [
46+
r2_wo_a,
47+
r2_wo_b,
48+
r2_wo_c,
49+
r2_wo_d,
50+
])
51+
self.assertListEqual(r2_wo_a.overlapping_regs, [r2_ro])
52+
self.assertListEqual(r2_wo_b.overlapping_regs, [r2_ro])
53+
self.assertListEqual(r2_wo_c.overlapping_regs, [r2_ro])
54+
self.assertListEqual(r2_wo_d.overlapping_regs, [r2_ro])
55+
56+
self.assertTrue(r3_ro_a.has_overlaps)
57+
self.assertTrue(r3_ro_b.has_overlaps)
58+
self.assertTrue(r3_ro_c.has_overlaps)
59+
self.assertTrue(r3_ro_d.has_overlaps)
60+
self.assertTrue(r3_wo.has_overlaps)
61+
self.assertListEqual(r3_ro_a.overlapping_regs, [r3_wo])
62+
self.assertListEqual(r3_ro_b.overlapping_regs, [r3_wo])
63+
self.assertListEqual(r3_ro_c.overlapping_regs, [r3_wo])
64+
self.assertListEqual(r3_ro_d.overlapping_regs, [r3_wo])
65+
self.assertListEqual(r3_wo.overlapping_regs, [
66+
r3_ro_a,
67+
r3_ro_b,
68+
r3_ro_c,
69+
r3_ro_d,
70+
])
71+
72+
self.assertFalse(r4.has_overlaps)
73+
self.assertListEqual(r4.overlapping_regs, [])
74+
75+
76+
def test_field_overlaps(self):
77+
root = self.compile(
78+
["rdl_src/overlaps.rdl"],
79+
"top"
80+
)
81+
top = root.top
82+
83+
with self.subTest("simple"):
84+
f_ro: FieldNode = top.find_by_path("simple_overlap.f_ro")
85+
f_wo: FieldNode = top.find_by_path("simple_overlap.f_wo")
86+
self.assertTrue(f_ro.has_overlaps)
87+
self.assertTrue(f_wo.has_overlaps)
88+
self.assertListEqual(f_ro.overlapping_fields, [f_wo])
89+
self.assertListEqual(f_wo.overlapping_fields, [f_ro])
90+
91+
with self.subTest("offset"):
92+
f_ro = top.find_by_path("offset_overlap.f_ro")
93+
f_wo = top.find_by_path("offset_overlap.f_wo")
94+
self.assertTrue(f_ro.has_overlaps)
95+
self.assertTrue(f_wo.has_overlaps)
96+
self.assertListEqual(f_ro.overlapping_fields, [f_wo])
97+
self.assertListEqual(f_wo.overlapping_fields, [f_ro])
98+
99+
with self.subTest("wide_narrow"):
100+
f_ro: FieldNode = top.find_by_path("wide_narrow_overlap.f_ro")
101+
f_wo_a: FieldNode = top.find_by_path("wide_narrow_overlap.f_wo_a")
102+
f_wo_b: FieldNode = top.find_by_path("wide_narrow_overlap.f_wo_b")
103+
f_wo_c: FieldNode = top.find_by_path("wide_narrow_overlap.f_wo_c")
104+
f_wo_d: FieldNode = top.find_by_path("wide_narrow_overlap.f_wo_d")
105+
106+
self.assertTrue(f_ro.has_overlaps)
107+
self.assertTrue(f_wo_a.has_overlaps)
108+
self.assertTrue(f_wo_b.has_overlaps)
109+
self.assertTrue(f_wo_c.has_overlaps)
110+
self.assertTrue(f_wo_d.has_overlaps)
111+
self.assertListEqual(f_ro.overlapping_fields, [
112+
f_wo_a,
113+
f_wo_b,
114+
f_wo_c,
115+
f_wo_d,
116+
])
117+
self.assertListEqual(f_wo_a.overlapping_fields, [f_ro])
118+
self.assertListEqual(f_wo_b.overlapping_fields, [f_ro])
119+
self.assertListEqual(f_wo_c.overlapping_fields, [f_ro])
120+
self.assertListEqual(f_wo_d.overlapping_fields, [f_ro])
121+
122+
with self.subTest("narrow_wide"):
123+
f_ro_a: FieldNode = top.find_by_path("narrow_wide_overlap.f_ro_a")
124+
f_ro_b: FieldNode = top.find_by_path("narrow_wide_overlap.f_ro_b")
125+
f_ro_c: FieldNode = top.find_by_path("narrow_wide_overlap.f_ro_c")
126+
f_ro_d: FieldNode = top.find_by_path("narrow_wide_overlap.f_ro_d")
127+
f_wo: FieldNode = top.find_by_path("narrow_wide_overlap.f_wo")
128+
129+
self.assertTrue(f_ro_a.has_overlaps)
130+
self.assertTrue(f_ro_b.has_overlaps)
131+
self.assertTrue(f_ro_c.has_overlaps)
132+
self.assertTrue(f_ro_d.has_overlaps)
133+
self.assertTrue(f_wo.has_overlaps)
134+
self.assertListEqual(f_ro_a.overlapping_fields, [f_wo])
135+
self.assertListEqual(f_ro_b.overlapping_fields, [f_wo])
136+
self.assertListEqual(f_ro_c.overlapping_fields, [f_wo])
137+
self.assertListEqual(f_ro_d.overlapping_fields, [f_wo])
138+
self.assertListEqual(f_wo.overlapping_fields, [
139+
f_ro_a,
140+
f_ro_b,
141+
f_ro_c,
142+
f_ro_d,
143+
])
144+
145+
with self.subTest("no overlap"):
146+
f1: FieldNode = top.find_by_path("r4.f1")
147+
f2: FieldNode = top.find_by_path("r4.f2")
148+
149+
self.assertFalse(f1.has_overlaps)
150+
self.assertFalse(f2.has_overlaps)
151+
self.assertListEqual(f1.overlapping_fields, [])
152+
self.assertListEqual(f2.overlapping_fields, [])

test/test_signals.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from unittest_utils import RDLSourceTestCase
22

3-
class TestParameters(RDLSourceTestCase):
3+
class TestSignals(RDLSourceTestCase):
44

55
def test_signalwidth(self):
66
root = self.compile(

0 commit comments

Comments
 (0)