Fix missing error message if multiple unconditional field assignments are inferred. #93

This commit is contained in:
Alex Mykyta
2025-03-06 22:12:26 -08:00
parent 54ac56e1c3
commit d3cd51f500
5 changed files with 60 additions and 27 deletions

View File

@@ -57,17 +57,13 @@ class SVLogic:
class NextStateConditional: 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: Provides information to generate the following content:
if(<conditional>) begin if(<conditional>) begin
<assignments> <assignments>
end 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 # Optional comment to emit next to the conditional
comment = "" comment = ""
@@ -77,7 +73,7 @@ class NextStateConditional:
def is_match(self, field: 'FieldNode') -> bool: def is_match(self, field: 'FieldNode') -> bool:
""" """
Returns True if this conditional is relevant to the field. If so, 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 be emitted
""" """
raise NotImplementedError raise NotImplementedError
@@ -107,3 +103,12 @@ class NextStateConditional:
will assign if present. will assign if present.
""" """
return [] 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 = ""

View File

@@ -9,6 +9,7 @@ from ..struct_generator import RDLStructGenerator
from ..forloop_generator import RDLForLoopGenerator from ..forloop_generator import RDLForLoopGenerator
from ..utils import get_indexed_path, clog2 from ..utils import get_indexed_path, clog2
from ..identifier_filter import kw_filter as kwf from ..identifier_filter import kw_filter as kwf
from .bases import NextStateUnconditional
if TYPE_CHECKING: if TYPE_CHECKING:
from . import FieldLogic from . import FieldLogic
@@ -117,6 +118,7 @@ class FieldLogicGenerator(RDLForLoopGenerator):
) )
self.intr_fields = [] # type: List[FieldNode] self.intr_fields = [] # type: List[FieldNode]
self.halt_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]: def enter_AddressableComponent(self, node: 'AddressableNode') -> Optional[WalkerAction]:
@@ -212,14 +214,21 @@ class FieldLogicGenerator(RDLForLoopGenerator):
def generate_field_storage(self, node: 'FieldNode') -> None: def generate_field_storage(self, node: 'FieldNode') -> None:
conditionals = self.field_logic.get_conditionals(node) conditionals = self.field_logic.get_conditionals(node)
extra_combo_signals = OrderedDict() extra_combo_signals = OrderedDict()
unconditional = None unconditional: Optional[NextStateUnconditional] = None
new_conditionals = [] new_conditionals = []
for conditional in conditionals: for conditional in conditionals:
for signal in conditional.get_extra_combo_signals(node): for signal in conditional.get_extra_combo_signals(node):
extra_combo_signals[signal.name] = signal extra_combo_signals[signal.name] = signal
if conditional.is_unconditional: if isinstance(conditional, NextStateUnconditional):
assert unconditional is None # Can only have one unconditional assignment per field 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 unconditional = conditional
else: else:
new_conditionals.append(conditional) new_conditionals.append(conditional)

View File

@@ -2,7 +2,7 @@ from typing import TYPE_CHECKING, List
from systemrdl.rdltypes import InterruptType from systemrdl.rdltypes import InterruptType
from .bases import NextStateConditional from .bases import NextStateConditional, NextStateUnconditional
if TYPE_CHECKING: if TYPE_CHECKING:
from systemrdl.node import FieldNode from systemrdl.node import FieldNode
@@ -134,12 +134,12 @@ class BothedgeStickybit(NextStateConditional):
"load_next_c = '1;", "load_next_c = '1;",
] ]
class PosedgeNonsticky(NextStateConditional): class PosedgeNonsticky(NextStateUnconditional):
""" """
Positive edge non-stickybit Positive edge non-stickybit
""" """
is_unconditional = True
comment = "posedge nonsticky" comment = "posedge nonsticky"
unconditional_explanation = "Edge-sensitive non-sticky interrupts always update the field state"
def is_match(self, field: 'FieldNode') -> bool: def is_match(self, field: 'FieldNode') -> bool:
return ( return (
field.is_hw_writable field.is_hw_writable
@@ -155,12 +155,12 @@ class PosedgeNonsticky(NextStateConditional):
"load_next_c = '1;", "load_next_c = '1;",
] ]
class NegedgeNonsticky(NextStateConditional): class NegedgeNonsticky(NextStateUnconditional):
""" """
Negative edge non-stickybit Negative edge non-stickybit
""" """
is_unconditional = True
comment = "negedge nonsticky" comment = "negedge nonsticky"
unconditional_explanation = "Edge-sensitive non-sticky interrupts always update the field state"
def is_match(self, field: 'FieldNode') -> bool: def is_match(self, field: 'FieldNode') -> bool:
return ( return (
field.is_hw_writable field.is_hw_writable
@@ -176,12 +176,12 @@ class NegedgeNonsticky(NextStateConditional):
"load_next_c = '1;", "load_next_c = '1;",
] ]
class BothedgeNonsticky(NextStateConditional): class BothedgeNonsticky(NextStateUnconditional):
""" """
edge-sensitive non-stickybit edge-sensitive non-stickybit
""" """
is_unconditional = True
comment = "bothedge nonsticky" comment = "bothedge nonsticky"
unconditional_explanation = "Edge-sensitive non-sticky interrupts always update the field state"
def is_match(self, field: 'FieldNode') -> bool: def is_match(self, field: 'FieldNode') -> bool:
return ( return (
field.is_hw_writable field.is_hw_writable

View File

@@ -1,18 +1,17 @@
from typing import TYPE_CHECKING, List from typing import TYPE_CHECKING, List
from .bases import NextStateConditional from .bases import NextStateConditional, NextStateUnconditional
if TYPE_CHECKING: if TYPE_CHECKING:
from systemrdl.node import FieldNode from systemrdl.node import FieldNode
class AlwaysWrite(NextStateConditional): class AlwaysWrite(NextStateUnconditional):
""" """
hw writable, without any qualifying we/wel hw writable, without any qualifying we/wel
""" """
is_unconditional = True
comment = "HW Write" 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: def is_match(self, field: 'FieldNode') -> bool:
return ( return (
@@ -40,8 +39,28 @@ class AlwaysWrite(NextStateConditional):
"load_next_c = '1;", "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" comment = "HW Write - we"
def is_match(self, field: 'FieldNode') -> bool: def is_match(self, field: 'FieldNode') -> bool:
return ( return (
@@ -58,8 +77,7 @@ class WEWrite(AlwaysWrite):
identifier = str(self.exp.dereferencer.get_value(prop)) identifier = str(self.exp.dereferencer.get_value(prop))
return identifier return identifier
class WELWrite(AlwaysWrite): class WELWrite(_QualifiedWrite):
is_unconditional = False
comment = "HW Write - wel" comment = "HW Write - wel"
def is_match(self, field: 'FieldNode') -> bool: def is_match(self, field: 'FieldNode') -> bool:
return ( return (

View File

@@ -1,13 +1,14 @@
from typing import TYPE_CHECKING, List from typing import TYPE_CHECKING, List
from .bases import NextStateConditional from .bases import NextStateUnconditional
if TYPE_CHECKING: if TYPE_CHECKING:
from systemrdl.node import FieldNode from systemrdl.node import FieldNode
class Singlepulse(NextStateConditional): class Singlepulse(NextStateUnconditional):
is_unconditional = True
comment = "singlepulse clears back to 0" 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: def is_match(self, field: 'FieldNode') -> bool:
return field.get_property('singlepulse') return field.get_property('singlepulse')