From f656230a801142801c6ffd5eea93551d1373ec4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Denkinger?= Date: Wed, 7 Feb 2024 16:51:30 +0100 Subject: [PATCH 01/23] Save commit --- docs_sphinx/Makefile | 20 +++ docs_sphinx/make.bat | 35 +++++ docs_sphinx/source/conf.py | 61 ++++++++ docs_sphinx/source/index.rst | 20 +++ src/peakrdl_halcpp/__peakrdl__.py | 41 ++--- src/peakrdl_halcpp/exporter.py | 182 +++++++++++++++-------- src/peakrdl_halcpp/haladdrmap.py | 84 +++++++---- src/peakrdl_halcpp/halutils.py | 63 ++++++-- src/peakrdl_halcpp/include/__init__.py | 14 ++ src/peakrdl_halcpp/templates/__init__.py | 8 + 10 files changed, 412 insertions(+), 116 deletions(-) create mode 100644 docs_sphinx/Makefile create mode 100644 docs_sphinx/make.bat create mode 100644 docs_sphinx/source/conf.py create mode 100644 docs_sphinx/source/index.rst diff --git a/docs_sphinx/Makefile b/docs_sphinx/Makefile new file mode 100644 index 0000000..d0c3cbf --- /dev/null +++ b/docs_sphinx/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = source +BUILDDIR = build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs_sphinx/make.bat b/docs_sphinx/make.bat new file mode 100644 index 0000000..6247f7e --- /dev/null +++ b/docs_sphinx/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=source +set BUILDDIR=build + +if "%1" == "" goto help + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/docs_sphinx/source/conf.py b/docs_sphinx/source/conf.py new file mode 100644 index 0000000..be7a916 --- /dev/null +++ b/docs_sphinx/source/conf.py @@ -0,0 +1,61 @@ +# Configuration file for the Sphinx documentation builder. +# +# This file only contains a selection of the most common options. For a full +# list see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +import os +import sys +sys.path.insert(0, os.path.abspath(os.path.join('..', '..', 'src'))) +print(os.path.abspath(os.path.join('..', '..', 'src'))) + +# -- Project information ----------------------------------------------------- + +project = 'PeakRDL-halcpp' +copyright = '2024, CERN' +author = 'CERN' + +# The full version, including alpha/beta/rc tags +release = '0.0.1' + + +# -- General configuration --------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + 'sphinx.ext.autodoc', + 'sphinx.ext.viewcode', + 'sphinx.ext.napoleon' +] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = [ + # 'include', + # 'templates' +] + + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = 'sphinx_rtd_theme' + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] diff --git a/docs_sphinx/source/index.rst b/docs_sphinx/source/index.rst new file mode 100644 index 0000000..6f00c53 --- /dev/null +++ b/docs_sphinx/source/index.rst @@ -0,0 +1,20 @@ +.. PeakRDL-halcpp documentation master file, created by + sphinx-quickstart on Wed Feb 7 14:22:36 2024. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to PeakRDL-halcpp's documentation! +========================================== + +.. toctree:: + :maxdepth: 4 + :caption: Contents: + + modules + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/src/peakrdl_halcpp/__peakrdl__.py b/src/peakrdl_halcpp/__peakrdl__.py index 39c3524..07560d6 100644 --- a/src/peakrdl_halcpp/__peakrdl__.py +++ b/src/peakrdl_halcpp/__peakrdl__.py @@ -1,11 +1,8 @@ from typing import TYPE_CHECKING -from peakrdl.plugins.exporter import ExporterSubcommandPlugin #pylint: disable=import-error -from peakrdl.config import schema #pylint: disable=import-error +from peakrdl.plugins.exporter import ExporterSubcommandPlugin # pylint: disable=import-error -# from peakrdl.main import main -from .exporter import HalExporter -import sys +from .exporter import HalExporter if TYPE_CHECKING: import argparse @@ -13,24 +10,30 @@ class Exporter(ExporterSubcommandPlugin): + """ + Generic PeakRDL template class for plugin creation. + """ short_desc = "Generate CPP Hardware Abstraction Layer libraries" long_desc = "Generate CPP Hardware Abstraction Layer libraries" - def add_exporter_arguments(self, arg_group: 'argparse.ArgumentParser') -> None: - + """ + Adds custom arguments to the plugin. + """ arg_group.add_argument( - "--ext", - nargs="*", - help="list of addrmap modules that have implemented _EXT class in _ext.h header file, used for extending functionality" - ) + "--ext", + nargs="*", + help="List of addrmap modules that have implemented _EXT class in \ + _ext.h header file, used for extending functionality." + ) arg_group.add_argument( "--list-files", dest="list_files", default=False, action="store_true", - help="Dont generate files, but instead just list the files that will be generated, and external files that need to be included" + help="Dont generate files, but instead just list the files that will be \ + generated, and external files that need to be included." ) arg_group.add_argument( @@ -38,20 +41,20 @@ def add_exporter_arguments(self, arg_group: 'argparse.ArgumentParser') -> None: dest="keep_buses", default=False, action="store_true", - help="If there is an addrmap containing only addrmaps, not registers, by default it will be ommited in hierarchy, it is possible to keep it by passing --keep-buses flag" + help="If there is an addrmap containing only addrmaps, not registers, by \ + default it will be omitted in hierarchy, it is possible to keep it by \ + passing --keep-buses flag." ) - def do_export(self, top_node: 'AddrmapNode', options: 'argparse.Namespace') -> None: + """ + Plugin entry function. + """ hal = HalExporter() hal.export( - nodes=top_node, + node=top_node, outdir=options.output, list_files=options.list_files, ext=options.ext, keep_buses=options.keep_buses, ) - - # if "-DUSE_ZICSR=1" not in sys.argv: - # sys.argv.append("-DUSE_ZICSR=1") - # main() diff --git a/src/peakrdl_halcpp/exporter.py b/src/peakrdl_halcpp/exporter.py index ef7ea6a..7566df2 100644 --- a/src/peakrdl_halcpp/exporter.py +++ b/src/peakrdl_halcpp/exporter.py @@ -1,86 +1,141 @@ -from systemrdl.node import Node, RootNode, AddrmapNode -import jinja2 -from typing import List, Union, Any import os +from typing import TYPE_CHECKING import shutil +import jinja2 as jj +from systemrdl.node import Node, RootNode, AddrmapNode + from .haladdrmap import * from .halutils import HalUtils +# Import the different types if checking is activated +if TYPE_CHECKING: + from typing import Union, Any, List, str, bool, Dict + + class HalExporter(): - def __init__(self): - self.cpp_dir = "include" + """This method will be used to add two numbers + + :param int num1: The first number + :param int num2: The second number + + :returns: The sum of two numbers + + :rtype: int + """ + + + def __init__(self, **kwargs: 'Any'): + # Check for stray kwargs + if kwargs: + raise TypeError("got an unexpected keyword argument '%s'" % + list(kwargs.keys())[0]) + # Include files generated output directory + self.cpp_dir = "include" + # HAL C++ base headers list (copied into cpp_dir) self.base_headers = [ - "halcpp_base.h", - "halcpp_utils.h", - "field_node.h", - "reg_node.h", - "regfile_node.h", - "array_nodes.h", - "addrmap_node.h", - "arch_io.h", - ] - - def list_files(self, - top : HalAddrmap, - outdir : str, - ): - out_files = [os.path.join(outdir, addrmap.type_name + ".h") for addrmap in top.get_addrmaps_recursive()] - out_files += [os.path.join(outdir, x) for x in self.base_headers] + out_files - print(*out_files) # Print files to stdout - - def copy_base_headers(self, outdir): - abspaths = [os.path.join(os.path.dirname(__file__), self.cpp_dir, x) for x in self.base_headers] + "halcpp_base.h", + "halcpp_utils.h", + "field_node.h", + "reg_node.h", + "regfile_node.h", + "array_nodes.h", + "addrmap_node.h", + "arch_io.h", + ] + + def list_files(self, top: HalAddrmap, outdir: str): + """ + Prints the generated files to stdout (without generating the files). + + Parameters + ---------- + top: HalAddrmap + Top level HalAddrmap object. + outdir: str + Output directory to which the generated files would be put. + """ + gen_files = [os.path.join(outdir, addrmap.type_name + ".h") + for addrmap in top.get_addrmaps_recursive()] + # Create base header files path + base_files = [os.path.join(self.cpp_dir, x) for x in self.base_headers] + # Add the base header files to the list of files + out_files = [os.path.join(outdir, x) for x in base_files] + gen_files + print(*out_files, sep="\n") + + def copy_base_headers(self, outdir: str): + """ + Copies the HAL base headers to the generated HAL files location given + by outdir. + + Parameters + ---------- + outdir: str + Output directory to which the generated files would be put. + """ + abspaths = [os.path.join(os.path.dirname( + __file__), self.cpp_dir, x) for x in self.base_headers] outdir = os.path.join(outdir, "include") if not os.path.exists(outdir): os.makedirs(outdir) [shutil.copy(x, outdir) for x in abspaths] - def export(self, - nodes: 'Union[Node, List[Node]]', - outdir: str, - list_files: bool=False, - ext : list=[], - keep_buses : bool=False, - **kwargs: 'Dict[str, Any]') -> None: - - - # if not a list - if not isinstance(nodes, list): - nodes = [nodes] + def export(self, node: 'Union[AddrmapNode, RootNode]', outdir: str, list_files: bool = False, ext: List = [], keep_buses: bool = False) -> None: + """ + Plugin exporter main function. + + Parameters + ---------- + nodes: Union[Node, List[Node]] + outdir: str + list_files: bool=False + ext: list=[] + keep_buses: bool=False + """ + + # print("+++++++++++DEBUG+++++++++++++++") + # print(f'Node type: {type(node)}') + # print(f'Node: {node}') + # print(f'Node address offset: {node.inst.addr_offset}') + # print(f'Node original type name: {node.orig_type_name}') + # desc = "/*\n" + # if node.get_property('desc') is not None: + # for l in node.get_property('desc').splitlines(): + # desc = desc + "* " + l + "\n" + # print(f'Node description:\n{desc}*/') + # print("+++++++++++++++++++++++++++++++") # If it is the root node, skip to top addrmap - for i, node in enumerate(nodes): - if isinstance(node, RootNode): - nodes[i] = node.top - - if kwargs: - raise TypeError("got an unexpected keyword argument '%s'" % list(kwargs.keys())[0]) - - try: - os.makedirs(outdir) - except FileExistsError: - pass + if isinstance(node, RootNode): + node = node.top + # Check the node is an AddrmapNode object + if not isinstance(node, AddrmapNode): + raise TypeError( + "'node' argument expects type AddrmapNode. Got '%s'" % type(node).__name__) halutils = HalUtils(ext) - assert isinstance(nodes[-1], AddrmapNode) top = halutils.build_hierarchy( - node=nodes[-1], - remove_root=False, # TODO fix - keep_buses=keep_buses, - ) - + node=node, + remove_root=False, # TODO fix + keep_buses=keep_buses, + ) if list_files: self.list_files(top, outdir) else: + # Create the output directory for the generated files + try: + os.makedirs(outdir) + except FileExistsError: + pass + for halnode in top.get_addrmaps_recursive(): context = { - 'halnode' : halnode, - 'halutils' : halutils, - } + 'halnode': halnode, + 'halutils': halutils, + } text = self.process_template(context) out_file = os.path.join(outdir, halnode.type_name + ".h") with open(out_file, 'w') as f: @@ -88,17 +143,18 @@ def export(self, self.copy_base_headers(outdir) - def process_template(self, context : dict) -> str: + def process_template(self, context: Dict) -> str: - env = jinja2.Environment( - loader=jinja2.FileSystemLoader('%s/templates/' % os.path.dirname(__file__)), + env = jj.Environment( + loader=jj.FileSystemLoader( + '%s/templates/' % os.path.dirname(__file__)), trim_blocks=True, lstrip_blocks=True) + # Benoit: what does it do? env.filters.update({ - 'zip' : zip, - }) + 'zip': zip, + }) res = env.get_template("addrmap.j2").render(context) return res - diff --git a/src/peakrdl_halcpp/haladdrmap.py b/src/peakrdl_halcpp/haladdrmap.py index 5721b05..042486d 100644 --- a/src/peakrdl_halcpp/haladdrmap.py +++ b/src/peakrdl_halcpp/haladdrmap.py @@ -2,7 +2,32 @@ from typing import List, Dict class HalBase: # TODO make abstract - def __init__(self, + """ + HakBase is the HAL base class for all the different nodes + (Addrmap, Reg, Mem, and Field). + + Methods + ------- + get_docstring(node : Node, parent : 'HalBase|None') -> str + Converts the node description into a C++ multi-line comment. + cpp_access_type() -> str: + Returns the node access type and must be overloaded by + the child class. + get_template_line() - str: + Returns the node C++ template line + get_cls_tmpl_spec(just_tmpl=False) -> str: + TBD + + type_name() -> str: + TBD + addr_offset() -> int: + TBD + orig_type_name() -> str: + TBD + get_parent_haladdrmap() -> HalAddrmap: + TBD + """ + def __init__(self, node : Node, parent : 'HalBase|None', ): @@ -14,19 +39,20 @@ def get_docstring(self) -> str: if self.node.get_property('desc') is not None: for l in self.node.get_property('desc').splitlines(): desc = desc + "* " + l + "\n" + print(desc + "*/") return desc + "*/" return "" @property - def cpp_type(self) -> str: + def cpp_access_type(self) -> str: raise NotImplementedError("You need to overload this method in inherited class") - def get_template_line(self): + def get_template_line(self) -> str: raise NotImplementedError("You need to overload this method in inherited class") def get_cls_tmpl_spec(self, just_tmpl=False) -> str: raise NotImplementedError("You need to overload this method in inherited class") - + @property def type_name(self) -> str: return self.orig_type_name @@ -48,10 +74,10 @@ def get_parent_haladdrmap(self) -> 'HalAddrmap': return self.parent assert self.parent is not None return self.parent.get_parent_haladdrmap() - + class HalField(HalBase): - def __init__(self, + def __init__(self, node: FieldNode, parent : 'HalReg', ): @@ -86,7 +112,7 @@ def get_enum(self): namespace_enums[name] = [enum_strings, enum_values, enum_desc, const_width, self.node.owning_addrmap] return True, name, enum_strings, enum_values, enum_desc, const_width - + return False, None, None, None, None, None def get_enum_name(self): @@ -98,7 +124,7 @@ def get_namespace_enums(self) -> 'Dict': return self.get_parent_haladdrmap().enums @property - def cpp_type(self) -> str: + def cpp_access_type(self) -> str: out = "" if self.node.is_sw_readable and self.node.is_sw_writable: return "FieldRW" @@ -115,8 +141,8 @@ def get_cls_tmpl_spec(self, just_tmpl=False) -> str: assert False, "You should not extend FieldNode classes" class HalReg(HalBase): - - def __init__(self, + + def __init__(self, node : RegNode, parent : 'HalAddrmap|HalRegfile', bus_offset : int = 0, @@ -127,7 +153,7 @@ def __init__(self, self.bus_offset = bus_offset self.fields = self.get_fields() - + @property # TODO move to base? def is_array(self) -> bool: return self.node.is_array @@ -140,7 +166,7 @@ def get_fields(self) -> 'List[HalField]': return [HalField(c, self) for c in self.node.children() if isinstance(c, FieldNode)] @property - def cpp_type(self): + def cpp_access_type(self): if self.node.has_sw_readable and self.node.has_sw_writable: return "RegRW" elif self.node.has_sw_writable and not self.node.has_sw_readable: @@ -162,7 +188,7 @@ def addr_offset(self) -> int: class HalArrReg(HalReg): - def __init__(self, + def __init__(self, node : RegNode, parent : 'HalAddrmap|HalRegfile', bus_offset : int = 0, @@ -181,8 +207,8 @@ def addr_offset(self): class HalMem(HalBase): - - def __init__(self, + + def __init__(self, node : MemNode, parent : 'HalAddrmap', bus_offset : int = 0, @@ -208,8 +234,8 @@ def get_template_line(self) -> str: return f"template" @property - def cpp_type(self) -> str: - assert False, "cpp_type should not be called on HalMem class" + def cpp_access_type(self) -> str: + assert False, "cpp_access_type should not be called on HalMem class" def get_cls_tmpl_spec(self, just_tmpl=False) -> str: str = self.type_name.upper() if not just_tmpl else "" @@ -224,8 +250,8 @@ def addr_offset(self) -> int: return self.bus_offset + self.node.address_offset class HalRegfile(HalBase): - - def __init__(self, + + def __init__(self, node : RegfileNode, parent : 'HalAddrmap', bus_offset : int = 0, @@ -237,7 +263,7 @@ def __init__(self, self.regs = self.get_regs() self.regfiles = self.get_regfiles() - + @property # TODO move to base? def is_array(self) -> bool: return self.node.is_array @@ -253,7 +279,7 @@ def get_regfiles(self) -> 'List[HalRegfile]': return [HalRegfile(c, self) for c in self.node.children() if isinstance(c, RegfileNode)] @property - def cpp_type(self): + def cpp_access_type(self): return "RegfileNode" def get_template_line(self) -> str: @@ -268,7 +294,7 @@ def addr_offset(self) -> int: return self.bus_offset + next(self.node.unrolled()).address_offset # type: ignore class HalArrRegfile(HalRegfile): - def __init__(self, + def __init__(self, node : RegfileNode, parent : 'HalAddrmap|HalRegfile', bus_offset : int = 0, @@ -288,6 +314,13 @@ def addr_offset(self): class HalAddrmap(HalBase): + """ + TBD + + Methods + ------- + TBD + """ def __init__(self, node : AddrmapNode, parent : 'HalAddrmap|None' = None, @@ -298,13 +331,14 @@ def __init__(self, self.parent = parent self.bus_offset = bus_offset + # Check that top level HAL has no parent but RootNode assert (self.parent == None) == isinstance(self.node.parent, RootNode) self.regs = self.get_regs() self.mems = self.get_mems() self.addrmaps = self.get_addrmaps() self.regfiles = self.get_regfiles() - + self.enums = {} @@ -346,7 +380,7 @@ def remove_buses(self): subc.parent = self remove_list.append(c) self.addrmaps.extend(c.addrmaps) # Steal all addrmaps from a bus - + [self.addrmaps.remove(c) for c in remove_list] def is_bus(self) -> bool: @@ -364,7 +398,7 @@ def get_template_line(self) -> str: if self.is_root_node: return "template " return "template " - + @property def type_name(self) -> str: return self.orig_type_name + "_hal" diff --git a/src/peakrdl_halcpp/halutils.py b/src/peakrdl_halcpp/halutils.py index a3d6644..8ebb0d1 100644 --- a/src/peakrdl_halcpp/halutils.py +++ b/src/peakrdl_halcpp/halutils.py @@ -5,16 +5,45 @@ from .haladdrmap import * class HalUtils(): - def __init__(self, - extern : List[str], - ) -> None: + """ + This is the top level C++ HAL exporter class. This class is called + by the do_export() call of the PeakRDL plugin Exporter class. + + Methods + ------- + get_include_file(halnode : HalAddrmap) -> str: + TBD + has_extern(halnode : HalAddrmap) -> bool: + TBD + get_extern(halnode : HalAddrmap) -> str: + TBD + get_unique_type_nodes(lst : 'List[HalBase]'): + TBD + generate_file_header(): + TBD + build_hierarchy(node : AddrmapNode, keep_buses : bool = False, + remove_root: bool = True) + -> HalAddrmap : top = HalAddrmap(node) + TBD + """ + def __init__(self, extern : List[str]) -> None: + """ + Initialize the extern variable with a list of external + functionalities to include to the HAL (i.e., 'higher level' functions). + """ self.extern = extern def get_include_file(self, halnode : HalAddrmap) -> str: + """ + + """ has_extern = self.has_extern(halnode) return halnode.orig_type_name + "_ext.h" if has_extern else halnode.type_name + ".h" def has_extern(self, halnode : HalAddrmap) -> bool: + """ + Returns True if the object contains external files (i.e., extern). + """ if self.extern is not None: if halnode.orig_type_name in self.extern: return True @@ -31,19 +60,35 @@ def get_unique_type_nodes(self, lst : 'List[HalBase]'): def generate_file_header(self): username = getpass.getuser() current_datetime = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") - comment = f"// Generated with PeakRD-halcpp : https://github.com/Risto97/PeakRDL-halcpp\n" + comment = f"// Generated with PeakRD-halcpp : https://github.com/Risto97/PeakRDL-halcpp\n" comment += f"// By user: {username} at: {current_datetime}\n" return comment def build_hierarchy(self, node : AddrmapNode, - keep_buses : bool = False, - remove_root : bool = True, + keep_buses : bool=False, + remove_root : bool=True, ) -> HalAddrmap: - top = HalAddrmap(node) + """ + TBD - # if remove_root: # TODO check this - # addrmaps.remove(node) + Parameters + ------- + node : AddrmapNode + TBD + keep_buses : bool=False + TBD + remove_root : bool=True + TBD + + Returns + ------- + HalAddrmap + TBD + """ + + # Initialize the HAL top address map (i.e., no parent) + top = HalAddrmap(node) if keep_buses is False: top.remove_buses() diff --git a/src/peakrdl_halcpp/include/__init__.py b/src/peakrdl_halcpp/include/__init__.py index e69de29..3b09e92 100644 --- a/src/peakrdl_halcpp/include/__init__.py +++ b/src/peakrdl_halcpp/include/__init__.py @@ -0,0 +1,14 @@ +""" +C++ HAL header files to be included + +List of files: +-------------- +addrmap_node.h +arch_io.h +array_nodes.h +field_node.h +halcpp_base.h +halcpp_utils.h +regfile_node.h +reg_node.h +""" diff --git a/src/peakrdl_halcpp/templates/__init__.py b/src/peakrdl_halcpp/templates/__init__.py index e69de29..cf3983a 100644 --- a/src/peakrdl_halcpp/templates/__init__.py +++ b/src/peakrdl_halcpp/templates/__init__.py @@ -0,0 +1,8 @@ +""" +Jinja2 template file for generating a C++ abstraction class +from a SystemRDL addrmap object + +List of files: +-------------- +addrmap.j2 +""" From d182a0b4153492e9c0e6321b4a6e1190a251ad94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Denkinger?= Date: Thu, 8 Feb 2024 17:06:20 +0100 Subject: [PATCH 02/23] Save commit. --- docs_sphinx/source/conf.py | 9 +- examples/atxmega_spi.rdl | 84 ++++- examples/class_test.py | 46 +++ requirements.txt | 45 +++ src/peakrdl_halcpp/__peakrdl__.py | 19 +- src/peakrdl_halcpp/exporter.py | 97 +++--- src/peakrdl_halcpp/haladdrmap.py | 408 +++++++++++++++--------- src/peakrdl_halcpp/halutils.py | 107 ++++--- src/peakrdl_halcpp/templates/addrmap.j2 | 16 +- 9 files changed, 552 insertions(+), 279 deletions(-) create mode 100644 examples/class_test.py create mode 100644 requirements.txt diff --git a/docs_sphinx/source/conf.py b/docs_sphinx/source/conf.py index be7a916..ab43c4f 100644 --- a/docs_sphinx/source/conf.py +++ b/docs_sphinx/source/conf.py @@ -33,9 +33,16 @@ extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.viewcode', - 'sphinx.ext.napoleon' + 'sphinx.ext.napoleon', + 'sphinx.ext.inheritance_diagram', ] +inheritance_node_attrs = dict( + color='"#6AB0DE"', + fillcolor='"#E7F2FA"', + style='"rounded, filled"' +) + # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] diff --git a/examples/atxmega_spi.rdl b/examples/atxmega_spi.rdl index e48c46e..bf8dc9b 100644 --- a/examples/atxmega_spi.rdl +++ b/examples/atxmega_spi.rdl @@ -5,30 +5,30 @@ addrmap atxmega_spi { Transcribed from original manual as an example exercise: http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-8331-8-and-16-bit-AVR-Microcontroller-XMEGA-AU_Manual.pdf "; - + default regwidth = 8; default sw = rw; default hw = r; - + reg { name = "Control Register"; - + field { desc = "Controls the SPI clock rate when configured in master mode"; } PRESCALER[1:0] = 0; - + field { desc = "These bits select the transfer mode"; } MODE[3:2] = 0; - + field { desc = "Selects master mode when written to one, and slave mode when written to zero. If SS is configured as an input and driven low while master mode is set, master mode will be cleared"; - + hw=rw; we; } MASTER[4:4] = 0; - + field { desc = "DORD decides the data order when a byte is shifted out from the DATA register. When DORD is written to one, the least-significant @@ -36,50 +36,100 @@ addrmap atxmega_spi { written to zero, the most-significant bit (msb) of the data byte is transmitted first"; } DORD[5:5] = 0; - + field { desc = "Setting this bit enables the SPI module. This bit must be set to enable any SPI operations"; } ENABLE[6:6] = 0; - + field { desc = "When this bit is set, the SPI speed (SCK frequency) will be doubled in master mode"; } CLK2X[7:7] = 0; } CTRL @ 0x0; - + reg { name = "Interrupt Control"; - + field { desc = "These bits enable the SPI interrupt and select the interrupt level"; } INTLVL[1:0] = 0; } INTCTRL @ 0x1; - + reg { field { sw=r; hw=rw; we; } WRCOL[6:6] = 0; - + field { sw=r; hw=rw; we; } IF[7:7] = 0; } STATUS @ 0x2; - + reg { desc = "The DATA register is used for sending and receiving data. Writing to the register initiates the data transmission, and the byte written to the register will be shifted out on the SPI output line. Reading the register causes the shift register receive buffer to be read, returning the last byte successfully received"; - + field { sw=w; hw=r; } WDATA[7:0]; - + field { sw=r; hw=w; } RDATA[7:0]; - + } DATA @ 0x3; + + addrmap atxmega_test{ + name = "ATXMEGA test addrmap"; + desc = "Testing purpose"; + + default regwidth = 8; + default sw = rw; + default hw = rw; + + reg { + name = "Test register"; + + field { + desc = "Test field"; + } TESTFIELD[7:0] = 0; + } TEST_DATA @ 0x4; + } TEST_ADDRMAP @ 0x4; +}; + +addrmap common { + reg { + field {} spam_y; + } spam_x; +}; + +addrmap foo { + common block_a; + common block_b; +}; + +addrmap bar { + common block_x; + common block_y; +}; + +addrmap atxmega_test{ + name = "ATXMEGA test addrmap"; + desc = "Testing purpose"; + + default regwidth = 8; + default sw = rw; + default hw = rw; + + reg { + name = "Test register"; + + field { + desc = "Test field"; + } TESTFIELD[7:0] = 0; + } TEST_DATA @ 0x0; }; diff --git a/examples/class_test.py b/examples/class_test.py new file mode 100644 index 0000000..2e69f18 --- /dev/null +++ b/examples/class_test.py @@ -0,0 +1,46 @@ +class BaseClass(): + def __init__(self): + self.my_var0 = 1 + self.my_var1 = 2 + +class DerivedClass(BaseClass): + def __init__(self): + super().__init__() + self.my_var2 = 3 + + def print_vars(self): + print(self.my_var0) + print(self.my_var1) + print(self.my_var2) + +from abc import ABC, abstractmethod + +class MyBaseClassAbstract(ABC): + def __init__(self): + self.my_var0 = 1 + self.my_var1 = 2 + @abstractmethod + def abstract_method(self): + pass + +class DerivedClassAbstract(MyBaseClassAbstract): + def __init__(self): + super().__init__() + self.my_var2 = 3 + def my_method(self): + print('my_method call') + def abstract_method(self): + print('Abstract method call') + def print_vars(self): + print(self.my_var0) + print(self.my_var1) + print(self.my_var2) + + +DerivedClass0 = DerivedClass() + +DerivedClass0.print_vars() + +# AbstracClass0 = MyBaseClassAbstract() +AbstracClass1 = DerivedClassAbstract() +AbstracClass1.print_vars() diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..7d34d72 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,45 @@ +alabaster==0.7.16 +antlr4-python3-runtime==4.11.1 +Babel==2.14.0 +certifi==2024.2.2 +charset-normalizer==3.3.2 +colorama==0.4.6 +docutils==0.20.1 +git-me-the-url==2.1.0 +gitdb==4.0.11 +GitPython==3.1.41 +idna==3.6 +imagesize==1.4.1 +importlib-metadata==7.0.1 +Jinja2==3.1.3 +Markdown==3.5.2 +MarkupSafe==2.1.5 +mypy-extensions==1.0.0 +packaging==23.2 +peakrdl==1.1.0 +peakrdl-cheader==1.0.0 +-e git+ssh://git@github.com/Risto97/PeakRDL-halcpp.git@f656230a801142801c6ffd5eea93551d1373ec4f#egg=peakrdl_halcpp +peakrdl-html==2.10.1 +peakrdl-ipxact==3.4.3 +peakrdl-regblock==0.20.0 +peakrdl-systemrdl==0.3.0 +peakrdl-uvm==2.3.0 +Pygments==2.17.2 +python-markdown-math==0.8 +requests==2.31.0 +smmap==5.0.1 +snowballstemmer==2.2.0 +Sphinx==7.2.6 +sphinx-rtd-theme==2.0.0 +sphinxcontrib-applehelp==1.0.8 +sphinxcontrib-devhelp==1.0.6 +sphinxcontrib-htmlhelp==2.0.5 +sphinxcontrib-jquery==4.1 +sphinxcontrib-jsmath==1.0.1 +sphinxcontrib-qthelp==1.0.7 +sphinxcontrib-serializinghtml==1.1.10 +systemrdl-compiler==1.27.3 +tomli==2.0.1 +typing_extensions==4.9.0 +urllib3==2.2.0 +zipp==3.17.0 diff --git a/src/peakrdl_halcpp/__peakrdl__.py b/src/peakrdl_halcpp/__peakrdl__.py index 07560d6..a286724 100644 --- a/src/peakrdl_halcpp/__peakrdl__.py +++ b/src/peakrdl_halcpp/__peakrdl__.py @@ -10,16 +10,13 @@ class Exporter(ExporterSubcommandPlugin): - """ - Generic PeakRDL template class for plugin creation. - """ - short_desc = "Generate CPP Hardware Abstraction Layer libraries" - long_desc = "Generate CPP Hardware Abstraction Layer libraries" + """Generates C++ Hardware Abstraction Layer (HAL) libraries.""" + + short_desc = 'Generates C++ Hardware Abstraction Layer (HAL) libraries.' + long_desc = 'Generates C++ Hardware Abstraction Layer (HAL) libraries.' def add_exporter_arguments(self, arg_group: 'argparse.ArgumentParser') -> None: - """ - Adds custom arguments to the plugin. - """ + """Adds custom arguments to the plugin.""" arg_group.add_argument( "--ext", nargs="*", @@ -47,14 +44,12 @@ def add_exporter_arguments(self, arg_group: 'argparse.ArgumentParser') -> None: ) def do_export(self, top_node: 'AddrmapNode', options: 'argparse.Namespace') -> None: - """ - Plugin entry function. - """ + """Plugin entry function.""" hal = HalExporter() hal.export( node=top_node, outdir=options.output, list_files=options.list_files, - ext=options.ext, + ext_modules=options.ext, keep_buses=options.keep_buses, ) diff --git a/src/peakrdl_halcpp/exporter.py b/src/peakrdl_halcpp/exporter.py index 7566df2..eda6848 100644 --- a/src/peakrdl_halcpp/exporter.py +++ b/src/peakrdl_halcpp/exporter.py @@ -1,39 +1,34 @@ import os -from typing import TYPE_CHECKING +from typing import Union, Any, List, Dict import shutil import jinja2 as jj -from systemrdl.node import Node, RootNode, AddrmapNode +from systemrdl.node import RootNode, AddrmapNode from .haladdrmap import * from .halutils import HalUtils -# Import the different types if checking is activated -if TYPE_CHECKING: - from typing import Union, Any, List, str, bool, Dict - class HalExporter(): - """This method will be used to add two numbers - - :param int num1: The first number - :param int num2: The second number + """HAL C++ PeakRDL plugin top class to generate the C++ HAL from SystemRDL. - :returns: The sum of two numbers + Class methods: - :rtype: int + - :func:`list_files` + - :func:`copy_base_headers` + - :func:`export` + - :func:`process_template` """ - - def __init__(self, **kwargs: 'Any'): + def __init__(self, **kwargs: Any): # Check for stray kwargs if kwargs: raise TypeError("got an unexpected keyword argument '%s'" % list(kwargs.keys())[0]) - # Include files generated output directory + #: HAL C++ copied header library location within the generated files output directory self.cpp_dir = "include" - # HAL C++ base headers list (copied into cpp_dir) + #: HAL C++ headers list (copied into :attr:`~cpp_dir`) self.base_headers = [ "halcpp_base.h", "halcpp_utils.h", @@ -46,15 +41,14 @@ def __init__(self, **kwargs: 'Any'): ] def list_files(self, top: HalAddrmap, outdir: str): - """ - Prints the generated files to stdout (without generating the files). + """Prints the generated files to stdout (without generating the files). Parameters ---------- top: HalAddrmap Top level HalAddrmap object. outdir: str - Output directory to which the generated files would be put. + Output directory in which the output files are generated. """ gen_files = [os.path.join(outdir, addrmap.type_name + ".h") for addrmap in top.get_addrmaps_recursive()] @@ -65,14 +59,13 @@ def list_files(self, top: HalAddrmap, outdir: str): print(*out_files, sep="\n") def copy_base_headers(self, outdir: str): - """ - Copies the HAL base headers to the generated HAL files location given - by outdir. + """Copies the HAL C++ headers to the generated files location given + by the outdir parameter. Parameters ---------- outdir: str - Output directory to which the generated files would be put. + Output directory in which the output files are generated. """ abspaths = [os.path.join(os.path.dirname( __file__), self.cpp_dir, x) for x in self.base_headers] @@ -81,30 +74,40 @@ def copy_base_headers(self, outdir: str): os.makedirs(outdir) [shutil.copy(x, outdir) for x in abspaths] - def export(self, node: 'Union[AddrmapNode, RootNode]', outdir: str, list_files: bool = False, ext: List = [], keep_buses: bool = False) -> None: - """ - Plugin exporter main function. + def export(self, + node: 'AddrmapNode', + outdir: str, + list_files: bool = False, + ext_modules: List = [str], + keep_buses: bool = False + ) -> None: + """Entry function called of the PeakRDL-halcpp plugin. Parameters ---------- - nodes: Union[Node, List[Node]] + node: AddrmapNode + Top AddrmapNode of the SystemRDL description. outdir: str - list_files: bool=False - ext: list=[] - keep_buses: bool=False + Output directory in which the output files are generated. + list_files: bool = False + Don't generate but print the files that would be generated. + ext_modules: List[str] + List of modules (i.e., SystemRDL addrmap objects) with extended functionalities. + keep_buses: bool = False + TBD """ - # print("+++++++++++DEBUG+++++++++++++++") - # print(f'Node type: {type(node)}') - # print(f'Node: {node}') - # print(f'Node address offset: {node.inst.addr_offset}') - # print(f'Node original type name: {node.orig_type_name}') - # desc = "/*\n" - # if node.get_property('desc') is not None: - # for l in node.get_property('desc').splitlines(): - # desc = desc + "* " + l + "\n" - # print(f'Node description:\n{desc}*/') - # print("+++++++++++++++++++++++++++++++") + print("+++++++++++DEBUG+++++++++++++++") + print(f'Node type: {type(node)}') + print(f'Node: {node}') + print(f'Node address offset: {node.inst.addr_offset}') + print(f'Node original type name: {node.orig_type_name}') + desc = "/*\n" + if node.get_property('desc') is not None: + for l in node.get_property('desc').splitlines(): + desc = desc + "* " + l + "\n" + print(f'Node description:\n{desc}*/') + print("+++++++++++++++++++++++++++++++") # If it is the root node, skip to top addrmap if isinstance(node, RootNode): @@ -114,11 +117,10 @@ def export(self, node: 'Union[AddrmapNode, RootNode]', outdir: str, list_files: raise TypeError( "'node' argument expects type AddrmapNode. Got '%s'" % type(node).__name__) - halutils = HalUtils(ext) + halutils = HalUtils(ext_modules) top = halutils.build_hierarchy( node=node, - remove_root=False, # TODO fix keep_buses=keep_buses, ) @@ -144,6 +146,15 @@ def export(self, node: 'Union[AddrmapNode, RootNode]', outdir: str, list_files: self.copy_base_headers(outdir) def process_template(self, context: Dict) -> str: + """ + _summary_ + + Args: + context (Dict): _description_ + + Returns: + str: _description_ + """ env = jj.Environment( loader=jj.FileSystemLoader( diff --git a/src/peakrdl_halcpp/haladdrmap.py b/src/peakrdl_halcpp/haladdrmap.py index 042486d..9c8814b 100644 --- a/src/peakrdl_halcpp/haladdrmap.py +++ b/src/peakrdl_halcpp/haladdrmap.py @@ -1,104 +1,156 @@ +from typing import Union, List, Dict +from abc import ABC, abstractmethod, abstractproperty + from systemrdl.node import Node, AddrmapNode, RegNode, RootNode, MemNode, FieldNode, AddressableNode, RegfileNode -from typing import List, Dict -class HalBase: # TODO make abstract - """ - HakBase is the HAL base class for all the different nodes - (Addrmap, Reg, Mem, and Field). - - Methods - ------- - get_docstring(node : Node, parent : 'HalBase|None') -> str - Converts the node description into a C++ multi-line comment. - cpp_access_type() -> str: - Returns the node access type and must be overloaded by - the child class. - get_template_line() - str: - Returns the node C++ template line - get_cls_tmpl_spec(just_tmpl=False) -> str: - TBD - - type_name() -> str: - TBD - addr_offset() -> int: - TBD - orig_type_name() -> str: - TBD - get_parent_haladdrmap() -> HalAddrmap: - TBD + +class HalBase(ABC): + """Base abstract class for all the different HAL nodes (Addrmap, Reg, Mem, and Field). + + .. inheritance-diagram:: peakrdl_halcpp.haladdrmap + :top-classes: peakrdl_halcpp.haladdrmap.HalBase + :parts: 1 + + Class methods: + + - :func:`get_docstring` + - :func:`get_cls_tmpl_spec` + - :func:`get_parent_haladdrmap` + - :func:`get_template_line` """ - def __init__(self, - node : Node, - parent : 'HalBase|None', - ): - self.node = node - self.parent = parent + + def __init__(self, node: Node, parent: Union['HalBase', None]): + self._node = node + self._parent = parent def get_docstring(self) -> str: + """Converts the node description into a C++ multi-line comment. + + Returns + ------- + str + C++ multi-line comment string. + """ desc = "/*\n" - if self.node.get_property('desc') is not None: - for l in self.node.get_property('desc').splitlines(): + if self._node.get_property('desc') is not None: + for l in self._node.get_property('desc').splitlines(): desc = desc + "* " + l + "\n" print(desc + "*/") return desc + "*/" - return "" + return "" - @property - def cpp_access_type(self) -> str: - raise NotImplementedError("You need to overload this method in inherited class") - - def get_template_line(self) -> str: - raise NotImplementedError("You need to overload this method in inherited class") + def get_parent_haladdrmap(self) -> 'HalAddrmap': + if isinstance(self._parent, HalAddrmap): + return self._parent + assert self._parent is not None + return self._parent.get_parent_haladdrmap() - def get_cls_tmpl_spec(self, just_tmpl=False) -> str: - raise NotImplementedError("You need to overload this method in inherited class") + @property + def orig_type_name(self) -> str: + if self._node.orig_type_name is not None: + return self._node.orig_type_name + else: + return self._node.inst_name @property def type_name(self) -> str: - return self.orig_type_name + """Node type name property. + Returns + ------- + str + String containing the node type name. + """ + return self.orig_type_name - @property + @abstractproperty def addr_offset(self) -> int: - raise NotImplementedError("You need to overload this method in inherited class") + """Node address offset property (relative address to parent node). + It must be overloaded by the child class. - @property - def orig_type_name(self) -> str: - if self.node.orig_type_name is not None: - return self.node.orig_type_name - else: - return self.node.inst_name + Returns + ------- + int + Relative address offset to the parent node. + """ + pass + + @abstractproperty + def cpp_access_type(self) -> str: + """Node access type (read and/or write) property. It must be + overloaded by the child class. + + Returns + ------- + str + A string with the child class name followed by the access + rights. For example, a field node with read only access + would return 'FieldRO'. + """ + pass + + @abstractmethod + def get_template_line(self) -> str: + """Returns the node C++ template line as a string. This method + must be overloaded by the child class. + + This C++ string template (e.g., 'template') + + Returns + ------- + str + C++ string template (e.g., 'template') of the + node type (e.g., reg, mem). + """ + pass + + @abstractmethod + def get_cls_tmpl_spec(self, just_tmpl: bool = False) -> str: + """This method must be overloaded by the child class. + + Parameters + ---------- + just_tmpl: (bool, optional) + TBD. Defaults to False. + + Returns + ------- + str + C++ string template (e.g., TBD) of ? + """ + pass - def get_parent_haladdrmap(self) -> 'HalAddrmap': - if isinstance(self.parent, HalAddrmap): - return self.parent - assert self.parent is not None - return self.parent.get_parent_haladdrmap() class HalField(HalBase): + """HAL class for PeakRDL field node. - def __init__(self, - node: FieldNode, - parent : 'HalReg', - ): + Class methods: + + - :func:`has_enum` + - :func:`get_enum` + - :func:`get_enum_name` + - :func:`get_namespace_enums` + - :func:`get_template_line` + - :func:`get_cls_tmpl_spec` + """ + + def __init__(self, node: FieldNode, parent: 'HalReg'): super().__init__(node, parent) - self.node = node # TODO REMOVE - self.parent = parent @property def width(self) -> int: - return self.node.width + return self._node.width def has_enum(self) -> bool: - return self.node.get_property('encode', default=False) != False + return self._node.get_property('encode', default=False) != False def get_enum(self): - encode = self.node.get_property('encode') + encode = self._node.get_property('encode') namespace_enums = self.get_namespace_enums() if encode is not None: name = encode.__name__ if name in namespace_enums: - if namespace_enums[name][-1] == self.node.owning_addrmap: # TODO WHAT??? + if namespace_enums[name][-1] == self._node.owning_addrmap: # TODO WHAT??? return False, None, None, None, None, None enum_strings = [] enum_values = [] @@ -110,13 +162,14 @@ def get_enum(self): const_width = max(enum_values).bit_length() - namespace_enums[name] = [enum_strings, enum_values, enum_desc, const_width, self.node.owning_addrmap] + namespace_enums[name] = [enum_strings, enum_values, + enum_desc, const_width, self._node.owning_addrmap] return True, name, enum_strings, enum_values, enum_desc, const_width return False, None, None, None, None, None def get_enum_name(self): - encode = self.node.get_property('encode') + encode = self._node.get_property('encode') if encode is not None: return encode.__name__ @@ -126,52 +179,56 @@ def get_namespace_enums(self) -> 'Dict': @property def cpp_access_type(self) -> str: out = "" - if self.node.is_sw_readable and self.node.is_sw_writable: + if self._node.is_sw_readable and self._node.is_sw_writable: return "FieldRW" - elif self.node.is_sw_writable and not self.node.is_sw_readable: + elif self._node.is_sw_writable and not self._node.is_sw_readable: return "FieldWO" - elif self.node.is_sw_readable: + elif self._node.is_sw_readable: return "FieldRO" return out + @property + def addr_offset(self) -> int: + assert False, "FieldNode has no offset" + def get_template_line(self) -> str: assert False, "You should not create a class from a FieldNode" def get_cls_tmpl_spec(self, just_tmpl=False) -> str: assert False, "You should not extend FieldNode classes" + class HalReg(HalBase): + """HAL class for PeakRDL reg node.""" def __init__(self, - node : RegNode, - parent : 'HalAddrmap|HalRegfile', - bus_offset : int = 0, + node: RegNode, + parent: 'HalAddrmap|HalRegfile', + bus_offset: int = 0, ): super().__init__(node, parent) - self.node = node # TODO REMOVE - self.parent = parent self.bus_offset = bus_offset self.fields = self.get_fields() - @property # TODO move to base? + @property # TODO move to base? def is_array(self) -> bool: - return self.node.is_array + return self._node.is_array @property def width(self) -> int: return max([c.node.high for c in self.fields]) + 1 def get_fields(self) -> 'List[HalField]': - return [HalField(c, self) for c in self.node.children() if isinstance(c, FieldNode)] + return [HalField(c, self) for c in self._node.children() if isinstance(c, FieldNode)] @property def cpp_access_type(self): - if self.node.has_sw_readable and self.node.has_sw_writable: + if self._node.has_sw_readable and self._node.has_sw_writable: return "RegRW" - elif self.node.has_sw_writable and not self.node.has_sw_readable: + elif self._node.has_sw_writable and not self._node.has_sw_readable: return "RegWO" - elif self.node.has_sw_readable: + elif self._node.has_sw_readable: return "RegRO" assert False @@ -184,50 +241,53 @@ def get_cls_tmpl_spec(self, just_tmpl=False) -> str: @property def addr_offset(self) -> int: - return self.bus_offset + self.node.address_offset + return self.bus_offset + self._node.address_offset class HalArrReg(HalReg): + """HAL class for PeakRDL array of reg node.""" + def __init__(self, - node : RegNode, - parent : 'HalAddrmap|HalRegfile', - bus_offset : int = 0, + node: RegNode, + parent: 'HalAddrmap|HalRegfile', + bus_offset: int = 0, ): super().__init__(node, parent) - self.node = node # TODO REMOVE + self.bus_offset = bus_offset assert node.is_array, "Register Node is not array" - assert self.node.size == self.node.array_stride, f"Different stride than regwidth is not supported {self.node.size} {self.node.array_stride}" + assert self._node.size == self._node.array_stride, f"Different stride than regwidth is not supported {self._node.size} {self._node.array_stride}" @property def addr_offset(self): - return self.bus_offset + next(self.node.unrolled()).address_offset # type: ignore + # type: ignore + return self.bus_offset + next(self._node.unrolled()).address_offset class HalMem(HalBase): + """HAL class for PeakRDL mem node.""" def __init__(self, - node : MemNode, - parent : 'HalAddrmap', - bus_offset : int = 0, + node: MemNode, + parent: 'HalAddrmap', + bus_offset: int = 0, ): super().__init__(node, parent) - self.node = node - self.parent = parent + self.bus_offset = bus_offset - for c in self.parent.node.children(): + for c in self._parent.node.children(): if isinstance(c, AddressableNode): - assert c == self.node, f"Addrmaps with anything else than one memory node is currently not allowed, it could be easily added" + assert c == self._node, f"Addrmaps with anything else than one memory node is currently not allowed, it could be easily added" @property def size(self) -> int: - return self.node.size + return self._node.size @property - def width(self) -> int: # TODO probably not good + def width(self) -> int: # TODO probably not good return self.size def get_template_line(self) -> str: @@ -243,40 +303,40 @@ def get_cls_tmpl_spec(self, just_tmpl=False) -> str: @property def type_name(self) -> str: - return self.parent.orig_type_name + return self._parent.orig_type_name @property def addr_offset(self) -> int: - return self.bus_offset + self.node.address_offset + return self.bus_offset + self._node.address_offset + class HalRegfile(HalBase): + """HAL class for PeakRDL regfile node.""" def __init__(self, - node : RegfileNode, - parent : 'HalAddrmap', - bus_offset : int = 0, + node: RegfileNode, + parent: 'HalAddrmap', + bus_offset: int = 0 ): super().__init__(node, parent) - self.node = node # TODO REMOVE - self.parent = parent self.bus_offset = bus_offset self.regs = self.get_regs() self.regfiles = self.get_regfiles() - @property # TODO move to base? + @property # TODO move to base? def is_array(self) -> bool: - return self.node.is_array + return self._node.is_array # @property # def width(self) -> int: # return max([c.node.high for c in self.]) + 1 def get_regs(self) -> 'List[HalReg]': - return [HalReg(c, self) for c in self.node.children() if isinstance(c, RegNode)] + return [HalReg(c, self) for c in self._node.children() if isinstance(c, RegNode)] def get_regfiles(self) -> 'List[HalRegfile]': - return [HalRegfile(c, self) for c in self.node.children() if isinstance(c, RegfileNode)] + return [HalRegfile(c, self) for c in self._node.children() if isinstance(c, RegfileNode)] @property def cpp_access_type(self): @@ -291,99 +351,152 @@ def get_cls_tmpl_spec(self, just_tmpl=False) -> str: @property def addr_offset(self) -> int: - return self.bus_offset + next(self.node.unrolled()).address_offset # type: ignore + # type: ignore + return self.bus_offset + next(self._node.unrolled()).address_offset + class HalArrRegfile(HalRegfile): + """HAL class for PeakRDL array of regfile node.""" + def __init__(self, - node : RegfileNode, - parent : 'HalAddrmap|HalRegfile', - bus_offset : int = 0, + node: RegfileNode, + parent: 'HalAddrmap|HalRegfile', + bus_offset: int = 0, ): super().__init__(node, parent) - self.node = node # TODO REMOVE + self.bus_offset = bus_offset assert node.is_array, "Register File Node is not array" - assert self.node.size == self.node.array_stride, f"Different stride than regwidth is not supported {self.node.size} {self.node.array_stride}" + assert self._node.size == self._node.array_stride, f"Different stride than regwidth is not supported {self._node.size} {self._node.array_stride}" @property def addr_offset(self): - return self.bus_offset + next(self.node.unrolled()).address_offset # type: ignore - + # type: ignore + return self.bus_offset + next(self._node.unrolled()).address_offset class HalAddrmap(HalBase): - """ - TBD + """HAL class for PeakRDL addrmap node. + + Class methods: + - Methods - ------- - TBD """ + def __init__(self, - node : AddrmapNode, - parent : 'HalAddrmap|None' = None, - bus_offset : int = 0, - ): + node: AddrmapNode, + parent: 'HalAddrmap|None' = None, + bus_offset: int = 0 + ): super().__init__(node, parent) - self.node = node # TODO REMOVE - self.parent = parent + + # What is this bus offset? self.bus_offset = bus_offset # Check that top level HAL has no parent but RootNode - assert (self.parent == None) == isinstance(self.node.parent, RootNode) + assert (self._parent == None) == isinstance(self._node.parent, RootNode) + # Traverse all the node hierarchy and extract of the different nodes: + # RegNode -> HalReg or HalArrReg self.regs = self.get_regs() + # Regfile -> HalRegfile or HalArrRegfile + self.regfiles = self.get_regfiles() + # MemNode -> HalMem self.mems = self.get_mems() + # AddrMapNode -> HalAddrMap self.addrmaps = self.get_addrmaps() - self.regfiles = self.get_regfiles() - - self.enums = {} @property def is_root_node(self) -> bool: - return self.parent == None + return self._parent == None def get_regs(self) -> 'List[HalReg]': + """Traverses the node hierarchy and extracts the RegNodes and the + array of RegNodes. + + Returns + ------- + List[HalReg] + List of RegNode objects each encapsulated in a HalReg (or HalArrReg) object. + """ regs = [] - for c in self.node.children(): + for c in self._node.children(): if isinstance(c, RegNode): reg = HalArrReg(c, self) if c.is_array else HalReg(c, self) regs.append(reg) return regs - # return [HalReg(c) for c in self.node.children() if isinstance(c, RegNode)] def get_mems(self) -> 'List[HalMem]': - return [HalMem(c, self) for c in self.node.children() if isinstance(c, MemNode)] + """Traverses the node hierarchy and extracts the MemNodes. + + Returns + ------- + List[HalMem] + List of MemNode objects each encapsulated in a HalMem object. + """ + return [HalMem(c, self) for c in self._node.children() if isinstance(c, MemNode)] def get_addrmaps(self) -> 'List[HalAddrmap]': - return [HalAddrmap(c, self) for c in self.node.children() if isinstance(c, AddrmapNode)] + """Traverses the node hierarchy and extracts the AddrmapNode. + + Returns + ------- + List[HalMem] + List of AddrmapNode objects each encapsulated in a HalAddrmap object. + """ + return [HalAddrmap(c, self) for c in self._node.children() if isinstance(c, AddrmapNode)] def get_regfiles(self) -> 'List[HalRegfile]': + """Traverses the node hierarchy and extracts the RegfileNodes and the + array of RegfileNodes. + + Returns + ------- + List[HalReg] + List of RegfileNode objects each encapsulated in a HalRegfile (or HalArrRegfile) object. + """ regfiles = [] - for c in self.node.children(): + for c in self._node.children(): if isinstance(c, RegfileNode): - regfile = HalArrRegfile(c, self) if c.is_array else HalRegfile(c, self) + regfile = HalArrRegfile( + c, self) if c.is_array else HalRegfile(c, self) regfiles.append(regfile) return regfiles def remove_buses(self): + """Removes buses (i.e., addrmaps containing only addrmaps). + + The address offset of the current AddrMapNode is added to each of + its child AddrMapNodes and the parent node of the current AddrMapNode is + passed to its child AddrMapNodes. + """ remove_list = [] for c in self.addrmaps: c.remove_buses() - if c.is_bus(): # Doesnt have registers or memories, only addrmaps - for subc in c.addrmaps: # Change parent + if c.is_bus(): + # Traverse the child AddrMapNodes and add to them the address + # offset of the current AddrMapNode and update their parent + for subc in c.addrmaps: subc.bus_offset += c.addr_offset - subc.parent = self + subc._parent = self remove_list.append(c) - self.addrmaps.extend(c.addrmaps) # Steal all addrmaps from a bus + # Extend the addrmaps list with the removed node one + self.addrmaps.extend(c.addrmaps) [self.addrmaps.remove(c) for c in remove_list] def is_bus(self) -> bool: + """Checks if the addrmap is a bus. + + Returns + ------- + bool + Returns True if the addrmap is a bus (i.e., addrmaps containing only addrmaps). + """ if len(self.regs) == 0 and len(self.mems) == 0 and len(self.regfiles) == 0: return True return False @@ -403,6 +516,10 @@ def get_template_line(self) -> str: def type_name(self) -> str: return self.orig_type_name + "_hal" + @property + def cpp_access_type(self) -> str: + assert False, "cpp_access_type not defined (is it needed)" + def get_cls_tmpl_spec(self, just_tmpl=False) -> str: str = self.type_name.upper() if not just_tmpl else "" @@ -420,5 +537,4 @@ def get_addrmaps_recursive(self): @property def addr_offset(self) -> int: - return self.bus_offset + self.node.address_offset - + return self.bus_offset + self._node.address_offset diff --git a/src/peakrdl_halcpp/halutils.py b/src/peakrdl_halcpp/halutils.py index 8ebb0d1..b84e30b 100644 --- a/src/peakrdl_halcpp/halutils.py +++ b/src/peakrdl_halcpp/halutils.py @@ -4,57 +4,65 @@ from .haladdrmap import * + class HalUtils(): """ - This is the top level C++ HAL exporter class. This class is called - by the do_export() call of the PeakRDL plugin Exporter class. - - Methods - ------- - get_include_file(halnode : HalAddrmap) -> str: - TBD - has_extern(halnode : HalAddrmap) -> bool: - TBD - get_extern(halnode : HalAddrmap) -> str: - TBD - get_unique_type_nodes(lst : 'List[HalBase]'): - TBD - generate_file_header(): - TBD - build_hierarchy(node : AddrmapNode, keep_buses : bool = False, - remove_root: bool = True) - -> HalAddrmap : top = HalAddrmap(node) - TBD + HAL utility class. + + Class methods: + + - :func:`get_include_file` + - :func:`has_extern` + - :func:`get_extern` + - :func:`get_unique_type_nodes` + - :func:`generate_file_header` + - :func:`build_hierarchy` """ - def __init__(self, extern : List[str]) -> None: - """ - Initialize the extern variable with a list of external - functionalities to include to the HAL (i.e., 'higher level' functions). - """ - self.extern = extern - def get_include_file(self, halnode : HalAddrmap) -> str: + def __init__(self, ext_modules: List[str]) -> None: + """Initializes the ext_modules variable with a list of external + modules implementing extended functionalities (e.g., a read_gpio_port() + function) to be included to the HAL. + + Parameters + ---------- + ext_modules: List[str] + List of modules (i.e., SystemRDL addrmap objects) with extended functionalities. """ + self.ext_modules = ext_modules + def get_include_file(self, halnode: HalAddrmap) -> str: + """Returns the HAL node base header file or the extended header file + if the later exists. """ has_extern = self.has_extern(halnode) return halnode.orig_type_name + "_ext.h" if has_extern else halnode.type_name + ".h" - def has_extern(self, halnode : HalAddrmap) -> bool: + def has_extern(self, halnode: HalAddrmap) -> bool: + """Returns True if the HAL node is listed as having extended functionalities. """ - Returns True if the object contains external files (i.e., extern). - """ - if self.extern is not None: - if halnode.orig_type_name in self.extern: + if self.ext_modules is not None: + if halnode.orig_type_name in self.ext_modules: return True return False - def get_extern(self, halnode : HalAddrmap) -> str: + def get_extern(self, halnode: HalAddrmap) -> str: + """Return the ??? name of the HAL node. + + Parameters + ---------- + halnode: HalAddrmap + HAL node corresponding to a SystemRDL addrmap object. + + Returns + ------- + str: ??? + """ if self.has_extern(halnode): return halnode.orig_type_name return halnode.type_name - def get_unique_type_nodes(self, lst : 'List[HalBase]'): + def get_unique_type_nodes(self, lst: List[HalBase]): return list({node.type_name: node for node in lst}.values()) def generate_file_header(self): @@ -64,33 +72,28 @@ def generate_file_header(self): comment += f"// By user: {username} at: {current_datetime}\n" return comment - def build_hierarchy(self, - node : AddrmapNode, - keep_buses : bool=False, - remove_root : bool=True, - ) -> HalAddrmap: - """ - TBD + def build_hierarchy(self, node: AddrmapNode, keep_buses: bool = False) -> HalAddrmap: + """_summary_ Parameters - ------- - node : AddrmapNode - TBD - keep_buses : bool=False - TBD - remove_root : bool=True - TBD + ---------- + node: AddrmapNode + _description_ + keep_buses: (bool, optional) + _description_. Defaults to False. Returns ------- HalAddrmap - TBD + _description_ """ # Initialize the HAL top address map (i.e., no parent) top = HalAddrmap(node) - - if keep_buses is False: + # By default the buses (i.e., addrmaps containing only addrmaps) are removed + if keep_buses is True: + return top + else: + # Could this be a nice one liner? top.remove_buses() - - return top + return top diff --git a/src/peakrdl_halcpp/templates/addrmap.j2 b/src/peakrdl_halcpp/templates/addrmap.j2 index bee6798..83de49d 100644 --- a/src/peakrdl_halcpp/templates/addrmap.j2 +++ b/src/peakrdl_halcpp/templates/addrmap.j2 @@ -34,10 +34,10 @@ public: using TYPE = {{ r.get_cls_tmpl_spec() }}; {% for f in r.fields %} - static halcpp::{{ f.cpp_type }}<{{ f.node.low }}, {{ f.node.high }}, TYPE> {{ f.node.inst_name }}; + static halcpp::{{ f.cpp_type }}<{{ f._node.low }}, {{ f._node.high }}, TYPE> {{ f._node.inst_name }}; {% endfor %} -{% if r.node.has_sw_writable %} +{% if r._node.has_sw_writable %} using halcpp::{{ r.cpp_type }}{{ r.get_cls_tmpl_spec(True) }}::operator=; {% endif %} @@ -56,7 +56,7 @@ public: {% if c.__class__.__name__ == "HalRegfile" %} {{ assert("Regfile inside Regfile Not supported yet") }} {% else %} - static {{ c.type_name|upper }}<0x{{ "%0x"|format(c.addr_offset|int) }}, {{ c.width }}, TYPE> {{ c.node.inst_name }}; + static {{ c.type_name|upper }}<0x{{ "%0x"|format(c.addr_offset|int) }}, {{ c.width }}, TYPE> {{ c._node.inst_name }}; {% endif %} {% endfor %} @@ -79,15 +79,15 @@ public: {% for c in halnode.addrmaps + halnode.regs + halnode.mems + halnode.regfiles %} {% if c.__class__.__name__ == "HalArrReg" %} - static halcpp::RegArrayNode<{{ halnode.orig_type_name }}_nm::{{ c.type_name|upper }}, 0x{{ "%0x"|format(c.addr_offset|int) }}, {{ c.width }}, {{ c.node.array_stride }}, TYPE , {{ c.node.array_dimensions|join(', ') }}> {{ c.node.inst_name }}; + static halcpp::RegArrayNode<{{ halnode.orig_type_name }}_nm::{{ c.type_name|upper }}, 0x{{ "%0x"|format(c.addr_offset|int) }}, {{ c.width }}, {{ c._node.array_stride }}, TYPE , {{ c._node.array_dimensions|join(', ') }}> {{ c._node.inst_name }}; {% elif c.__class__.__name__ == "HalReg" or c.__class__.__name__ == "HalMem" %} - static {{ halnode.orig_type_name }}_nm::{{ c.type_name|upper }}<0x{{ "%0x"|format(c.addr_offset|int) }}, {{ c.width }}, TYPE> {{ c.node.inst_name }}; + static {{ halnode.orig_type_name }}_nm::{{ c.type_name|upper }}<0x{{ "%0x"|format(c.addr_offset|int) }}, {{ c.width }}, TYPE> {{ c._node.inst_name }}; {% elif c.__class__.__name__ == "HalArrRegfile" %} - static halcpp::RegfileArrayNode<{{ halnode.orig_type_name }}_nm::{{ c.type_name|upper }}, 0x{{ "%0x"|format(c.addr_offset|int) }}, {{ c.node.array_stride }}, TYPE , {{ c.node.array_dimensions|join(', ') }}> {{ c.node.inst_name }}; + static halcpp::RegfileArrayNode<{{ halnode.orig_type_name }}_nm::{{ c.type_name|upper }}, 0x{{ "%0x"|format(c.addr_offset|int) }}, {{ c._node.array_stride }}, TYPE , {{ c._node.array_dimensions|join(', ') }}> {{ c._node.inst_name }}; {% elif c.__class__.__name__ == "HalRegfile" %} - static {{ halnode.orig_type_name }}_nm::{{ halutils.get_extern(c)|upper }}<0x{{ "%0x"|format(c.addr_offset|int) }}, TYPE> {{ c.node.inst_name }}; + static {{ halnode.orig_type_name }}_nm::{{ halutils.get_extern(c)|upper }}<0x{{ "%0x"|format(c.addr_offset|int) }}, TYPE> {{ c._node.inst_name }}; {% else %} - static {{ halutils.get_extern(c)|upper }}<0x{{ "%0x"|format(c.addr_offset|int) }}, TYPE> {{ c.node.inst_name }}; + static {{ halutils.get_extern(c)|upper }}<0x{{ "%0x"|format(c.addr_offset|int) }}, TYPE> {{ c._node.inst_name }}; {% endif %} {% endfor %} From 6d8fd84c0e663ebd6504502d1b9f8238b6ebcdad Mon Sep 17 00:00:00 2001 From: Benoit Date: Mon, 12 Feb 2024 08:31:08 +0100 Subject: [PATCH 03/23] Save commit. --- docs_sphinx/source/conf.py | 1 + requirements.txt | 1 - src/peakrdl_halcpp/exporter.py | 3 +- src/peakrdl_halcpp/haladdrmap.py | 67 +++++++++++++++++++++++--------- src/peakrdl_halcpp/halutils.py | 9 +++-- 5 files changed, 57 insertions(+), 24 deletions(-) diff --git a/docs_sphinx/source/conf.py b/docs_sphinx/source/conf.py index ab43c4f..24b7802 100644 --- a/docs_sphinx/source/conf.py +++ b/docs_sphinx/source/conf.py @@ -34,6 +34,7 @@ 'sphinx.ext.autodoc', 'sphinx.ext.viewcode', 'sphinx.ext.napoleon', + 'sphinx.ext.graphviz', 'sphinx.ext.inheritance_diagram', ] diff --git a/requirements.txt b/requirements.txt index 7d34d72..427f87a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -18,7 +18,6 @@ mypy-extensions==1.0.0 packaging==23.2 peakrdl==1.1.0 peakrdl-cheader==1.0.0 --e git+ssh://git@github.com/Risto97/PeakRDL-halcpp.git@f656230a801142801c6ffd5eea93551d1373ec4f#egg=peakrdl_halcpp peakrdl-html==2.10.1 peakrdl-ipxact==3.4.3 peakrdl-regblock==0.20.0 diff --git a/src/peakrdl_halcpp/exporter.py b/src/peakrdl_halcpp/exporter.py index eda6848..6d56b67 100644 --- a/src/peakrdl_halcpp/exporter.py +++ b/src/peakrdl_halcpp/exporter.py @@ -94,7 +94,7 @@ def export(self, ext_modules: List[str] List of modules (i.e., SystemRDL addrmap objects) with extended functionalities. keep_buses: bool = False - TBD + Keep AddrMapNodes containing only AddrMapNodes. """ print("+++++++++++DEBUG+++++++++++++++") @@ -119,6 +119,7 @@ def export(self, halutils = HalUtils(ext_modules) + # Build the hierachy using the HAL wrapper classes around PeakRDL nodes (e.g., AddrmapNodes, RegNodes) top = halutils.build_hierarchy( node=node, keep_buses=keep_buses, diff --git a/src/peakrdl_halcpp/haladdrmap.py b/src/peakrdl_halcpp/haladdrmap.py index 9c8814b..cc93f79 100644 --- a/src/peakrdl_halcpp/haladdrmap.py +++ b/src/peakrdl_halcpp/haladdrmap.py @@ -122,7 +122,7 @@ def get_cls_tmpl_spec(self, just_tmpl: bool = False) -> str: class HalField(HalBase): - """HAL class for PeakRDL field node. + """HAL wrapper class for PeakRDL FieldNode. Class methods: @@ -199,7 +199,7 @@ def get_cls_tmpl_spec(self, just_tmpl=False) -> str: class HalReg(HalBase): - """HAL class for PeakRDL reg node.""" + """HAL wrapper class for PeakRDL RegNode.""" def __init__(self, node: RegNode, @@ -245,7 +245,7 @@ def addr_offset(self) -> int: class HalArrReg(HalReg): - """HAL class for PeakRDL array of reg node.""" + """HAL wrapper class for PeakRDL array of RegNode.""" def __init__(self, node: RegNode, @@ -267,7 +267,7 @@ def addr_offset(self): class HalMem(HalBase): - """HAL class for PeakRDL mem node.""" + """HAL wrapper class for PeakRDL MemNode.""" def __init__(self, node: MemNode, @@ -311,7 +311,7 @@ def addr_offset(self) -> int: class HalRegfile(HalBase): - """HAL class for PeakRDL regfile node.""" + """HAL wrapper class for PeakRDL RegfileNode.""" def __init__(self, node: RegfileNode, @@ -356,7 +356,7 @@ def addr_offset(self) -> int: class HalArrRegfile(HalRegfile): - """HAL class for PeakRDL array of regfile node.""" + """HAL wrapper class for PeakRDL array of RegfileNode.""" def __init__(self, node: RegfileNode, @@ -378,7 +378,7 @@ def addr_offset(self): class HalAddrmap(HalBase): - """HAL class for PeakRDL addrmap node. + """HAL wrapper class for PeakRDL AddrmapNode. Class methods: @@ -411,7 +411,14 @@ def __init__(self, self.enums = {} @property - def is_root_node(self) -> bool: + def is_top_node(self) -> bool: + """Check if this is the top node. + + Returns + ------- + bool + Returns True if the node is the top one (i.e., no parent). + """ return self._parent == None def get_regs(self) -> 'List[HalReg]': @@ -502,15 +509,46 @@ def is_bus(self) -> bool: return False def get_regfiles_regs(self) -> 'List[HalReg]': + """Extracts the registers from the regfiles. + + Returns + ------- + List[HalReg] + List of registers (HalReg) contained in the node regfiles. + """ regs = [] for regfile in self.regfiles: regs.extend(regfile.regs) return regs def get_template_line(self) -> str: - if self.is_root_node: + """Returns the HAL template for AddrmapNode. + + Returns + ------- + str + C++ template structure for AddrmapNode. + """ + if self.is_top_node: + # Parent is set to void by default for the top node return "template " return "template " + + def get_cls_tmpl_spec(self, just_tmpl=False) -> str: + """Returns the HAL template parameters used for forwarding reference. + + The structure must matched the template returned by :func:`get_template_line`. + + Returns + ------- + str + C++ template parameters. + """ + str = self.type_name.upper() if not just_tmpl else "" + + if self.is_top_node: + return str + "" + return str + "" @property def type_name(self) -> str: @@ -518,20 +556,13 @@ def type_name(self) -> str: @property def cpp_access_type(self) -> str: - assert False, "cpp_access_type not defined (is it needed)" - - def get_cls_tmpl_spec(self, just_tmpl=False) -> str: - str = self.type_name.upper() if not just_tmpl else "" - - if self.is_root_node: - return str + "" - return str + "" + assert False, "cpp_access_type not defined (is it needed?)" def get_addrmaps_recursive(self): addrmaps = self.addrmaps.copy() for c in self.addrmaps: addrmaps.extend(c.get_addrmaps_recursive()) - if self.is_root_node: + if self.is_top_node: addrmaps.insert(0, self) return addrmaps diff --git a/src/peakrdl_halcpp/halutils.py b/src/peakrdl_halcpp/halutils.py index b84e30b..8dc534e 100644 --- a/src/peakrdl_halcpp/halutils.py +++ b/src/peakrdl_halcpp/halutils.py @@ -73,19 +73,20 @@ def generate_file_header(self): return comment def build_hierarchy(self, node: AddrmapNode, keep_buses: bool = False) -> HalAddrmap: - """_summary_ + """Build the hierachy using the HAL wrapper classes around PeakRDL + nodes (e.g., AddrmapNodes, RegNodes) Parameters ---------- node: AddrmapNode - _description_ + Top level AddrmapNode of the SystemRDL description keep_buses: (bool, optional) - _description_. Defaults to False. + Keep AddrMapNodes containing only AddrMapNodes. Defaults to False. Returns ------- HalAddrmap - _description_ + HalAddrmap top class containing the HAL wrapper class hierarchy. """ # Initialize the HAL top address map (i.e., no parent) From 526d754d2a40290f5c468177fe9ec21d9e206edc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Denkinger?= Date: Mon, 12 Feb 2024 10:18:19 +0100 Subject: [PATCH 04/23] HAL classes moved into different files. --- src/peakrdl_halcpp/exporter.py | 45 ++-- src/peakrdl_halcpp/haladdrmap.py | 427 ++++--------------------------- src/peakrdl_halcpp/halbase.py | 115 +++++++++ src/peakrdl_halcpp/halfield.py | 80 ++++++ src/peakrdl_halcpp/halmem.py | 50 ++++ src/peakrdl_halcpp/halreg.py | 77 ++++++ src/peakrdl_halcpp/halregfile.py | 74 ++++++ src/peakrdl_halcpp/halutils.py | 11 +- 8 files changed, 476 insertions(+), 403 deletions(-) create mode 100644 src/peakrdl_halcpp/halbase.py create mode 100644 src/peakrdl_halcpp/halfield.py create mode 100644 src/peakrdl_halcpp/halmem.py create mode 100644 src/peakrdl_halcpp/halreg.py create mode 100644 src/peakrdl_halcpp/halregfile.py diff --git a/src/peakrdl_halcpp/exporter.py b/src/peakrdl_halcpp/exporter.py index 6d56b67..2f25e09 100644 --- a/src/peakrdl_halcpp/exporter.py +++ b/src/peakrdl_halcpp/exporter.py @@ -5,7 +5,7 @@ import jinja2 as jj from systemrdl.node import RootNode, AddrmapNode -from .haladdrmap import * +from .haladdrmap import HalAddrmap from .halutils import HalUtils @@ -119,13 +119,15 @@ def export(self, halutils = HalUtils(ext_modules) - # Build the hierachy using the HAL wrapper classes around PeakRDL nodes (e.g., AddrmapNodes, RegNodes) + # Build the hierachy using the HAL wrapper classes around PeakRDL + # nodes (e.g., AddrmapNodes, RegNodes) top = halutils.build_hierarchy( node=node, keep_buses=keep_buses, ) if list_files: + # Only print the files that would be generated self.list_files(top, outdir) else: # Create the output directory for the generated files @@ -134,39 +136,50 @@ def export(self, except FileExistsError: pass + # Iterate over all the HalAddrmap objects for halnode in top.get_addrmaps_recursive(): + # Create a dictionary with the current HalAddrmap and + # the halutils objects context = { 'halnode': halnode, 'halutils': halutils, } + # The next lines generate the C++ header file for the + # HalAddrmap node using a jinja2 template. text = self.process_template(context) out_file = os.path.join(outdir, halnode.type_name + ".h") with open(out_file, 'w') as f: f.write(text) - + # Copy the base header files (fixed code) to the output directory self.copy_base_headers(outdir) def process_template(self, context: Dict) -> str: - """ - _summary_ + """Generates a C++ header file based on a given HalAddrmap node and + a C++ header file jinja2 template. - Args: - context (Dict): _description_ - - Returns: - str: _description_ + Parameters + ---------- + context: Dict + Dictionary containing a HalAddrmap node and the HalUtils object + passed to the jinja2 env + + Returns + ------- + str + Text of the generated C++ header for a given HalAddrmap node. """ - + # Create a jinja2 env with the template(s) contained in the templates + # folder located in the same directory than this file env = jj.Environment( loader=jj.FileSystemLoader( '%s/templates/' % os.path.dirname(__file__)), trim_blocks=True, lstrip_blocks=True) - - # Benoit: what does it do? + # Add the base zip function to the env env.filters.update({ 'zip': zip, }) - - res = env.get_template("addrmap.j2").render(context) - return res + # Render the C++ header text using the jinja2 template and the + # specific context + cpp_header_text = env.get_template("addrmap.j2").render(context) + return cpp_header_text diff --git a/src/peakrdl_halcpp/haladdrmap.py b/src/peakrdl_halcpp/haladdrmap.py index cc93f79..674f9ac 100644 --- a/src/peakrdl_halcpp/haladdrmap.py +++ b/src/peakrdl_halcpp/haladdrmap.py @@ -1,380 +1,11 @@ -from typing import Union, List, Dict -from abc import ABC, abstractmethod, abstractproperty +from typing import List -from systemrdl.node import Node, AddrmapNode, RegNode, RootNode, MemNode, FieldNode, AddressableNode, RegfileNode +from systemrdl.node import AddrmapNode, RegNode, RootNode, MemNode, RegfileNode - -class HalBase(ABC): - """Base abstract class for all the different HAL nodes (Addrmap, Reg, Mem, and Field). - - .. inheritance-diagram:: peakrdl_halcpp.haladdrmap - :top-classes: peakrdl_halcpp.haladdrmap.HalBase - :parts: 1 - - Class methods: - - - :func:`get_docstring` - - :func:`get_cls_tmpl_spec` - - :func:`get_parent_haladdrmap` - - :func:`get_template_line` - """ - - def __init__(self, node: Node, parent: Union['HalBase', None]): - self._node = node - self._parent = parent - - def get_docstring(self) -> str: - """Converts the node description into a C++ multi-line comment. - - Returns - ------- - str - C++ multi-line comment string. - """ - desc = "/*\n" - if self._node.get_property('desc') is not None: - for l in self._node.get_property('desc').splitlines(): - desc = desc + "* " + l + "\n" - print(desc + "*/") - return desc + "*/" - return "" - - def get_parent_haladdrmap(self) -> 'HalAddrmap': - if isinstance(self._parent, HalAddrmap): - return self._parent - assert self._parent is not None - return self._parent.get_parent_haladdrmap() - - @property - def orig_type_name(self) -> str: - if self._node.orig_type_name is not None: - return self._node.orig_type_name - else: - return self._node.inst_name - - @property - def type_name(self) -> str: - """Node type name property. - - Returns - ------- - str - String containing the node type name. - """ - return self.orig_type_name - - @abstractproperty - def addr_offset(self) -> int: - """Node address offset property (relative address to parent node). - It must be overloaded by the child class. - - Returns - ------- - int - Relative address offset to the parent node. - """ - pass - - @abstractproperty - def cpp_access_type(self) -> str: - """Node access type (read and/or write) property. It must be - overloaded by the child class. - - Returns - ------- - str - A string with the child class name followed by the access - rights. For example, a field node with read only access - would return 'FieldRO'. - """ - pass - - @abstractmethod - def get_template_line(self) -> str: - """Returns the node C++ template line as a string. This method - must be overloaded by the child class. - - This C++ string template (e.g., 'template') - - Returns - ------- - str - C++ string template (e.g., 'template') of the - node type (e.g., reg, mem). - """ - pass - - @abstractmethod - def get_cls_tmpl_spec(self, just_tmpl: bool = False) -> str: - """This method must be overloaded by the child class. - - Parameters - ---------- - just_tmpl: (bool, optional) - TBD. Defaults to False. - - Returns - ------- - str - C++ string template (e.g., TBD) of ? - """ - pass - - -class HalField(HalBase): - """HAL wrapper class for PeakRDL FieldNode. - - Class methods: - - - :func:`has_enum` - - :func:`get_enum` - - :func:`get_enum_name` - - :func:`get_namespace_enums` - - :func:`get_template_line` - - :func:`get_cls_tmpl_spec` - """ - - def __init__(self, node: FieldNode, parent: 'HalReg'): - super().__init__(node, parent) - - @property - def width(self) -> int: - return self._node.width - - def has_enum(self) -> bool: - return self._node.get_property('encode', default=False) != False - - def get_enum(self): - encode = self._node.get_property('encode') - namespace_enums = self.get_namespace_enums() - if encode is not None: - name = encode.__name__ - if name in namespace_enums: - if namespace_enums[name][-1] == self._node.owning_addrmap: # TODO WHAT??? - return False, None, None, None, None, None - enum_strings = [] - enum_values = [] - enum_desc = [] - for k, v in encode.members.items(): - enum_strings.append(encode.members[k].name) - enum_values.append(encode.members[k].value) - enum_desc.append(encode.members[k].rdl_desc) - - const_width = max(enum_values).bit_length() - - namespace_enums[name] = [enum_strings, enum_values, - enum_desc, const_width, self._node.owning_addrmap] - return True, name, enum_strings, enum_values, enum_desc, const_width - - return False, None, None, None, None, None - - def get_enum_name(self): - encode = self._node.get_property('encode') - if encode is not None: - return encode.__name__ - - def get_namespace_enums(self) -> 'Dict': - return self.get_parent_haladdrmap().enums - - @property - def cpp_access_type(self) -> str: - out = "" - if self._node.is_sw_readable and self._node.is_sw_writable: - return "FieldRW" - elif self._node.is_sw_writable and not self._node.is_sw_readable: - return "FieldWO" - elif self._node.is_sw_readable: - return "FieldRO" - return out - - @property - def addr_offset(self) -> int: - assert False, "FieldNode has no offset" - - def get_template_line(self) -> str: - assert False, "You should not create a class from a FieldNode" - - def get_cls_tmpl_spec(self, just_tmpl=False) -> str: - assert False, "You should not extend FieldNode classes" - - -class HalReg(HalBase): - """HAL wrapper class for PeakRDL RegNode.""" - - def __init__(self, - node: RegNode, - parent: 'HalAddrmap|HalRegfile', - bus_offset: int = 0, - ): - super().__init__(node, parent) - self.bus_offset = bus_offset - - self.fields = self.get_fields() - - @property # TODO move to base? - def is_array(self) -> bool: - return self._node.is_array - - @property - def width(self) -> int: - return max([c.node.high for c in self.fields]) + 1 - - def get_fields(self) -> 'List[HalField]': - return [HalField(c, self) for c in self._node.children() if isinstance(c, FieldNode)] - - @property - def cpp_access_type(self): - if self._node.has_sw_readable and self._node.has_sw_writable: - return "RegRW" - elif self._node.has_sw_writable and not self._node.has_sw_readable: - return "RegWO" - elif self._node.has_sw_readable: - return "RegRO" - assert False - - def get_template_line(self) -> str: - return f"template" - - def get_cls_tmpl_spec(self, just_tmpl=False) -> str: - str = self.type_name.upper() if not just_tmpl else "" - return str + "" - - @property - def addr_offset(self) -> int: - return self.bus_offset + self._node.address_offset - - -class HalArrReg(HalReg): - """HAL wrapper class for PeakRDL array of RegNode.""" - - def __init__(self, - node: RegNode, - parent: 'HalAddrmap|HalRegfile', - bus_offset: int = 0, - ): - super().__init__(node, parent) - - self.bus_offset = bus_offset - - assert node.is_array, "Register Node is not array" - - assert self._node.size == self._node.array_stride, f"Different stride than regwidth is not supported {self._node.size} {self._node.array_stride}" - - @property - def addr_offset(self): - # type: ignore - return self.bus_offset + next(self._node.unrolled()).address_offset - - -class HalMem(HalBase): - """HAL wrapper class for PeakRDL MemNode.""" - - def __init__(self, - node: MemNode, - parent: 'HalAddrmap', - bus_offset: int = 0, - ): - super().__init__(node, parent) - - self.bus_offset = bus_offset - - for c in self._parent.node.children(): - if isinstance(c, AddressableNode): - assert c == self._node, f"Addrmaps with anything else than one memory node is currently not allowed, it could be easily added" - - @property - def size(self) -> int: - return self._node.size - - @property - def width(self) -> int: # TODO probably not good - return self.size - - def get_template_line(self) -> str: - return f"template" - - @property - def cpp_access_type(self) -> str: - assert False, "cpp_access_type should not be called on HalMem class" - - def get_cls_tmpl_spec(self, just_tmpl=False) -> str: - str = self.type_name.upper() if not just_tmpl else "" - return str + "" - - @property - def type_name(self) -> str: - return self._parent.orig_type_name - - @property - def addr_offset(self) -> int: - return self.bus_offset + self._node.address_offset - - -class HalRegfile(HalBase): - """HAL wrapper class for PeakRDL RegfileNode.""" - - def __init__(self, - node: RegfileNode, - parent: 'HalAddrmap', - bus_offset: int = 0 - ): - super().__init__(node, parent) - self.bus_offset = bus_offset - - self.regs = self.get_regs() - self.regfiles = self.get_regfiles() - - @property # TODO move to base? - def is_array(self) -> bool: - return self._node.is_array - - # @property - # def width(self) -> int: - # return max([c.node.high for c in self.]) + 1 - - def get_regs(self) -> 'List[HalReg]': - return [HalReg(c, self) for c in self._node.children() if isinstance(c, RegNode)] - - def get_regfiles(self) -> 'List[HalRegfile]': - return [HalRegfile(c, self) for c in self._node.children() if isinstance(c, RegfileNode)] - - @property - def cpp_access_type(self): - return "RegfileNode" - - def get_template_line(self) -> str: - return f"template" - - def get_cls_tmpl_spec(self, just_tmpl=False) -> str: - str = self.type_name.upper() if not just_tmpl else "" - return str + "" - - @property - def addr_offset(self) -> int: - # type: ignore - return self.bus_offset + next(self._node.unrolled()).address_offset - - -class HalArrRegfile(HalRegfile): - """HAL wrapper class for PeakRDL array of RegfileNode.""" - - def __init__(self, - node: RegfileNode, - parent: 'HalAddrmap|HalRegfile', - bus_offset: int = 0, - ): - super().__init__(node, parent) - - self.bus_offset = bus_offset - - assert node.is_array, "Register File Node is not array" - - assert self._node.size == self._node.array_stride, f"Different stride than regwidth is not supported {self._node.size} {self._node.array_stride}" - - @property - def addr_offset(self): - # type: ignore - return self.bus_offset + next(self._node.unrolled()).address_offset +from .halbase import HalBase +from .halreg import HalReg, HalArrReg +from .halregfile import HalRegfile, HalArrRegfile +from .halmem import HalMem class HalAddrmap(HalBase): @@ -392,11 +23,12 @@ def __init__(self, ): super().__init__(node, parent) - # What is this bus offset? + # Does it have to be a parameter (now only modified by remove_buses function)? self.bus_offset = bus_offset # Check that top level HAL has no parent but RootNode - assert (self._parent == None) == isinstance(self._node.parent, RootNode) + assert (self._parent == None) == isinstance( + self._node.parent, RootNode) # Traverse all the node hierarchy and extract of the different nodes: # RegNode -> HalReg or HalArrReg @@ -452,7 +84,7 @@ def get_addrmaps(self) -> 'List[HalAddrmap]': Returns ------- - List[HalMem] + List[HalAddrmap] List of AddrmapNode objects each encapsulated in a HalAddrmap object. """ return [HalAddrmap(c, self) for c in self._node.children() if isinstance(c, AddrmapNode)] @@ -463,7 +95,7 @@ def get_regfiles(self) -> 'List[HalRegfile]': Returns ------- - List[HalReg] + List[HalRegfile] List of RegfileNode objects each encapsulated in a HalRegfile (or HalArrRegfile) object. """ regfiles = [] @@ -515,7 +147,7 @@ def get_regfiles_regs(self) -> 'List[HalReg]': ------- List[HalReg] List of registers (HalReg) contained in the node regfiles. - """ + """ regs = [] for regfile in self.regfiles: regs.extend(regfile.regs) @@ -533,7 +165,7 @@ def get_template_line(self) -> str: # Parent is set to void by default for the top node return "template " return "template " - + def get_cls_tmpl_spec(self, just_tmpl=False) -> str: """Returns the HAL template parameters used for forwarding reference. @@ -546,26 +178,57 @@ def get_cls_tmpl_spec(self, just_tmpl=False) -> str: """ str = self.type_name.upper() if not just_tmpl else "" + # Both lines are returning the same, why? if self.is_top_node: return str + "" return str + "" @property def type_name(self) -> str: + """Return the node name with the '_hal' suffix""" return self.orig_type_name + "_hal" @property def cpp_access_type(self) -> str: assert False, "cpp_access_type not defined (is it needed?)" - def get_addrmaps_recursive(self): + def get_addrmaps_recursive(self) -> List['HalAddrmap']: + """Recursively fetch the HalAddrmap nodes into a list. + + Gets the AddrMapNode hierarchy of the SystemRDL description. + Here is a pseudo-SystemRDL code example for an basic SoC design. + + addrmap mySoC { + addrmap myMem0 @ 0x40000000 + addrmap mySubsystem @ 0x44000000 { + addrmap myPeriph0 @ 0x00001000 + addrmap myPeriph1 @ 0x00002000 + } + addrmap myMem1 @ 0x41000000 + ... + } + + Called on the top node (i.e., mySoC) this function returns: + + [mySoC, myMem0, mySubsystem, myPeriph0, myPeriph1, myMem1] + + where each element is a HalAddrmap object. ONly the top node + insert its own reference (i.e., mySoC) at the beginning. + + Returns + ------- + List[HalAddrmap] + A list of all HalAddrmap nodes contained within this HalAddrmap node. + """ addrmaps = self.addrmaps.copy() for c in self.addrmaps: addrmaps.extend(c.get_addrmaps_recursive()) + # Top node insert its own reference, why? if self.is_top_node: addrmaps.insert(0, self) return addrmaps @property def addr_offset(self) -> int: + """Returns the node address offset relative to its parent.""" return self.bus_offset + self._node.address_offset diff --git a/src/peakrdl_halcpp/halbase.py b/src/peakrdl_halcpp/halbase.py new file mode 100644 index 0000000..48685b0 --- /dev/null +++ b/src/peakrdl_halcpp/halbase.py @@ -0,0 +1,115 @@ +from typing import Union +from abc import ABC, abstractmethod, abstractproperty + +from systemrdl.node import Node + + +class HalBase(ABC): + """Base abstract class for all the different HAL nodes (Addrmap, Reg, Mem, and Field). + + .. inheritance-diagram:: peakrdl_halcpp.haladdrmap + :top-classes: peakrdl_halcpp.haladdrmap.HalBase + :parts: 1 + + Class methods: + + - :func:`get_docstring` + - :func:`get_cls_tmpl_spec` + - :func:`get_parent_haladdrmap` + - :func:`get_template_line` + """ + + def __init__(self, node: Node, parent: Union['HalBase', None]): + self._node = node + self._parent = parent + + def get_docstring(self) -> str: + """Converts the node description into a C++ multi-line comment. + + Returns + ------- + str + C++ multi-line comment string. + """ + desc = "/*\n" + if self._node.get_property('desc') is not None: + for l in self._node.get_property('desc').splitlines(): + desc = desc + "* " + l + "\n" + print(desc + "*/") + return desc + "*/" + return "" + + def get_parent_haladdrmap(self) -> 'HalAddrmap': + if isinstance(self._parent, HalAddrmap): + return self._parent + assert self._parent is not None + return self._parent.get_parent_haladdrmap() + + @property + def orig_type_name(self) -> str: + if self._node.orig_type_name is not None: + return self._node.orig_type_name + else: + return self._node.inst_name + + @property + def type_name(self) -> str: + """Returns the node type name property.""" + return self.orig_type_name + + @abstractproperty + def addr_offset(self) -> int: + """Node address offset property (relative address to parent node). + It must be overloaded by the child class. + + Returns + ------- + int + Relative address offset to the parent node. + """ + pass + + @abstractproperty + def cpp_access_type(self) -> str: + """Node access type (read and/or write) property. It must be + overloaded by the child class. + + Returns + ------- + str + A string with the child class name followed by the access + rights. For example, a field node with read only access + would return 'FieldRO'. + """ + pass + + @abstractmethod + def get_template_line(self) -> str: + """Returns the node C++ template line as a string. This method + must be overloaded by the child class. + + This C++ string template (e.g., 'template') + + Returns + ------- + str + C++ string template (e.g., 'template') of the + node type (e.g., reg, mem). + """ + pass + + @abstractmethod + def get_cls_tmpl_spec(self, just_tmpl: bool = False) -> str: + """This method must be overloaded by the child class. + + Parameters + ---------- + just_tmpl: (bool, optional) + TBD. Defaults to False. + + Returns + ------- + str + C++ string template (e.g., TBD) of ? + """ + pass diff --git a/src/peakrdl_halcpp/halfield.py b/src/peakrdl_halcpp/halfield.py new file mode 100644 index 0000000..7d30364 --- /dev/null +++ b/src/peakrdl_halcpp/halfield.py @@ -0,0 +1,80 @@ +from typing import Dict + +from systemrdl.node import FieldNode + + +class HalField(HalBase): + """HAL wrapper class for PeakRDL FieldNode. + + Class methods: + + - :func:`has_enum` + - :func:`get_enum` + - :func:`get_enum_name` + - :func:`get_namespace_enums` + - :func:`get_template_line` + - :func:`get_cls_tmpl_spec` + """ + + def __init__(self, node: FieldNode, parent: 'HalReg'): + super().__init__(node, parent) + + @property + def width(self) -> int: + return self._node.width + + def has_enum(self) -> bool: + return self._node.get_property('encode', default=False) != False + + def get_enum(self): + encode = self._node.get_property('encode') + namespace_enums = self.get_namespace_enums() + if encode is not None: + name = encode.__name__ + if name in namespace_enums: + if namespace_enums[name][-1] == self._node.owning_addrmap: # TODO WHAT??? + return False, None, None, None, None, None + enum_strings = [] + enum_values = [] + enum_desc = [] + for k, v in encode.members.items(): + enum_strings.append(encode.members[k].name) + enum_values.append(encode.members[k].value) + enum_desc.append(encode.members[k].rdl_desc) + + const_width = max(enum_values).bit_length() + + namespace_enums[name] = [enum_strings, enum_values, + enum_desc, const_width, self._node.owning_addrmap] + return True, name, enum_strings, enum_values, enum_desc, const_width + + return False, None, None, None, None, None + + def get_enum_name(self): + encode = self._node.get_property('encode') + if encode is not None: + return encode.__name__ + + def get_namespace_enums(self) -> 'Dict': + return self.get_parent_haladdrmap().enums + + @property + def cpp_access_type(self) -> str: + out = "" + if self._node.is_sw_readable and self._node.is_sw_writable: + return "FieldRW" + elif self._node.is_sw_writable and not self._node.is_sw_readable: + return "FieldWO" + elif self._node.is_sw_readable: + return "FieldRO" + return out + + @property + def addr_offset(self) -> int: + assert False, "FieldNode has no offset" + + def get_template_line(self) -> str: + assert False, "You should not create a class from a FieldNode" + + def get_cls_tmpl_spec(self, just_tmpl=False) -> str: + assert False, "You should not extend FieldNode classes" diff --git a/src/peakrdl_halcpp/halmem.py b/src/peakrdl_halcpp/halmem.py new file mode 100644 index 0000000..5fad713 --- /dev/null +++ b/src/peakrdl_halcpp/halmem.py @@ -0,0 +1,50 @@ +from typing import List + +from systemrdl.node import MemNode, AddressableNode + +from .halbase import HalBase +from .haladdrmap import HalAddrmap + + +class HalMem(HalBase): + """HAL wrapper class for PeakRDL MemNode.""" + + def __init__(self, + node: MemNode, + parent: 'HalAddrmap', + bus_offset: int = 0, + ): + super().__init__(node, parent) + + self.bus_offset = bus_offset + + for c in self._parent.node.children(): + if isinstance(c, AddressableNode): + assert c == self._node, f"Addrmaps with anything else than one memory node is currently not allowed, it could be easily added" + + @property + def size(self) -> int: + return self._node.size + + @property + def width(self) -> int: # TODO probably not good + return self.size + + def get_template_line(self) -> str: + return f"template" + + @property + def cpp_access_type(self) -> str: + assert False, "cpp_access_type should not be called on HalMem class" + + def get_cls_tmpl_spec(self, just_tmpl=False) -> str: + str = self.type_name.upper() if not just_tmpl else "" + return str + "" + + @property + def type_name(self) -> str: + return self._parent.orig_type_name + + @property + def addr_offset(self) -> int: + return self.bus_offset + self._node.address_offset diff --git a/src/peakrdl_halcpp/halreg.py b/src/peakrdl_halcpp/halreg.py new file mode 100644 index 0000000..b026b0a --- /dev/null +++ b/src/peakrdl_halcpp/halreg.py @@ -0,0 +1,77 @@ +from systemrdl.node import AddrmapNode, RegNode, RootNode, MemNode, FieldNode, AddressableNode, RegfileNode +from typing import List + +from systemrdl.node import RegNode, FieldNode + +from .halbase import HalBase +from .halfield import HalField +from .halregfile import HalRegfile +from .haladdrmap import HalAddrmap + + +class HalReg(HalBase): + """HAL wrapper class for PeakRDL RegNode.""" + + def __init__(self, + node: RegNode, + parent: 'HalAddrmap|HalRegfile', + bus_offset: int = 0, + ): + super().__init__(node, parent) + self.bus_offset = bus_offset + + self.fields = self.get_fields() + + @property # TODO move to base? + def is_array(self) -> bool: + return self._node.is_array + + @property + def width(self) -> int: + return max([c.node.high for c in self.fields]) + 1 + + def get_fields(self) -> 'List[HalField]': + return [HalField(c, self) for c in self._node.children() if isinstance(c, FieldNode)] + + @property + def cpp_access_type(self): + if self._node.has_sw_readable and self._node.has_sw_writable: + return "RegRW" + elif self._node.has_sw_writable and not self._node.has_sw_readable: + return "RegWO" + elif self._node.has_sw_readable: + return "RegRO" + assert False + + def get_template_line(self) -> str: + return f"template" + + def get_cls_tmpl_spec(self, just_tmpl=False) -> str: + str = self.type_name.upper() if not just_tmpl else "" + return str + "" + + @property + def addr_offset(self) -> int: + return self.bus_offset + self._node.address_offset + + +class HalArrReg(HalReg): + """HAL wrapper class for PeakRDL array of RegNode.""" + + def __init__(self, + node: RegNode, + parent: 'HalAddrmap|HalRegfile', + bus_offset: int = 0, + ): + super().__init__(node, parent) + + self.bus_offset = bus_offset + + assert node.is_array, "Register Node is not array" + + assert self._node.size == self._node.array_stride, f"Different stride than regwidth is not supported {self._node.size} {self._node.array_stride}" + + @property + def addr_offset(self): + # type: ignore + return self.bus_offset + next(self._node.unrolled()).address_offset diff --git a/src/peakrdl_halcpp/halregfile.py b/src/peakrdl_halcpp/halregfile.py new file mode 100644 index 0000000..9da570b --- /dev/null +++ b/src/peakrdl_halcpp/halregfile.py @@ -0,0 +1,74 @@ +from typing import List + +from systemrdl.node import RegNode, RegfileNode + +from .halbase import HalBase +from .halreg import HalReg +from .haladdrmap import HalAddrmap + + +class HalRegfile(HalBase): + """HAL wrapper class for PeakRDL RegfileNode.""" + + def __init__(self, + node: RegfileNode, + parent: 'HalAddrmap', + bus_offset: int = 0 + ): + super().__init__(node, parent) + self.bus_offset = bus_offset + + self.regs = self.get_regs() + self.regfiles = self.get_regfiles() + + @property # TODO move to base? + def is_array(self) -> bool: + return self._node.is_array + + # @property + # def width(self) -> int: + # return max([c.node.high for c in self.]) + 1 + + def get_regs(self) -> 'List[HalReg]': + return [HalReg(c, self) for c in self._node.children() if isinstance(c, RegNode)] + + def get_regfiles(self) -> 'List[HalRegfile]': + return [HalRegfile(c, self) for c in self._node.children() if isinstance(c, RegfileNode)] + + @property + def cpp_access_type(self): + return "RegfileNode" + + def get_template_line(self) -> str: + return f"template" + + def get_cls_tmpl_spec(self, just_tmpl=False) -> str: + str = self.type_name.upper() if not just_tmpl else "" + return str + "" + + @property + def addr_offset(self) -> int: + # type: ignore + return self.bus_offset + next(self._node.unrolled()).address_offset + + +class HalArrRegfile(HalRegfile): + """HAL wrapper class for PeakRDL array of RegfileNode.""" + + def __init__(self, + node: RegfileNode, + parent: 'HalAddrmap|HalRegfile', + bus_offset: int = 0, + ): + super().__init__(node, parent) + + self.bus_offset = bus_offset + + assert node.is_array, "Register File Node is not array" + + assert self._node.size == self._node.array_stride, f"Different stride than regwidth is not supported {self._node.size} {self._node.array_stride}" + + @property + def addr_offset(self): + # type: ignore + return self.bus_offset + next(self._node.unrolled()).address_offset diff --git a/src/peakrdl_halcpp/halutils.py b/src/peakrdl_halcpp/halutils.py index 8dc534e..cf04469 100644 --- a/src/peakrdl_halcpp/halutils.py +++ b/src/peakrdl_halcpp/halutils.py @@ -2,7 +2,8 @@ import getpass import datetime -from .haladdrmap import * +from .halbase import HalBase +from .haladdrmap import HalAddrmap class HalUtils(): @@ -39,8 +40,7 @@ def get_include_file(self, halnode: HalAddrmap) -> str: return halnode.orig_type_name + "_ext.h" if has_extern else halnode.type_name + ".h" def has_extern(self, halnode: HalAddrmap) -> bool: - """Returns True if the HAL node is listed as having extended functionalities. - """ + """Returns True if the HAL node is listed as having extended functionalities.""" if self.ext_modules is not None: if halnode.orig_type_name in self.ext_modules: return True @@ -65,7 +65,8 @@ def get_extern(self, halnode: HalAddrmap) -> str: def get_unique_type_nodes(self, lst: List[HalBase]): return list({node.type_name: node for node in lst}.values()) - def generate_file_header(self): + def generate_file_header(self) -> str: + """Returns file header for generated files.""" username = getpass.getuser() current_datetime = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") comment = f"// Generated with PeakRD-halcpp : https://github.com/Risto97/PeakRDL-halcpp\n" @@ -73,7 +74,7 @@ def generate_file_header(self): return comment def build_hierarchy(self, node: AddrmapNode, keep_buses: bool = False) -> HalAddrmap: - """Build the hierachy using the HAL wrapper classes around PeakRDL + """Build the hierarchy using the HAL wrapper classes around PeakRDL nodes (e.g., AddrmapNodes, RegNodes) Parameters From 53dd83e2d481801ac2dbb1b76930b220e9f074d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Denkinger?= Date: Mon, 12 Feb 2024 16:01:38 +0100 Subject: [PATCH 05/23] Methods reorganization between HAL base and sublcasses to halutils. --- examples/class_test.py | 8 +++ src/peakrdl_halcpp/haladdrmap.py | 26 ++++++---- src/peakrdl_halcpp/halbase.py | 14 ++--- src/peakrdl_halcpp/halfield.py | 50 ++++-------------- src/peakrdl_halcpp/halmem.py | 20 +++++--- src/peakrdl_halcpp/halreg.py | 10 ++-- src/peakrdl_halcpp/halregfile.py | 23 ++++++--- src/peakrdl_halcpp/halutils.py | 65 ++++++++++++++++++++++-- src/peakrdl_halcpp/include/__init__.py | 16 +++--- src/peakrdl_halcpp/templates/__init__.py | 2 +- src/peakrdl_halcpp/templates/addrmap.j2 | 14 +++-- 11 files changed, 154 insertions(+), 94 deletions(-) diff --git a/examples/class_test.py b/examples/class_test.py index 2e69f18..5922c96 100644 --- a/examples/class_test.py +++ b/examples/class_test.py @@ -8,6 +8,8 @@ def __init__(self): super().__init__() self.my_var2 = 3 + self.mydict = {} + def print_vars(self): print(self.my_var0) print(self.my_var1) @@ -44,3 +46,9 @@ def print_vars(self): # AbstracClass0 = MyBaseClassAbstract() AbstracClass1 = DerivedClassAbstract() AbstracClass1.print_vars() + +classdict = DerivedClass0.mydict + +classdict['myentry'] = 'myvalue' + +print(DerivedClass0.mydict) diff --git a/src/peakrdl_halcpp/haladdrmap.py b/src/peakrdl_halcpp/haladdrmap.py index 674f9ac..b0a2a9e 100644 --- a/src/peakrdl_halcpp/haladdrmap.py +++ b/src/peakrdl_halcpp/haladdrmap.py @@ -39,8 +39,8 @@ def __init__(self, self.mems = self.get_mems() # AddrMapNode -> HalAddrMap self.addrmaps = self.get_addrmaps() - - self.enums = {} + # This will be populated when the HAL file is generated + self.enums = {} # type: ignore @property def is_top_node(self) -> bool: @@ -53,7 +53,7 @@ def is_top_node(self) -> bool: """ return self._parent == None - def get_regs(self) -> 'List[HalReg]': + def get_regs(self) -> List[HalReg]: """Traverses the node hierarchy and extracts the RegNodes and the array of RegNodes. @@ -69,7 +69,7 @@ def get_regs(self) -> 'List[HalReg]': regs.append(reg) return regs - def get_mems(self) -> 'List[HalMem]': + def get_mems(self) -> List[HalMem]: """Traverses the node hierarchy and extracts the MemNodes. Returns @@ -79,7 +79,7 @@ def get_mems(self) -> 'List[HalMem]': """ return [HalMem(c, self) for c in self._node.children() if isinstance(c, MemNode)] - def get_addrmaps(self) -> 'List[HalAddrmap]': + def get_addrmaps(self) -> List[HalAddrmap]: """Traverses the node hierarchy and extracts the AddrmapNode. Returns @@ -89,7 +89,7 @@ def get_addrmaps(self) -> 'List[HalAddrmap]': """ return [HalAddrmap(c, self) for c in self._node.children() if isinstance(c, AddrmapNode)] - def get_regfiles(self) -> 'List[HalRegfile]': + def get_regfiles(self) -> List[HalRegfile]: """Traverses the node hierarchy and extracts the RegfileNodes and the array of RegfileNodes. @@ -98,12 +98,16 @@ def get_regfiles(self) -> 'List[HalRegfile]': List[HalRegfile] List of RegfileNode objects each encapsulated in a HalRegfile (or HalArrRegfile) object. """ - regfiles = [] + regfiles: List[HalRegfile] = [] for c in self._node.children(): if isinstance(c, RegfileNode): - regfile = HalArrRegfile( - c, self) if c.is_array else HalRegfile(c, self) + if c.is_array: + regfile = HalArrRegfile(c, self) + else: + regfile = HalRegfile(c, self) # type: ignore + regfiles.append(regfile) + return regfiles def remove_buses(self): @@ -140,7 +144,7 @@ def is_bus(self) -> bool: return True return False - def get_regfiles_regs(self) -> 'List[HalReg]': + def get_regfiles_regs(self) -> List[HalReg]: """Extracts the registers from the regfiles. Returns @@ -154,7 +158,7 @@ def get_regfiles_regs(self) -> 'List[HalReg]': return regs def get_template_line(self) -> str: - """Returns the HAL template for AddrmapNode. + """Returns the HAL C++ template for AddrmapNode. Returns ------- diff --git a/src/peakrdl_halcpp/halbase.py b/src/peakrdl_halcpp/halbase.py index 48685b0..6fba78a 100644 --- a/src/peakrdl_halcpp/halbase.py +++ b/src/peakrdl_halcpp/halbase.py @@ -1,4 +1,4 @@ -from typing import Union +from typing import Union, Any from abc import ABC, abstractmethod, abstractproperty from systemrdl.node import Node @@ -39,11 +39,13 @@ def get_docstring(self) -> str: return desc + "*/" return "" - def get_parent_haladdrmap(self) -> 'HalAddrmap': - if isinstance(self._parent, HalAddrmap): - return self._parent - assert self._parent is not None - return self._parent.get_parent_haladdrmap() + def get_property(self, prop_name: str) -> Any: + """Returns the SystemRDL node property.""" + return self._node.get_property(prop_name) + + def get_parent(self) -> Union['HalBase', None]: + """Returns this node parent.""" + return self._parent @property def orig_type_name(self) -> str: diff --git a/src/peakrdl_halcpp/halfield.py b/src/peakrdl_halcpp/halfield.py index 7d30364..8449425 100644 --- a/src/peakrdl_halcpp/halfield.py +++ b/src/peakrdl_halcpp/halfield.py @@ -1,17 +1,18 @@ -from typing import Dict +from typing import TYPE_CHECKING from systemrdl.node import FieldNode +from .halbase import HalBase + +if TYPE_CHECKING: + from .halreg import HalReg + class HalField(HalBase): """HAL wrapper class for PeakRDL FieldNode. Class methods: - - :func:`has_enum` - - :func:`get_enum` - - :func:`get_enum_name` - - :func:`get_namespace_enums` - :func:`get_template_line` - :func:`get_cls_tmpl_spec` """ @@ -23,41 +24,6 @@ def __init__(self, node: FieldNode, parent: 'HalReg'): def width(self) -> int: return self._node.width - def has_enum(self) -> bool: - return self._node.get_property('encode', default=False) != False - - def get_enum(self): - encode = self._node.get_property('encode') - namespace_enums = self.get_namespace_enums() - if encode is not None: - name = encode.__name__ - if name in namespace_enums: - if namespace_enums[name][-1] == self._node.owning_addrmap: # TODO WHAT??? - return False, None, None, None, None, None - enum_strings = [] - enum_values = [] - enum_desc = [] - for k, v in encode.members.items(): - enum_strings.append(encode.members[k].name) - enum_values.append(encode.members[k].value) - enum_desc.append(encode.members[k].rdl_desc) - - const_width = max(enum_values).bit_length() - - namespace_enums[name] = [enum_strings, enum_values, - enum_desc, const_width, self._node.owning_addrmap] - return True, name, enum_strings, enum_values, enum_desc, const_width - - return False, None, None, None, None, None - - def get_enum_name(self): - encode = self._node.get_property('encode') - if encode is not None: - return encode.__name__ - - def get_namespace_enums(self) -> 'Dict': - return self.get_parent_haladdrmap().enums - @property def cpp_access_type(self) -> str: out = "" @@ -67,7 +33,9 @@ def cpp_access_type(self) -> str: return "FieldWO" elif self._node.is_sw_readable: return "FieldRO" - return out + else: + raise ValueError (f'Node field access rights are not found \ + {self._node.orig_type_name}') @property def addr_offset(self) -> int: diff --git a/src/peakrdl_halcpp/halmem.py b/src/peakrdl_halcpp/halmem.py index 5fad713..3a04145 100644 --- a/src/peakrdl_halcpp/halmem.py +++ b/src/peakrdl_halcpp/halmem.py @@ -1,9 +1,11 @@ -from typing import List +from typing import TYPE_CHECKING from systemrdl.node import MemNode, AddressableNode from .halbase import HalBase -from .haladdrmap import HalAddrmap + +if TYPE_CHECKING: + from .haladdrmap import HalAddrmap class HalMem(HalBase): @@ -18,9 +20,14 @@ def __init__(self, self.bus_offset = bus_offset - for c in self._parent.node.children(): - if isinstance(c, AddressableNode): - assert c == self._node, f"Addrmaps with anything else than one memory node is currently not allowed, it could be easily added" + parent_node = self.get_parent() + + if parent_node is not None: + for c in parent_node._node.children(): + if isinstance(c, AddressableNode): + assert c == self._node, f"Addrmaps with anything else than \ + one memory node is currently not allowed, \ + it could be easily added" @property def size(self) -> int: @@ -43,7 +50,8 @@ def get_cls_tmpl_spec(self, just_tmpl=False) -> str: @property def type_name(self) -> str: - return self._parent.orig_type_name + parent_node = self.get_parent() + return parent_node.orig_type_name # type: ignore @property def addr_offset(self) -> int: diff --git a/src/peakrdl_halcpp/halreg.py b/src/peakrdl_halcpp/halreg.py index b026b0a..63ea5b7 100644 --- a/src/peakrdl_halcpp/halreg.py +++ b/src/peakrdl_halcpp/halreg.py @@ -1,12 +1,13 @@ -from systemrdl.node import AddrmapNode, RegNode, RootNode, MemNode, FieldNode, AddressableNode, RegfileNode -from typing import List +from typing import TYPE_CHECKING, List from systemrdl.node import RegNode, FieldNode from .halbase import HalBase from .halfield import HalField from .halregfile import HalRegfile -from .haladdrmap import HalAddrmap + +if TYPE_CHECKING: + from .haladdrmap import HalAddrmap class HalReg(HalBase): @@ -28,7 +29,7 @@ def is_array(self) -> bool: @property def width(self) -> int: - return max([c.node.high for c in self.fields]) + 1 + return max([c._node.high for c in self.fields]) + 1 def get_fields(self) -> 'List[HalField]': return [HalField(c, self) for c in self._node.children() if isinstance(c, FieldNode)] @@ -73,5 +74,4 @@ def __init__(self, @property def addr_offset(self): - # type: ignore return self.bus_offset + next(self._node.unrolled()).address_offset diff --git a/src/peakrdl_halcpp/halregfile.py b/src/peakrdl_halcpp/halregfile.py index 9da570b..75787e1 100644 --- a/src/peakrdl_halcpp/halregfile.py +++ b/src/peakrdl_halcpp/halregfile.py @@ -1,10 +1,12 @@ -from typing import List +from typing import TYPE_CHECKING, List from systemrdl.node import RegNode, RegfileNode from .halbase import HalBase -from .halreg import HalReg -from .haladdrmap import HalAddrmap + +if TYPE_CHECKING: + from .halreg import HalReg + from .haladdrmap import HalAddrmap class HalRegfile(HalBase): @@ -33,7 +35,11 @@ def get_regs(self) -> 'List[HalReg]': return [HalReg(c, self) for c in self._node.children() if isinstance(c, RegNode)] def get_regfiles(self) -> 'List[HalRegfile]': - return [HalRegfile(c, self) for c in self._node.children() if isinstance(c, RegfileNode)] + regfiles_list = [] + for c in self._node.children(): + if isinstance(c, RegfileNode): + regfiles_list.append(HalRegfile(c, self)) # type: ignore + return regfiles_list @property def cpp_access_type(self): @@ -48,7 +54,6 @@ def get_cls_tmpl_spec(self, just_tmpl=False) -> str: @property def addr_offset(self) -> int: - # type: ignore return self.bus_offset + next(self._node.unrolled()).address_offset @@ -57,7 +62,7 @@ class HalArrRegfile(HalRegfile): def __init__(self, node: RegfileNode, - parent: 'HalAddrmap|HalRegfile', + parent: 'HalAddrmap', bus_offset: int = 0, ): super().__init__(node, parent) @@ -66,9 +71,11 @@ def __init__(self, assert node.is_array, "Register File Node is not array" - assert self._node.size == self._node.array_stride, f"Different stride than regwidth is not supported {self._node.size} {self._node.array_stride}" + assert self._node.size == self._node.array_stride, f"Different stride than \ + regwidth is not supported \ + {self._node.size} \ + {self._node.array_stride}" @property def addr_offset(self): - # type: ignore return self.bus_offset + next(self._node.unrolled()).address_offset diff --git a/src/peakrdl_halcpp/halutils.py b/src/peakrdl_halcpp/halutils.py index cf04469..2017b21 100644 --- a/src/peakrdl_halcpp/halutils.py +++ b/src/peakrdl_halcpp/halutils.py @@ -1,7 +1,9 @@ -from typing import List +from typing import List, Type, Dict, Union import getpass import datetime +from systemrdl.node import AddrmapNode + from .halbase import HalBase from .haladdrmap import HalAddrmap @@ -62,8 +64,20 @@ def get_extern(self, halnode: HalAddrmap) -> str: return halnode.orig_type_name return halnode.type_name - def get_unique_type_nodes(self, lst: List[HalBase]): - return list({node.type_name: node for node in lst}.values()) + def get_unique_type_nodes(self, halnode_lst: List[HalBase]): + """Returns a list of names (str) + + Parameters + ---------- + halnode_lst: List[HalBase] + List of objects based on the HalBase abstract class. + + Returns + ------- + _type_ + _description_ + """ + return list({halnode.type_name: halnode for halnode in halnode_lst}.values()) def generate_file_header(self) -> str: """Returns file header for generated files.""" @@ -73,6 +87,51 @@ def generate_file_header(self) -> str: comment += f"// By user: {username} at: {current_datetime}\n" return comment + def get_owning_addrmap(self, node: HalBase) -> Union[HalBase, HalAddrmap]: + """Returns the HalAddrmap enclosing this node.""" + parent_node = node.get_parent() + if isinstance(parent_node, HalAddrmap): + return parent_node + elif parent_node is not None: + return self.get_owning_addrmap(parent_node) + else: + raise ValueError(f'No HalAddrmap parent found in the hierarchy.') + + def get_node_enum(self, node: HalBase): + encode = node.get_property('encode') + if encode is not None: + haladdrmap_node = self.get_owning_addrmap(node) + if not isinstance(haladdrmap_node, HalAddrmap): + raise ValueError(f'Returned node is not an HalAddrmap object.') + + # Each addrmap is enclosed in a specific namespace + # Get this namespace enums and add the new ones + namespace_enums = haladdrmap_node.enums + + # Check the enum encoding is not already in the namespace + name = encode.__name__ + if name in namespace_enums: + # Is this check really needed? + # It should always be true, because if its not then the above check should not be true + # TODO WHAT??? + if namespace_enums[name][-1] == node.get_property('owning_addrmap'): + return False, None, None, None, None, None + enum_strings = [] + enum_values = [] + enum_desc = [] + for k, v in encode.members.items(): + enum_strings.append(encode.members[k].name) + enum_values.append(encode.members[k].value) + enum_desc.append(encode.members[k].rdl_desc) + + const_width = max(enum_values).bit_length() + + namespace_enums[name] = [enum_strings, enum_values, + enum_desc, const_width, node.get_property('owning_addrmap')] + return True, name, enum_strings, enum_values, enum_desc, const_width + + return False, None, None, None, None, None + def build_hierarchy(self, node: AddrmapNode, keep_buses: bool = False) -> HalAddrmap: """Build the hierarchy using the HAL wrapper classes around PeakRDL nodes (e.g., AddrmapNodes, RegNodes) diff --git a/src/peakrdl_halcpp/include/__init__.py b/src/peakrdl_halcpp/include/__init__.py index 3b09e92..76f2bd9 100644 --- a/src/peakrdl_halcpp/include/__init__.py +++ b/src/peakrdl_halcpp/include/__init__.py @@ -3,12 +3,12 @@ List of files: -------------- -addrmap_node.h -arch_io.h -array_nodes.h -field_node.h -halcpp_base.h -halcpp_utils.h -regfile_node.h -reg_node.h +- addrmap_node.h +- arch_io.h +- array_nodes.h +- field_node.h +- halcpp_base.h +- halcpp_utils.h +- regfile_node.h +- reg_node.h """ diff --git a/src/peakrdl_halcpp/templates/__init__.py b/src/peakrdl_halcpp/templates/__init__.py index cf3983a..29a849e 100644 --- a/src/peakrdl_halcpp/templates/__init__.py +++ b/src/peakrdl_halcpp/templates/__init__.py @@ -4,5 +4,5 @@ List of files: -------------- -addrmap.j2 +- addrmap.j2 """ diff --git a/src/peakrdl_halcpp/templates/addrmap.j2 b/src/peakrdl_halcpp/templates/addrmap.j2 index 83de49d..216f96d 100644 --- a/src/peakrdl_halcpp/templates/addrmap.j2 +++ b/src/peakrdl_halcpp/templates/addrmap.j2 @@ -1,9 +1,13 @@ +{# Get the header for the generate file (e.g, date and time of generation, by whom) #} {{ halutils.generate_file_header() }} + +{# Add the header guards #} #ifndef __{{ halnode.type_name|upper }}_H_ #define __{{ halnode.type_name|upper }}_H_ #include #include "include/halcpp_base.h" + #if defined(__clang__) #pragma clang diagnostic ignored "-Wundefined-var-template" #endif @@ -16,7 +20,7 @@ namespace {{ halnode.orig_type_name}}_nm { {% for r in halutils.get_unique_type_nodes(halnode.regs + halnode.get_regfiles_regs() ) %} {% for f in r.fields %} -{% set has_enum, enum_name, enum_strings, enum_values, enum_desc, const_width = f.get_enum() %} +{% set has_enum, enum_name, enum_strings, enum_values, enum_desc, const_width = halutils.get_node_enum(f) %} {% if has_enum %} class {{ enum_name }} { public: @@ -29,16 +33,16 @@ public: {{ r.get_docstring() }} {{ r.get_template_line() }} -class {{ r.type_name|upper }} : public halcpp::{{ r.cpp_type }}{{ r.get_cls_tmpl_spec(True) }} { +class {{ r.type_name|upper }} : public halcpp::{{ r.cpp_access_type }}{{ r.get_cls_tmpl_spec(True) }} { public: using TYPE = {{ r.get_cls_tmpl_spec() }}; {% for f in r.fields %} - static halcpp::{{ f.cpp_type }}<{{ f._node.low }}, {{ f._node.high }}, TYPE> {{ f._node.inst_name }}; + static halcpp::{{ f.cpp_access_type }}<{{ f._node.low }}, {{ f._node.high }}, TYPE> {{ f._node.inst_name }}; {% endfor %} {% if r._node.has_sw_writable %} - using halcpp::{{ r.cpp_type }}{{ r.get_cls_tmpl_spec(True) }}::operator=; + using halcpp::{{ r.cpp_access_type }}{{ r.get_cls_tmpl_spec(True) }}::operator=; {% endif %} }; @@ -48,7 +52,7 @@ public: {% for rf in halnode.regfiles %} {{ rf.get_docstring() }} {{ rf.get_template_line() }} -class {{ rf.type_name|upper }} : public halcpp::{{ rf.cpp_type }}{{ rf.get_cls_tmpl_spec(True)}} { +class {{ rf.type_name|upper }} : public halcpp::{{ rf.cpp_access_type }}{{ rf.get_cls_tmpl_spec(True)}} { public: using TYPE = {{ rf.get_cls_tmpl_spec() }}; From a0072300e68b9269f14824d758704f388752e084 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Denkinger?= Date: Mon, 12 Feb 2024 17:30:39 +0100 Subject: [PATCH 06/23] Save commit. --- docs_sphinx/source/conf.py | 2 + examples/atxmega_spi.rdl | 29 +++++++++++++- src/peakrdl_halcpp/exporter.py | 10 +++++ src/peakrdl_halcpp/haladdrmap.py | 68 +++++++++++++++++--------------- src/peakrdl_halcpp/halbase.py | 56 ++++++++++++++------------ src/peakrdl_halcpp/halreg.py | 2 +- src/peakrdl_halcpp/halregfile.py | 6 +-- src/peakrdl_halcpp/halutils.py | 15 ++----- 8 files changed, 113 insertions(+), 75 deletions(-) diff --git a/docs_sphinx/source/conf.py b/docs_sphinx/source/conf.py index 24b7802..3b8dd53 100644 --- a/docs_sphinx/source/conf.py +++ b/docs_sphinx/source/conf.py @@ -44,6 +44,8 @@ style='"rounded, filled"' ) +inheritance_graph_attrs = dict(rankdir="TB") + # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] diff --git a/examples/atxmega_spi.rdl b/examples/atxmega_spi.rdl index bf8dc9b..9a704ca 100644 --- a/examples/atxmega_spi.rdl +++ b/examples/atxmega_spi.rdl @@ -83,6 +83,30 @@ addrmap atxmega_spi { } DATA @ 0x3; + regfile { + reg { + name = "Test register"; + + field { + desc = "Test reg"; + } TESTFIELD[7:0] = 0; + } TEST_REG0 @ 0x0; + reg { + name = "Test register"; + + field { + desc = "Test reg"; + } TESTFIELD[7:0] = 0; + } TEST_REG1 @ 0x1; + reg { + name = "Test register"; + + field { + desc = "Test reg"; + } TESTFIELD[7:0] = 0; + } TEST_REG2 @ 0x2; + } TEST_REGFILE @ 0x4; + addrmap atxmega_test{ name = "ATXMEGA test addrmap"; desc = "Testing purpose"; @@ -98,7 +122,10 @@ addrmap atxmega_spi { desc = "Test field"; } TESTFIELD[7:0] = 0; } TEST_DATA @ 0x4; - } TEST_ADDRMAP @ 0x4; + } TEST_ADDRMAP @ 0x7; + + myaddrmap myaddrmap0 @0xC; + myaddrmap myaddrmap1 @0xD; }; addrmap common { diff --git a/src/peakrdl_halcpp/exporter.py b/src/peakrdl_halcpp/exporter.py index 2f25e09..a00411f 100644 --- a/src/peakrdl_halcpp/exporter.py +++ b/src/peakrdl_halcpp/exporter.py @@ -126,6 +126,16 @@ def export(self, keep_buses=keep_buses, ) + print("+++++++++++DEBUG+++++++++++++++") + regnodes = halutils.get_unique_type_nodes(top.regs + top.get_regfiles_regs()) + for reg in regnodes: + print(reg._node.inst_name) + print("NO UNIQUIFY") + regnodes = top.regs # + top.get_regfiles_regs() + for reg in regnodes: + print(reg._node.inst_name) + print("+++++++++++++++++++++++++++++++") + if list_files: # Only print the files that would be generated self.list_files(top, outdir) diff --git a/src/peakrdl_halcpp/haladdrmap.py b/src/peakrdl_halcpp/haladdrmap.py index b0a2a9e..6dd288b 100644 --- a/src/peakrdl_halcpp/haladdrmap.py +++ b/src/peakrdl_halcpp/haladdrmap.py @@ -12,8 +12,16 @@ class HalAddrmap(HalBase): """HAL wrapper class for PeakRDL AddrmapNode. Class methods: - - + - :func:`get_regs` + - :func:`get_mems` + - :func:`get_addrmaps` + - :func:`get_regfiles` + - :func:`remove_buses` + - :func:`is_bus` + - :func:`get_regfiles_regs` + - :func:`get_template_line` + - :func:`get_cls_tmpl_spec` + - :func:`get_addrmaps_recursive` """ def __init__(self, @@ -44,15 +52,23 @@ def __init__(self, @property def is_top_node(self) -> bool: - """Check if this is the top node. - - Returns - ------- - bool - Returns True if the node is the top one (i.e., no parent). - """ + """Check if this is the top node.""" return self._parent == None + @property + def type_name(self) -> str: + """Return the node name with the '_hal' suffix""" + return self.orig_type_name + "_hal" + + @property + def cpp_access_type(self) -> str: + assert False, "cpp_access_type not defined (is it needed?)" + + @property + def addr_offset(self) -> int: + """Returns the node address offset relative to its parent.""" + return self.bus_offset + self._node.address_offset + def get_regs(self) -> List[HalReg]: """Traverses the node hierarchy and extracts the RegNodes and the array of RegNodes. @@ -79,7 +95,7 @@ def get_mems(self) -> List[HalMem]: """ return [HalMem(c, self) for c in self._node.children() if isinstance(c, MemNode)] - def get_addrmaps(self) -> List[HalAddrmap]: + def get_addrmaps(self) -> List['HalAddrmap']: """Traverses the node hierarchy and extracts the AddrmapNode. Returns @@ -187,30 +203,23 @@ def get_cls_tmpl_spec(self, just_tmpl=False) -> str: return str + "" return str + "" - @property - def type_name(self) -> str: - """Return the node name with the '_hal' suffix""" - return self.orig_type_name + "_hal" - - @property - def cpp_access_type(self) -> str: - assert False, "cpp_access_type not defined (is it needed?)" - def get_addrmaps_recursive(self) -> List['HalAddrmap']: """Recursively fetch the HalAddrmap nodes into a list. Gets the AddrMapNode hierarchy of the SystemRDL description. Here is a pseudo-SystemRDL code example for an basic SoC design. - addrmap mySoC { - addrmap myMem0 @ 0x40000000 - addrmap mySubsystem @ 0x44000000 { - addrmap myPeriph0 @ 0x00001000 - addrmap myPeriph1 @ 0x00002000 + :: + + addrmap mySoC { + addrmap myMem0 @ 0x40000000 + addrmap mySubsystem @ 0x44000000 { + addrmap myPeriph0 @ 0x00001000 + addrmap myPeriph1 @ 0x00002000 + } + addrmap myMem1 @ 0x41000000 + ... } - addrmap myMem1 @ 0x41000000 - ... - } Called on the top node (i.e., mySoC) this function returns: @@ -231,8 +240,3 @@ def get_addrmaps_recursive(self) -> List['HalAddrmap']: if self.is_top_node: addrmaps.insert(0, self) return addrmaps - - @property - def addr_offset(self) -> int: - """Returns the node address offset relative to its parent.""" - return self.bus_offset + self._node.address_offset diff --git a/src/peakrdl_halcpp/halbase.py b/src/peakrdl_halcpp/halbase.py index 6fba78a..976a2cf 100644 --- a/src/peakrdl_halcpp/halbase.py +++ b/src/peakrdl_halcpp/halbase.py @@ -7,8 +7,12 @@ class HalBase(ABC): """Base abstract class for all the different HAL nodes (Addrmap, Reg, Mem, and Field). - .. inheritance-diagram:: peakrdl_halcpp.haladdrmap - :top-classes: peakrdl_halcpp.haladdrmap.HalBase + .. inheritance-diagram:: peakrdl_halcpp.haladdrmap.HalAddrmap + peakrdl_halcpp.halreg.HalReg + peakrdl_halcpp.halmem.HalMem + peakrdl_halcpp.halfield.HalField + peakrdl_halcpp.halregfile.HalRegfile + :top-classes: peakrdl_halcpp.halbase.HalBase :parts: 1 Class methods: @@ -23,30 +27,6 @@ def __init__(self, node: Node, parent: Union['HalBase', None]): self._node = node self._parent = parent - def get_docstring(self) -> str: - """Converts the node description into a C++ multi-line comment. - - Returns - ------- - str - C++ multi-line comment string. - """ - desc = "/*\n" - if self._node.get_property('desc') is not None: - for l in self._node.get_property('desc').splitlines(): - desc = desc + "* " + l + "\n" - print(desc + "*/") - return desc + "*/" - return "" - - def get_property(self, prop_name: str) -> Any: - """Returns the SystemRDL node property.""" - return self._node.get_property(prop_name) - - def get_parent(self) -> Union['HalBase', None]: - """Returns this node parent.""" - return self._parent - @property def orig_type_name(self) -> str: if self._node.orig_type_name is not None: @@ -85,6 +65,30 @@ def cpp_access_type(self) -> str: """ pass + def get_docstring(self) -> str: + """Converts the node description into a C++ multi-line comment. + + Returns + ------- + str + C++ multi-line comment string. + """ + desc = "/*\n" + if self._node.get_property('desc') is not None: + for l in self._node.get_property('desc').splitlines(): + desc = desc + "* " + l + "\n" + print(desc + "*/") + return desc + "*/" + return "" + + def get_property(self, prop_name: str) -> Any: + """Returns the SystemRDL node property.""" + return self._node.get_property(prop_name) + + def get_parent(self) -> Union['HalBase', None]: + """Returns this node parent.""" + return self._parent + @abstractmethod def get_template_line(self) -> str: """Returns the node C++ template line as a string. This method diff --git a/src/peakrdl_halcpp/halreg.py b/src/peakrdl_halcpp/halreg.py index 63ea5b7..1212e65 100644 --- a/src/peakrdl_halcpp/halreg.py +++ b/src/peakrdl_halcpp/halreg.py @@ -4,9 +4,9 @@ from .halbase import HalBase from .halfield import HalField -from .halregfile import HalRegfile if TYPE_CHECKING: + from .halregfile import HalRegfile from .haladdrmap import HalAddrmap diff --git a/src/peakrdl_halcpp/halregfile.py b/src/peakrdl_halcpp/halregfile.py index 75787e1..e648a3e 100644 --- a/src/peakrdl_halcpp/halregfile.py +++ b/src/peakrdl_halcpp/halregfile.py @@ -3,9 +3,9 @@ from systemrdl.node import RegNode, RegfileNode from .halbase import HalBase +from .halreg import HalReg if TYPE_CHECKING: - from .halreg import HalReg from .haladdrmap import HalAddrmap @@ -31,10 +31,10 @@ def is_array(self) -> bool: # def width(self) -> int: # return max([c.node.high for c in self.]) + 1 - def get_regs(self) -> 'List[HalReg]': + def get_regs(self) -> List[HalReg]: return [HalReg(c, self) for c in self._node.children() if isinstance(c, RegNode)] - def get_regfiles(self) -> 'List[HalRegfile]': + def get_regfiles(self) -> List['HalRegfile']: regfiles_list = [] for c in self._node.children(): if isinstance(c, RegfileNode): diff --git a/src/peakrdl_halcpp/halutils.py b/src/peakrdl_halcpp/halutils.py index 2017b21..f2be642 100644 --- a/src/peakrdl_halcpp/halutils.py +++ b/src/peakrdl_halcpp/halutils.py @@ -65,18 +65,9 @@ def get_extern(self, halnode: HalAddrmap) -> str: return halnode.type_name def get_unique_type_nodes(self, halnode_lst: List[HalBase]): - """Returns a list of names (str) - - Parameters - ---------- - halnode_lst: List[HalBase] - List of objects based on the HalBase abstract class. - - Returns - ------- - _type_ - _description_ - """ + """Uniquify a python list?""" + # Is this really necessary? You cannot have two nodes with the same name at the + # same hierarchy level -> peakRDL throws an error return list({halnode.type_name: halnode for halnode in halnode_lst}.values()) def generate_file_header(self) -> str: From c44e7824bce892574e2996c20fa7796709c3f157 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Denkinger?= Date: Thu, 15 Feb 2024 08:24:30 +0100 Subject: [PATCH 07/23] Python code commented. Jinja template commended. Code cleaning and reformating. --- src/peakrdl_halcpp/exporter.py | 20 +++------ src/peakrdl_halcpp/haladdrmap.py | 4 +- src/peakrdl_halcpp/halbase.py | 26 ++++++----- src/peakrdl_halcpp/halmem.py | 6 +-- src/peakrdl_halcpp/halutils.py | 43 ++++++++++--------- src/peakrdl_halcpp/templates/__init__.py | 2 +- .../templates/{addrmap.j2 => addrmap.h.j2} | 10 +++++ 7 files changed, 58 insertions(+), 53 deletions(-) rename src/peakrdl_halcpp/templates/{addrmap.j2 => addrmap.h.j2} (87%) diff --git a/src/peakrdl_halcpp/exporter.py b/src/peakrdl_halcpp/exporter.py index a00411f..b42aad1 100644 --- a/src/peakrdl_halcpp/exporter.py +++ b/src/peakrdl_halcpp/exporter.py @@ -28,17 +28,11 @@ def __init__(self, **kwargs: Any): #: HAL C++ copied header library location within the generated files output directory self.cpp_dir = "include" + + filetype = "*.h" + abspaths = os.path.join(os.path.dirname(__file__), self.cpp_dir) #: HAL C++ headers list (copied into :attr:`~cpp_dir`) - self.base_headers = [ - "halcpp_base.h", - "halcpp_utils.h", - "field_node.h", - "reg_node.h", - "regfile_node.h", - "array_nodes.h", - "addrmap_node.h", - "arch_io.h", - ] + self.base_headers = [f for f in os.listdir(abspaths) if f.endswith(filetype[1:])] def list_files(self, top: HalAddrmap, outdir: str): """Prints the generated files to stdout (without generating the files). @@ -130,8 +124,8 @@ def export(self, regnodes = halutils.get_unique_type_nodes(top.regs + top.get_regfiles_regs()) for reg in regnodes: print(reg._node.inst_name) - print("NO UNIQUIFY") - regnodes = top.regs # + top.get_regfiles_regs() + print("\nNO UNIQUIFY:") + regnodes = top.regs + top.get_regfiles_regs() for reg in regnodes: print(reg._node.inst_name) print("+++++++++++++++++++++++++++++++") @@ -191,5 +185,5 @@ def process_template(self, context: Dict) -> str: }) # Render the C++ header text using the jinja2 template and the # specific context - cpp_header_text = env.get_template("addrmap.j2").render(context) + cpp_header_text = env.get_template("addrmap.h.j2").render(context) return cpp_header_text diff --git a/src/peakrdl_halcpp/haladdrmap.py b/src/peakrdl_halcpp/haladdrmap.py index 6dd288b..74726ef 100644 --- a/src/peakrdl_halcpp/haladdrmap.py +++ b/src/peakrdl_halcpp/haladdrmap.py @@ -187,7 +187,7 @@ def get_template_line(self) -> str: return "template " def get_cls_tmpl_spec(self, just_tmpl=False) -> str: - """Returns the HAL template parameters used for forwarding reference. + """Returns the HAL template parameters used for instantiation. The structure must matched the template returned by :func:`get_template_line`. @@ -225,7 +225,7 @@ def get_addrmaps_recursive(self) -> List['HalAddrmap']: [mySoC, myMem0, mySubsystem, myPeriph0, myPeriph1, myMem1] - where each element is a HalAddrmap object. ONly the top node + where each element is a HalAddrmap object. Only the top node insert its own reference (i.e., mySoC) at the beginning. Returns diff --git a/src/peakrdl_halcpp/halbase.py b/src/peakrdl_halcpp/halbase.py index 976a2cf..740d316 100644 --- a/src/peakrdl_halcpp/halbase.py +++ b/src/peakrdl_halcpp/halbase.py @@ -1,17 +1,17 @@ -from typing import Union, Any +from typing import Union, Any, Optional from abc import ABC, abstractmethod, abstractproperty -from systemrdl.node import Node +from systemrdl.node import Node, AddrmapNode class HalBase(ABC): """Base abstract class for all the different HAL nodes (Addrmap, Reg, Mem, and Field). - .. inheritance-diagram:: peakrdl_halcpp.haladdrmap.HalAddrmap - peakrdl_halcpp.halreg.HalReg - peakrdl_halcpp.halmem.HalMem - peakrdl_halcpp.halfield.HalField - peakrdl_halcpp.halregfile.HalRegfile + .. inheritance-diagram:: peakrdl_halcpp.haladdrmap + peakrdl_halcpp.halreg + peakrdl_halcpp.halmem + peakrdl_halcpp.halfield + peakrdl_halcpp.halregfile :top-classes: peakrdl_halcpp.halbase.HalBase :parts: 1 @@ -66,13 +66,7 @@ def cpp_access_type(self) -> str: pass def get_docstring(self) -> str: - """Converts the node description into a C++ multi-line comment. - - Returns - ------- - str - C++ multi-line comment string. - """ + """Converts the node description into a C++ multi-line comment.""" desc = "/*\n" if self._node.get_property('desc') is not None: for l in self._node.get_property('desc').splitlines(): @@ -85,6 +79,10 @@ def get_property(self, prop_name: str) -> Any: """Returns the SystemRDL node property.""" return self._node.get_property(prop_name) + def get_owning_addrmapnode(self) -> Optional[AddrmapNode]: + """Returns the AddrmapNode owning this one.""" + return self._node.owning_addrmap + def get_parent(self) -> Union['HalBase', None]: """Returns this node parent.""" return self._parent diff --git a/src/peakrdl_halcpp/halmem.py b/src/peakrdl_halcpp/halmem.py index 3a04145..500f531 100644 --- a/src/peakrdl_halcpp/halmem.py +++ b/src/peakrdl_halcpp/halmem.py @@ -25,9 +25,9 @@ def __init__(self, if parent_node is not None: for c in parent_node._node.children(): if isinstance(c, AddressableNode): - assert c == self._node, f"Addrmaps with anything else than \ - one memory node is currently not allowed, \ - it could be easily added" + assert c == self._node, (f"Addrmaps with anything else than " + "one memory node is currently not allowed, " + "it could be easily added") @property def size(self) -> int: diff --git a/src/peakrdl_halcpp/halutils.py b/src/peakrdl_halcpp/halutils.py index f2be642..2739d72 100644 --- a/src/peakrdl_halcpp/halutils.py +++ b/src/peakrdl_halcpp/halutils.py @@ -34,33 +34,29 @@ def __init__(self, ext_modules: List[str]) -> None: """ self.ext_modules = ext_modules + print(f'HALUTILS EXT: {self.ext_modules}') + def get_include_file(self, halnode: HalAddrmap) -> str: """Returns the HAL node base header file or the extended header file if the later exists. """ + print('############## GET INCL ##############') has_extern = self.has_extern(halnode) return halnode.orig_type_name + "_ext.h" if has_extern else halnode.type_name + ".h" def has_extern(self, halnode: HalAddrmap) -> bool: """Returns True if the HAL node is listed as having extended functionalities.""" if self.ext_modules is not None: + print('############## HAS EXT ##############') + print(f'{halnode.orig_type_name} in {self.ext_modules}') if halnode.orig_type_name in self.ext_modules: return True return False def get_extern(self, halnode: HalAddrmap) -> str: - """Return the ??? name of the HAL node. - - Parameters - ---------- - halnode: HalAddrmap - HAL node corresponding to a SystemRDL addrmap object. - - Returns - ------- - str: ??? - """ + """Return the ??? name of the HAL node.""" if self.has_extern(halnode): + print('############## GET EXT ##############') return halnode.orig_type_name return halnode.type_name @@ -78,22 +74,29 @@ def generate_file_header(self) -> str: comment += f"// By user: {username} at: {current_datetime}\n" return comment - def get_owning_addrmap(self, node: HalBase) -> Union[HalBase, HalAddrmap]: - """Returns the HalAddrmap enclosing this node.""" + def get_owning_haladdrmap(self, node: HalBase) -> Union[HalBase, HalAddrmap]: + """Returns the HalAddrmap object enclosing this node.""" parent_node = node.get_parent() if isinstance(parent_node, HalAddrmap): return parent_node elif parent_node is not None: - return self.get_owning_addrmap(parent_node) + return self.get_owning_haladdrmap(parent_node) else: raise ValueError(f'No HalAddrmap parent found in the hierarchy.') - def get_node_enum(self, node: HalBase): - encode = node.get_property('encode') + def get_node_enum(self, halnode: HalBase): + encode = halnode.get_property('encode') + # print('+++++++++++++++++++++++++') + # print(encode) + # print(halnode._node.list_properties()) + # print(halnode.get_owning_addrmapnode()) + # print(halnode.get_owning_addrmapnode().inst_name) + # print(halnode.get_owning_addrmapnode().orig_type_name) + # print('+++++++++++++++++++++++++') if encode is not None: - haladdrmap_node = self.get_owning_addrmap(node) + haladdrmap_node = self.get_owning_haladdrmap(halnode) if not isinstance(haladdrmap_node, HalAddrmap): - raise ValueError(f'Returned node is not an HalAddrmap object.') + raise ValueError(f'Returned halnode is not an HalAddrmap object.') # Each addrmap is enclosed in a specific namespace # Get this namespace enums and add the new ones @@ -105,7 +108,7 @@ def get_node_enum(self, node: HalBase): # Is this check really needed? # It should always be true, because if its not then the above check should not be true # TODO WHAT??? - if namespace_enums[name][-1] == node.get_property('owning_addrmap'): + if namespace_enums[name][-1] == halnode.get_owning_addrmapnode(): return False, None, None, None, None, None enum_strings = [] enum_values = [] @@ -118,7 +121,7 @@ def get_node_enum(self, node: HalBase): const_width = max(enum_values).bit_length() namespace_enums[name] = [enum_strings, enum_values, - enum_desc, const_width, node.get_property('owning_addrmap')] + enum_desc, const_width, halnode.get_owning_addrmapnode()] return True, name, enum_strings, enum_values, enum_desc, const_width return False, None, None, None, None, None diff --git a/src/peakrdl_halcpp/templates/__init__.py b/src/peakrdl_halcpp/templates/__init__.py index 29a849e..c585389 100644 --- a/src/peakrdl_halcpp/templates/__init__.py +++ b/src/peakrdl_halcpp/templates/__init__.py @@ -4,5 +4,5 @@ List of files: -------------- -- addrmap.j2 +- addrmap.h.j2 """ diff --git a/src/peakrdl_halcpp/templates/addrmap.j2 b/src/peakrdl_halcpp/templates/addrmap.h.j2 similarity index 87% rename from src/peakrdl_halcpp/templates/addrmap.j2 rename to src/peakrdl_halcpp/templates/addrmap.h.j2 index 216f96d..ce58701 100644 --- a/src/peakrdl_halcpp/templates/addrmap.j2 +++ b/src/peakrdl_halcpp/templates/addrmap.h.j2 @@ -12,13 +12,17 @@ #pragma clang diagnostic ignored "-Wundefined-var-template" #endif +{# Include the child nodes (i.e., the child addrmap ndoes) #} {% for c in halutils.get_unique_type_nodes(halnode.addrmaps) %} #include "{{ halutils.get_include_file(c) }}" {% endfor %} namespace {{ halnode.orig_type_name}}_nm { + +{# ======== 1. Generate the registers ======== #} {% for r in halutils.get_unique_type_nodes(halnode.regs + halnode.get_regfiles_regs() ) %} +{# ======== 1.a Generate the enumeration class ======== #} {% for f in r.fields %} {% set has_enum, enum_name, enum_strings, enum_values, enum_desc, const_width = halutils.get_node_enum(f) %} {% if has_enum %} @@ -31,16 +35,19 @@ public: {% endif %} {% endfor %} +{# ======== 1.b Generate the register class ======== #} {{ r.get_docstring() }} {{ r.get_template_line() }} class {{ r.type_name|upper }} : public halcpp::{{ r.cpp_access_type }}{{ r.get_cls_tmpl_spec(True) }} { public: using TYPE = {{ r.get_cls_tmpl_spec() }}; +{# Add the fields to the register #} {% for f in r.fields %} static halcpp::{{ f.cpp_access_type }}<{{ f._node.low }}, {{ f._node.high }}, TYPE> {{ f._node.inst_name }}; {% endfor %} +{# Inherit the overloaded '=' operator from the base class if register can be write from software #} {% if r._node.has_sw_writable %} using halcpp::{{ r.cpp_access_type }}{{ r.get_cls_tmpl_spec(True) }}::operator=; @@ -49,6 +56,7 @@ public: {% endfor %} +{# ======== 2. Generate the register file classes ======== #} {% for rf in halnode.regfiles %} {{ rf.get_docstring() }} {{ rf.get_template_line() }} @@ -67,6 +75,7 @@ public: }; {% endfor %} +{# ======== 3. Generate the memory classes ======== #} {% for m in halutils.get_unique_type_nodes(halnode.mems) %} {{ m.get_template_line() }} class {{ m.type_name|upper }} : public MemNode{{ m.get_cls_tmpl_spec(True) }} { @@ -75,6 +84,7 @@ class {{ m.type_name|upper }} : public MemNode{{ m.get_cls_tmpl_spec(True) }} { {% endfor %} } +{# ======== 4. Generate the top HAL class of an addrmap component ======== #} {{ halnode.get_docstring() }} {{ halnode.get_template_line() }} class {{ halnode.type_name|upper }} : public AddrmapNode { From eb11f60b0e95eb452fb2575177873d84d4eb4755 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Denkinger?= Date: Thu, 15 Feb 2024 08:25:17 +0100 Subject: [PATCH 08/23] HAL C++ header files commented. --- src/peakrdl_halcpp/include/__init__.py | 5 +- src/peakrdl_halcpp/include/addrmap_node.h | 51 ++- src/peakrdl_halcpp/include/arch_io.h | 35 +- src/peakrdl_halcpp/include/array_nodes.h | 273 ++++++++++----- src/peakrdl_halcpp/include/field_node.h | 383 +++++++++++++++------- src/peakrdl_halcpp/include/halcpp.h | 14 + src/peakrdl_halcpp/include/halcpp_base.h | 26 -- src/peakrdl_halcpp/include/halcpp_utils.h | 163 ++++++--- src/peakrdl_halcpp/include/mem_node.h | 46 +++ src/peakrdl_halcpp/include/reg_node.h | 283 +++++++++++----- src/peakrdl_halcpp/include/regfile_node.h | 41 ++- 11 files changed, 929 insertions(+), 391 deletions(-) create mode 100644 src/peakrdl_halcpp/include/halcpp.h delete mode 100644 src/peakrdl_halcpp/include/halcpp_base.h create mode 100644 src/peakrdl_halcpp/include/mem_node.h diff --git a/src/peakrdl_halcpp/include/__init__.py b/src/peakrdl_halcpp/include/__init__.py index 76f2bd9..dd7bc5a 100644 --- a/src/peakrdl_halcpp/include/__init__.py +++ b/src/peakrdl_halcpp/include/__init__.py @@ -3,12 +3,13 @@ List of files: -------------- -- addrmap_node.h +- halcpp.h - arch_io.h +- addrmap_node.h - array_nodes.h - field_node.h -- halcpp_base.h - halcpp_utils.h +- mem_node.h - regfile_node.h - reg_node.h """ diff --git a/src/peakrdl_halcpp/include/addrmap_node.h b/src/peakrdl_halcpp/include/addrmap_node.h index 08af7e0..2367c73 100644 --- a/src/peakrdl_halcpp/include/addrmap_node.h +++ b/src/peakrdl_halcpp/include/addrmap_node.h @@ -4,32 +4,63 @@ #include "arch_io.h" #include -// TODO define architecture type size, so it replaces uint32_t - +/** + * @brief A class template representing an address map node. + * + * @tparam BASE The base relative address of the address map node. + * @tparam PARENT_TYPE The parent type of the address map node. + */ template -class AddrmapNode { +class AddrmapNode +{ public: + /** + * @brief Get the absolute address of the address map node. + */ static constexpr uint32_t get_abs_addr() { return PARENT_TYPE().get_abs_addr() + BASE; } + /** + * @brief Get the value at a specified address within the address map node. + */ static inline uint32_t get(const uint32_t addr) { return PARENT_TYPE::get(addr + BASE); } - static inline void set(const uint32_t addr, uint32_t val) { + + /** + * @brief Set the value at a specified address within the address map node. + */ + static inline void set(const uint32_t addr, uint32_t val) + { PARENT_TYPE::set(addr + BASE, val); } }; -/* Specialization for the Top hierarchy addrmap - * Top node does not have a parent. - * Insted it inherits ArchIoNode, that implements memory access for architecture +/** + * @brief Specialization for the top hierarchy address map node. + * + * The top node does not have a parent. Instead, it inherits ArchIoNode, + * which implements memory access for the architecture. + * + * @tparam BASE The base address of the top hierarchy address map node. */ template -class AddrmapNode : public ArchIoNode { +class AddrmapNode : public ArchIoNode +{ public: - + /** + * @brief Get the absolute address of the top hierarchy address map node. + */ static constexpr uint32_t get_abs_addr() { return BASE; } - static inline void set(uint32_t addr, uint32_t val) { + /** + * @brief Set the value at a specified address within the top hierarchy address map node. + */ + static inline void set(uint32_t addr, uint32_t val) + { ArchIoNode::write32(addr + BASE, val); } + + /** + * @brief Get the value at a specified address within the top hierarchy address map node. + */ static inline uint32_t get(uint32_t addr) { return ArchIoNode::read32(addr + BASE); } }; diff --git a/src/peakrdl_halcpp/include/arch_io.h b/src/peakrdl_halcpp/include/arch_io.h index ead8481..f858828 100644 --- a/src/peakrdl_halcpp/include/arch_io.h +++ b/src/peakrdl_halcpp/include/arch_io.h @@ -3,17 +3,40 @@ #include - -class MemIoNode { +/** + * @brief A class representing memory I/O operations. + * + * This class provides static methods for reading and writing 32-bit values from/to memory. + */ +class MemIoNode +{ public: - static inline uint32_t read32(uint32_t addr) { return *(volatile uint32_t*)addr; } - static inline void write32(uint32_t addr, uint32_t val) { *(volatile uint32_t*)addr = val; } + /** + * @brief Read a 32-bit value from the specified memory address. + * + * @param addr The memory address from which to read. + * @return uint32_t The value read from the memory address. + */ + static inline uint32_t read32(uint32_t addr) { return *(volatile uint32_t *)addr; } + /** + * @brief Write a 32-bit value to the specified memory address. + * + * @param addr The memory address to which to write. + * @param val The value to write to the memory address. + */ + static inline void write32(uint32_t addr, uint32_t val) { *(volatile uint32_t *)addr = val; } }; -class ArchIoNode : public MemIoNode { +/** + * @brief A class representing architecture-specific I/O operations. + * + * This class inherits from MemIoNode and can be used for architecture-specific I/O operations. + */ +class ArchIoNode : public MemIoNode +{ public: - + // No additional member functions or data members are defined in this class. }; #endif // !_ARCH_IO_H_ diff --git a/src/peakrdl_halcpp/include/array_nodes.h b/src/peakrdl_halcpp/include/array_nodes.h index 95c909b..660b76a 100644 --- a/src/peakrdl_halcpp/include/array_nodes.h +++ b/src/peakrdl_halcpp/include/array_nodes.h @@ -7,112 +7,205 @@ #include #include "halcpp_utils.h" -namespace halcpp{ -// Credit for this solution goes to https://www.reddit.com/user/IyeOnline/ - -template -constexpr uint32_t map_index() +namespace halcpp { - return INDEX == -1 ? DIM-1 : INDEX; -} -template -constexpr uint32_t linear_index( std::array nd_index, std::array dimensions ) -{ - uint32_t result = 0; - uint32_t product = 1; - for ( std::size_t i=0; i + constexpr uint32_t map_index() { - result += nd_index[N-i-1] * STRIDE * product; - product *= dimensions[N-i-1]; + return IDX == -1 ? DIM - 1 : IDX; } - return result; -} - -constexpr bool valid_index( int32_t I, uint32_t E ) -{ - return I >= -1 and I < static_cast(E); -} - -template< - template typename REG_T, - uint32_t BASE, uint32_t WIDTH, uint32_t STRIDE, typename PARENT_TYPE, uint32_t ... Extents -> -class RegArrayNode -{ -private: - static constexpr uint32_t Dimensions = sizeof...(Extents); - static_assert( Dimensions > 0 ); -public : - static constexpr uint32_t stride = STRIDE; - static_assert( Dimensions > 0 ); - - template - auto at() + /** + * @brief Calculates the linear index given a multidimensional index and dimensions. + * + * @tparam STRIDE The stride value. + * @tparam N_DIM The number of dimensions. + * @param nd_index The multidimensional index. + * @param dimensions The dimensions array. + * @return constexpr uint32_t The calculated linear index. + */ + template + constexpr uint32_t linear_index(std::array nd_index, std::array dimensions) { - static_assert( sizeof...(Indices) == Dimensions ); - static_assert( ( valid_index(Indices,Extents) && ... ) ); - constexpr uint32_t offset = BASE + linear_index( std::array{ map_index() ... }, std::array{ Extents ... } ); - - return REG_T< offset, WIDTH, PARENT_TYPE >(); + uint32_t result = 0; + uint32_t product = 1; + for (std::size_t i = 0; i < N_DIM; ++i) + { + result += nd_index[N_DIM - i - 1] * STRIDE * product; + product *= dimensions[N_DIM - i - 1]; + } + return result; } - template - constexpr uint32_t get_dim(){ - static_assert(IDX < Dimensions, "Index out of bounds"); - - constexpr std::array dimensions_array = {Extents...}; - return dimensions_array[IDX]; + /** + * @brief Checks if the given index is valid for a given size. + * + * @param index The index value. + * @param size The size. + * @return constexpr bool Returns true if the index is valid, otherwise false. + */ + constexpr bool valid_index(int32_t index, uint32_t size) + { + return index >= -1 && index < static_cast(size); } -}; -template< - template typename REGFILE_T, - uint32_t BASE, uint32_t STRIDE, typename PARENT_TYPE, uint32_t ... Extents -> -class RegfileArrayNode -{ -private: - static constexpr uint32_t Dimensions = sizeof...(Extents); - static_assert( Dimensions > 0 ); -public : - static constexpr uint32_t stride = STRIDE; - - template - auto at() + /** + * @brief A class representing an array of register nodes. + * + * @tparam REG_T The type of register node template. + * @tparam BASE The base relative address. + * @tparam WIDTH The width of the register. + * @tparam STRIDE The stride value. + * @tparam PARENT_TYPE The parent type. + * @tparam Extents The extents of the array dimensions. + */ + template