from typing import TYPE_CHECKING, Union, Optional from systemrdl.node import AddrmapNode, FieldNode, SignalNode, RegNode, AddressableNode from systemrdl.rdltypes import PropertyReference from .sv_int import SVInt if TYPE_CHECKING: from .exporter import RegblockExporter, DesignState 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 ds(self) -> 'DesignState': return self.exp.ds @property def top_node(self) -> AddrmapNode: return self.exp.ds.top_node def get_value(self, obj: Union[int, FieldNode, SignalNode, PropertyReference], width: Optional[int] = None) -> Union[SVInt, 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. The optional width argument can be provided to hint at the expression's desired bitwidth. """ if isinstance(obj, int): # Is a simple scalar value return SVInt(obj, width) 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, width) # 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, obj.width) 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, width) if isinstance(obj, PropertyReference): if isinstance(obj.node, FieldNode): return self.get_field_propref_value(obj.node, obj.name, width) 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, width: Optional[int] = None, ) -> Union[SVInt, 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), width) # 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, width) # 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, width) 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], reduce_substrobes: bool=True) -> str: """ Returns the Verilog string that represents the register's access strobe """ return self.address_decode.get_access_strobe(obj, reduce_substrobes) def get_external_block_access_strobe(self, obj: 'AddressableNode') -> str: """ Returns the Verilog string that represents the external block's access strobe """ return self.address_decode.get_external_block_access_strobe(obj) @property def default_resetsignal_name(self) -> str: s = "rst" if self.ds.default_reset_async: s = f"a{s}" if self.ds.default_reset_activelow: s = f"{s}_n" return s def get_resetsignal(self, obj: Optional[SignalNode] = None) -> str: """ Returns a normalized active-high reset signal """ if isinstance(obj, SignalNode): s = self.get_value(obj) if obj.get_property('activehigh'): return str(s) else: return f"~{s}" # No explicit reset signal specified. Fall back to default reset signal s = self.default_resetsignal_name if self.ds.default_reset_activelow: s = f"~{s}" return s def get_always_ff_event(self, resetsignal: Optional[SignalNode] = None) -> str: if resetsignal is None: # No explicit reset signal specified. Fall back to default reset signal if self.ds.default_reset_async: if self.ds.default_reset_activelow: return f"@(posedge clk or negedge {self.default_resetsignal_name})" else: return f"@(posedge clk or posedge {self.default_resetsignal_name})" else: return "@(posedge clk)" elif resetsignal.get_property('async') and resetsignal.get_property('activehigh'): return f"@(posedge clk or posedge {self.get_value(resetsignal)})" elif resetsignal.get_property('async') and not resetsignal.get_property('activehigh'): return f"@(posedge clk or negedge {self.get_value(resetsignal)})" return "@(posedge clk)"