Traversing the Register Model#
There are several ways you can explore a register model via the API.
Using Iterators#
Sometimes it is necessary to have more fine-grain control of how the register
model is explored. The Node.children()
method provides an iterator that can be used to manually traverse the tree.
top_node = root_node.get_child_by_name("top")
for child in top_node.children(unroll=True):
print(child.get_path())
The above outputs the following:
top.A[0]
top.A[1]
top.A[2]
top.A[3]
The Node API also provides the Node.descendants()
recursive iterator.
top_node = root_node.get_child_by_name("top")
for child in top_node.descendants():
print(child.get_path())
The above outputs the following:
top.A
top.A.f1
top.A.f2
Using the Walker/Listener#
One easy way to fully traverse a compiled register model is using a register-tree
listener interface, triggered by a RDLSimpleWalker.
The listener interface is a collection of callback methods contained in a class
extended from RDLListener. As the walker visits each
node, the type-specific callback method is triggered.
The built-in RDLSimpleWalker performs a depth-first walk
through the register model as shown in the diagram below:
Note
Since the RootNode does not actually
represent a register model component, the RDLSimpleWalker
intentionally skips it during traversal.
To create a listener, extend RDLListener and implement
your custom callback methods. In the example below, MyListener prints a
message each time the walker enters and exits type-specific nodes:
from systemrdl.walker import RDLListener, RDLSimpleWalker
class MyListener(RDLListener):
def enter_Addrmap(self, node):
print("Entering addrmap", node.get_path())
def exit_Addrmap(self, node):
print("Exiting addrmap", node.get_path())
def enter_Reg(self, node):
print("Entering register", node.get_path())
def exit_Reg(self, node):
print("Exiting register", node.get_path())
def enter_Field(self, node):
print("Entering field", node.get_path())
def exit_Field(self, node):
print("Exiting field", node.get_path())
Next, the walker can be started using an instance of
RDLSimpleWalker. In this example, the input root_node is
assumed to represent the top-level addrmap component called “top”.
RDLSimpleWalker().walk(root_node, MyListener())
Results in the following output:
Entering addrmap top
Entering register top.A[]
Entering field top.A[].f1
Exiting field top.A[].f1
Entering field top.A[].f2
Exiting field top.A[].f2
Exiting register top.A[]
Exiting addrmap top
To unroll all arrays during traversal, create a walker with loop unrolling enabled:
RDLSimpleWalker(unroll=True).walk(root_node, MyListener())
Entering addrmap top
Entering register top.A[0]
Entering field top.A[0].f1
Exiting field top.A[0].f1
Entering field top.A[0].f2
Exiting field top.A[0].f2
Exiting register top.A[0]
Entering register top.A[1]
Entering field top.A[1].f1
Exiting field top.A[1].f1
Entering field top.A[1].f2
Exiting field top.A[1].f2
Exiting register top.A[1]
... etc ...
Exiting register top.A[3]
Exiting addrmap top
Other Methods#
Node objects provide several other mechanisms to traverse the register model. This is not an exhaustive list, but highlights some of the more commonly used ones. See the class reference for more details.