Rework hwif to reuse typedefs more intelligently

This commit is contained in:
Alex Mykyta
2021-12-13 21:36:31 -08:00
parent ee8d74b455
commit 7d0130078d
6 changed files with 398 additions and 301 deletions

View File

@@ -0,0 +1,178 @@
from typing import TYPE_CHECKING, Union, List
from systemrdl.node import AddrmapNode, Node, SignalNode, FieldNode, AddressableNode
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, reuse_typedefs: bool):
self.exp = exp
self.package_name = package_name
self.has_input_struct = None
self.has_output_struct = None
self._indent_level = 0
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.top_node)
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.top_node)
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):
path = get_indexed_path(self.top_node, obj)
return "hwif_in." + path + ".value"
elif isinstance(obj, SignalNode):
# TODO: Implement this
raise NotImplementedError()
elif isinstance(obj, PropertyReference):
return self.get_implied_prop_input_identifier(obj.node, obj.name)
raise RuntimeError("Unhandled reference to: %s", 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("Unhandled reference to: %s", obj)
def get_implied_prop_output_identifier(self, field: FieldNode, prop: str) -> str:
assert prop in {
"anded", "ored", "xored", "swmod", "swacc",
"incrthreshold", "decrthreshold", "overflow", "underflow"
}
path = get_indexed_path(self.top_node, field)
return "hwif_out." + path + "." + prop

View File

@@ -0,0 +1,119 @@
from typing import TYPE_CHECKING
from ..struct_generator import RDLFlatStructGenerator
if TYPE_CHECKING:
from systemrdl.node import Node, SignalNode, FieldNode
class InputStructGenerator_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}__in_t'
def enter_Signal(self, node: 'SignalNode') -> None:
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 value if it is writable by hw
if node.is_hw_writable:
self.add_member("value", 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()
#-------------------------------------------------------------------------------
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)
return f'{scope_path}__{node.type_name}__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)
return f'{scope_path}__{node.type_name}__out_t'