Files
PeakRDL-regblock/src/peakrdl_regblock/scan_design.py
2023-05-15 22:53:17 -07:00

120 lines
4.8 KiB
Python

from typing import TYPE_CHECKING, Optional
from systemrdl.walker import RDLListener, RDLWalker, WalkerAction
from systemrdl.node import SignalNode, RegNode
if TYPE_CHECKING:
from systemrdl.node import Node, FieldNode, AddressableNode, AddrmapNode
from .exporter import RegblockExporter
class DesignScanner(RDLListener):
"""
Scans through the register model and validates that any unsupported features
are not present.
Also collects any information that is required prior to the start of the export process.
"""
def __init__(self, exp:'RegblockExporter') -> None:
self.exp = exp
self.ds = exp.ds
self.msg = self.top_node.env.msg
@property
def top_node(self) -> 'AddrmapNode':
return self.exp.ds.top_node
def _get_out_of_hier_field_reset(self) -> None:
current_node = self.top_node.parent
while current_node is not None:
for signal in current_node.signals():
if signal.get_property('field_reset'):
path = signal.get_path()
self.ds.out_of_hier_signals[path] = signal
return
current_node = current_node.parent
def do_scan(self) -> None:
# Collect cpuif reset, if any.
cpuif_reset = self.top_node.cpuif_reset
if cpuif_reset is not None:
path = cpuif_reset.get_path()
rel_path = cpuif_reset.get_rel_path(self.top_node)
if rel_path.startswith("^"):
self.ds.out_of_hier_signals[path] = cpuif_reset
else:
self.ds.in_hier_signal_paths.add(path)
# collect out-of-hier field_reset, if any
self._get_out_of_hier_field_reset()
# Ensure addrmap is not a bridge. This concept does not make sense for
# terminal components.
if self.top_node.get_property('bridge'):
self.msg.error(
"Regblock generator does not support exporting bridge address maps",
self.top_node.inst.property_src_ref.get('bridge', self.top_node.inst.inst_src_ref)
)
RDLWalker().walk(self.top_node, self)
if self.msg.had_error:
self.msg.fatal(
"Unable to export due to previous errors"
)
def enter_Component(self, node: 'Node') -> Optional[WalkerAction]:
if node.external and (node != self.top_node):
# Do not inspect external components. None of my business
return WalkerAction.SkipDescendants
# Collect any signals that are referenced by a property
for prop_name in node.list_properties():
value = node.get_property(prop_name)
if isinstance(value, SignalNode):
path = value.get_path()
rel_path = value.get_rel_path(self.top_node)
if rel_path.startswith("^"):
self.ds.out_of_hier_signals[path] = value
else:
self.ds.in_hier_signal_paths.add(path)
if prop_name == "encode":
if value not in self.ds.user_enums:
self.ds.user_enums.append(value)
return WalkerAction.Continue
def enter_AddressableComponent(self, node: 'AddressableNode') -> None:
if node.external and node != self.top_node:
self.ds.has_external_addressable = True
if not isinstance(node, RegNode):
self.ds.has_external_block = True
def enter_Reg(self, node: 'RegNode') -> None:
# The CPUIF's bus width is sized according to the largest accesswidth in the design
accesswidth = node.get_property('accesswidth')
self.exp.ds.cpuif_data_width = max(self.exp.ds.cpuif_data_width, accesswidth)
self.ds.has_buffered_write_regs = self.ds.has_buffered_write_regs or bool(node.get_property('buffer_writes'))
self.ds.has_buffered_read_regs = self.ds.has_buffered_read_regs or bool(node.get_property('buffer_reads'))
def enter_Signal(self, node: 'SignalNode') -> None:
if node.get_property('field_reset'):
path = node.get_path()
self.ds.in_hier_signal_paths.add(path)
def enter_Field(self, node: 'FieldNode') -> None:
if node.is_sw_writable and (node.msb < node.lsb):
self.ds.has_writable_msb0_fields = True
if node.get_property('paritycheck') and node.implements_storage:
self.ds.has_paritycheck = True
if node.get_property('reset') is None:
self.msg.warning(
f"Field '{node.inst_name}' includes parity check logic, but "
"its reset value was not defined. Will result in an undefined "
"value on the module's 'parity_error' output.",
self.top_node.inst.property_src_ref.get('paritycheck', self.top_node.inst.inst_src_ref)
)