diff --git a/src/peakrdl_regblock/addr_decode.py b/src/peakrdl_regblock/addr_decode.py index b38bf0d..0be0b36 100644 --- a/src/peakrdl_regblock/addr_decode.py +++ b/src/peakrdl_regblock/addr_decode.py @@ -19,7 +19,7 @@ class AddressDecode: @property def top_node(self) -> 'AddrmapNode': - return self.exp.top_node + return self.exp.ds.top_node def get_strobe_struct(self) -> str: struct_gen = DecodeStructGenerator() diff --git a/src/peakrdl_regblock/cpuif/axi4lite/__init__.py b/src/peakrdl_regblock/cpuif/axi4lite/__init__.py index 60553f6..31c3eb7 100644 --- a/src/peakrdl_regblock/cpuif/axi4lite/__init__.py +++ b/src/peakrdl_regblock/cpuif/axi4lite/__init__.py @@ -12,7 +12,7 @@ class AXI4Lite_Cpuif(CpuifBase): @property def regblock_latency(self) -> int: - return max(self.exp.min_read_latency, self.exp.min_write_latency) + return max(self.exp.ds.min_read_latency, self.exp.ds.min_write_latency) @property def max_outstanding(self) -> int: diff --git a/src/peakrdl_regblock/cpuif/base.py b/src/peakrdl_regblock/cpuif/base.py index 09510d9..a4f9b4a 100644 --- a/src/peakrdl_regblock/cpuif/base.py +++ b/src/peakrdl_regblock/cpuif/base.py @@ -14,11 +14,17 @@ class CpuifBase: # Path is relative to the location of the class that assigns this variable template_path = "" - def __init__(self, exp:'RegblockExporter', data_width:int=32, addr_width:int=32): + def __init__(self, exp:'RegblockExporter'): self.exp = exp - self.reset = exp.top_node.cpuif_reset - self.data_width = data_width - self.addr_width = addr_width + self.reset = exp.ds.top_node.cpuif_reset + + @property + def addr_width(self) -> int: + return self.exp.ds.addr_width + + @property + def data_width(self) -> int: + return self.exp.ds.cpuif_data_width @property def data_width_bytes(self) -> int: diff --git a/src/peakrdl_regblock/dereferencer.py b/src/peakrdl_regblock/dereferencer.py index 72b749e..d3ce1b6 100644 --- a/src/peakrdl_regblock/dereferencer.py +++ b/src/peakrdl_regblock/dereferencer.py @@ -30,7 +30,7 @@ class Dereferencer: @property def top_node(self) -> AddrmapNode: - return self.exp.top_node + return self.exp.ds.top_node def get_value(self, obj: Union[int, FieldNode, SignalNode, PropertyReference]) -> str: """ diff --git a/src/peakrdl_regblock/exporter.py b/src/peakrdl_regblock/exporter.py index 8312c01..47fa74b 100644 --- a/src/peakrdl_regblock/exporter.py +++ b/src/peakrdl_regblock/exporter.py @@ -1,5 +1,6 @@ import os -from typing import Union, Any, Type, Optional +from typing import TYPE_CHECKING, Union, Any, Type, Optional, Set, List +from collections import OrderedDict import jinja2 as jj from systemrdl.node import AddrmapNode, RootNode @@ -19,14 +20,16 @@ from .write_buffering import WriteBuffering from .read_buffering import ReadBuffering from .external_acks import ExternalWriteAckGenerator, ExternalReadAckGenerator +if TYPE_CHECKING: + from systemrdl.node import SignalNode + from systemrdl.rdltypes import UserEnum + class RegblockExporter: def __init__(self, **kwargs: Any) -> None: # Check for stray kwargs if kwargs: raise TypeError(f"got an unexpected keyword argument '{list(kwargs.keys())[0]}'") - - self.top_node = None # type: AddrmapNode self.hwif = None # type: Hwif self.cpuif = None # type: CpuifBase self.address_decode = None # type: AddressDecode @@ -35,8 +38,7 @@ class RegblockExporter: self.write_buffering = None # type: WriteBuffering self.read_buffering = None # type: ReadBuffering self.dereferencer = None # type: Dereferencer - self.min_read_latency = 0 - self.min_write_latency = 0 + self.ds = None # type: DesignState loader = jj.ChoiceLoader([ jj.FileSystemLoader(os.path.dirname(__file__)), @@ -114,83 +116,37 @@ class RegblockExporter: """ # If it is the root node, skip to top addrmap if isinstance(node, RootNode): - self.top_node = node.top + top_node = node.top else: - self.top_node = node - msg = self.top_node.env.msg + top_node = node + self.ds = DesignState(top_node, kwargs) cpuif_cls = kwargs.pop("cpuif_cls", None) or APB4_Cpuif # type: Type[CpuifBase] - module_name = kwargs.pop("module_name", None) or kwf(self.top_node.inst_name) # type: str - package_name = kwargs.pop("package_name", None) or (module_name + "_pkg") # type: str - reuse_hwif_typedefs = kwargs.pop("reuse_hwif_typedefs", True) # type: bool generate_hwif_report = kwargs.pop("generate_hwif_report", False) # type: bool - user_addr_width = kwargs.pop("address_width", None) # type: Optional[int] - - # Pipelining options - retime_read_fanin = kwargs.pop("retime_read_fanin", False) # type: bool - retime_read_response = kwargs.pop("retime_read_response", False) # type: bool - retime_external_reg = kwargs.pop("retime_external_reg", False) # type: bool - retime_external_regfile = kwargs.pop("retime_external_regfile", False) # type: bool - retime_external_mem = kwargs.pop("retime_external_mem", False) # type: bool - retime_external_addrmap = kwargs.pop("retime_external_addrmap", False) # type: bool # Check for stray kwargs if kwargs: raise TypeError(f"got an unexpected keyword argument '{list(kwargs.keys())[0]}'") - self.min_read_latency = 0 - self.min_write_latency = 0 - if retime_read_fanin: - self.min_read_latency += 1 - if retime_read_response: - self.min_read_latency += 1 - - addr_width = self.top_node.size.bit_length() - if user_addr_width is not None: - if user_addr_width < addr_width: - msg.fatal(f"User-specified address width shall be greater than or equal to {addr_width}.") - addr_width = user_addr_width - # Scan the design for pre-export information - scanner = DesignScanner(self) - scanner.do_scan() + DesignScanner(self).do_scan() if generate_hwif_report: - path = os.path.join(output_dir, f"{module_name}_hwif.rpt") + path = os.path.join(output_dir, f"{self.ds.module_name}_hwif.rpt") hwif_report_file = open(path, "w", encoding='utf-8') # pylint: disable=consider-using-with else: hwif_report_file = None # Construct exporter components - self.cpuif = cpuif_cls( - self, - data_width=scanner.cpuif_data_width, - addr_width=addr_width - ) + self.cpuif = cpuif_cls(self) self.hwif = Hwif( self, - package_name=package_name, - in_hier_signal_paths=scanner.in_hier_signal_paths, - out_of_hier_signals=scanner.out_of_hier_signals, - user_enums=scanner.user_enums, - reuse_typedefs=reuse_hwif_typedefs, hwif_report_file=hwif_report_file, - data_width=scanner.cpuif_data_width, - ) - self.readback = Readback( - self, - retime_read_fanin, - scanner.has_external_addressable ) + self.readback = Readback(self) self.address_decode = AddressDecode(self) - self.field_logic = FieldLogic( - self, - retime_external_reg=retime_external_reg, - retime_external_regfile=retime_external_regfile, - retime_external_mem=retime_external_mem, - retime_external_addrmap=retime_external_addrmap, - ) + self.field_logic = FieldLogic(self) self.write_buffering = WriteBuffering(self) self.read_buffering = ReadBuffering(self) self.dereferencer = Dereferencer(self) @@ -198,18 +154,10 @@ class RegblockExporter: ext_read_acks = ExternalReadAckGenerator(self) # Validate that there are no unsupported constructs - validator = DesignValidator(self) - validator.do_validate() + DesignValidator(self).do_validate() # Build Jinja template context context = { - "module_name": module_name, - "user_out_of_hier_signals": scanner.out_of_hier_signals.values(), - "has_writable_msb0_fields": scanner.has_writable_msb0_fields, - "has_buffered_write_regs": scanner.has_buffered_write_regs, - "has_buffered_read_regs": scanner.has_buffered_read_regs, - "has_external_addressable": scanner.has_external_addressable, - "has_external_block": scanner.has_external_block, "cpuif": self.cpuif, "hwif": self.hwif, "write_buffering": self.write_buffering, @@ -221,24 +169,82 @@ class RegblockExporter: "ext_write_acks": ext_write_acks, "ext_read_acks": ext_read_acks, "get_always_ff_event": self.dereferencer.get_always_ff_event, - "retime_read_response": retime_read_response, - "retime_read_fanin": retime_read_fanin, - "min_read_latency": self.min_read_latency, - "min_write_latency": self.min_write_latency, + "ds": self.ds, "kwf": kwf, } # Write out design os.makedirs(output_dir, exist_ok=True) - package_file_path = os.path.join(output_dir, package_name + ".sv") + package_file_path = os.path.join(output_dir, self.ds.package_name + ".sv") template = self.jj_env.get_template("package_tmpl.sv") stream = template.stream(context) stream.dump(package_file_path) - module_file_path = os.path.join(output_dir, module_name + ".sv") + module_file_path = os.path.join(output_dir, self.ds.module_name + ".sv") template = self.jj_env.get_template("module_tmpl.sv") stream = template.stream(context) stream.dump(module_file_path) if hwif_report_file: hwif_report_file.close() + + +class DesignState: + """ + Dumping ground for all sorts of variables that are relevant to a particular + design. + """ + + def __init__(self, top_node: AddrmapNode, kwargs: Any) -> None: + self.top_node = top_node + msg = top_node.env.msg + + #------------------------ + # Extract compiler args + #------------------------ + self.reuse_hwif_typedefs = kwargs.pop("reuse_hwif_typedefs", True) # type: bool + self.module_name = kwargs.pop("module_name", None) or kwf(self.top_node.inst_name) # type: str + self.package_name = kwargs.pop("package_name", None) or (self.module_name + "_pkg") # type: str + user_addr_width = kwargs.pop("address_width", None) # type: Optional[int] + + + # Pipelining options + self.retime_read_fanin = kwargs.pop("retime_read_fanin", False) # type: bool + self.retime_read_response = kwargs.pop("retime_read_response", False) # type: bool + self.retime_external_reg = kwargs.pop("retime_external_reg", False) # type: bool + self.retime_external_regfile = kwargs.pop("retime_external_regfile", False) # type: bool + self.retime_external_mem = kwargs.pop("retime_external_mem", False) # type: bool + self.retime_external_addrmap = kwargs.pop("retime_external_addrmap", False) # type: bool + + #------------------------ + # Info about the design + #------------------------ + self.min_read_latency = 0 + self.min_write_latency = 0 + self.cpuif_data_width = 0 + + # Collections of signals that were actually referenced by the design + self.in_hier_signal_paths = set() # type: Set[str] + self.out_of_hier_signals = OrderedDict() # type: OrderedDict[str, SignalNode] + + self.has_writable_msb0_fields = False + self.has_buffered_write_regs = False + self.has_buffered_read_regs = False + + self.has_external_block = False + self.has_external_addressable = False + + # Track any referenced enums + self.user_enums = [] # type: List[Type[UserEnum]] + + #------------------------ + if self.retime_read_fanin: + self.min_read_latency += 1 + if self.retime_read_response: + self.min_read_latency += 1 + + self.addr_width = self.top_node.size.bit_length() + if user_addr_width is not None: + if user_addr_width < self.addr_width: + msg.fatal(f"User-specified address width shall be greater than or equal to {self.addr_width}.") + self.addr_width = user_addr_width diff --git a/src/peakrdl_regblock/external_acks.py b/src/peakrdl_regblock/external_acks.py index 57b4eb8..966c05d 100644 --- a/src/peakrdl_regblock/external_acks.py +++ b/src/peakrdl_regblock/external_acks.py @@ -15,7 +15,7 @@ class ExternalWriteAckGenerator(RDLForLoopGenerator): self.exp = exp def get_implementation(self) -> str: - content = self.get_content(self.exp.top_node) + content = self.get_content(self.exp.ds.top_node) if content is None: return "" return content @@ -36,7 +36,7 @@ class ExternalReadAckGenerator(RDLForLoopGenerator): self.exp = exp def get_implementation(self) -> str: - content = self.get_content(self.exp.top_node) + content = self.get_content(self.exp.ds.top_node) if content is None: return "" return content diff --git a/src/peakrdl_regblock/field_logic/__init__.py b/src/peakrdl_regblock/field_logic/__init__.py index 304170b..8ab9f73 100644 --- a/src/peakrdl_regblock/field_logic/__init__.py +++ b/src/peakrdl_regblock/field_logic/__init__.py @@ -17,31 +17,24 @@ from .generators import CombinationalStructGenerator, FieldStorageStructGenerato if TYPE_CHECKING: from typing import Dict, List from systemrdl.node import AddrmapNode, FieldNode - from ..exporter import RegblockExporter + from ..exporter import RegblockExporter, DesignState class FieldLogic: - def __init__( - self, - exp:'RegblockExporter', - retime_external_reg: bool, - retime_external_regfile: bool, - retime_external_mem: bool, - retime_external_addrmap: bool, - ): + def __init__(self, exp:'RegblockExporter'): self.exp = exp - self.retime_external_reg = retime_external_reg - self.retime_external_regfile = retime_external_regfile - self.retime_external_mem = retime_external_mem - self.retime_external_addrmap = retime_external_addrmap self._hw_conditionals = {} # type: Dict[int, List[NextStateConditional]] self._sw_conditionals = {} # type: Dict[int, List[NextStateConditional]] self.init_conditionals() + @property + def ds(self) -> 'DesignState': + return self.exp.ds + @property def top_node(self) -> 'AddrmapNode': - return self.exp.top_node + return self.exp.ds.top_node def get_storage_struct(self) -> str: struct_gen = FieldStorageStructGenerator(self) diff --git a/src/peakrdl_regblock/field_logic/bases.py b/src/peakrdl_regblock/field_logic/bases.py index fa70773..5f629a5 100644 --- a/src/peakrdl_regblock/field_logic/bases.py +++ b/src/peakrdl_regblock/field_logic/bases.py @@ -77,7 +77,7 @@ class NextStateConditional: raise NotImplementedError def get_field_path(self, field:'FieldNode') -> str: - return get_indexed_path(self.exp.top_node, field) + return get_indexed_path(self.exp.ds.top_node, field) def get_predicate(self, field: 'FieldNode') -> str: """ diff --git a/src/peakrdl_regblock/field_logic/generators.py b/src/peakrdl_regblock/field_logic/generators.py index 6d09d2b..380c5d1 100644 --- a/src/peakrdl_regblock/field_logic/generators.py +++ b/src/peakrdl_regblock/field_logic/generators.py @@ -101,6 +101,7 @@ class FieldLogicGenerator(RDLForLoopGenerator): 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" ) @@ -321,7 +322,7 @@ class FieldLogicGenerator(RDLForLoopGenerator): def assign_external_reg_outputs(self, node: 'RegNode') -> None: - prefix = "hwif_out." + get_indexed_path(self.exp.top_node, node) + 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')) @@ -334,25 +335,25 @@ class FieldLogicGenerator(RDLForLoopGenerator): "prefix": prefix, "strb": strb, "bslice": bslice, - "retime": self.field_logic.retime_external_reg, + "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.top_node.cpuif_reset, + "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.top_node, node) + 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.field_logic.retime_external_regfile + retime = self.ds.retime_external_regfile elif isinstance(node, MemNode): - retime = self.field_logic.retime_external_mem + retime = self.ds.retime_external_mem elif isinstance(node, AddrmapNode): - retime = self.field_logic.retime_external_addrmap + retime = self.ds.retime_external_addrmap context = { "prefix": prefix, @@ -361,6 +362,6 @@ class FieldLogicGenerator(RDLForLoopGenerator): "retime": retime, 'get_always_ff_event': self.exp.dereferencer.get_always_ff_event, "get_resetsignal": self.exp.dereferencer.get_resetsignal, - "resetsignal": self.exp.top_node.cpuif_reset, + "resetsignal": self.exp.ds.top_node.cpuif_reset, } self.add_content(self.external_block_template.render(context)) diff --git a/src/peakrdl_regblock/hwif/__init__.py b/src/peakrdl_regblock/hwif/__init__.py index 272273e..7b50e5f 100644 --- a/src/peakrdl_regblock/hwif/__init__.py +++ b/src/peakrdl_regblock/hwif/__init__.py @@ -11,7 +11,7 @@ from .generators import InputStructGenerator_TypeScope, OutputStructGenerator_Ty from .generators import EnumGenerator if TYPE_CHECKING: - from ..exporter import RegblockExporter + from ..exporter import RegblockExporter, DesignState class Hwif: """ @@ -22,36 +22,30 @@ class Hwif: """ def __init__( - self, exp: 'RegblockExporter', package_name: str, - user_enums: List[Type[UserEnum]], - in_hier_signal_paths: Set[str], out_of_hier_signals: Dict[str, SignalNode], - reuse_typedefs: bool, hwif_report_file: Optional[TextIO], - data_width: int + self, exp: 'RegblockExporter', + hwif_report_file: Optional[TextIO] ): self.exp = exp - self.package_name = package_name self.has_input_struct = False self.has_output_struct = False - self.in_hier_signal_paths = in_hier_signal_paths - self.out_of_hier_signals = out_of_hier_signals - self.user_enums = user_enums - self.hwif_report_file = hwif_report_file - self.data_width = data_width - - if reuse_typedefs: + if self.ds.reuse_hwif_typedefs: self._gen_in_cls = InputStructGenerator_TypeScope self._gen_out_cls = OutputStructGenerator_TypeScope else: self._gen_in_cls = InputStructGenerator_Hier self._gen_out_cls = OutputStructGenerator_Hier + @property + def ds(self) -> 'DesignState': + return self.exp.ds + @property def top_node(self) -> AddrmapNode: - return self.exp.top_node + return self.exp.ds.top_node def get_package_contents(self) -> str: @@ -83,9 +77,7 @@ class Hwif: self.has_output_struct = False gen_enum = EnumGenerator() - enums = gen_enum.get_enums( - self.user_enums - ) + enums = gen_enum.get_enums(self.ds.user_enums) if enums is not None: lines.append(enums) @@ -105,10 +97,10 @@ class Hwif: lines = [] if self.has_input_struct: type_name = f"{self.top_node.inst_name}__in_t" - lines.append(f"input {self.package_name}::{type_name} hwif_in") + lines.append(f"input {self.ds.package_name}::{type_name} hwif_in") if self.has_output_struct: type_name = f"{self.top_node.inst_name}__out_t" - lines.append(f"output {self.package_name}::{type_name} hwif_out") + lines.append(f"output {self.ds.package_name}::{type_name} hwif_out") return ",\n".join(lines) @@ -156,7 +148,7 @@ class Hwif: path = get_indexed_path(self.top_node, obj) return "hwif_in." + path + ".next" elif isinstance(obj, SignalNode): - if obj.get_path() in self.out_of_hier_signals: + if obj.get_path() in self.ds.out_of_hier_signals: return kwf(obj.inst_name) path = get_indexed_path(self.top_node, obj) return "hwif_in." + path diff --git a/src/peakrdl_regblock/hwif/generators.py b/src/peakrdl_regblock/hwif/generators.py index 52ee238..b315795 100644 --- a/src/peakrdl_regblock/hwif/generators.py +++ b/src/peakrdl_regblock/hwif/generators.py @@ -63,12 +63,12 @@ class InputStructGenerator_Hier(HWIFStructGenerator): def enter_Signal(self, node: 'SignalNode') -> None: # only emit the signal if design scanner detected it is actually being used path = node.get_path() - if path in self.hwif.in_hier_signal_paths: + if path in self.hwif.ds.in_hier_signal_paths: self.add_member(kwf(node.inst_name), node.width) def _add_external_block_members(self, node: 'AddressableNode') -> None: self.add_member("rd_ack") - self.add_member("rd_data", self.hwif.data_width) + self.add_member("rd_data", self.hwif.ds.cpuif_data_width) self.add_member("wr_ack") def enter_Addrmap(self, node: 'AddrmapNode') -> None: @@ -95,7 +95,7 @@ class InputStructGenerator_Hier(HWIFStructGenerator): def enter_Reg(self, node: 'RegNode') -> Optional[WalkerAction]: super().enter_Reg(node) if node.external: - width = min(self.hwif.data_width, node.get_property('regwidth')) + width = min(self.hwif.ds.cpuif_data_width, node.get_property('regwidth')) self.add_member("rd_ack") self.add_member("rd_data", width) self.add_member("wr_ack") @@ -162,8 +162,8 @@ class OutputStructGenerator_Hier(HWIFStructGenerator): self.add_member("req") self.add_member("addr", (node.size - 1).bit_length()) self.add_member("req_is_wr") - self.add_member("wr_data", self.hwif.data_width) - self.add_member("wr_biten", self.hwif.data_width) + self.add_member("wr_data", self.hwif.ds.cpuif_data_width) + self.add_member("wr_biten", self.hwif.ds.cpuif_data_width) def enter_Addrmap(self, node: 'AddrmapNode') -> None: super().enter_Addrmap(node) @@ -189,7 +189,7 @@ class OutputStructGenerator_Hier(HWIFStructGenerator): def enter_Reg(self, node: 'RegNode') -> Optional[WalkerAction]: super().enter_Reg(node) if node.external: - width = min(self.hwif.data_width, node.get_property('regwidth')) + width = min(self.hwif.ds.cpuif_data_width, node.get_property('regwidth')) n_subwords = node.get_property("regwidth") // node.get_property("accesswidth") self.add_member("req", n_subwords) self.add_member("req_is_wr") diff --git a/src/peakrdl_regblock/module_tmpl.sv b/src/peakrdl_regblock/module_tmpl.sv index 9eba320..ca22b20 100644 --- a/src/peakrdl_regblock/module_tmpl.sv +++ b/src/peakrdl_regblock/module_tmpl.sv @@ -1,11 +1,11 @@ // Generated by PeakRDL-regblock - A free and open-source SystemVerilog generator // https://github.com/SystemRDL/PeakRDL-regblock -module {{module_name}} ( +module {{ds.module_name}} ( input wire clk, input wire rst, - {%- for signal in user_out_of_hier_signals %} + {%- for signal in ds.out_of_hier_signals.values() %} {%- if signal.width == 1 %} input wire {{kwf(signal.inst_name)}}, {%- else %} @@ -40,7 +40,7 @@ module {{module_name}} ( {{cpuif.get_implementation()|indent}} logic cpuif_req_masked; -{%- if has_external_addressable %} +{%- if ds.has_external_addressable %} logic external_req; logic external_pending; logic external_wr_ack; @@ -54,9 +54,9 @@ module {{module_name}} ( end end {%- endif %} -{% if min_read_latency == min_write_latency %} +{% if ds.min_read_latency == ds.min_write_latency %} // Read & write latencies are balanced. Stalls not required - {%- if has_external_addressable %} + {%- if ds.has_external_addressable %} // except if external assign cpuif_req_stall_rd = external_pending; assign cpuif_req_stall_wr = external_pending; @@ -64,9 +64,9 @@ module {{module_name}} ( assign cpuif_req_stall_rd = '0; assign cpuif_req_stall_wr = '0; {%- endif %} -{%- elif min_read_latency > min_write_latency %} +{%- elif ds.min_read_latency > ds.min_write_latency %} // Read latency > write latency. May need to delay next write that follows a read - logic [{{min_read_latency - min_write_latency - 1}}:0] cpuif_req_stall_sr; + logic [{{ds.min_read_latency - ds.min_write_latency - 1}}:0] cpuif_req_stall_sr; always_ff {{get_always_ff_event(cpuif.reset)}} begin if({{get_resetsignal(cpuif.reset)}}) begin cpuif_req_stall_sr <= '0; @@ -76,7 +76,7 @@ module {{module_name}} ( cpuif_req_stall_sr <= (cpuif_req_stall_sr >> 'd1); end end - {%- if has_external_addressable %} + {%- if ds.has_external_addressable %} assign cpuif_req_stall_rd = external_pending; assign cpuif_req_stall_wr = cpuif_req_stall_sr[0] | external_pending; {%- else %} @@ -85,7 +85,7 @@ module {{module_name}} ( {%- endif %} {%- else %} // Write latency > read latency. May need to delay next read that follows a write - logic [{{min_write_latency - min_read_latency - 1}}:0] cpuif_req_stall_sr; + logic [{{ds.min_write_latency - ds.min_read_latency - 1}}:0] cpuif_req_stall_sr; always_ff {{get_always_ff_event(cpuif.reset)}} begin if({{get_resetsignal(cpuif.reset)}}) begin cpuif_req_stall_sr <= '0; @@ -95,7 +95,7 @@ module {{module_name}} ( cpuif_req_stall_sr <= (cpuif_req_stall_sr >> 'd1); end end - {%- if has_external_addressable %} + {%- if ds.has_external_addressable %} assign cpuif_req_stall_rd = cpuif_req_stall_sr[0] | external_pending; assign cpuif_req_stall_wr = external_pending; {%- else %} @@ -112,10 +112,10 @@ module {{module_name}} ( //-------------------------------------------------------------------------- {{address_decode.get_strobe_struct()|indent}} decoded_reg_strb_t decoded_reg_strb; -{%- if has_external_addressable %} +{%- if ds.has_external_addressable %} logic decoded_strb_is_external; {% endif %} -{%- if has_external_block %} +{%- if ds.has_external_block %} logic [{{cpuif.addr_width-1}}:0] decoded_addr; {% endif %} logic decoded_req; @@ -124,25 +124,25 @@ module {{module_name}} ( logic [{{cpuif.data_width-1}}:0] decoded_wr_biten; always_comb begin - {%- if has_external_addressable %} + {%- if ds.has_external_addressable %} automatic logic is_external = '0; {% endif %} {{address_decode.get_implementation()|indent(8)}} - {%- if has_external_addressable %} + {%- if ds.has_external_addressable %} decoded_strb_is_external = is_external; external_req = is_external; {% endif %} end // Pass down signals to next stage -{%- if has_external_block %} +{%- if ds.has_external_block %} assign decoded_addr = cpuif_addr; {% endif %} assign decoded_req = cpuif_req_masked; assign decoded_req_is_wr = cpuif_req_is_wr; assign decoded_wr_data = cpuif_wr_data; assign decoded_wr_biten = cpuif_wr_biten; -{% if has_writable_msb0_fields %} +{% if ds.has_writable_msb0_fields %} // bitswap for use by fields with msb0 ordering logic [{{cpuif.data_width-1}}:0] decoded_wr_data_bswap; logic [{{cpuif.data_width-1}}:0] decoded_wr_biten_bswap; @@ -150,7 +150,7 @@ module {{module_name}} ( assign decoded_wr_biten_bswap = {<<{decoded_wr_biten}}; {%- endif %} -{%- if has_buffered_write_regs %} +{%- if ds.has_buffered_write_regs %} //-------------------------------------------------------------------------- // Write double-buffers @@ -168,7 +168,7 @@ module {{module_name}} ( {{field_logic.get_implementation()|indent}} -{%- if has_buffered_read_regs %} +{%- if ds.has_buffered_read_regs %} //-------------------------------------------------------------------------- // Read double-buffers @@ -181,7 +181,7 @@ module {{module_name}} ( //-------------------------------------------------------------------------- // Write response //-------------------------------------------------------------------------- -{%- if has_external_addressable %} +{%- if ds.has_external_addressable %} always_comb begin automatic logic wr_ack; wr_ack = '0; @@ -198,7 +198,7 @@ module {{module_name}} ( //-------------------------------------------------------------------------- // Readback //-------------------------------------------------------------------------- -{%- if has_external_addressable %} +{%- if ds.has_external_addressable %} logic readback_external_rd_ack_c; always_comb begin automatic logic rd_ack; @@ -208,7 +208,7 @@ module {{module_name}} ( end logic readback_external_rd_ack; - {%- if retime_read_fanin %} + {%- if ds.retime_read_fanin %} always_ff {{get_always_ff_event(cpuif.reset)}} begin if({{get_resetsignal(cpuif.reset)}}) begin readback_external_rd_ack <= '0; @@ -227,17 +227,17 @@ module {{module_name}} ( logic readback_done; logic [{{cpuif.data_width-1}}:0] readback_data; {{readback.get_implementation()|indent}} -{% if retime_read_response %} +{% if ds.retime_read_response %} always_ff {{get_always_ff_event(cpuif.reset)}} begin if({{get_resetsignal(cpuif.reset)}}) begin cpuif_rd_ack <= '0; cpuif_rd_data <= '0; cpuif_rd_err <= '0; - {%- if has_external_addressable %} + {%- if ds.has_external_addressable %} external_rd_ack <= '0; {%- endif %} end else begin - {%- if has_external_addressable %} + {%- if ds.has_external_addressable %} external_rd_ack <= readback_external_rd_ack; cpuif_rd_ack <= readback_done | readback_external_rd_ack; {%- else %} @@ -248,7 +248,7 @@ module {{module_name}} ( end end {% else %} - {%- if has_external_addressable %} + {%- if ds.has_external_addressable %} assign external_rd_ack = readback_external_rd_ack; assign cpuif_rd_ack = readback_done | readback_external_rd_ack; {%- else %} diff --git a/src/peakrdl_regblock/package_tmpl.sv b/src/peakrdl_regblock/package_tmpl.sv index 8a9c43e..ba52085 100644 --- a/src/peakrdl_regblock/package_tmpl.sv +++ b/src/peakrdl_regblock/package_tmpl.sv @@ -1,6 +1,6 @@ // Generated by PeakRDL-regblock - A free and open-source SystemVerilog generator // https://github.com/SystemRDL/PeakRDL-regblock -package {{hwif.package_name}}; +package {{ds.package_name}}; {{hwif.get_package_contents()|indent}} endpackage diff --git a/src/peakrdl_regblock/read_buffering/__init__.py b/src/peakrdl_regblock/read_buffering/__init__.py index 396746c..6aee6b2 100644 --- a/src/peakrdl_regblock/read_buffering/__init__.py +++ b/src/peakrdl_regblock/read_buffering/__init__.py @@ -16,7 +16,7 @@ class ReadBuffering: @property def top_node(self) -> 'AddrmapNode': - return self.exp.top_node + return self.exp.ds.top_node def get_storage_struct(self) -> str: struct_gen = RBufStorageStructGenerator() diff --git a/src/peakrdl_regblock/readback/__init__.py b/src/peakrdl_regblock/readback/__init__.py index e5e632a..ef27fec 100644 --- a/src/peakrdl_regblock/readback/__init__.py +++ b/src/peakrdl_regblock/readback/__init__.py @@ -4,18 +4,21 @@ import math from .generators import ReadbackAssignmentGenerator if TYPE_CHECKING: - from ..exporter import RegblockExporter + from ..exporter import RegblockExporter, DesignState from systemrdl.node import AddrmapNode class Readback: - def __init__(self, exp:'RegblockExporter', do_fanin_stage: bool, has_external_addressable: bool): + def __init__(self, exp:'RegblockExporter'): self.exp = exp - self.do_fanin_stage = do_fanin_stage - self.has_external_addressable = has_external_addressable + self.do_fanin_stage = self.ds.retime_read_fanin + + @property + def ds(self) -> 'DesignState': + return self.exp.ds @property def top_node(self) -> 'AddrmapNode': - return self.exp.top_node + return self.exp.ds.top_node def get_implementation(self) -> str: gen = ReadbackAssignmentGenerator(self.exp) @@ -33,7 +36,7 @@ class Readback: 'get_always_ff_event': self.exp.dereferencer.get_always_ff_event, "cpuif": self.exp.cpuif, "do_fanin_stage": self.do_fanin_stage, - "has_external_addressable": self.has_external_addressable, + "ds": self.ds, } if self.do_fanin_stage: diff --git a/src/peakrdl_regblock/readback/templates/readback.sv b/src/peakrdl_regblock/readback/templates/readback.sv index a90b0b8..564acbe 100644 --- a/src/peakrdl_regblock/readback/templates/readback.sv +++ b/src/peakrdl_regblock/readback/templates/readback.sv @@ -35,7 +35,7 @@ always_ff @(posedge clk) begin readback_done_r <= '0; end else begin readback_array_r <= readback_array_c; - {%- if has_external_addressable %} + {%- if ds.has_external_addressable %} readback_done_r <= decoded_req & ~decoded_req_is_wr & ~decoded_strb_is_external; {%- else %} readback_done_r <= decoded_req & ~decoded_req_is_wr; @@ -58,7 +58,7 @@ end // Reduce the array always_comb begin automatic logic [{{cpuif.data_width-1}}:0] readback_data_var; - {%- if has_external_addressable %} + {%- if ds.has_external_addressable %} readback_done = decoded_req & ~decoded_req_is_wr & ~decoded_strb_is_external; {%- else %} readback_done = decoded_req & ~decoded_req_is_wr; diff --git a/src/peakrdl_regblock/scan_design.py b/src/peakrdl_regblock/scan_design.py index bb96ab9..4a19190 100644 --- a/src/peakrdl_regblock/scan_design.py +++ b/src/peakrdl_regblock/scan_design.py @@ -1,13 +1,11 @@ -from typing import TYPE_CHECKING, Set, Optional, Type, List -from collections import OrderedDict +from typing import TYPE_CHECKING, Optional from systemrdl.walker import RDLListener, RDLWalker, WalkerAction from systemrdl.node import SignalNode, RegNode if TYPE_CHECKING: - from systemrdl.node import Node, FieldNode, AddressableNode + from systemrdl.node import Node, FieldNode, AddressableNode, AddrmapNode from .exporter import RegblockExporter - from systemrdl.rdltypes import UserEnum class DesignScanner(RDLListener): @@ -19,63 +17,53 @@ class DesignScanner(RDLListener): """ def __init__(self, exp:'RegblockExporter') -> None: self.exp = exp - self.cpuif_data_width = 0 - self.msg = exp.top_node.env.msg + self.ds = exp.ds + self.msg = self.top_node.env.msg - # Collections of signals that were actually referenced by the design - self.in_hier_signal_paths = set() # type: Set[str] - self.out_of_hier_signals = OrderedDict() # type: OrderedDict[str, SignalNode] - - self.has_writable_msb0_fields = False - self.has_buffered_write_regs = False - self.has_buffered_read_regs = False - - self.has_external_block = False - self.has_external_addressable = False - - # Track any referenced enums - self.user_enums = [] # type: List[Type[UserEnum]] + @property + def top_node(self) -> 'AddrmapNode': + return self.exp.ds.top_node def _get_out_of_hier_field_reset(self) -> None: - current_node = self.exp.top_node.parent + current_node = self.top_node.parent while current_node is not None: for signal in current_node.signals(): if signal.get_property('field_reset'): path = signal.get_path() - self.out_of_hier_signals[path] = signal + self.ds.out_of_hier_signals[path] = signal return current_node = current_node.parent def do_scan(self) -> None: # Collect cpuif reset, if any. - cpuif_reset = self.exp.top_node.cpuif_reset + cpuif_reset = self.top_node.cpuif_reset if cpuif_reset is not None: path = cpuif_reset.get_path() - rel_path = cpuif_reset.get_rel_path(self.exp.top_node) + rel_path = cpuif_reset.get_rel_path(self.top_node) if rel_path.startswith("^"): - self.out_of_hier_signals[path] = cpuif_reset + self.ds.out_of_hier_signals[path] = cpuif_reset else: - self.in_hier_signal_paths.add(path) + self.ds.in_hier_signal_paths.add(path) # collect out-of-hier field_reset, if any self._get_out_of_hier_field_reset() # Ensure addrmap is not a bridge. This concept does not make sense for # terminal components. - if self.exp.top_node.get_property('bridge'): + if self.top_node.get_property('bridge'): self.msg.error( "Regblock generator does not support exporting bridge address maps", - self.exp.top_node.inst.property_src_ref.get('bridge', self.exp.top_node.inst.inst_src_ref) + self.top_node.inst.property_src_ref.get('bridge', self.top_node.inst.inst_src_ref) ) - RDLWalker().walk(self.exp.top_node, self) + RDLWalker().walk(self.top_node, self) if self.msg.had_error: self.msg.fatal( "Unable to export due to previous errors" ) def enter_Component(self, node: 'Node') -> Optional[WalkerAction]: - if node.external and (node != self.exp.top_node): + if node.external and (node != self.top_node): # Do not inspect external components. None of my business return WalkerAction.SkipDescendants @@ -84,37 +72,37 @@ class DesignScanner(RDLListener): value = node.get_property(prop_name) if isinstance(value, SignalNode): path = value.get_path() - rel_path = value.get_rel_path(self.exp.top_node) + rel_path = value.get_rel_path(self.top_node) if rel_path.startswith("^"): - self.out_of_hier_signals[path] = value + self.ds.out_of_hier_signals[path] = value else: - self.in_hier_signal_paths.add(path) + self.ds.in_hier_signal_paths.add(path) if prop_name == "encode": - if value not in self.user_enums: - self.user_enums.append(value) + if value not in self.ds.user_enums: + self.ds.user_enums.append(value) return WalkerAction.Continue def enter_AddressableComponent(self, node: 'AddressableNode') -> None: - if node.external and node != self.exp.top_node: - self.has_external_addressable = True + if node.external and node != self.top_node: + self.ds.has_external_addressable = True if not isinstance(node, RegNode): - self.has_external_block = True + self.ds.has_external_block = True def enter_Reg(self, node: 'RegNode') -> None: # The CPUIF's bus width is sized according to the largest accesswidth in the design accesswidth = node.get_property('accesswidth') - self.cpuif_data_width = max(self.cpuif_data_width, accesswidth) + self.exp.ds.cpuif_data_width = max(self.exp.ds.cpuif_data_width, accesswidth) - self.has_buffered_write_regs = self.has_buffered_write_regs or bool(node.get_property('buffer_writes')) - self.has_buffered_read_regs = self.has_buffered_read_regs or bool(node.get_property('buffer_reads')) + self.ds.has_buffered_write_regs = self.ds.has_buffered_write_regs or bool(node.get_property('buffer_writes')) + self.ds.has_buffered_read_regs = self.ds.has_buffered_read_regs or bool(node.get_property('buffer_reads')) def enter_Signal(self, node: 'SignalNode') -> None: if node.get_property('field_reset'): path = node.get_path() - self.in_hier_signal_paths.add(path) + self.ds.in_hier_signal_paths.add(path) def enter_Field(self, node: 'FieldNode') -> None: if node.is_sw_writable and (node.msb < node.lsb): - self.has_writable_msb0_fields = True + self.ds.has_writable_msb0_fields = True diff --git a/src/peakrdl_regblock/validate_design.py b/src/peakrdl_regblock/validate_design.py index f3e5898..9a15917 100644 --- a/src/peakrdl_regblock/validate_design.py +++ b/src/peakrdl_regblock/validate_design.py @@ -19,20 +19,24 @@ class DesignValidator(RDLListener): """ def __init__(self, exp:'RegblockExporter') -> None: self.exp = exp - self.msg = exp.top_node.env.msg + self.msg = self.top_node.env.msg self._contains_external_block_stack = [] # type: List[bool] self.contains_external_block = False + @property + def top_node(self) -> 'AddrmapNode': + return self.exp.ds.top_node + def do_validate(self) -> None: - RDLWalker().walk(self.exp.top_node, self) + RDLWalker().walk(self.top_node, self) if self.msg.had_error: self.msg.fatal( "Unable to export due to previous errors" ) def enter_Component(self, node: 'Node') -> Optional[WalkerAction]: - if node.external and (node != self.exp.top_node): + if node.external and (node != self.top_node): # Do not inspect external components. None of my business return WalkerAction.SkipDescendants @@ -40,7 +44,7 @@ class DesignValidator(RDLListener): for prop_name in node.list_properties(): value = node.get_property(prop_name) if isinstance(value, (PropertyReference, Node)): - if not ref_is_internal(self.exp.top_node, value): + if not ref_is_internal(self.top_node, value): if isinstance(value, PropertyReference): src_ref = value.src_ref else: @@ -55,7 +59,7 @@ class DesignValidator(RDLListener): # If encountering a CPUIF reset that is nested within the register model, # warn that it will be ignored. # Only cpuif resets in the top-level node or above will be honored - if node.get_property('cpuif_reset') and (node.parent != self.exp.top_node): + if node.get_property('cpuif_reset') and (node.parent != self.top_node): self.msg.warning( "Only cpuif_reset signals that are instantiated in the top-level " "addrmap or above will be honored. Any cpuif_reset signals nested " @@ -81,7 +85,7 @@ class DesignValidator(RDLListener): if not isinstance(node, RegNode): # Entering a block-like node - if node == self.exp.top_node: + if node == self.top_node: # Ignore top addrmap's external property when entering self._contains_external_block_stack.append(False) else: diff --git a/src/peakrdl_regblock/write_buffering/__init__.py b/src/peakrdl_regblock/write_buffering/__init__.py index c71ab48..06ae55c 100644 --- a/src/peakrdl_regblock/write_buffering/__init__.py +++ b/src/peakrdl_regblock/write_buffering/__init__.py @@ -16,7 +16,7 @@ class WriteBuffering: @property def top_node(self) -> 'AddrmapNode': - return self.exp.top_node + return self.exp.ds.top_node def get_storage_struct(self) -> str: