more field logic

This commit is contained in:
Alex Mykyta
2021-10-28 22:34:29 -07:00
parent f473dfb9e7
commit d5c5d42390
23 changed files with 488 additions and 264 deletions

View File

@@ -32,15 +32,14 @@ Basically, i'd define a ton of helper functions that return the signal identifie
Dev Todo list Dev Todo list
================================================================================ ================================================================================
- tidy up stuff - Link more functions to the dereferencer
- merge FieldBuilder and FieldLogic classes. It makes no sense for these to be separate I shouldn't have to go to the hwif or whatever
- propagate the exporter class EVERYWHERE dereferencer should have all the query functions
shorten it to simply "self.exp"
- Build out a few more NextStateConditional implementations
- hw we
- readback mux - readback mux
- Other field output assignments
- HWIF layer - HWIF layer
- User Signals - User Signals
Generate these in the io struct? I forget what I decided Generate these in the io struct? I forget what I decided

View File

@@ -82,7 +82,19 @@ X If a node ispresent=true, and any of it's properties are a reference,
then those references' ispresent shall also be true then those references' ispresent shall also be true
This is an explicit clause in the spec: 5.3.1-i This is an explicit clause in the spec: 5.3.1-i
! Flag illegal sw actions if not readable/writable
The following combinations dont get flagged currently:
sw=w; rclr;
sw=w; rset;
sw=r; woset;
sw=r; woclr;
their counterparts do get flagged. such as:
sw=w; onread=rclr;
! hwclr/hwset/we/wel probably shouldn't be able to reference itself
y->hwclr = y;
y->we = y;
... it works, but should it be allowed? Seems like user-error

View File

@@ -74,7 +74,7 @@ NextStateConditional Class
it instructs the FieldBuider that code for this conditional shall be emitted it instructs the FieldBuider that code for this conditional shall be emitted
TODO: better name than "is_match"? More like "is this relevant" TODO: better name than "is_match"? More like "is this relevant"
- get_conditional(self, field: FieldNode) -> str: - get_predicate(self, field: FieldNode) -> str:
Returns the rendered conditional text Returns the rendered conditional text
- get_assignments(self, field: FieldNode) -> List[str]: - get_assignments(self, field: FieldNode) -> List[str]:

View File

@@ -11,12 +11,12 @@ if TYPE_CHECKING:
from .exporter import RegblockExporter from .exporter import RegblockExporter
class AddressDecode: class AddressDecode:
def __init__(self, exporter:'RegblockExporter'): def __init__(self, exp:'RegblockExporter'):
self.exporter = exporter self.exp = exp
@property @property
def top_node(self) -> AddrmapNode: def top_node(self) -> AddrmapNode:
return self.exporter.top_node return self.exp.top_node
def get_strobe_struct(self) -> str: def get_strobe_struct(self) -> str:
struct_gen = DecodeStructGenerator() struct_gen = DecodeStructGenerator()

View File

@@ -9,8 +9,8 @@ if TYPE_CHECKING:
class CpuifBase: class CpuifBase:
template_path = "cpuif/base_tmpl.sv" template_path = "cpuif/base_tmpl.sv"
def __init__(self, exporter:'RegblockExporter', cpuif_reset:'SignalBase', data_width:int=32, addr_width:int=32): def __init__(self, exp:'RegblockExporter', cpuif_reset:'SignalBase', data_width:int=32, addr_width:int=32):
self.exporter = exporter self.exp = exp
self.cpuif_reset = cpuif_reset self.cpuif_reset = cpuif_reset
self.data_width = data_width self.data_width = data_width
self.addr_width = addr_width self.addr_width = addr_width
@@ -28,5 +28,5 @@ class CpuifBase:
"get_always_ff_event": get_always_ff_event, "get_always_ff_event": get_always_ff_event,
} }
template = self.exporter.jj_env.get_template(self.template_path) template = self.exp.jj_env.get_template(self.template_path)
return template.render(context) return template.render(context)

View File

@@ -13,24 +13,24 @@ class Dereferencer:
This class provides an interface to convert conceptual SystemRDL references This class provides an interface to convert conceptual SystemRDL references
into Verilog identifiers into Verilog identifiers
""" """
def __init__(self, exporter:'RegblockExporter'): def __init__(self, exp:'RegblockExporter'):
self.exporter = exporter self.exp = exp
@property @property
def hwif(self) -> 'Hwif': def hwif(self) -> 'Hwif':
return self.exporter.hwif return self.exp.hwif
@property @property
def address_decode(self) -> 'AddressDecode': def address_decode(self) -> 'AddressDecode':
return self.exporter.address_decode return self.exp.address_decode
@property @property
def field_logic(self) -> 'FieldLogic': def field_logic(self) -> 'FieldLogic':
return self.exporter.field_logic return self.exp.field_logic
@property @property
def top_node(self) -> AddrmapNode: def top_node(self) -> AddrmapNode:
return self.exporter.top_node return self.exp.top_node
def get_value(self, obj: Union[int, FieldNode, SignalNode, PropertyReference]) -> str: def get_value(self, obj: Union[int, FieldNode, SignalNode, PropertyReference]) -> str:
""" """

View File

