Files
PeakRDL-regblock/peakrdl/regblock/hwif.py
2021-12-04 17:31:12 -08:00

289 lines
11 KiB
Python

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.get_property("counter"):
if not node.get_property("incr"):
# User did not provide their own incr component reference.
# Imply an input
contents.append("logic incr;")
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("incrwidth")
if width:
# Implies a corresponding incrvalue input
contents.append(f"logic [{width-1}:0] incrvalue;")
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"]:
if node.get_property(prop_name):
contents.append(f"logic {prop_name};")
# TODO: Are there was_written/was_read strobes too?
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):
assert obj.name in {'hwclr', 'hwset', 'swwe', 'swwel', 'we', 'wel'}
path = get_indexed_path(self.top_node, obj.node)
return "hwif_in." + path + "." + 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'}
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):
assert obj.name in {"anded", "ored", "xored", "swmod", "swacc"}
assert obj.node.get_property(obj.name)
path = get_indexed_path(self.top_node, obj.node)
return "hwif_out." + path + "." + 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"}
path = get_indexed_path(self.top_node, field)
return "hwif_out." + path + "." + prop