Switch to use regular non-namespaced package
This commit is contained in:
196
src/peakrdl_regblock/hwif/__init__.py
Normal file
196
src/peakrdl_regblock/hwif/__init__.py
Normal file
@@ -0,0 +1,196 @@
|
||||
from typing import TYPE_CHECKING, Union, List, Set, Dict
|
||||
|
||||
from systemrdl.node import AddrmapNode, Node, SignalNode, FieldNode, AddressableNode, RegNode
|
||||
from systemrdl.rdltypes import PropertyReference
|
||||
|
||||
from ..utils import get_indexed_path
|
||||
|
||||
from .generators import InputStructGenerator_Hier, OutputStructGenerator_Hier
|
||||
from .generators import InputStructGenerator_TypeScope, OutputStructGenerator_TypeScope
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ..exporter import RegblockExporter
|
||||
|
||||
class Hwif:
|
||||
"""
|
||||
Defines how the hardware input/output signals are generated:
|
||||
- Field outputs
|
||||
- Field inputs
|
||||
- Signal inputs (except those that are promoted to the top)
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, exp: 'RegblockExporter', package_name: str,
|
||||
in_hier_signal_paths: Set[str], out_of_hier_signals: Dict[str, SignalNode],
|
||||
reuse_typedefs: bool
|
||||
):
|
||||
self.exp = exp
|
||||
self.package_name = package_name
|
||||
|
||||
self.has_input_struct = False
|
||||
self.has_output_struct = False
|
||||
|
||||
self.in_hier_signal_paths = in_hier_signal_paths
|
||||
self.out_of_hier_signals = out_of_hier_signals
|
||||
|
||||
if reuse_typedefs:
|
||||
self._gen_in_cls = InputStructGenerator_TypeScope
|
||||
self._gen_out_cls = OutputStructGenerator_TypeScope
|
||||
else:
|
||||
self._gen_in_cls = InputStructGenerator_Hier
|
||||
self._gen_out_cls = OutputStructGenerator_Hier
|
||||
|
||||
@property
|
||||
def top_node(self) -> AddrmapNode:
|
||||
return self.exp.top_node
|
||||
|
||||
|
||||
def get_package_contents(self) -> str:
|
||||
"""
|
||||
If this hwif requires a package, generate the string
|
||||
"""
|
||||
lines = []
|
||||
|
||||
gen_in = self._gen_in_cls(self)
|
||||
structs_in = gen_in.get_struct(
|
||||
self.top_node,
|
||||
f"{self.top_node.inst_name}__in_t"
|
||||
)
|
||||
if structs_in is not None:
|
||||
self.has_input_struct = True
|
||||
lines.append(structs_in)
|
||||
else:
|
||||
self.has_input_struct = False
|
||||
|
||||
gen_out = self._gen_out_cls(self)
|
||||
structs_out = gen_out.get_struct(
|
||||
self.top_node,
|
||||
f"{self.top_node.inst_name}__out_t"
|
||||
)
|
||||
if structs_out is not None:
|
||||
self.has_output_struct = True
|
||||
lines.append(structs_out)
|
||||
else:
|
||||
self.has_output_struct = False
|
||||
|
||||
return "\n\n".join(lines)
|
||||
|
||||
|
||||
@property
|
||||
def port_declaration(self) -> str:
|
||||
"""
|
||||
Returns the declaration string for all I/O ports in the hwif group
|
||||
"""
|
||||
|
||||
# Assume get_package_declaration() is always called prior to this
|
||||
assert self.has_input_struct is not None
|
||||
assert self.has_output_struct is not None
|
||||
|
||||
lines = []
|
||||
if self.has_input_struct:
|
||||
type_name = f"{self.top_node.inst_name}__in_t"
|
||||
lines.append(f"input {self.package_name}::{type_name} hwif_in")
|
||||
if self.has_output_struct:
|
||||
type_name = f"{self.top_node.inst_name}__out_t"
|
||||
lines.append(f"output {self.package_name}::{type_name} hwif_out")
|
||||
|
||||
return ",\n".join(lines)
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# hwif utility functions
|
||||
#---------------------------------------------------------------------------
|
||||
def has_value_input(self, obj: Union[FieldNode, SignalNode]) -> bool:
|
||||
"""
|
||||
Returns True if the object infers an input wire in the hwif
|
||||
"""
|
||||
if isinstance(obj, FieldNode):
|
||||
return obj.is_hw_writable
|
||||
elif isinstance(obj, SignalNode):
|
||||
# Signals are implicitly always inputs
|
||||
return True
|
||||
else:
|
||||
raise RuntimeError
|
||||
|
||||
|
||||
def has_value_output(self, obj: FieldNode) -> bool:
|
||||
"""
|
||||
Returns True if the object infers an output wire in the hwif
|
||||
"""
|
||||
return obj.is_hw_readable
|
||||
|
||||
|
||||
def get_input_identifier(self, obj: Union[FieldNode, SignalNode, PropertyReference]) -> str:
|
||||
"""
|
||||
Returns the identifier string that best represents the input object.
|
||||
|
||||
if obj is:
|
||||
Field: the fields hw input value port
|
||||
Signal: signal input value
|
||||
Prop reference:
|
||||
could be an implied hwclr/hwset/swwe/swwel/we/wel input
|
||||
|
||||
raises an exception if obj is invalid
|
||||
"""
|
||||
if isinstance(obj, FieldNode):
|
||||
next_value = obj.get_property('next')
|
||||
if next_value is not None:
|
||||
# 'next' property replaces the inferred input signal
|
||||
return self.exp.dereferencer.get_value(next_value)
|
||||
# Otherwise, use inferred
|
||||
path = get_indexed_path(self.top_node, obj)
|
||||
return "hwif_in." + path + ".next"
|
||||
elif isinstance(obj, SignalNode):
|
||||
if obj.get_path() in self.out_of_hier_signals:
|
||||
return obj.inst_name
|
||||
path = get_indexed_path(self.top_node, obj)
|
||||
return "hwif_in." + path
|
||||
elif isinstance(obj, PropertyReference):
|
||||
return self.get_implied_prop_input_identifier(obj.node, obj.name)
|
||||
|
||||
raise RuntimeError(f"Unhandled reference to: {obj}")
|
||||
|
||||
|
||||
def get_implied_prop_input_identifier(self, field: FieldNode, prop: str) -> str:
|
||||
assert prop in {
|
||||
'hwclr', 'hwset', 'swwe', 'swwel', 'we', 'wel',
|
||||
'incr', 'decr', 'incrvalue', 'decrvalue'
|
||||
}
|
||||
path = get_indexed_path(self.top_node, field)
|
||||
return "hwif_in." + path + "." + prop
|
||||
|
||||
|
||||
def get_output_identifier(self, obj: Union[FieldNode, PropertyReference]) -> str:
|
||||
"""
|
||||
Returns the identifier string that best represents the output object.
|
||||
|
||||
if obj is:
|
||||
Field: the fields hw output value port
|
||||
Property ref: this is also part of the struct
|
||||
|
||||
raises an exception if obj is invalid
|
||||
"""
|
||||
if isinstance(obj, FieldNode):
|
||||
path = get_indexed_path(self.top_node, obj)
|
||||
return "hwif_out." + path + ".value"
|
||||
elif isinstance(obj, PropertyReference):
|
||||
# TODO: this might be dead code.
|
||||
# not sure when anything would call this function with a prop ref
|
||||
# when dereferencer's get_value is more useful here
|
||||
assert obj.node.get_property(obj.name)
|
||||
return self.get_implied_prop_output_identifier(obj.node, obj.name)
|
||||
|
||||
raise RuntimeError(f"Unhandled reference to: {obj}")
|
||||
|
||||
|
||||
def get_implied_prop_output_identifier(self, node: Union[FieldNode, RegNode], prop: str) -> str:
|
||||
if isinstance(node, FieldNode):
|
||||
assert prop in {
|
||||
"anded", "ored", "xored", "swmod", "swacc",
|
||||
"incrthreshold", "decrthreshold", "overflow", "underflow",
|
||||
}
|
||||
elif isinstance(node, RegNode):
|
||||
assert prop in {
|
||||
"intr", "halt",
|
||||
}
|
||||
path = get_indexed_path(self.top_node, node)
|
||||
return "hwif_out." + path + "." + prop
|
||||
168
src/peakrdl_regblock/hwif/generators.py
Normal file
168
src/peakrdl_regblock/hwif/generators.py
Normal file
@@ -0,0 +1,168 @@
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from systemrdl.node import FieldNode
|
||||
|
||||
from ..struct_generator import RDLFlatStructGenerator
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from systemrdl.node import Node, SignalNode, RegNode
|
||||
from . import Hwif
|
||||
|
||||
class InputStructGenerator_Hier(RDLFlatStructGenerator):
|
||||
def __init__(self, hwif: 'Hwif') -> None:
|
||||
super().__init__()
|
||||
self.hwif = hwif
|
||||
self.top_node = hwif.top_node
|
||||
|
||||
def get_typdef_name(self, node:'Node') -> str:
|
||||
base = node.get_rel_path(
|
||||
self.top_node.parent,
|
||||
hier_separator="__",
|
||||
array_suffix="x",
|
||||
empty_array_suffix="x"
|
||||
)
|
||||
return f'{base}__in_t'
|
||||
|
||||
def enter_Signal(self, node: 'SignalNode') -> None:
|
||||
# only emit the signal if design scanner detected it is actually being used
|
||||
path = node.get_path()
|
||||
if path in self.hwif.in_hier_signal_paths:
|
||||
self.add_member(node.inst_name, node.width)
|
||||
|
||||
def enter_Field(self, node: 'FieldNode') -> None:
|
||||
type_name = self.get_typdef_name(node)
|
||||
self.push_struct(type_name, node.inst_name)
|
||||
|
||||
# Provide input to field's next value if it is writable by hw, and it
|
||||
# was not overridden by the 'next' property
|
||||
if node.is_hw_writable and node.get_property('next') is None:
|
||||
self.add_member("next", node.width)
|
||||
|
||||
# Generate implied inputs
|
||||
for prop_name in ["we", "wel", "swwe", "swwel", "hwclr", "hwset"]:
|
||||
# if property is boolean and true, implies a corresponding input signal on the hwif
|
||||
if node.get_property(prop_name) is True:
|
||||
self.add_member(prop_name)
|
||||
|
||||
# Generate any implied counter inputs
|
||||
if node.is_up_counter:
|
||||
if not node.get_property('incr'):
|
||||
# User did not provide their own incr component reference.
|
||||
# Imply an input
|
||||
self.add_member('incr')
|
||||
|
||||
width = node.get_property('incrwidth')
|
||||
if width:
|
||||
# Implies a corresponding incrvalue input
|
||||
self.add_member('incrvalue', width)
|
||||
|
||||
if node.is_down_counter:
|
||||
if not node.get_property('decr'):
|
||||
# User did not provide their own decr component reference.
|
||||
# Imply an input
|
||||
self.add_member('decr')
|
||||
|
||||
width = node.get_property('decrwidth')
|
||||
if width:
|
||||
# Implies a corresponding decrvalue input
|
||||
self.add_member('decrvalue', width)
|
||||
|
||||
def exit_Field(self, node: 'FieldNode') -> None:
|
||||
self.pop_struct()
|
||||
|
||||
|
||||
class OutputStructGenerator_Hier(RDLFlatStructGenerator):
|
||||
def __init__(self, top_node: 'Node'):
|
||||
super().__init__()
|
||||
self.top_node = top_node
|
||||
|
||||
def get_typdef_name(self, node:'Node') -> str:
|
||||
base = node.get_rel_path(
|
||||
self.top_node.parent,
|
||||
hier_separator="__",
|
||||
array_suffix="x",
|
||||
empty_array_suffix="x"
|
||||
)
|
||||
return f'{base}__out_t'
|
||||
|
||||
def enter_Field(self, node: 'FieldNode') -> None:
|
||||
type_name = self.get_typdef_name(node)
|
||||
self.push_struct(type_name, node.inst_name)
|
||||
|
||||
# Expose field's value if it is readable by hw
|
||||
if node.is_hw_readable:
|
||||
self.add_member("value", node.width)
|
||||
|
||||
# Generate output bit signals enabled via property
|
||||
for prop_name in ["anded", "ored", "xored", "swmod", "swacc", "overflow", "underflow"]:
|
||||
if node.get_property(prop_name):
|
||||
self.add_member(prop_name)
|
||||
|
||||
if node.get_property('incrthreshold') is not False: # (explicitly not False. Not 0)
|
||||
self.add_member('incrthreshold')
|
||||
if node.get_property('decrthreshold') is not False: # (explicitly not False. Not 0)
|
||||
self.add_member('decrthreshold')
|
||||
|
||||
def exit_Field(self, node: 'FieldNode') -> None:
|
||||
self.pop_struct()
|
||||
|
||||
def exit_Reg(self, node: 'RegNode') -> None:
|
||||
if node.is_interrupt_reg:
|
||||
self.add_member('intr')
|
||||
if node.is_halt_reg:
|
||||
self.add_member('halt')
|
||||
super().exit_Reg(node)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
class InputStructGenerator_TypeScope(InputStructGenerator_Hier):
|
||||
def get_typdef_name(self, node:'Node') -> str:
|
||||
scope_path = node.inst.get_scope_path("__")
|
||||
if scope_path is None:
|
||||
# Unable to determine a reusable type name. Fall back to hierarchical path
|
||||
# Add prefix to prevent collision when mixing namespace methods
|
||||
scope_path = "xtern__" + super().get_typdef_name(node)
|
||||
|
||||
if isinstance(node, FieldNode):
|
||||
extra_suffix = get_field_type_name_suffix(node)
|
||||
else:
|
||||
extra_suffix = ""
|
||||
|
||||
return f'{scope_path}__{node.type_name}{extra_suffix}__in_t'
|
||||
|
||||
class OutputStructGenerator_TypeScope(OutputStructGenerator_Hier):
|
||||
def get_typdef_name(self, node:'Node') -> str:
|
||||
scope_path = node.inst.get_scope_path("__")
|
||||
if scope_path is None:
|
||||
# Unable to determine a reusable type name. Fall back to hierarchical path
|
||||
# Add prefix to prevent collision when mixing namespace methods
|
||||
scope_path = "xtern__" + super().get_typdef_name(node)
|
||||
|
||||
if isinstance(node, FieldNode):
|
||||
extra_suffix = get_field_type_name_suffix(node)
|
||||
else:
|
||||
extra_suffix = ""
|
||||
|
||||
return f'{scope_path}__{node.type_name}{extra_suffix}__out_t'
|
||||
|
||||
|
||||
def get_field_type_name_suffix(field: FieldNode) -> str:
|
||||
"""
|
||||
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 field.inst.original_def is None:
|
||||
return ""
|
||||
|
||||
if field.inst.original_def.type_name is None:
|
||||
# is an anonymous definition. No extra suffix needed
|
||||
return ""
|
||||
|
||||
if "fieldwidth" in field.list_properties():
|
||||
# fieldwidth was explicitly set. This type name is already sufficiently distinct
|
||||
return ""
|
||||
|
||||
if field.width == 1:
|
||||
# field width is the default. Skip suffix
|
||||
return ""
|
||||
|
||||
return f"_w{field.width}"
|
||||
Reference in New Issue
Block a user