@@ -55,7 +55,7 @@ class RegblockExporter:
) )
def export(self, node: Union[RootNode, AddrmapNode], output_path:str, **kwargs): def export(self, node: Union[RootNode, AddrmapNode], output_dir:str, **kwargs):
# If it is the root node, skip to top addrmap # If it is the root node, skip to top addrmap
if isinstance(node, RootNode): if isinstance(node, RootNode):
self.top_node = node.top self.top_node = node.top
@@ -67,6 +67,8 @@ class RegblockExporter:
hwif_cls = kwargs.pop("hwif_cls", Hwif) hwif_cls = kwargs.pop("hwif_cls", Hwif)
module_name = kwargs.pop("module_name", self.top_node.inst_name) module_name = kwargs.pop("module_name", self.top_node.inst_name)
package_name = kwargs.pop("package_name", module_name + "_pkg") package_name = kwargs.pop("package_name", module_name + "_pkg")
module_file_path = os.path.join(output_dir, module_name + ".sv")
package_file_path = os.path.join(output_dir, package_name + ".sv")
# Check for stray kwargs # Check for stray kwargs
if kwargs: if kwargs:
@@ -110,6 +112,10 @@ class RegblockExporter:
} }
# Write out design # Write out design
template = self.jj_env.get_template("package_tmpl.sv")
stream = template.stream(context)
stream.dump(package_file_path)
template = self.jj_env.get_template("module_tmpl.sv") template = self.jj_env.get_template("module_tmpl.sv")
stream = template.stream(context) stream = template.stream(context)
stream.dump(output_path) stream.dump(module_file_path)

View File

@@ -1,24 +1,34 @@
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from systemrdl.node import AddrmapNode, FieldNode from systemrdl.rdltypes import PropertyReference, PrecedenceType
from systemrdl.rdltypes import PropertyReference
from .bases import AssignmentPrecedence, NextStateConditional
from . import sw_onread
from . import sw_onwrite
from . import hw_write
from . import hw_set_clr
from ..utils import get_indexed_path from ..utils import get_indexed_path
from .field_builder import FieldBuilder, FieldStorageStructGenerator
from .field_builder import CombinationalStructGenerator, FieldLogicGenerator
from .generators import CombinationalStructGenerator, FieldStorageStructGenerator, FieldLogicGenerator
if TYPE_CHECKING: if TYPE_CHECKING:
from typing import Dict, List
from systemrdl.node import AddrmapNode, FieldNode
from ..exporter import RegblockExporter from ..exporter import RegblockExporter
class FieldLogic: class FieldLogic:
def __init__(self, exporter:'RegblockExporter'): def __init__(self, exp:'RegblockExporter'):
self.exporter = exporter self.exp = exp
self.field_builder = FieldBuilder(exporter)
self._hw_conditionals = {} # type: Dict[int, List[NextStateConditional]]
self._sw_conditionals = {} # type: Dict[int, List[NextStateConditional]]
self.init_conditionals()
@property @property
def top_node(self) -> AddrmapNode: def top_node(self) -> 'AddrmapNode':
return self.exporter.top_node return self.exp.top_node
def get_storage_struct(self) -> str: def get_storage_struct(self) -> str:
struct_gen = FieldStorageStructGenerator() struct_gen = FieldStorageStructGenerator()
@@ -31,7 +41,7 @@ class FieldLogic:
return s + "\nfield_storage_t field_storage;" return s + "\nfield_storage_t field_storage;"
def get_combo_struct(self) -> str: def get_combo_struct(self) -> str:
struct_gen = CombinationalStructGenerator(self.field_builder) struct_gen = CombinationalStructGenerator(self)
s = struct_gen.get_struct(self.top_node, "field_combo_t") s = struct_gen.get_struct(self.top_node, "field_combo_t")
# Only declare the storage struct if it exists # Only declare the storage struct if it exists
@@ -41,7 +51,7 @@ class FieldLogic:
return s + "\nfield_combo_t field_combo;" return s + "\nfield_combo_t field_combo;"
def get_implementation(self) -> str: def get_implementation(self) -> str:
gen = FieldLogicGenerator(self.field_builder) gen = FieldLogicGenerator(self)
s = gen.get_content(self.top_node) s = gen.get_content(self.top_node)
if s is None: if s is None:
return "" return ""
@@ -50,22 +60,23 @@ class FieldLogic:
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
# Field utility functions # Field utility functions
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
def get_storage_identifier(self, node: FieldNode) -> str: def get_storage_identifier(self, node: 'FieldNode') -> str:
""" """
Returns the Verilog string that represents the storage register element Returns the Verilog string that represents the storage register element
for the referenced field for the referenced field
""" """
assert node.implements_storage assert node.implements_storage
path = get_indexed_path(self.top_node, node) path = get_indexed_path(self.top_node, node)
return "field_storage." + path return f"field_storage.{path}"
def get_field_next_identifier(self, node: FieldNode) -> str: def get_field_next_identifier(self, node: 'FieldNode') -> str:
""" """
Returns a Verilog string that represents the field's next-state. Returns a Verilog string that represents the field's next-state.
This is specifically for use in Field->next property references. This is specifically for use in Field->next property references.
""" """
# TODO: Implement this assert node.implements_storage
raise NotImplementedError path = get_indexed_path(self.top_node, node)
return f"field_combo.{path}.next"
def get_counter_control_identifier(self, prop_ref: PropertyReference) -> str: def get_counter_control_identifier(self, prop_ref: PropertyReference) -> str:
""" """
@@ -75,3 +86,103 @@ class FieldLogic:
""" """
# TODO: Implement this # TODO: Implement this
raise NotImplementedError raise NotImplementedError
#---------------------------------------------------------------------------
# Field Logic Conditionals
#---------------------------------------------------------------------------
def add_hw_conditional(self, conditional: NextStateConditional, precedence: AssignmentPrecedence) -> None:
"""
Register a NextStateConditional action for hardware-triggered field updates.
Categorizing conditionals correctly by hw/sw ensures that the RDL precedence
property can be reliably honored.
The ``precedence`` argument determines the conditional assignment's priority over
other assignments of differing precedence.
If multiple conditionals of the same precedence are registered, they are
searched sequentially and only the first to match the given field is used.
Conditionals are searched in reverse order that they were registered.
"""
if precedence not in self._hw_conditionals:
self._hw_conditionals[precedence] = []
self._hw_conditionals[precedence].append(conditional)
def add_sw_conditional(self, conditional: NextStateConditional, precedence: AssignmentPrecedence) -> None:
"""
Register a NextStateConditional action for software-triggered field updates.
Categorizing conditionals correctly by hw/sw ensures that the RDL precedence
property can be reliably honored.
The ``precedence`` argument determines the conditional assignment's priority over
other assignments of differing precedence.
If multiple conditionals of the same precedence are registered, they are
searched sequentially and only the first to match the given field is used.
Conditionals are searched in reverse order that they were registered.
"""
if precedence not in self._sw_conditionals:
self._sw_conditionals[precedence] = []
self._sw_conditionals[precedence].append(conditional)
def init_conditionals(self) -> None:
"""
Initialize all possible conditionals here.
Remember: The order in which conditionals are added matters within the
same assignment precedence.
"""
# TODO: Add all the other things
self.add_sw_conditional(sw_onread.ClearOnRead(self.exp), AssignmentPrecedence.SW_ONREAD)
self.add_sw_conditional(sw_onread.SetOnRead(self.exp), AssignmentPrecedence.SW_ONREAD)
self.add_sw_conditional(sw_onwrite.WriteOneSet(self.exp), AssignmentPrecedence.SW_ONWRITE)
self.add_sw_conditional(sw_onwrite.WriteOneClear(self.exp), AssignmentPrecedence.SW_ONWRITE)
self.add_sw_conditional(sw_onwrite.WriteOneToggle(self.exp), AssignmentPrecedence.SW_ONWRITE)
self.add_sw_conditional(sw_onwrite.WriteZeroSet(self.exp), AssignmentPrecedence.SW_ONWRITE)
self.add_sw_conditional(sw_onwrite.WriteZeroClear(self.exp), AssignmentPrecedence.SW_ONWRITE)
self.add_sw_conditional(sw_onwrite.WriteZeroToggle(self.exp), AssignmentPrecedence.SW_ONWRITE)
self.add_sw_conditional(sw_onwrite.WriteClear(self.exp), AssignmentPrecedence.SW_ONWRITE)
self.add_sw_conditional(sw_onwrite.WriteSet(self.exp), AssignmentPrecedence.SW_ONWRITE)
self.add_sw_conditional(sw_onwrite.Write(self.exp), AssignmentPrecedence.SW_ONWRITE)
self.add_hw_conditional(hw_write.AlwaysWrite(self.exp), AssignmentPrecedence.HW_WRITE)
self.add_hw_conditional(hw_write.WELWrite(self.exp), AssignmentPrecedence.HW_WRITE)
self.add_hw_conditional(hw_write.WEWrite(self.exp), AssignmentPrecedence.HW_WRITE)
self.add_hw_conditional(hw_set_clr.HWClear(self.exp), AssignmentPrecedence.HWCLR)
self.add_hw_conditional(hw_set_clr.HWSet(self.exp), AssignmentPrecedence.HWSET)
def _get_X_conditionals(self, conditionals: 'Dict[int, List[NextStateConditional]]', field: 'FieldNode') -> 'List[NextStateConditional]':
result = []
precedences = sorted(conditionals.keys(), reverse=True)
for precedence in precedences:
for conditional in conditionals[precedence]:
if conditional.is_match(field):
result.append(conditional)
return result
def get_conditionals(self, field: 'FieldNode') -> 'List[NextStateConditional]':
"""
Get a list of NextStateConditional objects that apply to the given field.
The returned list is sorted in priority order - the conditional with highest
precedence is first in the list.
"""
sw_precedence = (field.get_property('precedence') == PrecedenceType.sw)
result = []
if sw_precedence:
result.extend(self._get_X_conditionals(self._sw_conditionals, field))
result.extend(self._get_X_conditionals(self._hw_conditionals, field))
if not sw_precedence:
result.extend(self._get_X_conditionals(self._sw_conditionals, field))
return result

