diff --git a/src/peakrdl_regblock/field_logic/bases.py b/src/peakrdl_regblock/field_logic/bases.py index 9237821..2a13676 100644 --- a/src/peakrdl_regblock/field_logic/bases.py +++ b/src/peakrdl_regblock/field_logic/bases.py @@ -57,17 +57,13 @@ class SVLogic: class NextStateConditional: """ - Decribes a single conditional action that determines the next state of a field + Describes a single conditional action that determines the next state of a field Provides information to generate the following content: if() begin end """ - # Assign to True if predicate can never evaluate to false. - # This will be generated as an 'else' clause, or a direct assignment - is_unconditional = False - # Optional comment to emit next to the conditional comment = "" @@ -77,7 +73,7 @@ class NextStateConditional: def is_match(self, field: 'FieldNode') -> bool: """ Returns True if this conditional is relevant to the field. If so, - it instructs the FieldBuider that Verilog for this conditional shall + it instructs the FieldBuilder that Verilog for this conditional shall be emitted """ raise NotImplementedError @@ -107,3 +103,12 @@ class NextStateConditional: will assign if present. """ return [] + +class NextStateUnconditional(NextStateConditional): + """ + Use this class if predicate can never evaluate to false. + This will be generated as an 'else' clause, or a direct assignment + """ + + # Explanation text for use in error message about conflicts + unconditional_explanation = "" diff --git a/src/peakrdl_regblock/field_logic/generators.py b/src/peakrdl_regblock/field_logic/generators.py index 95655b2..04e474f 100644 --- a/src/peakrdl_regblock/field_logic/generators.py +++ b/src/peakrdl_regblock/field_logic/generators.py @@ -9,6 +9,7 @@ from ..struct_generator import RDLStructGenerator from ..forloop_generator import RDLForLoopGenerator from ..utils import get_indexed_path, clog2 from ..identifier_filter import kw_filter as kwf +from .bases import NextStateUnconditional if TYPE_CHECKING: from . import FieldLogic @@ -117,6 +118,7 @@ class FieldLogicGenerator(RDLForLoopGenerator): ) self.intr_fields = [] # type: List[FieldNode] self.halt_fields = [] # type: List[FieldNode] + self.msg = self.ds.top_node.env.msg def enter_AddressableComponent(self, node: 'AddressableNode') -> Optional[WalkerAction]: @@ -212,14 +214,21 @@ class FieldLogicGenerator(RDLForLoopGenerator): def generate_field_storage(self, node: 'FieldNode') -> None: conditionals = self.field_logic.get_conditionals(node) extra_combo_signals = OrderedDict() - unconditional = None + unconditional: Optional[NextStateUnconditional] = None new_conditionals = [] for conditional in conditionals: for signal in conditional.get_extra_combo_signals(node): extra_combo_signals[signal.name] = signal - if conditional.is_unconditional: - assert unconditional is None # Can only have one unconditional assignment per field + if isinstance(conditional, NextStateUnconditional): + if unconditional is not None: + # Too inconvenient to validate this early. Easier to validate here in-place generically + self.msg.fatal( + "Field has multiple conflicting properties that unconditionally set its state:\n" + f" * {conditional.unconditional_explanation}\n" + f" * {unconditional.unconditional_explanation}", + node.inst.inst_src_ref + ) unconditional = conditional else: new_conditionals.append(conditional) diff --git a/src/peakrdl_regblock/field_logic/hw_interrupts.py b/src/peakrdl_regblock/field_logic/hw_interrupts.py index 45dd2ef..e4ed0cc 100644 --- a/src/peakrdl_regblock/field_logic/hw_interrupts.py +++ b/src/peakrdl_regblock/field_logic/hw_interrupts.py @@ -2,7 +2,7 @@ from typing import TYPE_CHECKING, List from systemrdl.rdltypes import InterruptType -from .bases import NextStateConditional +from .bases import NextStateConditional, NextStateUnconditional if TYPE_CHECKING: from systemrdl.node import FieldNode @@ -134,12 +134,12 @@ class BothedgeStickybit(NextStateConditional): "load_next_c = '1;", ] -class PosedgeNonsticky(NextStateConditional): +class PosedgeNonsticky(NextStateUnconditional): """ Positive edge non-stickybit """ - is_unconditional = True comment = "posedge nonsticky" + unconditional_explanation = "Edge-sensitive non-sticky interrupts always update the field state" def is_match(self, field: 'FieldNode') -> bool: return ( field.is_hw_writable @@ -155,12 +155,12 @@ class PosedgeNonsticky(NextStateConditional): "load_next_c = '1;", ] -class NegedgeNonsticky(NextStateConditional): +class NegedgeNonsticky(NextStateUnconditional): """ Negative edge non-stickybit """ - is_unconditional = True comment = "negedge nonsticky" + unconditional_explanation = "Edge-sensitive non-sticky interrupts always update the field state" def is_match(self, field: 'FieldNode') -> bool: return ( field.is_hw_writable @@ -176,12 +176,12 @@ class NegedgeNonsticky(NextStateConditional): "load_next_c = '1;", ] -class BothedgeNonsticky(NextStateConditional): +class BothedgeNonsticky(NextStateUnconditional): """ edge-sensitive non-stickybit """ - is_unconditional = True comment = "bothedge nonsticky" + unconditional_explanation = "Edge-sensitive non-sticky interrupts always update the field state" def is_match(self, field: 'FieldNode') -> bool: return ( field.is_hw_writable diff --git a/src/peakrdl_regblock/field_logic/hw_write.py b/src/peakrdl_regblock/field_logic/hw_write.py index 1efbf44..5a629ce 100644 --- a/src/peakrdl_regblock/field_logic/hw_write.py +++ b/src/peakrdl_regblock/field_logic/hw_write.py @@ -1,18 +1,17 @@ from typing import TYPE_CHECKING, List -from .bases import NextStateConditional +from .bases import NextStateConditional, NextStateUnconditional if TYPE_CHECKING: from systemrdl.node import FieldNode -class AlwaysWrite(NextStateConditional): +class AlwaysWrite(NextStateUnconditional): """ hw writable, without any qualifying we/wel """ - - is_unconditional = True comment = "HW Write" + unconditional_explanation = "A hardware-writable field without a write-enable (we/wel) will always update the field value" def is_match(self, field: 'FieldNode') -> bool: return ( @@ -40,8 +39,28 @@ class AlwaysWrite(NextStateConditional): "load_next_c = '1;", ] -class WEWrite(AlwaysWrite): - is_unconditional = False + +class _QualifiedWrite(NextStateConditional): + def get_assignments(self, field: 'FieldNode') -> List[str]: + hwmask = field.get_property('hwmask') + hwenable = field.get_property('hwenable') + I = str(self.exp.hwif.get_input_identifier(field)) + R = self.exp.field_logic.get_storage_identifier(field) + if hwmask is not None: + M = self.exp.dereferencer.get_value(hwmask) + next_val = f"{I} & ~{M} | {R} & {M}" + elif hwenable is not None: + E = self.exp.dereferencer.get_value(hwenable) + next_val = f"{I} & {E} | {R} & ~{E}" + else: + next_val = I + + return [ + f"next_c = {next_val};", + "load_next_c = '1;", + ] + +class WEWrite(_QualifiedWrite): comment = "HW Write - we" def is_match(self, field: 'FieldNode') -> bool: return ( @@ -58,8 +77,7 @@ class WEWrite(AlwaysWrite): identifier = str(self.exp.dereferencer.get_value(prop)) return identifier -class WELWrite(AlwaysWrite): - is_unconditional = False +class WELWrite(_QualifiedWrite): comment = "HW Write - wel" def is_match(self, field: 'FieldNode') -> bool: return ( diff --git a/src/peakrdl_regblock/field_logic/sw_singlepulse.py b/src/peakrdl_regblock/field_logic/sw_singlepulse.py index e8863e4..9ed476b 100644 --- a/src/peakrdl_regblock/field_logic/sw_singlepulse.py +++ b/src/peakrdl_regblock/field_logic/sw_singlepulse.py @@ -1,13 +1,14 @@ from typing import TYPE_CHECKING, List -from .bases import NextStateConditional +from .bases import NextStateUnconditional if TYPE_CHECKING: from systemrdl.node import FieldNode -class Singlepulse(NextStateConditional): - is_unconditional = True +class Singlepulse(NextStateUnconditional): comment = "singlepulse clears back to 0" + unconditional_explanation = "The 'singlepulse' property unconditionally clears a field when not written" + def is_match(self, field: 'FieldNode') -> bool: return field.get_property('singlepulse')