# Copyright 2026 Apheleia
#
# Description:
# Apheleia Verification Library Register Abstraction Component
from fnmatch import fnmatch
import avl
from peakrdl_ipxact import IPXACTImporter
from systemrdl import RDLCompileError, RDLCompiler
from systemrdl.component import Addrmap
from systemrdl.node import FieldNode, RegNode
from ._reg import Reg
[docs]
class RAL(avl.Component):
[docs]
def __init__(self, name : str, parent : avl.Component | None) -> None:
"""
Initialize the example environment with a simple memory model and an x86 QEMU agent.
:param name: The name of the environment.
:type name: str
:param parent: The parent component in the AVL hierarchy.
:type parent: avl.Component
:return: None
"""
super().__init__(name, parent)
self._rdlc_ : RDLCompiler = RDLCompiler()
"""SystemRDL Compiler"""
self._addrmaps_ : dict[str, Addrmap] = {}
"""List of address maps"""
self._class_overrides_ = {
RegNode : Reg,
}
def __str__(self) -> str:
"""
Custom String Formatting
:return: The formatted string
:rtype: str
"""
_str_ = ""
for addrmap in self._addrmaps_.values():
_str_ += str(addrmap.children()[0])
return _str_
def _assign_addrmaps_(self) -> None:
"""
Assign the address maps to the component
:return: None
"""
for name, comp in self._rdlc_.root.comp_defs.items():
if isinstance(comp, Addrmap):
root = self._rdlc_.elaborate(name)
self._addrmaps_[name] = root
def _load_rdl_(self, path : str) -> None:
"""
Load the RAL definition from RDL file
:param path: The path to the RAL definition file
:type path: str
:return: None
"""
try:
self._rdlc_.compile_file(path)
except RDLCompileError as e:
self.fatal(f"RDL Compilation failed: {e}")
self._assign_addrmaps_()
def _load_ipxact_(self, path : str) -> None:
"""
Load the RAL definition from IPXACT XML
:param path: The path to the XML definition file
:type path: str
:return: None
"""
try:
ipxact = IPXACTImporter(self._rdlc_)
ipxact.import_file(path)
except RDLCompileError as e:
self.fatal(f"IPXACT Compilation failed: {e}")
self._assign_addrmaps_()
[docs]
def load_definition(self, path : str) -> None:
"""
Load the RAL definition from a file
:param path: The path to the RAL definition file
:type path: str
:return: None
"""
if path.endswith(".rdl"):
self._load_rdl_(path)
elif path.endswith(".xml"):
self._load_ipxact_(path)
else:
self.fatal(f"Unsupported RDL file type: {path}")
[docs]
def find_by_name(self, addrmaps : list[Addrmap] = None, name : str = "*", types : tuple[type] = None) -> list[RegNode | FieldNode]:
"""
Find a node by name
:param addrmaps: The address maps to search
:type addrmaps: list[Addrmap]
:param name: The name to search for
:type name: str
:param types: The types to search for
:type types: tuple[type]
:return: The found node
:rtype: list[RegNode | FieldNode]
"""
matches = []
if addrmaps is None:
addrmaps = self._addrmaps_.values()
if types is None:
types = (RegNode, FieldNode)
for addrmap in addrmaps:
for node in addrmap.descendants():
if isinstance(node, types):
if "." in name:
if fnmatch(node.get_rel_path(addrmap), name):
matches.append(node)
else:
if fnmatch(node.inst_name, name):
matches.append(node)
return matches
[docs]
def find_by_address(self, addrmaps : list[Addrmap] = None, address : int = 0, types : tuple[type] = None) -> list[RegNode | FieldNode]:
"""
Find a node by Address
:param addrmaps: The address maps to search
:type addrmaps: list[Addrmap]
:param address: The address to search for
:type address: int
:param types: The types to search for
:type types: tuple[type]
:return: The found node
:rtype: list[RegNode | FieldNode]
"""
matches = []
for m in self.find_by_name(addrmaps=addrmaps, name = "*", types=types):
if isinstance(m, FieldNode):
if m.parent.absolute_address == address:
matches.append(m)
else:
if m.absolute_address == address:
matches.append(m)
return matches
[docs]
def assign_callbacks(self, addrmaps : list[Addrmap] = None, read : callable = None, write : callable = None) -> None:
"""
Assign callbacks to the RAL
:param addrmaps: The address maps to assign callbacks to
:type addrmaps: list[Addrmap]
:param read: The read callback
:type read: callable
:param write: The write callback
:type write: callable
:return: None
"""
if addrmaps is None:
addrmaps = self._addrmaps_.values()
for addrmap in addrmaps:
if read is not None:
addrmap.read_callback = read
if write is not None:
addrmap.write_callback = write
__all__ = ["RAL"]