View File

@@ -26,7 +26,7 @@ class AssignmentPrecedence(enum.IntEnum):
SW_ONWRITE = 4000 SW_ONWRITE = 4000
# Hardware access assignment groups # Hardware access assignment groups
HW_WE = 3000 HW_WRITE = 3000
HWSET = 2000 HWSET = 2000
HWCLR = 1000 HWCLR = 1000
COUNTER_INCR_DECR = 0 COUNTER_INCR_DECR = 0
@@ -62,8 +62,10 @@ class NextStateConditional:
<assignments> <assignments>
end end
""" """
def __init__(self, exporter:'RegblockExporter'):
self.exporter = exporter comment = ""
def __init__(self, exp:'RegblockExporter'):
self.exp = exp
def is_match(self, field: 'FieldNode') -> bool: def is_match(self, field: 'FieldNode') -> bool:
""" """
@@ -74,9 +76,9 @@ class NextStateConditional:
raise NotImplementedError raise NotImplementedError
def get_field_path(self, field:'FieldNode') -> str: def get_field_path(self, field:'FieldNode') -> str:
return get_indexed_path(self.exporter.top_node, field) return get_indexed_path(self.exp.top_node, field)
def get_conditional(self, field: 'FieldNode') -> str: def get_predicate(self, field: 'FieldNode') -> str:
""" """
Returns the rendered conditional text Returns the rendered conditional text
""" """
@@ -92,4 +94,8 @@ class NextStateConditional:
""" """
def get_extra_combo_signals(self, field: 'FieldNode') -> List[SVLogic]: def get_extra_combo_signals(self, field: 'FieldNode') -> List[SVLogic]:
"""
Return any additional combinational signals that this conditional
will assign if present.
"""
return [] return []

