Skip to content

Commit

Permalink
Add method to generate a globally scoped type name. #187
Browse files Browse the repository at this point in the history
  • Loading branch information
amykyta3 committed Oct 11, 2023
1 parent 4d8f628 commit 4aca675
Show file tree
Hide file tree
Showing 5 changed files with 133 additions and 1 deletion.
15 changes: 15 additions & 0 deletions .readthedocs.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details

version: 2

build:
os: ubuntu-22.04
tools:
python: "3.11"

sphinx:
configuration: docs/conf.py

python:
install:
- requirements: docs/requirements.txt
2 changes: 1 addition & 1 deletion systemrdl/__about__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "1.26.1"
__version__ = "1.27.0"
62 changes: 62 additions & 0 deletions systemrdl/node.py
Original file line number Diff line number Diff line change
Expand Up @@ -686,6 +686,45 @@ def orig_type_name(self) -> Optional[str]:
else:
return self.inst.original_def.type_name

def get_global_type_name(self, separator: str = "__") -> Optional[str]:
"""
Returns a globally scoped type name that can be used to uniquely identify this
node's type.
If scope or type name information is not available due to the node being imported
from a non-RDL source, this will return None.
.. versionadded:: 1.27.0
"""

if (self.type_name is None) or (self.inst.parent_scope is None):
# Scope information is not known
return None

if isinstance(self.inst.parent_scope, comp.Root):
# Declaration of this was in the root scope
return self.type_name

# Due to namespace nesting properties, it is guaranteed that the parent
# scope definition is also going to be one of the node's ancestors.
# Seek up and find it
current_parent_node = self.parent
while current_parent_node:
if current_parent_node.inst.original_def is None:
# Original def reference is unknown
return None
if current_parent_node.inst.original_def is self.inst.parent_scope:
# Parent node's definition matches the scope we're looking for
parent_scope_path = current_parent_node.get_global_type_name(separator)
if parent_scope_path is None:
return None
return parent_scope_path + separator + self.type_name

current_parent_node = current_parent_node.parent

# Failed to find the path
return None

@property
def external(self) -> bool:
"""
Expand Down Expand Up @@ -1006,6 +1045,29 @@ class SignalNode(VectorNode):

#===============================================================================
class FieldNode(VectorNode):
def get_global_type_name(self, separator: str = "__") -> Optional[str]:
name = super().get_global_type_name(separator)
if name is None:
return None

# Fields may reuse the same type, but end up instantiating different widths
# Uniquify the type name further if the field width was overridden when instantiating
if self.inst.original_def is None:
suffix = ""
elif self.inst.original_def.type_name is None:
# is an anonymous definition. No extra suffix needed
suffix = ""
elif "fieldwidth" in self.list_properties():
# fieldwidth was explicitly set.
# This type name is already sufficiently distinct
suffix = ""
elif self.width == 1:
# field width is the default. Skip suffix
suffix = ""
else:
suffix = "_w%d" % self.width

return name + suffix

@property
def is_virtual(self) -> bool:
Expand Down
25 changes: 25 additions & 0 deletions test/rdl_src/global_type_names.rdl
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
reg r_global {
field {} f1[4];

field myfield {};
myfield f2;
myfield f3[4];
field myfield2 {fieldwidth = 4;};
myfield2 f4;
};

regfile rf_global #(
longint unsigned NUM = 4
){
reg r_local {
field {} f_param[NUM];
field myfield {};
myfield f_param2[NUM];
} r1;
r_global r2;
};

addrmap top {
rf_global rf1;
rf_global #(.NUM (8)) rf2;
};
30 changes: 30 additions & 0 deletions test/test_global_type_names.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from unittest_utils import RDLSourceTestCase

class TestGlobalTypeNames(RDLSourceTestCase):

def test_global_type_names(self):
root = self.compile(
["rdl_src/global_type_names.rdl"],
"top"
)
top = root.top

self.assertEqual(top.find_by_path("rf1").get_global_type_name("|"), "rf_global")
self.assertEqual(top.find_by_path("rf1.r1").get_global_type_name("|"), "rf_global|r_local")
self.assertEqual(top.find_by_path("rf1.r1.f_param").get_global_type_name("|"), "rf_global|r_local|f_param")
self.assertEqual(top.find_by_path("rf1.r1.f_param2").get_global_type_name("|"), "rf_global|r_local|myfield_w4")
self.assertEqual(top.find_by_path("rf1.r2").get_global_type_name("|"), "r_global")
self.assertEqual(top.find_by_path("rf1.r2.f1").get_global_type_name("|"), "r_global|f1")
self.assertEqual(top.find_by_path("rf1.r2.f2").get_global_type_name("|"), "r_global|myfield")
self.assertEqual(top.find_by_path("rf1.r2.f3").get_global_type_name("|"), "r_global|myfield_w4")
self.assertEqual(top.find_by_path("rf1.r2.f4").get_global_type_name("|"), "r_global|myfield2")

self.assertEqual(top.find_by_path("rf2").get_global_type_name("|"), "rf_global_NUM_8")
self.assertEqual(top.find_by_path("rf2.r1").get_global_type_name("|"), "rf_global_NUM_8|r_local")
self.assertEqual(top.find_by_path("rf2.r1.f_param").get_global_type_name("|"), "rf_global_NUM_8|r_local|f_param")
self.assertEqual(top.find_by_path("rf2.r1.f_param2").get_global_type_name("|"), "rf_global_NUM_8|r_local|myfield_w8")
self.assertEqual(top.find_by_path("rf2.r2").get_global_type_name("|"), "r_global")
self.assertEqual(top.find_by_path("rf2.r2.f1").get_global_type_name("|"), "r_global|f1")
self.assertEqual(top.find_by_path("rf2.r2.f2").get_global_type_name("|"), "r_global|myfield")
self.assertEqual(top.find_by_path("rf2.r2.f3").get_global_type_name("|"), "r_global|myfield_w4")
self.assertEqual(top.find_by_path("rf2.r2.f4").get_global_type_name("|"), "r_global|myfield2")

0 comments on commit 4aca675

Please sign in to comment.