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
================================================================================
- tidy up stuff
- merge FieldBuilder and FieldLogic classes. It makes no sense for these to be separate
- propagate the exporter class EVERYWHERE
shorten it to simply "self.exp"
- Build out a few more NextStateConditional implementations
- hw we
- Link more functions to the dereferencer
I shouldn't have to go to the hwif or whatever
dereferencer should have all the query functions
- readback mux
- Other field output assignments
- HWIF layer
- User Signals
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
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
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
- get_assignments(self, field: FieldNode) -> List[str]:

View File

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

View File

@@ -9,8 +9,8 @@ if TYPE_CHECKING:
class CpuifBase:
template_path = "cpuif/base_tmpl.sv"
def __init__(self, exporter:'RegblockExporter', cpuif_reset:'SignalBase', data_width:int=32, addr_width:int=32):
self.exporter = exporter
def __init__(self, exp:'RegblockExporter', cpuif_reset:'SignalBase', data_width:int=32, addr_width:int=32):
self.exp = exp
self.cpuif_reset = cpuif_reset
self.data_width = data_width
self.addr_width = addr_width
@@ -28,5 +28,5 @@ class CpuifBase:
"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)

View File

@@ -13,24 +13,24 @@ class Dereferencer:
This class provides an interface to convert conceptual SystemRDL references
into Verilog identifiers
"""
def __init__(self, exporter:'RegblockExporter'):
self.exporter = exporter
def __init__(self, exp:'RegblockExporter'):
self.exp = exp
@property
def hwif(self) -> 'Hwif':
return self.exporter.hwif
return self.exp.hwif
@property
def address_decode(self) -> 'AddressDecode':
return self.exporter.address_decode
return self.exp.address_decode
@property
def field_logic(self) -> 'FieldLogic':
return self.exporter.field_logic
return self.exp.field_logic
@property
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:
"""

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 isinstance(node, RootNode):
self.top_node = node.top
@@ -67,6 +67,8 @@ class RegblockExporter:
hwif_cls = kwargs.pop("hwif_cls", Hwif)
module_name = kwargs.pop("module_name", self.top_node.inst_name)
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
if kwargs:
@@ -110,6 +112,10 @@ class RegblockExporter:
}
# 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")
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 systemrdl.node import AddrmapNode, FieldNode
from systemrdl.rdltypes import PropertyReference
from systemrdl.rdltypes import PropertyReference, PrecedenceType
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 .field_builder import FieldBuilder, FieldStorageStructGenerator
from .field_builder import CombinationalStructGenerator, FieldLogicGenerator
from .generators import CombinationalStructGenerator, FieldStorageStructGenerator, FieldLogicGenerator
if TYPE_CHECKING:
from typing import Dict, List
from systemrdl.node import AddrmapNode, FieldNode
from ..exporter import RegblockExporter
class FieldLogic:
def __init__(self, exporter:'RegblockExporter'):
self.exporter = exporter
self.field_builder = FieldBuilder(exporter)
def __init__(self, exp:'RegblockExporter'):
self.exp = exp
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 top_node(self) -> 'AddrmapNode':
return self.exp.top_node
def get_storage_struct(self) -> str:
struct_gen = FieldStorageStructGenerator()
@@ -31,7 +41,7 @@ class FieldLogic:
return s + "\nfield_storage_t field_storage;"
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")
# Only declare the storage struct if it exists
@@ -41,7 +51,7 @@ class FieldLogic:
return s + "\nfield_combo_t field_combo;"
def get_implementation(self) -> str:
gen = FieldLogicGenerator(self.field_builder)
gen = FieldLogicGenerator(self)
s = gen.get_content(self.top_node)
if s is None:
return ""
@@ -50,22 +60,23 @@ class FieldLogic:
#---------------------------------------------------------------------------
# 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
for the referenced field
"""
assert node.implements_storage
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.
This is specifically for use in Field->next property references.
"""
# TODO: Implement this
raise NotImplementedError
assert node.implements_storage
path = get_indexed_path(self.top_node, node)
return f"field_combo.{path}.next"
def get_counter_control_identifier(self, prop_ref: PropertyReference) -> str:
"""
@@ -75,3 +86,103 @@ class FieldLogic:
"""
# TODO: Implement this
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
# Hardware access assignment groups
HW_WE = 3000
HW_WRITE = 3000
HWSET = 2000
HWCLR = 1000
COUNTER_INCR_DECR = 0
@@ -62,8 +62,10 @@ class NextStateConditional:
<assignments>
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:
"""
@@ -74,9 +76,9 @@ class NextStateConditional:
raise NotImplementedError
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
"""
@@ -92,4 +94,8 @@ class NextStateConditional:
"""
def get_extra_combo_signals(self, field: 'FieldNode') -> List[SVLogic]:
"""
Return any additional combinational signals that this conditional
will assign if present.
"""
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:
return field.get_property("onread") == self.onreadtype
def get_conditional(self, field: 'FieldNode') -> str:
strb = self.exporter.dereferencer.get_access_strobe(field)
def get_predicate(self, field: 'FieldNode') -> str:
strb = self.exp.dereferencer.get_access_strobe(field)
return f"decoded_req && !decoded_req_is_wr && {strb}"
class ClearOnRead(_OnRead):
comment = "SW clear on read"
onreadtype = OnReadType.rclr
def get_assignments(self, field: 'FieldNode') -> List[str]:
@@ -28,6 +29,7 @@ class ClearOnRead(_OnRead):
class SetOnRead(_OnRead):
comment = "SW set on read"
onreadtype = OnReadType.rset
def get_assignments(self, field: 'FieldNode') -> List[str]:

View File

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

View File

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

View File

@@ -16,8 +16,8 @@ class Hwif:
- Signal inputs (except those that are promoted to the top)
"""
def __init__(self, exporter: 'RegblockExporter', package_name: str):
self.exporter = exporter
def __init__(self, exp: 'RegblockExporter', package_name: str):
self.exp = exp
self.package_name = package_name
self.has_input_struct = None
@@ -26,22 +26,17 @@ class Hwif:
@property
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
"""
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_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)
@@ -259,6 +254,12 @@ class Hwif:
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:
"""
Returns the identifier string that best represents the output object.
@@ -279,3 +280,9 @@ class Hwif:
return "hwif_out." + path + "." + obj.name
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 @@
{{hwif.get_package_declaration()}}
// TODO: Add a banner
module {{module_name}} (
input wire clk,
{%- 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
class ReadbackMux:
def __init__(self, exporter:'RegblockExporter'):
self.exporter = exporter
def __init__(self, exp:'RegblockExporter'):
self.exp = exp
@property
def top_node(self) -> AddrmapNode:
return self.exporter.top_node
return self.exp.top_node
def get_implementation(self) -> str:
# TODO: Count the number of readable registers

View File

@@ -1,10 +1,10 @@
import re
from typing import TYPE_CHECKING
import textwrap
if TYPE_CHECKING:
from systemrdl.node import Node
from .signals import SignalBase
from typing import Optional
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)
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:
return f"@(posedge clk or posedge {resetsignal.identifier})"
elif resetsignal.is_async and not resetsignal.is_activehigh:

2
tst.py
View File

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

28
x.rdl
View File

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