View File

@@ -1,176 +0,0 @@
from typing import TYPE_CHECKING
from collections import OrderedDict
from systemrdl.rdltypes import PrecedenceType
from .bases import AssignmentPrecedence, NextStateConditional
from . import sw_onread
from . import sw_onwrite
from ..struct_generator import RDLStructGenerator
from ..forloop_generator import RDLForLoopGenerator
from ..utils import get_indexed_path, get_always_ff_event
from ..signals import RDLSignal
if TYPE_CHECKING:
from typing import Dict, List
from systemrdl.node import FieldNode, AddrmapNode
from ..exporter import RegblockExporter
class FieldBuilder:
def __init__(self, exporter:'RegblockExporter'):
self.exporter = exporter
self._hw_conditionals = {} # type: Dict[int, List[NextStateConditional]]
self._sw_conditionals = {} # type: Dict[int, List[NextStateConditional]]
self.init_conditionals()
@property
def top_node(self) -> 'AddrmapNode':
return self.exporter.top_node
def add_hw_conditional(self, conditional: NextStateConditional, precedence: AssignmentPrecedence) -> None:
# TODO: Add docstring!
if precedence not in self._hw_conditionals:
self._hw_conditionals[precedence] = []
self._hw_conditionals[precedence].append(conditional)
def add_sw_conditional(self, conditional: NextStateConditional, precedence: AssignmentPrecedence) -> None:
# TODO: Add docstring!
if precedence not in self._sw_conditionals:
self._sw_conditionals[precedence] = []
self._sw_conditionals[precedence].append(conditional)
def init_conditionals(self) -> None:
# TODO: Add docstring!
# TODO: Add all the other things
self.add_sw_conditional(sw_onread.ClearOnRead(self.exporter), AssignmentPrecedence.SW_ONREAD)
self.add_sw_conditional(sw_onread.SetOnRead(self.exporter), AssignmentPrecedence.SW_ONREAD)
self.add_hw_conditional(sw_onwrite.WriteOneSet(self.exporter), AssignmentPrecedence.SW_ONWRITE)
self.add_hw_conditional(sw_onwrite.WriteOneClear(self.exporter), AssignmentPrecedence.SW_ONWRITE)
self.add_hw_conditional(sw_onwrite.WriteOneToggle(self.exporter), AssignmentPrecedence.SW_ONWRITE)
self.add_hw_conditional(sw_onwrite.WriteZeroSet(self.exporter), AssignmentPrecedence.SW_ONWRITE)
self.add_hw_conditional(sw_onwrite.WriteZeroClear(self.exporter), AssignmentPrecedence.SW_ONWRITE)
self.add_hw_conditional(sw_onwrite.WriteZeroToggle(self.exporter), AssignmentPrecedence.SW_ONWRITE)
self.add_hw_conditional(sw_onwrite.WriteClear(self.exporter), AssignmentPrecedence.SW_ONWRITE)
self.add_hw_conditional(sw_onwrite.WriteSet(self.exporter), AssignmentPrecedence.SW_ONWRITE)
self.add_hw_conditional(sw_onwrite.Write(self.exporter), AssignmentPrecedence.SW_ONWRITE)
def _get_X_conditionals(self, conditionals: 'Dict[int, List[NextStateConditional]]', field: 'FieldNode') -> 'List[NextStateConditional]':
result = []
precedences = sorted(conditionals.keys(), reverse=True)
for precedence in precedences:
for conditional in conditionals[precedence]:
if conditional.is_match(field):
result.append(conditional)
return result
def get_conditionals(self, field: 'FieldNode') -> 'List[NextStateConditional]':
# TODO: Add docstring! - list of NextStateConditional. Highest precedence comes first
sw_precedence = (field.get_property('precedence') == PrecedenceType.sw)
result = []
if sw_precedence:
result.extend(self._get_X_conditionals(self._sw_conditionals, field))
result.extend(self._get_X_conditionals(self._hw_conditionals, field))
if not sw_precedence:
result.extend(self._get_X_conditionals(self._sw_conditionals, field))
return result
class CombinationalStructGenerator(RDLStructGenerator):
def __init__(self, field_builder: FieldBuilder):
super().__init__()
self.field_builder = field_builder
def enter_Field(self, node: 'FieldNode') -> None:
# If a field doesn't implement storage, it is not relevant here
if not node.implements_storage:
return
# collect any extra combo signals that this field requires
extra_combo_signals = OrderedDict()
for conditional in self.field_builder.get_conditionals(node):
for signal in conditional.get_extra_combo_signals(node):
if signal.name in extra_combo_signals:
# Assert that subsequent declarations of the same signal
# are identical
assert signal == extra_combo_signals[signal.name]
else:
extra_combo_signals[signal.name] = signal
self.push_struct(node.inst_name)
self.add_member("next", node.width)
self.add_member("load_next")
for signal in extra_combo_signals.values():
self.add_member(signal.name, signal.width)
self.pop_struct()
class FieldStorageStructGenerator(RDLStructGenerator):
def enter_Field(self, node: 'FieldNode') -> None:
if node.implements_storage:
self.add_member(node.inst_name, node.width)
class FieldLogicGenerator(RDLForLoopGenerator):
i_type = "genvar"
def __init__(self, field_builder: FieldBuilder):
super().__init__()
self.field_builder = field_builder
self.template = self.field_builder.exporter.jj_env.get_template(
"field_logic/templates/field_storage.sv"
)
def enter_Field(self, node: 'FieldNode') -> None:
# If a field doesn't implement storage, it is not relevant here
if not node.implements_storage:
return
conditionals = self.field_builder.get_conditionals(node)
extra_combo_signals = OrderedDict()
for conditional in conditionals:
for signal in conditional.get_extra_combo_signals(node):
extra_combo_signals[signal.name] = signal
reset_value = node.get_property("reset") or 0
sig = node.get_property("resetsignal")
if sig is not None:
resetsignal = RDLSignal(sig)
else:
resetsignal = self.field_builder.exporter.default_resetsignal
context = {
'node': node,
'reset': self.field_builder.exporter.dereferencer.get_value(reset_value),
'field_path': get_indexed_path(self.field_builder.top_node, node),
'extra_combo_signals': extra_combo_signals,
'conditionals': conditionals,
'resetsignal': resetsignal,
'get_always_ff_event': get_always_ff_event,
'has_value_output': self.field_builder.exporter.hwif.has_value_output,
'get_output_identifier': self.field_builder.exporter.hwif.get_output_identifier,
}
self.add_content(self.template.render(context))

