120 lines
4.8 KiB
Python
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)
|
|
)
|