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:
"""
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(<conditional>) begin
<assignments>
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 = ""

View File

@@ -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)

View File

@@ -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

View File

@@ -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 (

View File

@@ -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')