View File

@@ -0,0 +1,99 @@
from typing import TYPE_CHECKING
from collections import OrderedDict
from ..struct_generator import RDLStructGenerator
from ..forloop_generator import RDLForLoopGenerator
from ..utils import get_indexed_path, get_always_ff_event
from ..signals import RDLSignal
if TYPE_CHECKING:
from . import FieldLogic
from systemrdl.node import FieldNode
class CombinationalStructGenerator(RDLStructGenerator):
def __init__(self, field_logic: 'FieldLogic'):
super().__init__()
self.field_logic = field_logic
def enter_Field(self, node: 'FieldNode') -> None:
# If a field doesn't implement storage, it is not relevant here
if not node.implements_storage:
return
# collect any extra combo signals that this field requires
extra_combo_signals = OrderedDict()
for conditional in self.field_logic.get_conditionals(node):
for signal in conditional.get_extra_combo_signals(node):
if signal.name in extra_combo_signals:
# Assert that subsequent declarations of the same signal
# are identical
assert signal == extra_combo_signals[signal.name]
else:
extra_combo_signals[signal.name] = signal
self.push_struct(node.inst_name)
self.add_member("next", node.width)
self.add_member("load_next")
for signal in extra_combo_signals.values():
self.add_member(signal.name, signal.width)
self.pop_struct()
class FieldStorageStructGenerator(RDLStructGenerator):
def enter_Field(self, node: 'FieldNode') -> None:
if node.implements_storage:
self.add_member(node.inst_name, node.width)
class FieldLogicGenerator(RDLForLoopGenerator):
i_type = "genvar"
def __init__(self, field_logic: 'FieldLogic'):
super().__init__()
self.field_logic = field_logic
self.template = self.field_logic.exp.jj_env.get_template(
"field_logic/templates/field_storage.sv"
)
def enter_Field(self, node: 'FieldNode') -> None:
# If a field doesn't implement storage, it is not relevant here
if not node.implements_storage:
return
conditionals = self.field_logic.get_conditionals(node)
extra_combo_signals = OrderedDict()
for conditional in conditionals:
for signal in conditional.get_extra_combo_signals(node):
extra_combo_signals[signal.name] = signal
sig = node.get_property("resetsignal")
if sig is not None:
resetsignal = RDLSignal(sig)
else:
resetsignal = self.field_logic.exp.default_resetsignal
reset_value = node.get_property("reset")
if reset_value is not None:
reset_value_str = self.field_logic.exp.dereferencer.get_value(reset_value)
else:
# 5.9.1-g: If no reset value given, the field is not reset, even if it has a resetsignal.
reset_value_str = None
resetsignal = None
context = {
'node': node,
'reset': reset_value_str,
'field_path': get_indexed_path(self.field_logic.top_node, node),
'extra_combo_signals': extra_combo_signals,
'conditionals': conditionals,
'resetsignal': resetsignal,
'get_always_ff_event': get_always_ff_event,
'has_value_output': self.field_logic.exp.hwif.has_value_output,
'get_output_identifier': self.field_logic.exp.hwif.get_output_identifier,
}
self.add_content(self.template.render(context))

View File

@@ -0,0 +1,76 @@
from typing import TYPE_CHECKING, List
from .bases import NextStateConditional
if TYPE_CHECKING:
from systemrdl.node import FieldNode
class HWSet(NextStateConditional):
comment = "HW Set"
def is_match(self, field: 'FieldNode') -> bool:
return bool(field.get_property('hwset'))
def get_predicate(self, field: 'FieldNode') -> str:
prop = field.get_property('hwset')
if isinstance(prop, bool):
identifier = self.exp.hwif.get_implied_prop_input_identifier(field, "hwset")
else:
# signal or field
identifier = self.exp.dereferencer.get_value(prop)
return identifier
def get_assignments(self, field: 'FieldNode') -> List[str]:
field_path = self.get_field_path(field)
hwmask = field.get_property('hwmask')
hwenable = field.get_property('hwenable')
R = f"field_storage.{field_path}"
if hwmask is not None:
M = self.exp.dereferencer.get_value(hwmask)
next_val = f"{R} | ~{M}"
elif hwenable is not None:
E = self.exp.dereferencer.get_value(hwenable)
next_val = f"{R} | {E}"
else:
next_val = "'1"
return [
f"field_combo.{field_path}.next = {next_val};",
f"field_combo.{field_path}.load_next = '1;",
]
class HWClear(NextStateConditional):
comment = "HW Clear"
def is_match(self, field: 'FieldNode') -> bool:
return bool(field.get_property('hwclr'))
def get_predicate(self, field: 'FieldNode') -> str:
prop = field.get_property('hwclr')
if isinstance(prop, bool):
identifier = self.exp.hwif.get_implied_prop_input_identifier(field, "hwclr")
else:
# signal or field
identifier = self.exp.dereferencer.get_value(prop)
return identifier
def get_assignments(self, field: 'FieldNode') -> List[str]:
field_path = self.get_field_path(field)
hwmask = field.get_property('hwmask')
hwenable = field.get_property('hwenable')
R = f"field_storage.{field_path}"
if hwmask is not None:
M = self.exp.dereferencer.get_value(hwmask)
next_val = f"{R} & {M}"
elif hwenable is not None:
E = self.exp.dereferencer.get_value(hwenable)
next_val = f"{R} & ~{E}"
else:
next_val = "'0"
return [
f"field_combo.{field_path}.next = {next_val};",
f"field_combo.{field_path}.load_next = '1;",
]

