diff --git a/docs/dev_notes/Validation Needed b/docs/dev_notes/Validation Needed index b9ee814..078660e 100644 --- a/docs/dev_notes/Validation Needed +++ b/docs/dev_notes/Validation Needed @@ -103,7 +103,7 @@ X sticky=true + "(posedge|negedge|bothedge) intr" Edge-sensitivty doesnt make sense for full-field stickiness X we/wel + implied or explicit "sticky"/"stickybit" - we/wel modifier doesnt make sense here. + we/wel modifier doesn't make sense here. ! hwclr/hwset/we/wel probably shouldn't be able to reference itself y->hwclr = y; diff --git a/docs/props/field.rst b/docs/props/field.rst index a2a6e20..4b6f03f 100644 --- a/docs/props/field.rst +++ b/docs/props/field.rst @@ -396,45 +396,52 @@ Interrupt Properties intr ^^^^ +If set, this field becomes an interrupt field. +The enclosing register infers an output signal ``hwif_out..intr`` which denotes +that an interrupt is active. This is an or-reduction of all interrupt fields +after applying the appropriate ``enable`` or ``mask`` to the field value. + level (default) - |NO| + |EX| + + Interrupt is level-sensitive. posedge - |NO| + |EX| negedge - |NO| + |EX| bothedge - |NO| + |EX| nonsticky - |NO| + |EX| enable ^^^^^^ -|NO| +|EX| mask ^^^^ -|NO| +|EX| haltenable ^^^^^^^^^^ -|NO| +|EX| haltmask ^^^^^^^^ -|NO| +|EX| sticky ^^^^^^ -|NO| +|EX| stickybit ^^^^^^^^^ -|NO| +|EX| -------------------------------------------------------------------------------- diff --git a/docs/props/rhs_props.rst b/docs/props/rhs_props.rst index 51671eb..dc4f3c7 100644 --- a/docs/props/rhs_props.rst +++ b/docs/props/rhs_props.rst @@ -206,8 +206,8 @@ Register reg -> intr ^^^^^^^^^^^ -|NO| +|EX| reg -> halt ^^^^^^^^^^^ -|NO| +|EX| diff --git a/peakrdl/regblock/dereferencer.py b/peakrdl/regblock/dereferencer.py index 6013515..5e1220b 100644 --- a/peakrdl/regblock/dereferencer.py +++ b/peakrdl/regblock/dereferencer.py @@ -191,7 +191,8 @@ class Dereferencer: def get_reg_propref_value(self, reg: RegNode, prop_name: str) -> str: - # TODO: halt, intr + if prop_name in {'halt', 'intr'}: + return self.hwif.get_implied_prop_output_identifier(reg, prop_name) raise NotImplementedError diff --git a/peakrdl/regblock/field_logic/__init__.py b/peakrdl/regblock/field_logic/__init__.py index 88e246e..cc870ed 100644 --- a/peakrdl/regblock/field_logic/__init__.py +++ b/peakrdl/regblock/field_logic/__init__.py @@ -1,6 +1,6 @@ from typing import TYPE_CHECKING -from systemrdl.rdltypes import PropertyReference, PrecedenceType +from systemrdl.rdltypes import PropertyReference, PrecedenceType, InterruptType from systemrdl.node import Node from .bases import AssignmentPrecedence, NextStateConditional @@ -9,6 +9,7 @@ from . import sw_onwrite from . import sw_singlepulse from . import hw_write from . import hw_set_clr +from . import hw_interrupts from ..utils import get_indexed_path @@ -33,7 +34,7 @@ class FieldLogic: return self.exp.top_node def get_storage_struct(self) -> str: - struct_gen = FieldStorageStructGenerator() + struct_gen = FieldStorageStructGenerator(self) s = struct_gen.get_struct(self.top_node, "field_storage_t") # Only declare the storage struct if it exists @@ -71,6 +72,15 @@ class FieldLogic: path = get_indexed_path(self.top_node, field) return f"field_storage.{path}.value" + def get_next_q_identifier(self, field: 'FieldNode') -> str: + """ + Returns the Verilog string that represents the storage register element + for the delayed 'next' input value + """ + assert field.implements_storage + path = get_indexed_path(self.top_node, field) + return f"field_storage.{path}.next_q" + def get_field_combo_identifier(self, field: 'FieldNode', name: str) -> str: """ Returns a Verilog string that represents a field's internal combinational @@ -194,6 +204,23 @@ class FieldLogic: return "1'b0" + def has_next_q(self, field: 'FieldNode') -> bool: + """ + Some fields require a delayed version of their 'next' input signal in + order to do edge-detection. + + Returns True if this is the case. + """ + if field.get_property('intr type') in { + InterruptType.posedge, + InterruptType.negedge, + InterruptType.bothedge + }: + return True + + return False + + #--------------------------------------------------------------------------- # Field Logic Conditionals #--------------------------------------------------------------------------- @@ -260,6 +287,14 @@ class FieldLogic: self.add_hw_conditional(hw_write.AlwaysWrite(self.exp), AssignmentPrecedence.HW_WRITE) self.add_hw_conditional(hw_write.WELWrite(self.exp), AssignmentPrecedence.HW_WRITE) self.add_hw_conditional(hw_write.WEWrite(self.exp), AssignmentPrecedence.HW_WRITE) + self.add_hw_conditional(hw_interrupts.Sticky(self.exp), AssignmentPrecedence.HW_WRITE) + self.add_hw_conditional(hw_interrupts.Stickybit(self.exp), AssignmentPrecedence.HW_WRITE) + self.add_hw_conditional(hw_interrupts.PosedgeStickybit(self.exp), AssignmentPrecedence.HW_WRITE) + self.add_hw_conditional(hw_interrupts.NegedgeStickybit(self.exp), AssignmentPrecedence.HW_WRITE) + self.add_hw_conditional(hw_interrupts.BothedgeStickybit(self.exp), AssignmentPrecedence.HW_WRITE) + self.add_hw_conditional(hw_interrupts.PosedgeNonsticky(self.exp), AssignmentPrecedence.HW_WRITE) + self.add_hw_conditional(hw_interrupts.NegedgeNonsticky(self.exp), AssignmentPrecedence.HW_WRITE) + self.add_hw_conditional(hw_interrupts.BothedgeNonsticky(self.exp), AssignmentPrecedence.HW_WRITE) self.add_hw_conditional(hw_set_clr.HWClear(self.exp), AssignmentPrecedence.HWCLR) diff --git a/peakrdl/regblock/field_logic/generators.py b/peakrdl/regblock/field_logic/generators.py index 0b91842..03582dc 100644 --- a/peakrdl/regblock/field_logic/generators.py +++ b/peakrdl/regblock/field_logic/generators.py @@ -8,7 +8,7 @@ from ..utils import get_indexed_path, get_always_ff_event if TYPE_CHECKING: from . import FieldLogic - from systemrdl.node import FieldNode + from systemrdl.node import FieldNode, RegNode class CombinationalStructGenerator(RDLStructGenerator): @@ -61,12 +61,19 @@ class CombinationalStructGenerator(RDLStructGenerator): class FieldStorageStructGenerator(RDLStructGenerator): + def __init__(self, field_logic: 'FieldLogic'): + super().__init__() + self.field_logic = field_logic + def enter_Field(self, node: 'FieldNode') -> None: self.push_struct(node.inst_name) if node.implements_storage: self.add_member("value", node.width) + if self.field_logic.has_next_q(node): + self.add_member("next_q", node.width) + self.pop_struct() @@ -79,6 +86,13 @@ class FieldLogicGenerator(RDLForLoopGenerator): self.field_storage_template = self.field_logic.exp.jj_env.get_template( "field_logic/templates/field_storage.sv" ) + self.intr_fields = [] + self.halt_fields = [] + + + def enter_Reg(self, node: 'RegNode') -> None: + self.intr_fields = [] + self.halt_fields = [] def enter_Field(self, node: 'FieldNode') -> None: @@ -87,6 +101,65 @@ class FieldLogicGenerator(RDLForLoopGenerator): self.assign_field_outputs(node) + if node.get_property('intr'): + self.intr_fields.append(node) + if node.get_property('haltenable') or node.get_property('haltmask'): + self.halt_fields.append(node) + + + def exit_Reg(self, node: 'RegNode') -> None: + # Assign register's intr output + if self.intr_fields: + strs = [] + for field in self.intr_fields: + enable = field.get_property('enable') + mask = field.get_property('mask') + F = self.exp.dereferencer.get_value(field) + if enable: + E = self.exp.dereferencer.get_value(enable) + s = f"|({F} & {E})" + elif mask: + M = self.exp.dereferencer.get_value(mask) + s = f"|({F} & ~{M})" + else: + s = f"|{F}" + strs.append(s) + + self.add_content( + f"assign {self.exp.hwif.get_implied_prop_output_identifier(node, 'intr')} =" + ) + self.add_content( + " " + + "\n || ".join(strs) + + ";" + ) + + # Assign register's halt output + if self.halt_fields: + strs = [] + for field in self.halt_fields: + enable = field.get_property('haltenable') + mask = field.get_property('haltmask') + F = self.exp.dereferencer.get_value(field) + if enable: + E = self.exp.dereferencer.get_value(enable) + s = f"|({F} & {E})" + elif mask: + M = self.exp.dereferencer.get_value(mask) + s = f"|({F} & ~{M})" + else: + s = f"|{F}" + strs.append(s) + + self.add_content( + f"assign {self.exp.hwif.get_implied_prop_output_identifier(node, 'halt')} =" + ) + self.add_content( + " " + + "\n || ".join(strs) + + ";" + ) + def generate_field_storage(self, node: 'FieldNode') -> None: conditionals = self.field_logic.get_conditionals(node) @@ -115,6 +188,7 @@ class FieldLogicGenerator(RDLForLoopGenerator): 'get_always_ff_event': lambda resetsignal : get_always_ff_event(self.exp.dereferencer, resetsignal), 'get_value': self.exp.dereferencer.get_value, 'get_resetsignal': self.exp.dereferencer.get_resetsignal, + 'get_input_identifier': self.exp.hwif.get_input_identifier, } self.add_content(self.field_storage_template.render(context)) diff --git a/peakrdl/regblock/field_logic/hw_interrupts.py b/peakrdl/regblock/field_logic/hw_interrupts.py new file mode 100644 index 0000000..27b43e4 --- /dev/null +++ b/peakrdl/regblock/field_logic/hw_interrupts.py @@ -0,0 +1,202 @@ +from typing import TYPE_CHECKING, List + +from .bases import NextStateConditional + +from systemrdl.rdltypes import InterruptType + +if TYPE_CHECKING: + from systemrdl.node import FieldNode + + +class Sticky(NextStateConditional): + """ + Normal multi-bit sticky + """ + comment = "multi-bit sticky" + def is_match(self, field: 'FieldNode') -> bool: + return ( + field.is_hw_writable + and field.get_property('sticky') + ) + + def get_predicate(self, field: 'FieldNode') -> str: + I = self.exp.hwif.get_input_identifier(field) + R = self.exp.field_logic.get_storage_identifier(field) + return f"({R} == '0) && ({I} != '0)" + + def get_assignments(self, field: 'FieldNode') -> List[str]: + I = self.exp.hwif.get_input_identifier(field) + return [ + f"next_c = {I};", + f"load_next_c = '1;", + ] + + +class Stickybit(NextStateConditional): + """ + Normal stickybit + """ + comment = "stickybit" + def is_match(self, field: 'FieldNode') -> bool: + return ( + field.is_hw_writable + and field.get_property('stickybit') + ) + + def get_predicate(self, field: 'FieldNode') -> str: + return self.exp.hwif.get_input_identifier(field) + + def get_assignments(self, field: 'FieldNode') -> List[str]: + I = self.exp.hwif.get_input_identifier(field) + R = self.exp.field_logic.get_storage_identifier(field) + return [ + f"next_c = {R} | {I};", + f"load_next_c = '1;", + ] + +class PosedgeStickybit(NextStateConditional): + """ + Positive edge stickybit + """ + comment = "posedge stickybit" + def is_match(self, field: 'FieldNode') -> bool: + return ( + field.is_hw_writable + and field.get_property('stickybit') + and field.get_property('intr type') == InterruptType.posedge + ) + + def get_predicate(self, field: 'FieldNode') -> str: + I = self.exp.hwif.get_input_identifier(field) + Iq = self.exp.field_logic.get_next_q_identifier(field) + return f"~{Iq} & {I}" + + def get_assignments(self, field: 'FieldNode') -> List[str]: + I = self.exp.hwif.get_input_identifier(field) + Iq = self.exp.field_logic.get_next_q_identifier(field) + R = self.exp.field_logic.get_storage_identifier(field) + return [ + f"next_c = {R} | (~{Iq} & {I});", + f"load_next_c = '1;", + ] + +class NegedgeStickybit(NextStateConditional): + """ + Negative edge stickybit + """ + comment = "negedge stickybit" + def is_match(self, field: 'FieldNode') -> bool: + return ( + field.is_hw_writable + and field.get_property('stickybit') + and field.get_property('intr type') == InterruptType.negedge + ) + + def get_predicate(self, field: 'FieldNode') -> str: + I = self.exp.hwif.get_input_identifier(field) + Iq = self.exp.field_logic.get_next_q_identifier(field) + return f"{Iq} & ~{I}" + + def get_assignments(self, field: 'FieldNode') -> List[str]: + I = self.exp.hwif.get_input_identifier(field) + Iq = self.exp.field_logic.get_next_q_identifier(field) + R = self.exp.field_logic.get_storage_identifier(field) + return [ + f"next_c = {R} | ({Iq} & ~{I});", + f"load_next_c = '1;", + ] + +class BothedgeStickybit(NextStateConditional): + """ + edge-sensitive stickybit + """ + comment = "bothedge stickybit" + def is_match(self, field: 'FieldNode') -> bool: + return ( + field.is_hw_writable + and field.get_property('stickybit') + and field.get_property('intr type') == InterruptType.bothedge + ) + + def get_predicate(self, field: 'FieldNode') -> str: + I = self.exp.hwif.get_input_identifier(field) + Iq = self.exp.field_logic.get_next_q_identifier(field) + return f"{Iq} ^ {I}" + + def get_assignments(self, field: 'FieldNode') -> List[str]: + I = self.exp.hwif.get_input_identifier(field) + Iq = self.exp.field_logic.get_next_q_identifier(field) + R = self.exp.field_logic.get_storage_identifier(field) + return [ + f"next_c = {R} | ({Iq} ^ {I});", + f"load_next_c = '1;", + ] + +class PosedgeNonsticky(NextStateConditional): + """ + Positive edge non-stickybit + """ + comment = "posedge nonsticky" + def is_match(self, field: 'FieldNode') -> bool: + return ( + field.is_hw_writable + and not field.get_property('stickybit') + and field.get_property('intr type') == InterruptType.posedge + ) + + def get_predicate(self, field: 'FieldNode') -> str: + return "1" + + def get_assignments(self, field: 'FieldNode') -> List[str]: + I = self.exp.hwif.get_input_identifier(field) + Iq = self.exp.field_logic.get_next_q_identifier(field) + return [ + f"next_c = ~{Iq} & {I};", + f"load_next_c = '1;", + ] + +class NegedgeNonsticky(NextStateConditional): + """ + Negative edge non-stickybit + """ + comment = "negedge nonsticky" + def is_match(self, field: 'FieldNode') -> bool: + return ( + field.is_hw_writable + and not field.get_property('stickybit') + and field.get_property('intr type') == InterruptType.negedge + ) + + def get_predicate(self, field: 'FieldNode') -> str: + return "1" + + def get_assignments(self, field: 'FieldNode') -> List[str]: + I = self.exp.hwif.get_input_identifier(field) + Iq = self.exp.field_logic.get_next_q_identifier(field) + return [ + f"next_c = {Iq} & ~{I};", + f"load_next_c = '1;", + ] + +class BothedgeNonsticky(NextStateConditional): + """ + edge-sensitive non-stickybit + """ + comment = "bothedge nonsticky" + def is_match(self, field: 'FieldNode') -> bool: + return ( + field.is_hw_writable + and not field.get_property('stickybit') + and field.get_property('intr type') == InterruptType.bothedge + ) + + def get_predicate(self, field: 'FieldNode') -> str: + return "1" + + def get_assignments(self, field: 'FieldNode') -> List[str]: + I = self.exp.hwif.get_input_identifier(field) + Iq = self.exp.field_logic.get_next_q_identifier(field) + return [ + f"next_c = {Iq} ^ {I};", + f"load_next_c = '1;", + ] diff --git a/peakrdl/regblock/field_logic/templates/field_storage.sv b/peakrdl/regblock/field_logic/templates/field_storage.sv index a6ca856..971cc0a 100644 --- a/peakrdl/regblock/field_logic/templates/field_storage.sv +++ b/peakrdl/regblock/field_logic/templates/field_storage.sv @@ -29,4 +29,7 @@ always_ff {{get_always_ff_event(resetsignal)}} begin end else {% endif %}if({{field_logic.get_field_combo_identifier(node, "load_next")}}) begin {{field_logic.get_storage_identifier(node)}} <= {{field_logic.get_field_combo_identifier(node, "next")}}; end + {%- if field_logic.has_next_q(node) %} + {{field_logic.get_next_q_identifier(node)}} <= {{get_input_identifier(node)}}; + {%- endif %} end diff --git a/peakrdl/regblock/hwif/__init__.py b/peakrdl/regblock/hwif/__init__.py index a75fb37..1052dcb 100644 --- a/peakrdl/regblock/hwif/__init__.py +++ b/peakrdl/regblock/hwif/__init__.py @@ -1,6 +1,6 @@ from typing import TYPE_CHECKING, Union, List, Set, Dict -from systemrdl.node import AddrmapNode, Node, SignalNode, FieldNode, AddressableNode +from systemrdl.node import AddrmapNode, Node, SignalNode, FieldNode, AddressableNode, RegNode from systemrdl.rdltypes import PropertyReference from ..utils import get_indexed_path @@ -182,10 +182,15 @@ class Hwif: 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) + def get_implied_prop_output_identifier(self, node: Union[FieldNode, RegNode], prop: str) -> str: + if isinstance(node, FieldNode): + assert prop in { + "anded", "ored", "xored", "swmod", "swacc", + "incrthreshold", "decrthreshold", "overflow", "underflow", + } + elif isinstance(node, RegNode): + assert prop in { + "intr", "halt", + } + path = get_indexed_path(self.top_node, node) return "hwif_out." + path + "." + prop diff --git a/peakrdl/regblock/hwif/generators.py b/peakrdl/regblock/hwif/generators.py index 37342e9..c20aa18 100644 --- a/peakrdl/regblock/hwif/generators.py +++ b/peakrdl/regblock/hwif/generators.py @@ -2,7 +2,7 @@ from typing import TYPE_CHECKING from ..struct_generator import RDLFlatStructGenerator if TYPE_CHECKING: - from systemrdl.node import Node, SignalNode, FieldNode + from systemrdl.node import Node, SignalNode, FieldNode, RegNode from . import Hwif class InputStructGenerator_Hier(RDLFlatStructGenerator): @@ -103,6 +103,13 @@ class OutputStructGenerator_Hier(RDLFlatStructGenerator): def exit_Field(self, node: 'FieldNode') -> None: self.pop_struct() + def exit_Reg(self, node: 'RegNode') -> None: + if node.is_interrupt_reg: + self.add_member('intr') + if node.is_halt_reg: + self.add_member('halt') + super().exit_Reg(node) + #------------------------------------------------------------------------------- class InputStructGenerator_TypeScope(InputStructGenerator_Hier): def get_typdef_name(self, node:'Node') -> str: