Rework hwif to reuse typedefs more intelligently
This commit is contained in:
@@ -70,6 +70,7 @@ class RegblockExporter:
|
|||||||
cpuif_cls = kwargs.pop("cpuif_cls", APB3_Cpuif)
|
cpuif_cls = kwargs.pop("cpuif_cls", APB3_Cpuif)
|
||||||
module_name = kwargs.pop("module_name", self.top_node.inst_name)
|
module_name = kwargs.pop("module_name", self.top_node.inst_name)
|
||||||
package_name = kwargs.pop("package_name", module_name + "_pkg")
|
package_name = kwargs.pop("package_name", module_name + "_pkg")
|
||||||
|
reuse_hwif_typedefs = kwargs.pop("reuse_hwif_typedefs", True)
|
||||||
|
|
||||||
# Pipelining options
|
# Pipelining options
|
||||||
retime_read_fanin = kwargs.pop("retime_read_fanin", False)
|
retime_read_fanin = kwargs.pop("retime_read_fanin", False)
|
||||||
@@ -107,6 +108,7 @@ class RegblockExporter:
|
|||||||
self.hwif = Hwif(
|
self.hwif = Hwif(
|
||||||
self,
|
self,
|
||||||
package_name=package_name,
|
package_name=package_name,
|
||||||
|
reuse_typedefs=reuse_hwif_typedefs
|
||||||
)
|
)
|
||||||
|
|
||||||
self.readback = Readback(
|
self.readback = Readback(
|
||||||
|
|||||||
@@ -1,299 +0,0 @@
|
|||||||
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
|
|
||||||
|
|
||||||
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):
|
|
||||||
self.exp = exp
|
|
||||||
self.package_name = package_name
|
|
||||||
|
|
||||||
self.has_input_struct = None
|
|
||||||
self.has_output_struct = None
|
|
||||||
self._indent_level = 0
|
|
||||||
|
|
||||||
@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 = []
|
|
||||||
|
|
||||||
self.has_input_struct = self._do_struct_addressable(lines, self.top_node, is_input=True)
|
|
||||||
self.has_output_struct = self._do_struct_addressable(lines, self.top_node, is_input=False)
|
|
||||||
|
|
||||||
return "\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:
|
|
||||||
lines.append(f"input {self.package_name}::{self._get_struct_name(self.top_node, is_input=True)} hwif_in")
|
|
||||||
if self.has_output_struct:
|
|
||||||
lines.append(f"output {self.package_name}::{self._get_struct_name(self.top_node, is_input=False)} hwif_out")
|
|
||||||
|
|
||||||
return ",\n".join(lines)
|
|
||||||
|
|
||||||
|
|
||||||
#---------------------------------------------------------------------------
|
|
||||||
# Struct generation functions
|
|
||||||
#---------------------------------------------------------------------------
|
|
||||||
@property
|
|
||||||
def _indent(self) -> str:
|
|
||||||
return " " * self._indent_level
|
|
||||||
|
|
||||||
def _get_node_array_suffix(self, node:AddressableNode) -> str:
|
|
||||||
if node.is_array:
|
|
||||||
return "".join([f'[{dim}]' for dim in node.array_dimensions])
|
|
||||||
return ""
|
|
||||||
|
|
||||||
def _get_struct_name(self, node:Node, is_input:bool = True) -> str:
|
|
||||||
base = node.get_rel_path(
|
|
||||||
self.top_node.parent,
|
|
||||||
hier_separator="__",
|
|
||||||
array_suffix="x",
|
|
||||||
empty_array_suffix="x"
|
|
||||||
)
|
|
||||||
if is_input:
|
|
||||||
return f'{base}__in_t'
|
|
||||||
return f'{base}__out_t'
|
|
||||||
|
|
||||||
def _do_struct_addressable(self, lines:list, node:AddressableNode, is_input:bool = True) -> bool:
|
|
||||||
|
|
||||||
struct_children = []
|
|
||||||
|
|
||||||
# Generate structs for children first
|
|
||||||
for child in node.children():
|
|
||||||
if isinstance(child, AddressableNode):
|
|
||||||
if self._do_struct_addressable(lines, child, is_input):
|
|
||||||
struct_children.append(child)
|
|
||||||
elif isinstance(child, FieldNode):
|
|
||||||
if self._do_struct_field(lines, child, is_input):
|
|
||||||
struct_children.append(child)
|
|
||||||
elif is_input and isinstance(child, SignalNode):
|
|
||||||
# No child struct needed here
|
|
||||||
# TODO: Skip if this is a top-level child
|
|
||||||
struct_children.append(child)
|
|
||||||
|
|
||||||
# Generate this addressable node's struct
|
|
||||||
if struct_children:
|
|
||||||
lines.append("")
|
|
||||||
lines.append(f"{self._indent}// {node.get_rel_path(self.top_node.parent)}")
|
|
||||||
lines.append(f"{self._indent}typedef struct {{")
|
|
||||||
self._indent_level += 1
|
|
||||||
for child in struct_children:
|
|
||||||
if isinstance(child, AddressableNode):
|
|
||||||
lines.append(f"{self._indent}{self._get_struct_name(child, is_input)} {child.inst_name}{self._get_node_array_suffix(child)};")
|
|
||||||
elif isinstance(child, FieldNode):
|
|
||||||
lines.append(f"{self._indent}{self._get_struct_name(child, is_input)} {child.inst_name};")
|
|
||||||
elif isinstance(child, SignalNode):
|
|
||||||
if child.width == 1:
|
|
||||||
lines.append(f"{self._indent}logic {child.inst_name};")
|
|
||||||
else:
|
|
||||||
lines.append(f"{self._indent}logic [{child.msb}:{child.lsb}] {child.inst_name};")
|
|
||||||
|
|
||||||
self._indent_level -= 1
|
|
||||||
lines.append(f"{self._indent}}} {self._get_struct_name(node, is_input)};")
|
|
||||||
|
|
||||||
return bool(struct_children)
|
|
||||||
|
|
||||||
def _do_struct_field(self, lines:list, node:FieldNode, is_input:bool = True) -> bool:
|
|
||||||
contents = []
|
|
||||||
|
|
||||||
if is_input:
|
|
||||||
contents = self._get_struct_input_field_contents(node)
|
|
||||||
else:
|
|
||||||
contents = self._get_struct_output_field_contents(node)
|
|
||||||
|
|
||||||
if contents:
|
|
||||||
lines.append("")
|
|
||||||
lines.append(f"{self._indent}// {node.get_rel_path(self.top_node.parent)}")
|
|
||||||
lines.append(f"{self._indent}typedef struct {{")
|
|
||||||
self._indent_level += 1
|
|
||||||
for member in contents:
|
|
||||||
lines.append(self._indent + member)
|
|
||||||
self._indent_level -= 1
|
|
||||||
lines.append(f"{self._indent}}} {self._get_struct_name(node, is_input)};")
|
|
||||||
|
|
||||||
return bool(contents)
|
|
||||||
|
|
||||||
def _get_struct_input_field_contents(self, node:FieldNode) -> List[str]:
|
|
||||||
contents = []
|
|
||||||
|
|
||||||
# Provide input to field's value if it is writable by hw
|
|
||||||
if self.has_value_input(node):
|
|
||||||
if node.width == 1:
|
|
||||||
contents.append("logic value;")
|
|
||||||
else:
|
|
||||||
contents.append(f"logic [{node.width-1}:0] value;")
|
|
||||||
|
|
||||||
# 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:
|
|
||||||
contents.append(f"logic {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
|
|
||||||
contents.append("logic incr;")
|
|
||||||
|
|
||||||
width = node.get_property('incrwidth')
|
|
||||||
if width:
|
|
||||||
# Implies a corresponding incrvalue input
|
|
||||||
contents.append(f"logic [{width-1}:0] incrvalue;")
|
|
||||||
|
|
||||||
if node.is_down_counter:
|
|
||||||
if not node.get_property('decr'):
|
|
||||||
# User did not provide their own decr component reference.
|
|
||||||
# Imply an input
|
|
||||||
contents.append("logic decr;")
|
|
||||||
|
|
||||||
width = node.get_property('decrwidth')
|
|
||||||
if width:
|
|
||||||
# Implies a corresponding decrvalue input
|
|
||||||
contents.append(f"logic [{width-1}:0] decrvalue;")
|
|
||||||
|
|
||||||
# TODO:
|
|
||||||
"""
|
|
||||||
signals!
|
|
||||||
any signal instances instantiated in the scope
|
|
||||||
"""
|
|
||||||
|
|
||||||
return contents
|
|
||||||
|
|
||||||
def _get_struct_output_field_contents(self, node:FieldNode) -> List[str]:
|
|
||||||
contents = []
|
|
||||||
|
|
||||||
# Expose field's value if it is readable by hw
|
|
||||||
if self.has_value_output(node):
|
|
||||||
if node.width == 1:
|
|
||||||
contents.append("logic value;")
|
|
||||||
else:
|
|
||||||
contents.append(f"logic [{node.width-1}:0] value;")
|
|
||||||
|
|
||||||
# Generate output bit signals enabled via property
|
|
||||||
for prop_name in ["anded", "ored", "xored", "swmod", "swacc", "overflow", "underflow"]:
|
|
||||||
if node.get_property(prop_name):
|
|
||||||
contents.append(f"logic {prop_name};")
|
|
||||||
|
|
||||||
if node.get_property('incrthreshold') is not False: # (explicitly not False. Not 0)
|
|
||||||
contents.append("logic incrthreshold;")
|
|
||||||
if node.get_property('decrthreshold') is not False: # (explicitly not False. Not 0)
|
|
||||||
contents.append("logic decrthreshold;")
|
|
||||||
|
|
||||||
return contents
|
|
||||||
|
|
||||||
#---------------------------------------------------------------------------
|
|
||||||
# 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
|
|
||||||
178
peakrdl/regblock/hwif/__init__.py
Normal file
178
peakrdl/regblock/hwif/__init__.py
Normal 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
|
||||||
119
peakrdl/regblock/hwif/generators.py
Normal file
119
peakrdl/regblock/hwif/generators.py
Normal 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'
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
from typing import TYPE_CHECKING, Optional, List
|
from typing import TYPE_CHECKING, Optional, List
|
||||||
import textwrap
|
import textwrap
|
||||||
|
from collections import OrderedDict
|
||||||
|
|
||||||
from systemrdl.walker import RDLListener, RDLWalker
|
from systemrdl.walker import RDLListener, RDLWalker
|
||||||
|
|
||||||
@@ -38,9 +39,11 @@ class _AnonymousStruct(_StructBase):
|
|||||||
|
|
||||||
|
|
||||||
class _TypedefStruct(_StructBase):
|
class _TypedefStruct(_StructBase):
|
||||||
def __init__(self, type_name: str):
|
def __init__(self, type_name: str, inst_name: Optional[str] = None, array_dimensions: Optional[List[int]] = None):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.type_name = type_name
|
self.type_name = type_name
|
||||||
|
self.inst_name = inst_name
|
||||||
|
self.array_dimensions = array_dimensions
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return (
|
return (
|
||||||
@@ -49,6 +52,16 @@ class _TypedefStruct(_StructBase):
|
|||||||
+ f"\n}} {self.type_name};"
|
+ f"\n}} {self.type_name};"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def instantiation(self) -> str:
|
||||||
|
if self.array_dimensions:
|
||||||
|
suffix = "[" + "][".join((str(n) for n in self.array_dimensions)) + "]"
|
||||||
|
else:
|
||||||
|
suffix = ""
|
||||||
|
|
||||||
|
return f"{self.type_name} {self.inst_name}{suffix};"
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------------
|
||||||
|
|
||||||
class StructGenerator:
|
class StructGenerator:
|
||||||
|
|
||||||
@@ -137,3 +150,86 @@ class RDLStructGenerator(StructGenerator, RDLListener):
|
|||||||
|
|
||||||
def enter_Field(self, node: 'FieldNode') -> None:
|
def enter_Field(self, node: 'FieldNode') -> None:
|
||||||
self.add_member(node.inst_name, node.width)
|
self.add_member(node.inst_name, node.width)
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class FlatStructGenerator(StructGenerator):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self.typedefs = OrderedDict()
|
||||||
|
|
||||||
|
def push_struct(self, type_name: str, inst_name: str, array_dimensions: Optional[List[int]] = None) -> None:
|
||||||
|
s = _TypedefStruct(type_name, inst_name, array_dimensions)
|
||||||
|
self._struct_stack.append(s)
|
||||||
|
|
||||||
|
def pop_struct(self) -> None:
|
||||||
|
s = self._struct_stack.pop()
|
||||||
|
|
||||||
|
if s.children:
|
||||||
|
# struct is not empty. Attach it to the parent
|
||||||
|
self.current_struct.children.append(s.instantiation)
|
||||||
|
|
||||||
|
# Add to collection of struct definitions
|
||||||
|
if s.type_name not in self.typedefs:
|
||||||
|
self.typedefs[s.type_name] = s
|
||||||
|
|
||||||
|
def finish(self) -> Optional[str]:
|
||||||
|
s = self._struct_stack.pop()
|
||||||
|
assert not self._struct_stack
|
||||||
|
|
||||||
|
# no children, no struct.
|
||||||
|
if not s.children:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Add to collection of struct definitions
|
||||||
|
if s.type_name not in self.typedefs:
|
||||||
|
self.typedefs[s.type_name] = s
|
||||||
|
|
||||||
|
all_structs = [str(s) for s in self.typedefs.values()]
|
||||||
|
|
||||||
|
return "\n\n".join(all_structs)
|
||||||
|
|
||||||
|
|
||||||
|
class RDLFlatStructGenerator(FlatStructGenerator, RDLListener):
|
||||||
|
"""
|
||||||
|
Struct generator that naively translates an RDL node tree into a flat list
|
||||||
|
of typedefs
|
||||||
|
|
||||||
|
This can be extended to add more intelligent behavior
|
||||||
|
"""
|
||||||
|
|
||||||
|
def get_typdef_name(self, node:'Node') -> str:
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def get_struct(self, node: 'Node', type_name: str) -> Optional[str]:
|
||||||
|
self.start(type_name)
|
||||||
|
|
||||||
|
walker = RDLWalker()
|
||||||
|
walker.walk(node, self, skip_top=True)
|
||||||
|
|
||||||
|
return self.finish()
|
||||||
|
|
||||||
|
def enter_Addrmap(self, node: 'AddrmapNode') -> None:
|
||||||
|
type_name = self.get_typdef_name(node)
|
||||||
|
self.push_struct(type_name, node.inst_name, node.array_dimensions)
|
||||||
|
|
||||||
|
def exit_Addrmap(self, node: 'AddrmapNode') -> None:
|
||||||
|
self.pop_struct()
|
||||||
|
|
||||||
|
def enter_Regfile(self, node: 'RegfileNode') -> None:
|
||||||
|
type_name = self.get_typdef_name(node)
|
||||||
|
self.push_struct(type_name, node.inst_name, node.array_dimensions)
|
||||||
|
|
||||||
|
def exit_Regfile(self, node: 'RegfileNode') -> None:
|
||||||
|
self.pop_struct()
|
||||||
|
|
||||||
|
def enter_Reg(self, node: 'RegNode') -> None:
|
||||||
|
type_name = self.get_typdef_name(node)
|
||||||
|
self.push_struct(type_name, node.inst_name, node.array_dimensions)
|
||||||
|
|
||||||
|
def exit_Reg(self, node: 'RegNode') -> None:
|
||||||
|
self.pop_struct()
|
||||||
|
|
||||||
|
def enter_Field(self, node: 'FieldNode') -> None:
|
||||||
|
self.add_member(node.inst_name, node.width)
|
||||||
|
|||||||
@@ -15,9 +15,10 @@ def get_permutations(spec):
|
|||||||
return param_list
|
return param_list
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
# TODO: this wont scale well. Create groups of permutatuions. not necessary to permute everything all the time.
|
# TODO: this wont scale well. Create groups of permutations. not necessary to permute everything all the time.
|
||||||
TEST_PARAMS = get_permutations({
|
TEST_PARAMS = get_permutations({
|
||||||
"cpuif": all_cpuif,
|
"cpuif": all_cpuif,
|
||||||
"retime_read_fanin": [True, False],
|
"retime_read_fanin": [True, False],
|
||||||
"retime_read_response": [True, False],
|
"retime_read_response": [True, False],
|
||||||
|
"reuse_hwif_typedefs": [True, False],
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user