View File

@@ -0,0 +1,78 @@
from typing import TYPE_CHECKING, List
from .bases import NextStateConditional
if TYPE_CHECKING:
from systemrdl.node import FieldNode
class AlwaysWrite(NextStateConditional):
"""
hw writable, without any qualifying we/wel
"""
comment = "HW Write"
def is_match(self, field: 'FieldNode') -> bool:
return (
field.is_hw_writable
and not field.get_property('we')
and not field.get_property('wel')
)
def get_predicate(self, field: 'FieldNode') -> str:
# TODO: make exporter promote this to an "else"?
return "1"
def get_assignments(self, field: 'FieldNode') -> List[str]:
field_path = self.get_field_path(field)
hwmask = field.get_property('hwmask')
hwenable = field.get_property('hwenable')
I = self.exp.hwif.get_input_identifier(field)
R = f"field_storage.{field_path}"
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 = self.exp.hwif.get_input_identifier(field)
return [
f"field_combo.{field_path}.next = {next_val};",
f"field_combo.{field_path}.load_next = '1;",
]
class WEWrite(AlwaysWrite):
comment = "HW Write - we"
def is_match(self, field: 'FieldNode') -> bool:
return (
field.is_hw_writable
and field.get_property('we')
)
def get_predicate(self, field: 'FieldNode') -> str:
prop = field.get_property('we')
if isinstance(prop, bool):
identifier = self.exp.hwif.get_implied_prop_input_identifier(field, "we")
else:
# signal or field
identifier = self.exp.dereferencer.get_value(prop)
return identifier
class WELWrite(AlwaysWrite):
comment = "HW Write - wel"
def is_match(self, field: 'FieldNode') -> bool:
return (
field.is_hw_writable
and field.get_property('wel')
)
def get_predicate(self, field: 'FieldNode') -> str:
prop = field.get_property('wel')
if isinstance(prop, bool):
identifier = self.exp.hwif.get_implied_prop_input_identifier(field, "wel")
else:
# signal or field
identifier = self.exp.dereferencer.get_value(prop)
return f"!{identifier}"

View File

@@ -12,11 +12,12 @@ class _OnRead(NextStateConditional):
def is_match(self, field: 'FieldNode') -> bool: def is_match(self, field: 'FieldNode') -> bool:
return field.get_property("onread") == self.onreadtype return field.get_property("onread") == self.onreadtype
def get_conditional(self, field: 'FieldNode') -> str: def get_predicate(self, field: 'FieldNode') -> str:
strb = self.exporter.dereferencer.get_access_strobe(field) strb = self.exp.dereferencer.get_access_strobe(field)
return f"decoded_req && !decoded_req_is_wr && {strb}" return f"decoded_req && !decoded_req_is_wr && {strb}"
class ClearOnRead(_OnRead): class ClearOnRead(_OnRead):
comment = "SW clear on read"
onreadtype = OnReadType.rclr onreadtype = OnReadType.rclr
def get_assignments(self, field: 'FieldNode') -> List[str]: def get_assignments(self, field: 'FieldNode') -> List[str]:
@@ -28,6 +29,7 @@ class ClearOnRead(_OnRead):
class SetOnRead(_OnRead): class SetOnRead(_OnRead):
comment = "SW set on read"
onreadtype = OnReadType.rset onreadtype = OnReadType.rset
def get_assignments(self, field: 'FieldNode') -> List[str]: def get_assignments(self, field: 'FieldNode') -> List[str]:

View File

