Fix missing error message if multiple unconditional field assignments are inferred. #93
This commit is contained in:
@@ -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 = ""
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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')
|
||||
|
||||
|
||||
Reference in New Issue
Block a user