Files
PeakRDL-regblock/src/peakrdl_regblock/field_logic/generators.py
2023-05-15 22:53:17 -07:00

373 lines
14 KiB
Python

from typing import TYPE_CHECKING, List, Optional
from collections import OrderedDict
from systemrdl.walker import WalkerAction
from systemrdl.node import RegNode, RegfileNode, MemNode, AddrmapNode
from ..struct_generator import RDLStructGenerator
from ..forloop_generator import RDLForLoopGenerator
from ..utils import get_indexed_path
from ..identifier_filter import kw_filter as kwf
if TYPE_CHECKING:
from . import FieldLogic
from systemrdl.node import FieldNode, AddressableNode
from .bases import SVLogic
class CombinationalStructGenerator(RDLStructGenerator):
def __init__(self, field_logic: 'FieldLogic'):
super().__init__()
self.field_logic = field_logic
def enter_AddressableComponent(self, node: 'AddressableNode') -> Optional[WalkerAction]:
super().enter_AddressableComponent(node)
if node.external:
return WalkerAction.SkipDescendants
return WalkerAction.Continue
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() # type: OrderedDict[str, SVLogic]
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(kwf(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)
if node.is_up_counter:
self.add_up_counter_members(node)
if node.is_down_counter:
self.add_down_counter_members(node)
if node.get_property('paritycheck'):
self.add_member("parity_error")
self.pop_struct()
def add_up_counter_members(self, node: 'FieldNode') -> None:
self.add_member('incrthreshold')
if self.field_logic.counter_incrsaturates(node):
self.add_member('incrsaturate')
else:
self.add_member('overflow')
def add_down_counter_members(self, node: 'FieldNode') -> None:
self.add_member('decrthreshold')
if self.field_logic.counter_decrsaturates(node):
self.add_member('decrsaturate')
else:
self.add_member('underflow')
class FieldStorageStructGenerator(RDLStructGenerator):
def __init__(self, field_logic: 'FieldLogic') -> None:
super().__init__()
self.field_logic = field_logic
def enter_AddressableComponent(self, node: 'AddressableNode') -> Optional[WalkerAction]:
super().enter_AddressableComponent(node)
if node.external:
return WalkerAction.SkipDescendants
return WalkerAction.Continue
def enter_Field(self, node: 'FieldNode') -> None:
self.push_struct(kwf(node.inst_name))
if node.implements_storage:
self.add_member("value", node.width)
if node.get_property('paritycheck'):
self.add_member("parity")
if self.field_logic.has_next_q(node):
self.add_member("next_q", node.width)
self.pop_struct()
class FieldLogicGenerator(RDLForLoopGenerator):
i_type = "genvar"
def __init__(self, field_logic: 'FieldLogic') -> None:
super().__init__()
self.field_logic = field_logic
self.exp = field_logic.exp
self.ds = self.exp.ds
self.field_storage_template = self.exp.jj_env.get_template(
"field_logic/templates/field_storage.sv"
)
self.external_reg_template = self.exp.jj_env.get_template(
"field_logic/templates/external_reg.sv"
)
self.external_block_template = self.exp.jj_env.get_template(
"field_logic/templates/external_block.sv"
)
self.intr_fields = [] # type: List[FieldNode]
self.halt_fields = [] # type: List[FieldNode]
def enter_AddressableComponent(self, node: 'AddressableNode') -> Optional[WalkerAction]:
super().enter_AddressableComponent(node)
if node.external and not isinstance(node, RegNode):
# Is an external block
self.assign_external_block_outputs(node)
# Do not recurse
return WalkerAction.SkipDescendants
return WalkerAction.Continue
def enter_Reg(self, node: 'RegNode') -> Optional[WalkerAction]:
self.intr_fields = []
self.halt_fields = []
if node.external:
self.assign_external_reg_outputs(node)
# Do not recurse to fields
return WalkerAction.SkipDescendants
return WalkerAction.Continue
def enter_Field(self, node: 'FieldNode') -> None:
if node.implements_storage:
self.generate_field_storage(node)
self.assign_field_outputs(node)
if node.get_property('intr'):
self.intr_fields.append(node)
if node.get_property('haltenable') or node.get_property('haltmask'):
self.halt_fields.append(node)
def exit_Reg(self, node: 'RegNode') -> None:
# Assign register's intr output
if self.intr_fields:
strs = []
for field in self.intr_fields:
enable = field.get_property('enable')
mask = field.get_property('mask')
F = self.exp.dereferencer.get_value(field)
if enable:
E = self.exp.dereferencer.get_value(enable)
s = f"|({F} & {E})"
elif mask:
M = self.exp.dereferencer.get_value(mask)
s = f"|({F} & ~{M})"
else:
s = f"|{F}"
strs.append(s)
self.add_content(
f"assign {self.exp.hwif.get_implied_prop_output_identifier(node, 'intr')} ="
)
self.add_content(
" "
+ "\n || ".join(strs)
+ ";"
)
# Assign register's halt output
if self.halt_fields:
strs = []
for field in self.halt_fields:
enable = field.get_property('haltenable')
mask = field.get_property('haltmask')
F = self.exp.dereferencer.get_value(field)
if enable:
E = self.exp.dereferencer.get_value(enable)
s = f"|({F} & {E})"
elif mask:
M = self.exp.dereferencer.get_value(mask)
s = f"|({F} & ~{M})"
else:
s = f"|{F}"
strs.append(s)
self.add_content(
f"assign {self.exp.hwif.get_implied_prop_output_identifier(node, 'halt')} ="
)
self.add_content(
" "
+ "\n || ".join(strs)
+ ";"
)
def generate_field_storage(self, node: 'FieldNode') -> None:
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
resetsignal = node.get_property('resetsignal')
reset_value = node.get_property('reset')
if reset_value is not None:
reset_value_str = self.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_logic': self.field_logic,
'extra_combo_signals': extra_combo_signals,
'conditionals': conditionals,
'resetsignal': resetsignal,
'get_always_ff_event': self.exp.dereferencer.get_always_ff_event,
'get_value': self.exp.dereferencer.get_value,
'get_resetsignal': self.exp.dereferencer.get_resetsignal,
'get_input_identifier': self.exp.hwif.get_input_identifier,
'ds': self.ds,
}
self.add_content(self.field_storage_template.render(context))
def assign_field_outputs(self, node: 'FieldNode') -> None:
# Field value output
if self.exp.hwif.has_value_output(node):
output_identifier = self.exp.hwif.get_output_identifier(node)
value = self.exp.dereferencer.get_value(node)
self.add_content(
f"assign {output_identifier} = {value};"
)
# Inferred logical reduction outputs
if node.get_property('anded'):
output_identifier = self.exp.hwif.get_implied_prop_output_identifier(node, "anded")
value = self.exp.dereferencer.get_field_propref_value(node, "anded")
self.add_content(
f"assign {output_identifier} = {value};"
)
if node.get_property('ored'):
output_identifier = self.exp.hwif.get_implied_prop_output_identifier(node, "ored")
value = self.exp.dereferencer.get_field_propref_value(node, "ored")
self.add_content(
f"assign {output_identifier} = {value};"
)
if node.get_property('xored'):
output_identifier = self.exp.hwif.get_implied_prop_output_identifier(node, "xored")
value = self.exp.dereferencer.get_field_propref_value(node, "xored")
self.add_content(
f"assign {output_identifier} = {value};"
)
# Software access strobes
if node.get_property('swmod'):
output_identifier = self.exp.hwif.get_implied_prop_output_identifier(node, "swmod")
value = self.field_logic.get_swmod_identifier(node)
self.add_content(
f"assign {output_identifier} = {value};"
)
if node.get_property('swacc'):
output_identifier = self.exp.hwif.get_implied_prop_output_identifier(node, "swacc")
value = self.field_logic.get_swacc_identifier(node)
self.add_content(
f"assign {output_identifier} = {value};"
)
if node.get_property('rd_swacc'):
output_identifier = self.exp.hwif.get_implied_prop_output_identifier(node, "rd_swacc")
value = self.field_logic.get_rd_swacc_identifier(node)
self.add_content(
f"assign {output_identifier} = {value};"
)
if node.get_property('wr_swacc'):
output_identifier = self.exp.hwif.get_implied_prop_output_identifier(node, "wr_swacc")
value = self.field_logic.get_wr_swacc_identifier(node)
self.add_content(
f"assign {output_identifier} = {value};"
)
# Counter thresholds
if node.get_property('incrthreshold') is not False: # (explicitly not False. Not 0)
output_identifier = self.exp.hwif.get_implied_prop_output_identifier(node, "incrthreshold")
value = self.field_logic.get_field_combo_identifier(node, 'incrthreshold')
self.add_content(
f"assign {output_identifier} = {value};"
)
if node.get_property('decrthreshold') is not False: # (explicitly not False. Not 0)
output_identifier = self.exp.hwif.get_implied_prop_output_identifier(node, "decrthreshold")
value = self.field_logic.get_field_combo_identifier(node, 'decrthreshold')
self.add_content(
f"assign {output_identifier} = {value};"
)
# Counter events
if node.get_property('overflow'):
output_identifier = self.exp.hwif.get_implied_prop_output_identifier(node, "overflow")
value = self.field_logic.get_field_combo_identifier(node, 'overflow')
self.add_content(
f"assign {output_identifier} = {value};"
)
if node.get_property('underflow'):
output_identifier = self.exp.hwif.get_implied_prop_output_identifier(node, "underflow")
value = self.field_logic.get_field_combo_identifier(node, 'underflow')
self.add_content(
f"assign {output_identifier} = {value};"
)
def assign_external_reg_outputs(self, node: 'RegNode') -> None:
prefix = "hwif_out." + get_indexed_path(self.exp.ds.top_node, node)
strb = self.exp.dereferencer.get_access_strobe(node)
width = min(self.exp.cpuif.data_width, node.get_property('regwidth'))
if width != self.exp.cpuif.data_width:
bslice = f"[{width - 1}:0]"
else:
bslice = ""
context = {
"prefix": prefix,
"strb": strb,
"bslice": bslice,
"retime": self.ds.retime_external_reg,
'get_always_ff_event': self.exp.dereferencer.get_always_ff_event,
"get_resetsignal": self.exp.dereferencer.get_resetsignal,
"resetsignal": self.exp.ds.top_node.cpuif_reset,
}
self.add_content(self.external_reg_template.render(context))
def assign_external_block_outputs(self, node: 'AddressableNode') -> None:
prefix = "hwif_out." + get_indexed_path(self.exp.ds.top_node, node)
strb = self.exp.dereferencer.get_external_block_access_strobe(node)
addr_width = node.size.bit_length()
retime = False
if isinstance(node, RegfileNode):
retime = self.ds.retime_external_regfile
elif isinstance(node, MemNode):
retime = self.ds.retime_external_mem
elif isinstance(node, AddrmapNode):
retime = self.ds.retime_external_addrmap
context = {
"prefix": prefix,
"strb": strb,
"addr_width": addr_width,
"retime": retime,
'get_always_ff_event': self.exp.dereferencer.get_always_ff_event,
"get_resetsignal": self.exp.dereferencer.get_resetsignal,
"resetsignal": self.exp.ds.top_node.cpuif_reset,
}
self.add_content(self.external_block_template.render(context))