@@ -7,16 +7,19 @@ from .bases import NextStateConditional
if TYPE_CHECKING: if TYPE_CHECKING:
from systemrdl.node import FieldNode from systemrdl.node import FieldNode
# TODO: implement sw=w1 "write once" fields
class _OnWrite(NextStateConditional): class _OnWrite(NextStateConditional):
onwritetype = None onwritetype = None
def is_match(self, field: 'FieldNode') -> bool: def is_match(self, field: 'FieldNode') -> bool:
return field.get_property("onwrite") == self.onwritetype return field.get_property("onwrite") == self.onwritetype
def get_conditional(self, field: 'FieldNode') -> str: def get_predicate(self, field: 'FieldNode') -> str:
strb = self.exporter.dereferencer.get_access_strobe(field) strb = self.exp.dereferencer.get_access_strobe(field)
return f"decoded_req && decoded_req_is_wr && {strb}" return f"decoded_req && decoded_req_is_wr && {strb}"
class WriteOneSet(_OnWrite): class WriteOneSet(_OnWrite):
comment = "SW write 1 set"
onwritetype = OnWriteType.woset onwritetype = OnWriteType.woset
def get_assignments(self, field: 'FieldNode') -> List[str]: def get_assignments(self, field: 'FieldNode') -> List[str]:
@@ -27,6 +30,7 @@ class WriteOneSet(_OnWrite):
] ]
class WriteOneClear(_OnWrite): class WriteOneClear(_OnWrite):
comment = "SW write 1 clear"
onwritetype = OnWriteType.woclr onwritetype = OnWriteType.woclr
def get_assignments(self, field: 'FieldNode') -> List[str]: def get_assignments(self, field: 'FieldNode') -> List[str]:
@@ -37,6 +41,7 @@ class WriteOneClear(_OnWrite):
] ]
class WriteOneToggle(_OnWrite): class WriteOneToggle(_OnWrite):
comment = "SW write 1 toggle"
onwritetype = OnWriteType.wot onwritetype = OnWriteType.wot
def get_assignments(self, field: 'FieldNode') -> List[str]: def get_assignments(self, field: 'FieldNode') -> List[str]:
@@ -47,6 +52,7 @@ class WriteOneToggle(_OnWrite):
] ]
class WriteZeroSet(_OnWrite): class WriteZeroSet(_OnWrite):
comment = "SW write 0 set"
onwritetype = OnWriteType.wzs onwritetype = OnWriteType.wzs
def get_assignments(self, field: 'FieldNode') -> List[str]: def get_assignments(self, field: 'FieldNode') -> List[str]:
@@ -57,6 +63,7 @@ class WriteZeroSet(_OnWrite):
] ]
class WriteZeroClear(_OnWrite): class WriteZeroClear(_OnWrite):
comment = "SW write 0 clear"
onwritetype = OnWriteType.wzc onwritetype = OnWriteType.wzc
def get_assignments(self, field: 'FieldNode') -> List[str]: def get_assignments(self, field: 'FieldNode') -> List[str]:
@@ -67,6 +74,7 @@ class WriteZeroClear(_OnWrite):
] ]
class WriteZeroToggle(_OnWrite): class WriteZeroToggle(_OnWrite):
comment = "SW write 0 toggle"
onwritetype = OnWriteType.wzt onwritetype = OnWriteType.wzt
def get_assignments(self, field: 'FieldNode') -> List[str]: def get_assignments(self, field: 'FieldNode') -> List[str]:
@@ -77,6 +85,7 @@ class WriteZeroToggle(_OnWrite):
] ]
class WriteClear(_OnWrite): class WriteClear(_OnWrite):
comment = "SW write clear"
onwritetype = OnWriteType.wclr onwritetype = OnWriteType.wclr
def get_assignments(self, field: 'FieldNode') -> List[str]: def get_assignments(self, field: 'FieldNode') -> List[str]:
@@ -87,6 +96,7 @@ class WriteClear(_OnWrite):
] ]
class WriteSet(_OnWrite): class WriteSet(_OnWrite):
comment = "SW write set"
onwritetype = OnWriteType.wset onwritetype = OnWriteType.wset
def get_assignments(self, field: 'FieldNode') -> List[str]: def get_assignments(self, field: 'FieldNode') -> List[str]:
@@ -97,6 +107,7 @@ class WriteSet(_OnWrite):
] ]
class Write(_OnWrite): class Write(_OnWrite):
comment = "SW write"
onwritetype = None onwritetype = None
def get_assignments(self, field: 'FieldNode') -> List[str]: def get_assignments(self, field: 'FieldNode') -> List[str]:

View File

@@ -1,12 +1,12 @@
// Field: {{node.get_path()}} // Field: {{node.get_path()}}
always_comb begin always_comb begin
field_combo.{{field_path}}.next = '0; field_combo.{{field_path}}.next = field_storage.{{field_path}};
field_combo.{{field_path}}.load_next = '0; field_combo.{{field_path}}.load_next = '0;
{%- for signal in extra_combo_signals %} {%- for signal in extra_combo_signals %}
field_combo.{{field_path}}.{{signal.name}} = {{signal.default_assignment}}; field_combo.{{field_path}}.{{signal.name}} = {{signal.default_assignment}};
{%- endfor %} {%- endfor %}
{%- for conditional in conditionals %} {% for conditional in conditionals %}
{% if not loop.first %}end else {% endif %}if({{conditional.get_conditional(node)}}) begin {%- if not loop.first %} else {% endif %}if({{conditional.get_predicate(node)}}) begin // {{conditional.comment}}
{%- for assignment in conditional.get_assignments(node) %} {%- for assignment in conditional.get_assignments(node) %}
{{assignment|indent}} {{assignment|indent}}
{%- endfor %} {%- endfor %}
@@ -14,9 +14,10 @@ always_comb begin
{%- endfor %} {%- endfor %}
end end
always_ff {{get_always_ff_event(resetsignal)}} begin always_ff {{get_always_ff_event(resetsignal)}} begin
{% if resetsignal is not none -%}
if({{resetsignal.activehigh_identifier}}) begin if({{resetsignal.activehigh_identifier}}) begin
field_storage.{{field_path}} <= {{reset}}; field_storage.{{field_path}} <= {{reset}};
end else if(field_combo.{{field_path}}.load_next) begin end else {% endif %}if(field_combo.{{field_path}}.load_next) begin
field_storage.{{field_path}} <= field_combo.{{field_path}}.next; field_storage.{{field_path}} <= field_combo.{{field_path}}.next;
end end
end end

View File

@@ -16,8 +16,8 @@ class Hwif:
- Signal inputs (except those that are promoted to the top) - Signal inputs (except those that are promoted to the top)
""" """
def __init__(self, exporter: 'RegblockExporter', package_name: str): def __init__(self, exp: 'RegblockExporter', package_name: str):
self.exporter = exporter self.exp = exp
self.package_name = package_name self.package_name = package_name
self.has_input_struct = None self.has_input_struct = None
@@ -26,22 +26,17 @@ class Hwif:
@property @property
def top_node(self) -> AddrmapNode: def top_node(self) -> AddrmapNode:
return self.exporter.top_node return self.exp.top_node
def get_package_declaration(self) -> str: def get_package_contents(self) -> str:
""" """
If this hwif requires a package, generate the string If this hwif requires a package, generate the string
""" """
lines = [] lines = []
lines.append(f"package {self.package_name};")
self._indent_level += 1
self.has_input_struct = self._do_struct_addressable(lines, self.top_node, is_input=True) self.has_input_struct = self._do_struct_addressable(lines, self.top_node, is_input=True)
self.has_output_struct = self._do_struct_addressable(lines, self.top_node, is_input=False) self.has_output_struct = self._do_struct_addressable(lines, self.top_node, is_input=False)
self._indent_level -= 1
lines.append("")
lines.append("endpackage")
return "\n".join(lines) return "\n".join(lines)
@@ -259,6 +254,12 @@ class Hwif:
raise RuntimeError("Unhandled reference to: %s", obj) raise RuntimeError("Unhandled reference to: %s", obj)
def get_implied_prop_input_identifier(self, field: FieldNode, prop: str) -> str:
assert prop in {'hwclr', 'hwset', 'swwe', 'swwel', 'we', 'wel'}
path = get_indexed_path(self.top_node, field)
return "hwif_in." + path + "." + prop
def get_output_identifier(self, obj: Union[FieldNode, PropertyReference]) -> str: def get_output_identifier(self, obj: Union[FieldNode, PropertyReference]) -> str:
""" """
Returns the identifier string that best represents the output object. Returns the identifier string that best represents the output object.
@@ -279,3 +280,9 @@ class Hwif:
return "hwif_out." + path + "." + obj.name return "hwif_out." + path + "." + obj.name
raise RuntimeError("Unhandled reference to: %s", obj) raise RuntimeError("Unhandled reference to: %s", obj)
def get_implied_prop_output_identifier(self, field: FieldNode, prop: str) -> str:
assert prop in {"anded", "ored", "xored", "swmod", "swacc"}
path = get_indexed_path(self.top_node, field)
return "hwif_out." + path + "." + prop

View File

@@ -1,6 +1,4 @@
// TODO: Add a banner
{{hwif.get_package_declaration()}}
module {{module_name}} ( module {{module_name}} (
input wire clk, input wire clk,
{%- for signal in reset_signals %} {%- for signal in reset_signals %}

View File

@@ -0,0 +1,4 @@
// TODO: Add a banner
package {{hwif.package_name}};
{{hwif.get_package_contents()|indent}}
endpackage

View File

@@ -8,12 +8,12 @@ if TYPE_CHECKING:
from .exporter import RegblockExporter from .exporter import RegblockExporter
class ReadbackMux: class ReadbackMux:
def __init__(self, exporter:'RegblockExporter'): def __init__(self, exp:'RegblockExporter'):
self.exporter = exporter self.exp = exp
@property @property
def top_node(self) -> AddrmapNode: def top_node(self) -> AddrmapNode:
return self.exporter.top_node return self.exp.top_node
def get_implementation(self) -> str: def get_implementation(self) -> str:
# TODO: Count the number of readable registers # TODO: Count the number of readable registers

View File

@@ -1,10 +1,10 @@
import re import re
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
import textwrap
if TYPE_CHECKING: if TYPE_CHECKING:
from systemrdl.node import Node from systemrdl.node import Node
from .signals import SignalBase from .signals import SignalBase
from typing import Optional
def get_indexed_path(top_node: 'Node', target_node: 'Node') -> str: def get_indexed_path(top_node: 'Node', target_node: 'Node') -> str:
""" """
@@ -22,7 +22,9 @@ def get_indexed_path(top_node: 'Node', target_node: 'Node') -> str:
return re.sub(r'!', repl(), path) return re.sub(r'!', repl(), path)
def get_always_ff_event(resetsignal: 'SignalBase') -> str: def get_always_ff_event(resetsignal: 'Optional[SignalBase]') -> str:
if resetsignal is None:
return "@(posedge clk)"
if resetsignal.is_async and resetsignal.is_activehigh: if resetsignal.is_async and resetsignal.is_activehigh:
return f"@(posedge clk or posedge {resetsignal.identifier})" return f"@(posedge clk or posedge {resetsignal.identifier})"
elif resetsignal.is_async and not resetsignal.is_activehigh: elif resetsignal.is_async and not resetsignal.is_activehigh:

2
tst.py
View File

@@ -18,4 +18,4 @@ except RDLCompileError:
R = RegblockExporter() R = RegblockExporter()
R.export(root, "out.sv") R.export(root, ".")

26
x.rdl
View File

@@ -2,26 +2,14 @@
addrmap top { addrmap top {
reg { reg {
field { field {
hw=r; sw=rw; hw=w; sw=rw; precedence=sw;
} y = 0; } y = 0;
} whee[2][8];
regfile {
reg {
field { field {
hw=w; sw=r; hw=na; sw=rw;
} abc[16:2] = 20; } x = 1;
field {
hw=r; sw=rw; y->we;
} def[4] = 0; y->hwset;
} aaa[4]; } whee;
reg {
field {
hw=r; sw=rw;
} abc = 0;
field {
hw=r; sw=rw;
} def = 0;
} bbb;
} asdf[20];
}; };