Files
PeakRDL-regblock/peakrdl/regblock/dereferencer.py
2022-02-25 23:23:47 -08:00

217 lines
7.6 KiB
Python

from typing import TYPE_CHECKING, Union, Optional
from systemrdl.node import AddrmapNode, FieldNode, SignalNode, RegNode
from systemrdl.rdltypes import PropertyReference
if TYPE_CHECKING:
from .exporter import RegblockExporter
from .hwif import Hwif
from .field_logic import FieldLogic
from .addr_decode import AddressDecode
class Dereferencer:
"""
This class provides an interface to convert conceptual SystemRDL references
into Verilog identifiers
"""
def __init__(self, exp:'RegblockExporter'):
self.exp = exp
@property
def hwif(self) -> 'Hwif':
return self.exp.hwif
@property
def address_decode(self) -> 'AddressDecode':
return self.exp.address_decode
@property
def field_logic(self) -> 'FieldLogic':
return self.exp.field_logic
@property
def top_node(self) -> AddrmapNode:
return self.exp.top_node
def get_value(self, obj: Union[int, FieldNode, SignalNode, PropertyReference]) -> str:
"""
Returns the Verilog string that represents the readable value associated
with the object.
If given a simple scalar value, then the corresponding Verilog literal is returned.
If obj references a structural systemrdl object, then the corresponding Verilog
expression is returned that represents its value.
"""
if isinstance(obj, int):
# Is a simple scalar value
return f"'h{obj:x}"
if isinstance(obj, FieldNode):
if obj.implements_storage:
return self.field_logic.get_storage_identifier(obj)
if self.hwif.has_value_input(obj):
return self.hwif.get_input_identifier(obj)
# Field does not have a storage element, nor does it have a HW input
# must be a constant value as defined by its reset value
reset_value = obj.get_property('reset')
if reset_value is not None:
return self.get_value(reset_value)
else:
# No reset value defined!
obj.env.msg.warning(
f"Field '{obj.inst_name}' is a constant but does not have a known value (missing reset). Assigning it a value of X.",
obj.inst.inst_src_ref
)
return "'X"
if isinstance(obj, SignalNode):
# Signals are always inputs from the hwif
return self.hwif.get_input_identifier(obj)
if isinstance(obj, PropertyReference):
if isinstance(obj.node, FieldNode):
return self.get_field_propref_value(obj.node, obj.name)
elif isinstance(obj.node, RegNode):
return self.get_reg_propref_value(obj.node, obj.name)
else:
raise RuntimeError
raise RuntimeError(f"Unhandled reference to: {obj}")
def get_field_propref_value(self, field: FieldNode, prop_name: str) -> str:
# Value reduction properties.
# Wrap with the appropriate Verilog reduction operator
if prop_name == "anded":
val = self.get_value(field)
return f"&({val})"
elif prop_name == "ored":
val = self.get_value(field)
return f"|({val})"
elif prop_name == "xored":
val = self.get_value(field)
return f"^({val})"
# references that directly access a property value
if prop_name in {
'decrvalue',
'enable',
'haltenable',
'haltmask',
'hwenable',
'hwmask',
'incrvalue',
'mask',
'reset',
'resetsignal',
}:
return self.get_value(field.get_property(prop_name))
# Field Next
if prop_name == "next":
prop_value = field.get_property(prop_name)
if prop_value is None:
# unset by the user, points to the implied internal signal
return self.field_logic.get_field_combo_identifier(field, "next")
else:
return self.get_value(prop_value)
# References to another component value, or an implied input
if prop_name in {'hwclr', 'hwset'}:
prop_value = field.get_property(prop_name)
if prop_value is True:
# Points to inferred hwif input
return self.hwif.get_implied_prop_input_identifier(field, prop_name)
elif prop_value is False:
# This should never happen, as this is checked by the compiler's validator
raise RuntimeError
else:
return self.get_value(prop_value)
# References to another component value, or an implied input
# May have a complementary partner property
complementary_pairs = {
"we": "wel",
"wel": "we",
"swwe": "swwel",
"swwel": "swwe",
}
if prop_name in complementary_pairs:
prop_value = field.get_property(prop_name)
if prop_value is True:
# Points to inferred hwif input
return self.hwif.get_implied_prop_input_identifier(field, prop_name)
elif prop_value is False:
# Try complementary property
prop_value = field.get_property(complementary_pairs[prop_name])
if prop_value is True:
# Points to inferred hwif input
return f"!({self.hwif.get_implied_prop_input_identifier(field, complementary_pairs[prop_name])})"
elif prop_value is False:
# This should never happen, as this is checked by the compiler's validator
raise RuntimeError
else:
return f"!({self.get_value(prop_value)})"
else:
return self.get_value(prop_value)
if prop_name == "swacc":
return self.field_logic.get_swacc_identifier(field)
if prop_name == "swmod":
return self.field_logic.get_swmod_identifier(field)
# translate aliases
aliases = {
"saturate": "incrsaturate",
"threshold": "incrthreshold",
}
prop_name = aliases.get(prop_name, prop_name)
# Counter properties
if prop_name == 'incr':
return self.field_logic.get_counter_incr_strobe(field)
if prop_name == 'decr':
return self.field_logic.get_counter_decr_strobe(field)
if prop_name in {
'decrsaturate',
'decrthreshold',
'incrsaturate',
'incrthreshold',
'overflow',
'underflow',
}:
return self.field_logic.get_field_combo_identifier(field, prop_name)
raise RuntimeError(f"Unhandled reference to: {field}->{prop_name}")
def get_reg_propref_value(self, reg: RegNode, prop_name: str) -> str:
if prop_name in {'halt', 'intr'}:
return self.hwif.get_implied_prop_output_identifier(reg, prop_name)
raise NotImplementedError
def get_access_strobe(self, obj: Union[RegNode, FieldNode]) -> str:
"""
Returns the Verilog string that represents the register's access strobe
"""
return self.address_decode.get_access_strobe(obj)
def get_resetsignal(self, obj: Optional[SignalNode]) -> str:
"""
Returns a normalized active-high reset signal
"""
if isinstance(obj, SignalNode):
s = self.get_value(obj)
if obj.get_property('activehigh'):
return s
else:
return f"~{s}"
# default reset signal
return "rst"