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:
|
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 = ""
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 (
|
||||||
|
|||||||
@@ -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')
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user