From d5c5d42390ba9d57e70587f1dab129265883e066 Mon Sep 17 00:00:00 2001 From: Alex Mykyta Date: Thu, 28 Oct 2021 22:34:29 -0700 Subject: [PATCH] more field logic --- doc/logbooks/000-Main-Logbook | 11 +- doc/logbooks/Validation Needed | 12 ++ doc/logbooks/template-layers/4-fields | 2 +- peakrdl/regblock/addr_decode.py | 6 +- peakrdl/regblock/cpuif/base.py | 6 +- peakrdl/regblock/dereferencer.py | 12 +- peakrdl/regblock/exporter.py | 10 +- peakrdl/regblock/field_logic/__init__.py | 143 ++++++++++++-- peakrdl/regblock/field_logic/bases.py | 16 +- peakrdl/regblock/field_logic/field_builder.py | 176 ------------------ peakrdl/regblock/field_logic/generators.py | 99 ++++++++++ peakrdl/regblock/field_logic/hw_set_clr.py | 76 ++++++++ peakrdl/regblock/field_logic/hw_write.py | 78 ++++++++ peakrdl/regblock/field_logic/sw_onread.py | 6 +- peakrdl/regblock/field_logic/sw_onwrite.py | 15 +- .../field_logic/templates/field_storage.sv | 9 +- peakrdl/regblock/hwif.py | 25 ++- peakrdl/regblock/module_tmpl.sv | 4 +- peakrdl/regblock/package_tmpl.sv | 4 + peakrdl/regblock/readback_mux.py | 6 +- peakrdl/regblock/utils.py | 6 +- tst.py | 2 +- x.rdl | 28 +-- 23 files changed, 488 insertions(+), 264 deletions(-) delete mode 100644 peakrdl/regblock/field_logic/field_builder.py create mode 100644 peakrdl/regblock/field_logic/generators.py create mode 100644 peakrdl/regblock/field_logic/hw_set_clr.py create mode 100644 peakrdl/regblock/field_logic/hw_write.py create mode 100644 peakrdl/regblock/package_tmpl.sv diff --git a/doc/logbooks/000-Main-Logbook b/doc/logbooks/000-Main-Logbook index b789b10..6cab80b 100644 --- a/doc/logbooks/000-Main-Logbook +++ b/doc/logbooks/000-Main-Logbook @@ -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 diff --git a/doc/logbooks/Validation Needed b/doc/logbooks/Validation Needed index 076771e..d89eab5 100644 --- a/doc/logbooks/Validation Needed +++ b/doc/logbooks/Validation Needed @@ -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 diff --git a/doc/logbooks/template-layers/4-fields b/doc/logbooks/template-layers/4-fields index 6fb4a5c..ed03318 100644 --- a/doc/logbooks/template-layers/4-fields +++ b/doc/logbooks/template-layers/4-fields @@ -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]: diff --git a/peakrdl/regblock/addr_decode.py b/peakrdl/regblock/addr_decode.py index 777f3bc..7702682 100644 --- a/peakrdl/regblock/addr_decode.py +++ b/peakrdl/regblock/addr_decode.py @@ -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() diff --git a/peakrdl/regblock/cpuif/base.py b/peakrdl/regblock/cpuif/base.py index 7100f09..b9bf3a6 100644 --- a/peakrdl/regblock/cpuif/base.py +++ b/peakrdl/regblock/cpuif/base.py @@ -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) diff --git a/peakrdl/regblock/dereferencer.py b/peakrdl/regblock/dereferencer.py index a9035f9..8c81356 100644 --- a/peakrdl/regblock/dereferencer.py +++ b/peakrdl/regblock/dereferencer.py @@ -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: """ diff --git a/peakrdl/regblock/exporter.py b/peakrdl/regblock/exporter.py index 20703e6..b06c872 100644 --- a/peakrdl/regblock/exporter.py +++ b/peakrdl/regblock/exporter.py @@ -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) diff --git a/peakrdl/regblock/field_logic/__init__.py b/peakrdl/regblock/field_logic/__init__.py index 3bc9450..5d45353 100644 --- a/peakrdl/regblock/field_logic/__init__.py +++ b/peakrdl/regblock/field_logic/__init__.py @@ -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 diff --git a/peakrdl/regblock/field_logic/bases.py b/peakrdl/regblock/field_logic/bases.py index 7da9f46..3f3521c 100644 --- a/peakrdl/regblock/field_logic/bases.py +++ b/peakrdl/regblock/field_logic/bases.py @@ -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: 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 [] diff --git a/peakrdl/regblock/field_logic/field_builder.py b/peakrdl/regblock/field_logic/field_builder.py deleted file mode 100644 index a919e6f..0000000 --- a/peakrdl/regblock/field_logic/field_builder.py +++ /dev/null @@ -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)) diff --git a/peakrdl/regblock/field_logic/generators.py b/peakrdl/regblock/field_logic/generators.py new file mode 100644 index 0000000..88014d2 --- /dev/null +++ b/peakrdl/regblock/field_logic/generators.py @@ -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)) diff --git a/peakrdl/regblock/field_logic/hw_set_clr.py b/peakrdl/regblock/field_logic/hw_set_clr.py new file mode 100644 index 0000000..b5321fd --- /dev/null +++ b/peakrdl/regblock/field_logic/hw_set_clr.py @@ -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;", + ] diff --git a/peakrdl/regblock/field_logic/hw_write.py b/peakrdl/regblock/field_logic/hw_write.py new file mode 100644 index 0000000..55bf4ed --- /dev/null +++ b/peakrdl/regblock/field_logic/hw_write.py @@ -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}" diff --git a/peakrdl/regblock/field_logic/sw_onread.py b/peakrdl/regblock/field_logic/sw_onread.py index f4309c5..bb6f1f4 100644 --- a/peakrdl/regblock/field_logic/sw_onread.py +++ b/peakrdl/regblock/field_logic/sw_onread.py @@ -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]: diff --git a/peakrdl/regblock/field_logic/sw_onwrite.py b/peakrdl/regblock/field_logic/sw_onwrite.py index 182b067..fd3df1f 100644 --- a/peakrdl/regblock/field_logic/sw_onwrite.py +++ b/peakrdl/regblock/field_logic/sw_onwrite.py @@ -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]: diff --git a/peakrdl/regblock/field_logic/templates/field_storage.sv b/peakrdl/regblock/field_logic/templates/field_storage.sv index ee55130..15f2bfb 100644 --- a/peakrdl/regblock/field_logic/templates/field_storage.sv +++ b/peakrdl/regblock/field_logic/templates/field_storage.sv @@ -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 diff --git a/peakrdl/regblock/hwif.py b/peakrdl/regblock/hwif.py index cb75f64..545751b 100644 --- a/peakrdl/regblock/hwif.py +++ b/peakrdl/regblock/hwif.py @@ -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 diff --git a/peakrdl/regblock/module_tmpl.sv b/peakrdl/regblock/module_tmpl.sv index f8a038a..a5a32a2 100644 --- a/peakrdl/regblock/module_tmpl.sv +++ b/peakrdl/regblock/module_tmpl.sv @@ -1,6 +1,4 @@ - -{{hwif.get_package_declaration()}} - +// TODO: Add a banner module {{module_name}} ( input wire clk, {%- for signal in reset_signals %} diff --git a/peakrdl/regblock/package_tmpl.sv b/peakrdl/regblock/package_tmpl.sv new file mode 100644 index 0000000..fa9c02d --- /dev/null +++ b/peakrdl/regblock/package_tmpl.sv @@ -0,0 +1,4 @@ +// TODO: Add a banner +package {{hwif.package_name}}; + {{hwif.get_package_contents()|indent}} +endpackage diff --git a/peakrdl/regblock/readback_mux.py b/peakrdl/regblock/readback_mux.py index 89273b6..0e61a1c 100644 --- a/peakrdl/regblock/readback_mux.py +++ b/peakrdl/regblock/readback_mux.py @@ -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 diff --git a/peakrdl/regblock/utils.py b/peakrdl/regblock/utils.py index 93a7f7d..cbc4bfa 100644 --- a/peakrdl/regblock/utils.py +++ b/peakrdl/regblock/utils.py @@ -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: diff --git a/tst.py b/tst.py index 245dfc1..3232fd0 100755 --- a/tst.py +++ b/tst.py @@ -18,4 +18,4 @@ except RDLCompileError: R = RegblockExporter() -R.export(root, "out.sv") +R.export(root, ".") diff --git a/x.rdl b/x.rdl index a26fb6f..b7b2d45 100644 --- a/x.rdl +++ b/x.rdl @@ -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; };