From 3d1b38e426fbcb6875373e970fafa1f5a75e0103 Mon Sep 17 00:00:00 2001 From: Alex Mykyta Date: Tue, 14 Apr 2020 22:33:00 -0700 Subject: [PATCH] Refactor UVM export. v2.0 --- .gitignore | 134 +++++ .travis.yml | 38 ++ MANIFEST.in | 1 + README.md | 48 +- peakrdl/uvm/__about__.py | 1 + peakrdl/uvm/__init__.py | 3 + peakrdl/uvm/exporter.py | 393 ++++++++++++++ peakrdl/uvm/pre_export_listener.py | 43 ++ peakrdl/uvm/templates/main.sv | 27 + peakrdl/uvm/templates/top_include.svh | 7 + peakrdl/uvm/templates/top_pkg.sv | 8 + peakrdl/uvm/templates/utils.sv | 55 ++ peakrdl/uvm/templates/uvm_reg.sv | 129 +++++ peakrdl/uvm/templates/uvm_reg_block-mem.sv | 84 +++ peakrdl/uvm/templates/uvm_reg_block.sv | 100 ++++ peakrdl/uvm/templates/uvm_vreg.sv | 70 +++ ralbot/uvmgen/__about__.py | 1 - ralbot/uvmgen/__init__.py | 3 - ralbot/uvmgen/exporter.py | 341 ------------ ralbot/uvmgen/typemaps.py | 66 --- setup.py | 21 +- test/.gitignore | 3 + test/accelera-generic_example.rdl | 190 ------- test/generate_testcase_data.py | 108 ++++ test/hwa_wrapper.rdl | 85 --- test/pylint.rc | 590 +++++++++++++++++++++ test/run.sh | 45 ++ test/test_uvm_gen.py | 36 -- test/test_write_enable.rdl | 50 -- test/testcases/.gitignore | 2 + test/testcases/basic.rdl | 50 ++ test/vsim_test.sh | 19 + 32 files changed, 1953 insertions(+), 798 deletions(-) create mode 100644 .gitignore create mode 100644 .travis.yml create mode 100644 peakrdl/uvm/__about__.py create mode 100644 peakrdl/uvm/__init__.py create mode 100644 peakrdl/uvm/exporter.py create mode 100644 peakrdl/uvm/pre_export_listener.py create mode 100644 peakrdl/uvm/templates/main.sv create mode 100644 peakrdl/uvm/templates/top_include.svh create mode 100644 peakrdl/uvm/templates/top_pkg.sv create mode 100644 peakrdl/uvm/templates/utils.sv create mode 100644 peakrdl/uvm/templates/uvm_reg.sv create mode 100644 peakrdl/uvm/templates/uvm_reg_block-mem.sv create mode 100644 peakrdl/uvm/templates/uvm_reg_block.sv create mode 100644 peakrdl/uvm/templates/uvm_vreg.sv delete mode 100644 ralbot/uvmgen/__about__.py delete mode 100644 ralbot/uvmgen/__init__.py delete mode 100644 ralbot/uvmgen/exporter.py delete mode 100644 ralbot/uvmgen/typemaps.py create mode 100644 test/.gitignore delete mode 100644 test/accelera-generic_example.rdl create mode 100644 test/generate_testcase_data.py delete mode 100644 test/hwa_wrapper.rdl create mode 100644 test/pylint.rc create mode 100755 test/run.sh delete mode 100755 test/test_uvm_gen.py delete mode 100644 test/test_write_enable.rdl create mode 100644 test/testcases/.gitignore create mode 100644 test/testcases/basic.rdl create mode 100755 test/vsim_test.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6672cbe --- /dev/null +++ b/.gitignore @@ -0,0 +1,134 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# Other tool litter +*.rpt +*.wlf +**/transcript \ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..7b375c0 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,38 @@ +language: python +python: + - 3.5 + - 3.6 + - 3.7 + - 3.8 + +script: + - python -m pip install . + - cd test + - python generate_testcase_data.py basic testcases/basic.rdl + +after_success: + - coveralls + +stages: + - test + # Only execute deployment stage on tagged release commits in the form of "v1.2.3" + # and from your repository (e.g. not PRs). + # Also allows alpha/beta releases such as "v1.2.3b2" + - name: deploy + if: repo = SystemRDL/PeakRDL-uvm AND tag =~ ^v\d+\.\d+\.\w+$ + +jobs: + include: + # Run lint + - name: Lint + install: + - python -m pip install pylint + - python -m pip install . + script: pylint --rcfile test/pylint.rc peakrdl + + # Deploy source distribution + - stage: deploy + name: Deploy source distribution + install: python -m pip install twine + script: python setup.py sdist --formats=gztar + after_success: python -m twine upload --skip-existing dist/*.tar.gz diff --git a/MANIFEST.in b/MANIFEST.in index 33e0aad..4bd67e8 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1 +1,2 @@ +recursive-include peakrdl/uvm/templates * recursive-exclude test * \ No newline at end of file diff --git a/README.md b/README.md index 4d0cde4..0ae59a8 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,13 @@ -[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/ralbot-uvm.svg)](https://pypi.org/project/ralbot-uvm) +[![Build Status](https://travis-ci.org/SystemRDL/PeakRDL-uvm.svg?branch=master)](https://travis-ci.org/SystemRDL/PeakRDL-uvm) +[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/peakrdl-uvm.svg)](https://pypi.org/project/peakrdl-uvm) -# RALBot-uvm +# PeakRDL-uvm Generate UVM register model from compiled SystemRDL input ## Installing -Install from [PyPi](https://pypi.org/project/ralbot-uvm) using pip: +Install from [PyPi](https://pypi.org/project/peakrdl-uvm) using pip: - python3 -m pip install ralbot-uvm + python3 -m pip install peakrdl-uvm -------------------------------------------------------------------------------- @@ -17,7 +18,7 @@ to the exporter. ```python import sys from systemrdl import RDLCompiler, RDLCompileError -from ralbot.uvmgen import uvmGenExporter +from peakrdl.uvm import UVMExporter rdlc = RDLCompiler() @@ -27,23 +28,24 @@ try: except RDLCompileError: sys.exit(1) -file = "test.svh" -exporter = uvmGenExporter() -exporter.export(root, file) +exporter = UVMExporter() +exporter.export(root, "test.sv") ``` -------------------------------------------------------------------------------- ## Reference -### `uvmGenExporter(**kwargs)` -Constructor for the uvmGen exporter class +### `UVMExporter(**kwargs)` +Constructor for the UVM Exporter class **Optional Parameters** -* `indentLvl` - * String to use for each indent level. Defaults to three spaces. +* `user_template_dir` + * Path to a directory where user-defined template overrides are stored. +* `user_template_context` + * Additional context variables to load into the template namespace. -### `uvmGenExporter.export(node, path)` +### `UVMExporter.export(node, path, **kwargs)` Perform the export! **Parameters** @@ -51,4 +53,22 @@ Perform the export! * `node` * Top-level node to export. Can be the top-level `RootNode` or any internal `AddrmapNode`. * `path` - * Output file. Can be (dir+filename without suffix. such as "output/test_uvmgen") + * Output file. + +**Optional Parameters** + +* `export_as_package` + * If True (Default), UVM register model is exported as a SystemVerilog + package. Package name is based on the output file name. + * If False, register model is exported as an includable header. +* `reuse_class_definitions` + * If True (Default), exporter attempts to re-use class definitions + where possible. Class names are based on the lexical scope of the + original SystemRDL definitions. + * If False, class definitions are not reused. Class names are based on + the instance's hierarchical path. +* `use_uvm_factory` + * If True, class definitions and class instances are created using the + UVM factory. + * If False (Default), UVM factory is disabled. Classes are created + directly via new() constructors. diff --git a/peakrdl/uvm/__about__.py b/peakrdl/uvm/__about__.py new file mode 100644 index 0000000..8c0d5d5 --- /dev/null +++ b/peakrdl/uvm/__about__.py @@ -0,0 +1 @@ +__version__ = "2.0.0" diff --git a/peakrdl/uvm/__init__.py b/peakrdl/uvm/__init__.py new file mode 100644 index 0000000..59caa44 --- /dev/null +++ b/peakrdl/uvm/__init__.py @@ -0,0 +1,3 @@ +from .__about__ import __version__ + +from .exporter import UVMExporter diff --git a/peakrdl/uvm/exporter.py b/peakrdl/uvm/exporter.py new file mode 100644 index 0000000..3aacd3d --- /dev/null +++ b/peakrdl/uvm/exporter.py @@ -0,0 +1,393 @@ +import os +import re + +import jinja2 as jj +from systemrdl.node import RootNode, Node, RegNode, AddrmapNode, RegfileNode +from systemrdl.node import FieldNode, MemNode, AddressableNode +from systemrdl.rdltypes import AccessType, OnReadType, OnWriteType +from systemrdl import RDLWalker + +from .pre_export_listener import PreExportListener + +class UVMExporter: + + def __init__(self, **kwargs): + """ + Constructor for the UVM Exporter class + + Parameters + ---------- + user_template_dir: str + Path to a directory where user-defined template overrides are stored. + user_template_context: dict + Additional context variables to load into the template namespace. + """ + user_template_dir = kwargs.pop("user_template_dir", None) + self.user_template_context = kwargs.pop("user_template_context", dict()) + + # Check for stray kwargs + if kwargs: + raise TypeError("got an unexpected keyword argument '%s'" % list(kwargs.keys())[0]) + + if user_template_dir: + loader = jj.ChoiceLoader([ + jj.FileSystemLoader(user_template_dir), + jj.FileSystemLoader(os.path.join(os.path.dirname(__file__), "templates")), + jj.PrefixLoader({ + 'user': jj.FileSystemLoader(user_template_dir), + 'base': jj.FileSystemLoader(os.path.join(os.path.dirname(__file__), "templates")) + }, delimiter=":") + ]) + else: + loader = jj.ChoiceLoader([ + jj.FileSystemLoader(os.path.join(os.path.dirname(__file__), "templates")), + jj.PrefixLoader({ + 'base': jj.FileSystemLoader(os.path.join(os.path.dirname(__file__), "templates")) + }, delimiter=":") + ]) + + self.jj_env = jj.Environment( + loader=loader, + undefined=jj.StrictUndefined + ) + + # Define variables used during export + + # Top-level node + self.top = None + + # Dictionary of group-like nodes (addrmap, regfile) and their bus + # widths. + # key = node path + # value = max accesswidth/memwidth used in node's descendants + self.bus_width_db = {} + + # Dictionary of root-level type definitions + # key = definition type name + # value = representative object + # components, this is the original_def (which can be None in some cases) + self.namespace_db = {} + + self.reuse_class_definitions = True + + + def export(self, node: Node, path: str, **kwargs): + """ + Perform the export! + + Parameters + ---------- + node: systemrdl.Node + Top-level node to export. Can be the top-level `RootNode` or any + internal `AddrmapNode`. + path: str + Output file. + export_as_package: bool + If True (Default), UVM register model is exported as a SystemVerilog + package. Package name is based on the output file name. + + If False, register model is exported as an includable header. + reuse_class_definitions: bool + If True (Default), exporter attempts to re-use class definitions + where possible. Class names are based on the lexical scope of the + original SystemRDL definitions. + + If False, class definitions are not reused. Class names are based on + the instance's hierarchical path. + use_uvm_factory: bool + If True, class definitions and class instances are created using the + UVM factory. + + If False (Default), UVM factory is disabled. Classes are created + directly via new() constructors. + """ + export_as_package = kwargs.pop("export_as_package", True) + use_uvm_factory = kwargs.pop("use_uvm_factory", False) + self.reuse_class_definitions = kwargs.pop("reuse_class_definitions", True) + + # Check for stray kwargs + if kwargs: + raise TypeError("got an unexpected keyword argument '%s'" % list(kwargs.keys())[0]) + + # If it is the root node, skip to top addrmap + if isinstance(node, RootNode): + node = node.top + self.top = node + + # First, traverse the model and collect some information + self.bus_width_db = {} + RDLWalker().walk(self.top, PreExportListener(self)) + + context = { + 'top_node': node, + 'RegNode': RegNode, + 'RegfileNode': RegfileNode, + 'AddrmapNode': AddrmapNode, + 'MemNode': MemNode, + 'AddressableNode': AddressableNode, + 'isinstance': isinstance, + 'class_needs_definition': self._class_needs_definition, + 'get_class_name': self._get_class_name, + 'get_class_friendly_name': self._get_class_friendly_name, + 'get_inst_name': self._get_inst_name, + 'get_field_access': self._get_field_access, + 'get_array_address_offset_expr': self._get_array_address_offset_expr, + 'get_endianness': self._get_endianness, + 'get_bus_width': self._get_bus_width, + 'get_mem_access': self._get_mem_access, + 'roundup_to': self._roundup_to, + 'roundup_pow2': self._roundup_pow2, + 'use_uvm_factory': use_uvm_factory, + } + + context.update(self.user_template_context) + + if export_as_package: + context['package_name'] = self._get_package_name(path) + template = self.jj_env.get_template("top_pkg.sv") + else: + context['include_guard'] = self._get_include_guard(path) + template = self.jj_env.get_template("top_include.svh") + stream = template.stream(context) + stream.dump(path) + + + def _get_package_name(self, path: str) -> str: + s = os.path.splitext(os.path.basename(path))[0] + s = re.sub(r'[^\w]', "_", s) + return s + + + def _get_include_guard(self, path: str) -> str: + s = os.path.basename(path) + s = re.sub(r'[^\w]', "_", s).upper() + return s + + + def _get_class_name(self, node: Node) -> str: + """ + Returns the class type name. + Shall be unique enough to prevent type name collisions + """ + if self.reuse_class_definitions: + scope_path = node.inst.get_scope_path(scope_separator="__") + + if (scope_path is not None) and (node.type_name is not None): + if scope_path: + class_name = scope_path + "__" + node.type_name + else: + class_name = node.type_name + else: + # Unable to determine a reusable type name. Fall back to hierarchical path + class_name = node.get_rel_path( + self.top.parent, + hier_separator="__", array_suffix="", empty_array_suffix="" + ) + # Add prefix to prevent collision when mixing namespace methods + class_name = "xtern__" + class_name + else: + class_name = node.get_rel_path( + self.top.parent, + hier_separator="__", array_suffix="", empty_array_suffix="" + ) + + return class_name + + + def _get_class_friendly_name(self, node: Node) -> str: + """ + Returns a useful string that helps identify the class definition in + a comment + """ + if self.reuse_class_definitions: + scope_path = node.inst.get_scope_path() + + if (scope_path is not None) and (node.type_name is not None): + if scope_path: + friendly_name = scope_path + "::" + node.type_name + else: + friendly_name = node.type_name + else: + # Unable to determine a reusable type name. Fall back to hierarchical path + friendly_name = node.get_rel_path( + self.top.parent, + hier_separator="__", array_suffix="", empty_array_suffix="" + ) + else: + friendly_name = node.get_rel_path(self.top.parent) + + return type(node.inst).__name__ + " - " + friendly_name + + + def _get_inst_name(self, node: Node) -> str: + """ + Returns the class instance name + """ + return node.inst_name + + + def _class_needs_definition(self, node: Node) -> bool: + """ + Checks if the class that defines this node already exists. + If not, returns True, indicating that a definition shall be emitted. + + If returning True, then the act of calling this function also registers + the class definition into the namespace database so that future calls + for equivalent node types will return False + """ + type_name = self._get_class_name(node) + + if type_name in self.namespace_db: + obj = self.namespace_db[type_name] + + # Sanity-check for collisions + if (obj is None) or (obj is not node.inst.original_def): + raise RuntimeError("Namespace collision! Type-name generation is not robust enough to create unique names!") + + # This object likely represents the existing class definition + # Ok to omit the re-definition + return False + + # Need to emit a new definition + # First, register it in the namespace + self.namespace_db[type_name] = node.inst.original_def + return True + + + def _get_field_access(self, field: FieldNode) -> str: + """ + Get field's UVM access string + """ + sw = field.get_property("sw") + onread = field.get_property("onread") + onwrite = field.get_property("onwrite") + + if sw == AccessType.rw: + if (onwrite is None) and (onread is None): + return "RW" + elif (onread == OnReadType.rclr) and (onwrite == OnWriteType.woset): + return "W1SRC" + elif (onread == OnReadType.rclr) and (onwrite == OnWriteType.wzs): + return "W0SRC" + elif (onread == OnReadType.rclr) and (onwrite == OnWriteType.wset): + return "WSRC" + elif (onread == OnReadType.rset) and (onwrite == OnWriteType.woclr): + return "W1CRS" + elif (onread == OnReadType.rset) and (onwrite == OnWriteType.wzc): + return "W0CRS" + elif (onread == OnReadType.rset) and (onwrite == OnWriteType.wclr): + return "WCRS" + elif onwrite == OnWriteType.woclr: + return "W1C" + elif onwrite == OnWriteType.woset: + return "W1S" + elif onwrite == OnWriteType.wot: + return "W1T" + elif onwrite == OnWriteType.wzc: + return "W0C" + elif onwrite == OnWriteType.wzs: + return "W0S" + elif onwrite == OnWriteType.wzt: + return "W0T" + elif onwrite == OnWriteType.wclr: + return "WC" + elif onwrite == OnWriteType.wset: + return "WS" + elif onread == OnReadType.rclr: + return "WRC" + elif onread == OnReadType.rset: + return "WRS" + else: + return "RW" + + elif sw == AccessType.r: + if onread is None: + return "RO" + elif onread == OnReadType.rclr: + return "RC" + elif onread == OnReadType.rset: + return "RS" + else: + return "RO" + + elif sw == AccessType.w: + if onwrite is None: + return "WO" + elif onwrite == OnWriteType.wclr: + return "WOC" + elif onwrite == OnWriteType.wset: + return "WOS" + else: + return "WO" + + elif sw == AccessType.rw1: + return "W1" + + elif sw == AccessType.w1: + return "WO1" + + else: # na + return "NOACCESS" + + + def _get_mem_access(self, mem: MemNode) -> str: + sw = mem.get_property("sw") + if sw == AccessType.r: + return "R" + else: + return "RW" + + + def _get_array_address_offset_expr(self, node: AddressableNode) -> str: + """ + Returns an expression to calculate the address offset + for example, a 4-dimensional array allocated as: + [A][B][C][D] @ X += Y + results in: + X + i0*B*C*D*Y + i1*C*D*Y + i2*D*Y + i3*Y + """ + s = "'h%x" % node.raw_address_offset + if node.is_array: + for i in range(len(node.array_dimensions)): + m = node.array_stride + for j in range(i+1, len(node.array_dimensions)): + m *= node.array_dimensions[j] + s += " + i%d*'h%x" % (i, m) + return s + + + def _get_endianness(self, node: Node) -> str: + amap = node.owning_addrmap + if amap.get_property("bigendian"): + return "UVM_BIG_ENDIAN" + elif amap.get_property("littleendian"): + return "UVM_LITTLE_ENDIAN" + else: + return "UVM_NO_ENDIAN" + + + def _get_bus_width(self, node: Node) -> int: + """ + Returns group-like node's bus width (in bytes) + """ + width = self.bus_width_db[node.get_path()] + + # Divide by 8, rounded up + if width % 8: + return width // 8 + 1 + else: + return width // 8 + + + def _roundup_to(self, x: int, n: int) -> int: + """ + Round x up to the nearest n + """ + if x % n: + return (x//n + 1) * n + else: + return (x//n) * n + + + def _roundup_pow2(self, x): + return 1<<(x-1).bit_length() diff --git a/peakrdl/uvm/pre_export_listener.py b/peakrdl/uvm/pre_export_listener.py new file mode 100644 index 0000000..336dcd5 --- /dev/null +++ b/peakrdl/uvm/pre_export_listener.py @@ -0,0 +1,43 @@ + +from systemrdl import RDLListener + +class PreExportListener(RDLListener): + def __init__(self, exporter): + self.exporter = exporter + + # Max width in bits + self.max_width_stack = [] + + def enter_Addrmap(self, node): + self.enter_group(node) + + def exit_Addrmap(self, node): + self.exit_group(node) + + def enter_Regfile(self, node): + self.enter_group(node) + + def exit_Regfile(self, node): + self.exit_group(node) + + def enter_Reg(self, node): + # Update max width in stack + self.max_width_stack[-1] = max(node.get_property("accesswidth"), self.max_width_stack[-1]) + + def enter_Mem(self, node): + # Update max width in stack + self.max_width_stack[-1] = max(node.get_property("memwidth"), self.max_width_stack[-1]) + + + def enter_group(self, node): # pylint: disable=unused-argument + self.max_width_stack.append(0) + + def exit_group(self, node): + max_width = self.max_width_stack.pop() + + # Register this node in the bus_width_db + self.exporter.bus_width_db[node.get_path()] = max_width + + # Propagate max width to parent + if self.max_width_stack: + self.max_width_stack[-1] = max(max_width, self.max_width_stack[-1]) diff --git a/peakrdl/uvm/templates/main.sv b/peakrdl/uvm/templates/main.sv new file mode 100644 index 0000000..42b85c3 --- /dev/null +++ b/peakrdl/uvm/templates/main.sv @@ -0,0 +1,27 @@ +{% import 'uvm_reg.sv' as uvm_reg with context %} +{% import 'uvm_vreg.sv' as uvm_vreg with context %} +{% import 'uvm_reg_block-mem.sv' as uvm_reg_block_mem with context %} +{% import 'uvm_reg_block.sv' as uvm_reg_block with context %} + + +{% macro top() -%} + {%- for node in top_node.descendants(in_post_order=True) -%} + {{child_def(node)}} + {%- endfor -%} + {{child_def(top_node)}} +{%- endmacro %} + + +{% macro child_def(node) -%} + {%- if isinstance(node, RegNode) -%} + {%- if node.is_virtual -%} + {{uvm_vreg.class_definition(node)}} + {%- else -%} + {{uvm_reg.class_definition(node)}} + {%- endif -%} + {%- elif isinstance(node, (RegfileNode, AddrmapNode)) -%} + {{uvm_reg_block.class_definition(node)}} + {%- elif isinstance(node, MemNode) -%} + {{uvm_reg_block_mem.class_definition(node)}} + {%- endif -%} +{%- endmacro %} diff --git a/peakrdl/uvm/templates/top_include.svh b/peakrdl/uvm/templates/top_include.svh new file mode 100644 index 0000000..3429c27 --- /dev/null +++ b/peakrdl/uvm/templates/top_include.svh @@ -0,0 +1,7 @@ +{% import 'main.sv' as main with context %} +// This file was autogenerated by PeakRDL-uvm +`ifndef {{include_guard}} +`define {{include_guard}} + {{ main.top()|indent}} +`endif + diff --git a/peakrdl/uvm/templates/top_pkg.sv b/peakrdl/uvm/templates/top_pkg.sv new file mode 100644 index 0000000..9304ec7 --- /dev/null +++ b/peakrdl/uvm/templates/top_pkg.sv @@ -0,0 +1,8 @@ +{% import 'main.sv' as main with context %} +// This file was autogenerated by PeakRDL-uvm +package {{package_name}}; + `include "uvm_macros.svh" + import uvm_pkg::*; + {{ main.top()|indent}} +endpackage: {{package_name}} + diff --git a/peakrdl/uvm/templates/utils.sv b/peakrdl/uvm/templates/utils.sv new file mode 100644 index 0000000..a42ae9c --- /dev/null +++ b/peakrdl/uvm/templates/utils.sv @@ -0,0 +1,55 @@ +/* + * If node is an array, emit the array suffixes for each dimension + * for example, a 3-dimensional array: + * [2][4][6] + */ +{% macro array_inst_suffix(node) -%} + {%- if node.is_array -%} + {%- for dim in node.array_dimensions -%} + [{{dim}}] + {%- endfor -%} + {%- endif -%} +{%- endmacro %} + + +/* + * If node is an array, emit a list of iterators + * for example, a 3-dimensional array: + * i0, i1, i2 + */ +{% macro array_iterator_list(node) -%} + {%- if node.is_array -%} + {%- for dim in node.array_dimensions -%} + {{- "i%d" % loop.index0 -}} + {%- if not loop.last %}, {% endif -%} + {%- endfor -%} + {%- endif -%} +{%- endmacro %} + + +/* + * If node is an array, emit a list of array suffix iterators + * for example, a 3-dimensional array: + * [i0][i1][i2] + */ +{% macro array_iterator_suffix(node) -%} + {%- if node.is_array -%} + {%- for dim in node.array_dimensions -%} + {{- "[i%d]" % loop.index0 -}} + {%- endfor -%} + {%- endif -%} +{%- endmacro %} + + +/* + * If node is an array, emit an array suffix format string + * for example, a 3-dimensional array: + * [%0d][%0d][%0d] + */ +{% macro array_suffix_format(node) -%} + {%- if node.is_array -%} + {%- for _ in node.array_dimensions -%} + {{- "[%0d]" -}} + {%- endfor -%} + {%- endif -%} +{%- endmacro %} diff --git a/peakrdl/uvm/templates/uvm_reg.sv b/peakrdl/uvm/templates/uvm_reg.sv new file mode 100644 index 0000000..759e9ef --- /dev/null +++ b/peakrdl/uvm/templates/uvm_reg.sv @@ -0,0 +1,129 @@ +{% import 'utils.sv' as utils with context %} + +//------------------------------------------------------------------------------ +// uvm_reg definition +//------------------------------------------------------------------------------ +{% macro class_definition(node) -%} +{%- if class_needs_definition(node) %} +// {{get_class_friendly_name(node)}} +class {{get_class_name(node)}} extends uvm_reg; +{%- if use_uvm_factory %} + `uvm_object_utils({{get_class_name(node)}}) +{%- endif %} + {{child_insts(node)|indent}} + {{function_new(node)|indent}} + + {{function_build(node)|indent}} +endclass : {{get_class_name(node)}} +{% endif -%} +{%- endmacro %} + + +//------------------------------------------------------------------------------ +// Child instances +//------------------------------------------------------------------------------ +{% macro child_insts(node) -%} +{%- for field in node.fields() -%} +rand uvm_reg_field {{get_inst_name(field)}}; +{% endfor -%} +{%- endmacro %} + + +//------------------------------------------------------------------------------ +// new() function +//------------------------------------------------------------------------------ +{% macro function_new(node) -%} +function new(string name = "{{get_class_name(node)}}"); + super.new(name, {{node.get_property('regwidth')}}, UVM_NO_COVERAGE); +endfunction : new +{%- endmacro %} + + +//------------------------------------------------------------------------------ +// build() function +//------------------------------------------------------------------------------ +{% macro function_build(node) -%} +virtual function void build(); + {%- for field in node.fields() %} + {%- if use_uvm_factory %} + this.{{get_inst_name(field)}} = uvm_reg_field::type_id::create("{{get_inst_name(field)}}"); + {%- else %} + this.{{get_inst_name(field)}} = new("{{get_inst_name(field)}}"); + {%- endif %} + this.{{get_inst_name(field)}}.configure(this, {{field.width}}, {{field.lsb}}, "{{get_field_access(field)}}", {{field.is_volatile|int}}, {{"'h%x" % field.get_property('reset', default=0)}}, 1, 1, 0); + {%- endfor %} +endfunction : build +{%- endmacro %} + + +//------------------------------------------------------------------------------ +// build() actions for uvm_reg instance (called by parent) +//------------------------------------------------------------------------------ +{% macro build_instance(node) -%} +{%- if node.is_array %} +foreach(this.{{get_inst_name(node)}}[{{utils.array_iterator_list(node)}}]) begin + {%- if use_uvm_factory %} + this.{{get_inst_name(node)}}{{utils.array_iterator_suffix(node)}} = {{get_class_name(node)}}::type_id::create($sformatf("{{get_inst_name(node)}}{{utils.array_suffix_format(node)}}", {{utils.array_iterator_list(node)}})); + {%- else %} + this.{{get_inst_name(node)}}{{utils.array_iterator_suffix(node)}} = new($sformatf("{{get_inst_name(node)}}{{utils.array_suffix_format(node)}}", {{utils.array_iterator_list(node)}})); + {%- endif %} + this.{{get_inst_name(node)}}{{utils.array_iterator_suffix(node)}}.configure(this); + {{add_hdl_path_slices(node, get_inst_name(node) + utils.array_iterator_suffix(node))|trim|indent}} + this.{{get_inst_name(node)}}{{utils.array_iterator_suffix(node)}}.build(); + this.default_map.add_reg(this.{{get_inst_name(node)}}{{utils.array_iterator_suffix(node)}}, {{get_array_address_offset_expr(node)}}); +end +{%- else %} +{%- if use_uvm_factory %} +this.{{get_inst_name(node)}} = {{get_class_name(node)}}::type_id::create("{{get_inst_name(node)}}"); +{%- else %} +this.{{get_inst_name(node)}} = new("{{get_inst_name(node)}}"); +{%- endif %} +this.{{get_inst_name(node)}}.configure(this); +{{add_hdl_path_slices(node, get_inst_name(node))|trim}} +this.{{get_inst_name(node)}}.build(); +this.default_map.add_reg(this.{{get_inst_name(node)}}, {{"'h%x" % node.raw_address_offset}}); +{%- endif %} +{%- endmacro %} + +//------------------------------------------------------------------------------ +// Load HDL path slices for this reg instance +//------------------------------------------------------------------------------ +{% macro add_hdl_path_slices(node, inst_ref) -%} +{%- if node.get_property('hdl_path') %} +{{inst_ref}}.add_hdl_path_slice("{{node.get_property('hdl_path')}}", -1, -1); +{%- endif -%} + +{%- if node.get_property('hdl_path_gate') %} +{{inst_ref}}.add_hdl_path_slice("{{node.get_property('hdl_path_gate')}}", -1, -1, 0, "GATE"); +{%- endif -%} + +{%- for field in node.fields() %} +{%- if field.get_property('hdl_path_slice') is none -%} +{%- elif field.get_property('hdl_path_slice')|length == 1 %} +{{inst_ref}}.add_hdl_path_slice("{{field.get_property('hdl_path_slice')[0]}}", {{field.lsb}}, {{field.width}}); +{%- elif field.get_property('hdl_path_slice')|length == field.width %} +{%- for slice in field.get_property('hdl_path_slice') %} +{%- if field.msb > field.lsb %} +{{inst_ref}}.add_hdl_path_slice("{{slice}}", {{field.msb - loop.index0}}, 1); +{%- else %} +{{inst_ref}}.add_hdl_path_slice("{{slice}}", {{field.msb + loop.index0}}, 1); +{%- endif %} +{%- endfor %} +{%- endif %} +{%- endfor -%} + +{%- for field in node.fields() %} +{%- if field.get_property('hdl_path_gate_slice') is none -%} +{%- elif field.get_property('hdl_path_gate_slice')|length == 1 %} +{{inst_ref}}.add_hdl_path_slice("{{field.get_property('hdl_path_gate_slice')[0]}}", {{field.lsb}}, {{field.width}}, 0, "GATE"); +{%- elif field.get_property('hdl_path_gate_slice')|length == field.width %} +{%- for slice in field.get_property('hdl_path_gate_slice') %} +{%- if field.msb > field.lsb %} +{{inst_ref}}.add_hdl_path_slice("{{slice}}", {{field.msb - loop.index0}}, 1, 0, "GATE"); +{%- else %} +{{inst_ref}}.add_hdl_path_slice("{{slice}}", {{field.msb + loop.index0}}, 1, 0, "GATE"); +{%- endif %} +{%- endfor %} +{%- endif %} +{%- endfor %} +{%- endmacro %} diff --git a/peakrdl/uvm/templates/uvm_reg_block-mem.sv b/peakrdl/uvm/templates/uvm_reg_block-mem.sv new file mode 100644 index 0000000..fb67211 --- /dev/null +++ b/peakrdl/uvm/templates/uvm_reg_block-mem.sv @@ -0,0 +1,84 @@ +{% import 'utils.sv' as utils with context %} + +//------------------------------------------------------------------------------ +// uvm_reg_block definition for memories +//------------------------------------------------------------------------------ +{% macro class_definition(node) -%} +{%- if class_needs_definition(node) %} +// {{get_class_friendly_name(node)}} +class {{get_class_name(node)}} extends uvm_reg_block; +{%- if use_uvm_factory %} + `uvm_object_utils({{get_class_name(node)}}) +{%- endif %} + rand uvm_mem m_mem; + {{child_insts(node)|indent}} + {{function_new(node)|indent}} + + {{function_build(node)|indent}} +endclass : {{get_class_name(node)}} +{% endif -%} +{%- endmacro %} + + +//------------------------------------------------------------------------------ +// Child instances +//------------------------------------------------------------------------------ +{% macro child_insts(node) -%} +{%- for child in node.children() if isinstance(child, RegNode) -%} +rand {{get_class_name(child)}} {{get_inst_name(child)}}; +{% endfor -%} +{%- endmacro %} + + +//------------------------------------------------------------------------------ +// new() function +//------------------------------------------------------------------------------ +{% macro function_new(node) -%} +function new(string name = "{{get_class_name(node)}}"); + super.new(name); +endfunction : new +{%- endmacro %} + + +//------------------------------------------------------------------------------ +// build() function +//------------------------------------------------------------------------------ +{% macro function_build(node) -%} +virtual function void build(); + this.default_map = create_map("reg_map", 0, {{roundup_to(node.get_property('memwidth'), 8) / 8}}, {{get_endianness(node)}}); + this.m_mem = new("m_mem", {{node.get_property('mementries')}}, {{node.get_property('memwidth')}}, "{{get_mem_access(node)}}"); + this.m_mem.configure(this); + this.default_map.add_mem(this.m_mem, 0); + {%- for child in node.children() if isinstance(child, RegNode) -%} + {{uvm_vreg.build_instance(child)|indent}} + {%- endfor %} +endfunction : build +{%- endmacro %} + + +//------------------------------------------------------------------------------ +// build() actions for uvm_reg_block instance (called by parent) +//------------------------------------------------------------------------------ +{% macro build_instance(node) -%} +{%- if node.is_array %} +foreach(this.{{get_inst_name(node)}}[{{utils.array_iterator_list(node)}}]) begin + {%- if use_uvm_factory %} + this.{{get_inst_name(node)}}{{utils.array_iterator_suffix(node)}} = {{get_class_name(node)}}::type_id::create($sformatf("{{get_inst_name(node)}}{{utils.array_suffix_format(node)}}", {{utils.array_iterator_list(node)}})); + {%- else %} + this.{{get_inst_name(node)}}{{utils.array_iterator_suffix(node)}} = new($sformatf("{{get_inst_name(node)}}{{utils.array_suffix_format(node)}}", {{utils.array_iterator_list(node)}})); + {%- endif %} + this.{{get_inst_name(node)}}{{utils.array_iterator_suffix(node)}}.configure(this); + this.{{get_inst_name(node)}}{{utils.array_iterator_suffix(node)}}.build(); + this.default_map.add_submap(this.{{get_inst_name(node)}}{{utils.array_iterator_suffix(node)}}.default_map, {{get_array_address_offset_expr(node)}}); +end +{%- else %} +{%- if use_uvm_factory %} +this.{{get_inst_name(node)}} = {{get_class_name(node)}}::type_id::create("{{get_inst_name(node)}}"); +{%- else %} +this.{{get_inst_name(node)}} = new("{{get_inst_name(node)}}"); +{%- endif %} +this.{{get_inst_name(node)}}.configure(this); +this.{{get_inst_name(node)}}.build(); +this.default_map.add_submap(this.{{get_inst_name(node)}}.default_map, {{"'h%x" % node.raw_address_offset}}); +{%- endif %} +{%- endmacro %} diff --git a/peakrdl/uvm/templates/uvm_reg_block.sv b/peakrdl/uvm/templates/uvm_reg_block.sv new file mode 100644 index 0000000..ac8a538 --- /dev/null +++ b/peakrdl/uvm/templates/uvm_reg_block.sv @@ -0,0 +1,100 @@ +{% import 'utils.sv' as utils with context %} + +//------------------------------------------------------------------------------ +// uvm_reg_block definition +//------------------------------------------------------------------------------ +{% macro class_definition(node) -%} +{%- if class_needs_definition(node) %} +// {{get_class_friendly_name(node)}} +class {{get_class_name(node)}} extends uvm_reg_block; +{%- if use_uvm_factory %} + `uvm_object_utils({{get_class_name(node)}}) +{%- endif %} + {{child_insts(node)|indent}} + {{function_new(node)|indent}} + + {{function_build(node)|indent}} +endclass : {{get_class_name(node)}} +{% endif -%} +{%- endmacro %} + + +//------------------------------------------------------------------------------ +// Child instances +//------------------------------------------------------------------------------ +{% macro child_insts(node) -%} +{%- for child in node.children() if isinstance(child, AddressableNode) -%} +rand {{get_class_name(child)}} {{get_inst_name(child)}}{{utils.array_inst_suffix(child)}}; +{% endfor -%} +{%- endmacro %} + + +//------------------------------------------------------------------------------ +// new() function +//------------------------------------------------------------------------------ +{% macro function_new(node) -%} +function new(string name = "{{get_class_name(node)}}"); + super.new(name); +endfunction : new +{%- endmacro %} + + +//------------------------------------------------------------------------------ +// build() function +//------------------------------------------------------------------------------ +{% macro function_build(node) -%} +virtual function void build(); + this.default_map = create_map("reg_map", 0, {{get_bus_width(node)}}, {{get_endianness(node)}}); + {%- for child in node.children() -%} + {%- if isinstance(child, RegNode) -%} + {{uvm_reg.build_instance(child)|indent}} + {%- elif isinstance(child, (RegfileNode, AddrmapNode)) -%} + {{build_instance(child)|indent}} + {%- elif isinstance(child, MemNode) -%} + {{uvm_reg_block_mem.build_instance(child)|indent}} + {%- endif -%} + {%- endfor %} +endfunction : build +{%- endmacro %} + + +//------------------------------------------------------------------------------ +// build() actions for uvm_reg_block instance (called by parent) +//------------------------------------------------------------------------------ +{% macro build_instance(node) -%} +{%- if node.is_array %} +foreach(this.{{get_inst_name(node)}}[{{utils.array_iterator_list(node)}}]) begin + {%- if use_uvm_factory %} + this.{{get_inst_name(node)}}{{utils.array_iterator_suffix(node)}} = {{get_class_name(node)}}::type_id::create($sformatf("{{get_inst_name(node)}}{{utils.array_suffix_format(node)}}", {{utils.array_iterator_list(node)}})); + {%- else %} + this.{{get_inst_name(node)}}{{utils.array_iterator_suffix(node)}} = new($sformatf("{{get_inst_name(node)}}{{utils.array_suffix_format(node)}}", {{utils.array_iterator_list(node)}})); + {%- endif %} + {%- if node.get_property('hdl_path') %} + this.{{get_inst_name(node)}}{{utils.array_iterator_suffix(node)}}.configure(this, "{{node.get_property('hdl_path')}}"); + {%- else %} + this.{{get_inst_name(node)}}{{utils.array_iterator_suffix(node)}}.configure(this); + {%- endif %} + {%- if node.get_property('hdl_path_gate') %} + this.{{get_inst_name(node)}}{{utils.array_iterator_suffix(node)}}.add_hdl_path("{{node.get_property('hdl_path_gate')}}", "GATE"); + {%- endif %} + this.{{get_inst_name(node)}}{{utils.array_iterator_suffix(node)}}.build(); + this.default_map.add_submap(this.{{get_inst_name(node)}}{{utils.array_iterator_suffix(node)}}.default_map, {{get_array_address_offset_expr(node)}}); +end +{%- else %} +{%- if use_uvm_factory %} +this.{{get_inst_name(node)}} = {{get_class_name(node)}}::type_id::create("{{get_inst_name(node)}}"); +{%- else %} +this.{{get_inst_name(node)}} = new("{{get_inst_name(node)}}"); +{%- endif %} +{%- if node.get_property('hdl_path') %} +this.{{get_inst_name(node)}}.configure(this, "{{node.get_property('hdl_path')}}"); +{%- else %} +this.{{get_inst_name(node)}}.configure(this); +{%- endif %} +{%- if node.get_property('hdl_path_gate') %} +this.{{get_inst_name(node)}}.add_hdl_path("{{node.get_property('hdl_path_gate')}}", "GATE"); +{%- endif %} +this.{{get_inst_name(node)}}.build(); +this.default_map.add_submap(this.{{get_inst_name(node)}}.default_map, {{"'h%x" % node.raw_address_offset}}); +{%- endif %} +{%- endmacro %} diff --git a/peakrdl/uvm/templates/uvm_vreg.sv b/peakrdl/uvm/templates/uvm_vreg.sv new file mode 100644 index 0000000..67e0b10 --- /dev/null +++ b/peakrdl/uvm/templates/uvm_vreg.sv @@ -0,0 +1,70 @@ +{% import 'utils.sv' as utils with context %} + +//------------------------------------------------------------------------------ +// uvm_vreg definition +//------------------------------------------------------------------------------ +{% macro class_definition(node) -%} +{%- if class_needs_definition(node) %} +// {{get_class_friendly_name(node)}} +class {{get_class_name(node)}} extends uvm_vreg; +{%- if use_uvm_factory %} + `uvm_object_utils({{get_class_name(node)}}) +{%- endif %} + {{child_insts(node)|indent}} + {{function_new(node)|indent}} + + {{function_build(node)|indent}} +endclass : {{get_class_name(node)}} +{% endif -%} +{%- endmacro %} + + +//------------------------------------------------------------------------------ +// Child instances +//------------------------------------------------------------------------------ +{% macro child_insts(node) -%} +{%- for field in node.fields() -%} +rand uvm_vreg_field {{get_inst_name(field)}}; +{% endfor -%} +{%- endmacro %} + + +//------------------------------------------------------------------------------ +// new() function +//------------------------------------------------------------------------------ +{% macro function_new(node) -%} +function new(string name = "{{get_class_name(node)}}"); + super.new(name, {{node.get_property('regwidth')}}); +endfunction : new +{%- endmacro %} + + +//------------------------------------------------------------------------------ +// build() function +//------------------------------------------------------------------------------ +{% macro function_build(node) -%} +virtual function void build(); + {%- for field in node.fields() %} + {%- if use_uvm_factory %} + this.{{get_inst_name(field)}} = uvm_vreg_field::type_id::create("{{get_inst_name(field)}}"); + {%- else %} + this.{{get_inst_name(field)}} = new("{{get_inst_name(field)}}"); + {%- endif %} + this.{{get_inst_name(field)}}.configure(this, {{field.width}}, {{field.lsb}}); + {%- endfor %} +endfunction : build +{%- endmacro %} + + +//------------------------------------------------------------------------------ +// build() actions for uvm_reg instance (called by parent) +//------------------------------------------------------------------------------ +{% macro build_instance(node) -%} +{%- if use_uvm_factory %} +this.{{get_inst_name(node)}} = {{get_class_name(node)}}::type_id::create("{{get_inst_name(node)}}"); +{%- else %} +this.{{get_inst_name(node)}} = new("{{get_inst_name(node)}}"); +{%- endif %} +this.{{get_inst_name(node)}}.configure(this, this.m_mem, {{node.inst.n_elements}}); +this.{{get_inst_name(node)}}.build(); +{%- endmacro %} diff --git a/ralbot/uvmgen/__about__.py b/ralbot/uvmgen/__about__.py deleted file mode 100644 index a82b376..0000000 --- a/ralbot/uvmgen/__about__.py +++ /dev/null @@ -1 +0,0 @@ -__version__ = "1.1.1" diff --git a/ralbot/uvmgen/__init__.py b/ralbot/uvmgen/__init__.py deleted file mode 100644 index cab4dd8..0000000 --- a/ralbot/uvmgen/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from .__about__ import __version__ - -from .exporter import uvmGenExporter diff --git a/ralbot/uvmgen/exporter.py b/ralbot/uvmgen/exporter.py deleted file mode 100644 index ed2d242..0000000 --- a/ralbot/uvmgen/exporter.py +++ /dev/null @@ -1,341 +0,0 @@ -import os -from systemrdl.node import AddressableNode, RootNode -from systemrdl.node import AddrmapNode, MemNode -from systemrdl.node import RegNode, RegfileNode, FieldNode -from . import typemaps -#=============================================================================== -class uvmGenExporter: - def __init__(self, **kwargs): - self.indent = kwargs.pop("indentLvl", " ") - self.uvmRegContent = list() - self.uvmMemContent = list() - self.uvmRegBlockContent = list() - self._max_width = None - - # Check for stray kwargs - if kwargs: - raise TypeError("got an unexpected keyword argument '%s'" % list(kwargs.keys())[0]) - - self.filename = "" - self.dirname = "." - self.isSwReadable = True - self.isSwWriteable = True - self.isRclr = False - self.isRset = False - self.isWoset = False - self.isWoclr = False - - #--------------------------------------------------------------------------- - def export(self, node, path): - # Make sure output directory structure exists - if os.path.dirname(path): - os.makedirs(os.path.dirname(path), exist_ok=True) - self.dirname = os.path.split(path)[0] - filename = os.path.basename(path) - filename = os.path.splitext(filename)[0] - self.filename = filename + "_uvmreg.sv" - filename = self.filename.upper().replace('.', '_') - self.genDefineMacro(filename) - - # If it is the root node, skip to top addrmap - if isinstance(node, RootNode): - node = node.top - - if not isinstance(node, AddrmapNode): - raise TypeError("'node' argument expects type AddrmapNode. Got '%s'" % type(node).__name__) - - # Determine if top-level node should be exploded across multiple - # addressBlock groups - explode = False - - if isinstance(node, AddrmapNode): - addrblockable_children = 0 - non_addrblockable_children = 0 - - for child in node.children(): - if not isinstance(child, AddressableNode): - continue - - if isinstance(child, (AddrmapNode, MemNode)) and not child.is_array: - addrblockable_children += 1 - else: - non_addrblockable_children += 1 - - if (non_addrblockable_children == 0) and (addrblockable_children >= 1): - explode = True - - # Do the export! - if explode: - # top-node becomes the memoryMap - # Top-node's children become their own addressBlocks - for child in node.children(): - if not isinstance(child, AddressableNode): - continue - self.add_addressBlock(child) - else: - # Not exploding apart the top-level node - # Wrap it in a dummy memoryMap that bears it's name - # Export top-level node as a single addressBlock - self.add_addressBlock(node) - - # Write out UVM RegModel file - self.uvmRegBlockContent.append("`endif") - with open(os.path.join(self.dirname, self.filename), "w") as f: - f.write('\n'.join(self.uvmRegContent + self.uvmMemContent + self.uvmRegBlockContent)) - #--------------------------------------------------------------------------- - def genDefineMacro(self, tag): - self.uvmRegContent.append("`ifndef __%s__" % tag) - self.uvmRegContent.append("`define __%s__" % tag) - #--------------------------------------------------------------------------- - def add_uvm_block_content(self, indentLvl="", content=""): - self.uvmRegBlockContent.append(indentLvl + content) - #--------------------------------------------------------------------------- - def add_uvm_reg_content(self, indentLvl="", content=""): - self.uvmRegContent.append(indentLvl + content) - #--------------------------------------------------------------------------- - def add_uvm_mem_content(self, indentLvl="", content=""): - self.uvmMemContent.append(indentLvl + content) - #--------------------------------------------------------------------------- - def add_addressBlock(self, node): - self._max_width = None - regNode = list() - regBlockNode = list() - memNode = list() - - for child in node.children(): - if isinstance(child, RegNode): - self.add_register(node, child) - regNode.append(child); - elif isinstance(child, (AddrmapNode, RegfileNode)): - self.add_registerFile(node, child) - regBlockNode.append(child) - elif isinstance(child, MemNode): - self.add_memFile(node, child) - memNode.append(child) - - # Width should be known by now - # If mem, and width isn't known, check memwidth - if isinstance(node, MemNode) and (self._max_width is None): - self._max_width = node.get_property("memwidth") - - allNodes = regNode + regBlockNode + memNode - self.add_uvm_block_content(content="class %s extends uvm_reg_block;" % node.inst_name) - self.add_variable_declare_func(node, allNodes) - self.add_uvm_block_content(''' - `uvm_object_utils(%s) - function new(string name = "%s"); - super.new(name, UVM_NO_COVERAGE); - endfunction ''' %(node.inst_name, node.inst_name)) - self.add_build_func(node, allNodes) - #--------------------------------------------------------------------------- - def add_registerFile(self, parent, node): - self._max_width = None - regNode = list() - regBlockNode = list() - memNode = list() - for child in node.children(): - if isinstance(child, RegNode): - self.add_register(node, child) - regNode.append(child); - elif isinstance(child, (AddrmapNode, RegfileNode)): - self.add_registerFile(node, child) - regBlockNode.append(child) - elif isinstance(child, MemNode): - self.add_memFile(node, child) - memNode.append(child) - - allNodes = regNode + regBlockNode + memNode - self.add_uvm_block_content(content="class %s extends uvm_reg_block;" % self.get_class_name(parent, node)) - self.add_variable_declare_func(node, allNodes) - self.add_uvm_block_content(''' - `uvm_object_utils(%s) - function new(string name = "%s"); - super.new(name, UVM_NO_COVERAGE); - endfunction ''' %(self.get_class_name(parent, node), self.get_class_name(parent, node))) - self.add_build_func(node, allNodes) - - #--------------------------------------------------------------------------- - def add_register(self, parent, node): - if self._max_width is None: - self._max_width = max(node.get_property("accesswidth"), node.get_property("regwidth")) - else: - self._max_width = max(node.get_property("accesswidth"), node.get_property("regwidth"), self._max_width) - - self.add_uvm_reg_content(content = "class %s extends uvm_reg;" % self.get_class_name(parent, node)) - - for field in node.fields(): - self.add_uvm_reg_content(self.indent, "rand uvm_reg_field %s;" % field.inst_name); - - self.add_uvm_reg_content(self.indent, "") - self.add_uvm_reg_content(self.indent, "virtual function void build();") - for field in node.fields(): - isRand = "1" if field.is_sw_writable else "0" - isVolatile = "1" if field.is_volatile else "0" - self.setSwRdWrProperty(field) - self.add_uvm_reg_content(self.indent*2, "%s = uvm_reg_field::type_id::create(\"%s\", null, get_full_name());" % (field.inst_name, field.inst_name)) - self.add_uvm_reg_content(self.indent*2, "%s.configure(this, %0d, %0d, \"%s\", %s, %s, %s, %s);" %(field.inst_name, field.width, field.low, self.getFieldAccessType(field), isVolatile, self.resetStr(field), isRand, self.isOnlyField(node))) - self.add_uvm_reg_content(self.indent, "endfunction") - - self.add_uvm_reg_content(''' - function new(string name = "%s"); - super.new(name, %0d, UVM_NO_COVERAGE); - endfunction - - `uvm_object_utils(%s) -endclass\n''' %(self.get_class_name(parent, node), node.get_property("regwidth"), self.get_class_name(parent, node))) - #--------------------------------------------------------------------------- - # generate uvm reg model content function - #--------------------------------------------------------------------------- - def add_variable_declare_func(self, parent, allNodes): - for child in allNodes: - if child.is_array: - for dim in child.array_dimensions: - self.add_uvm_block_content(self.indent, "rand %s %s[%0d];" %(self.get_class_name(parent, child), child.inst_name, dim)); - else: - self.add_uvm_block_content(self.indent, "rand %s %s;" %(self.get_class_name(parent, child), child.inst_name)); - - def add_build_func(self, parentNode, allNodes): - self.add_uvm_block_content(self.indent, "") - self.add_uvm_block_content(self.indent, "virtual function void build();") - self.add_uvm_block_content(self.indent*2, "default_map = create_map(\"default_map\", `UVM_REG_ADDR_WIDTH'h0, %0d, UVM_LITTLE_ENDIAN, 1);" % (self._max_width/8)) - for child in allNodes: - if isinstance(child, RegNode): - self.add_build_reg_content(parentNode, child) - elif isinstance(child, (AddrmapNode, RegfileNode)): - self.add_build_block_content(parentNode, child) - elif isinstance(child, MemNode): - self.add_build_mem_content(parentNode, child) - - self.add_uvm_block_content(self.indent, "endfunction") - self.add_uvm_block_content(content="endclass\n") - - def add_build_reg_content(self, parentNode, child): - if child.is_array: - self.add_uvm_block_content(self.indent*2, "foreach (this.%s[i]) begin" %child.inst_name) - self.add_uvm_block_content(self.indent*3, "%s[i] = %s::type_id::create($psprintf(\"%s[%%d]\",i));" % (child.inst_name, self.get_class_name(parentNode, child), child.inst_name)) - self.add_uvm_block_content(self.indent*3, "%s[i].configure(this, null, \"%s[i]\");" % (child.inst_name, child.inst_name)) - self.add_uvm_block_content(self.indent*3, "%s[i].build();" %(child.inst_name)) - self.add_uvm_block_content(self.indent*3, "default_map.add_reg(%s[i], `UVM_REG_ADDR_WIDTH'h%x+i*`UVM_REG_ADDR_WIDTH'h%x, \"%s\", 0);" % (child.inst_name, child.raw_address_offset, child.array_stride, self.getRegAccessType(child))) - self.add_uvm_block_content(self.indent*2, "end") - else: - self.add_uvm_block_content(self.indent*2, "%s = %s::type_id::create(\"%s\");" % (child.inst_name, self.get_class_name(parentNode, child), child.inst_name)) - self.add_uvm_block_content(self.indent*2, "%s.configure(this, null, \"%s\");" % (child.inst_name, child.inst_name)) - self.add_uvm_block_content(self.indent*2, "%s.build();" %(child.inst_name)) - self.add_uvm_block_content(self.indent*2, "default_map.add_reg(%s, `UVM_REG_ADDR_WIDTH'h%x, \"%s\", 0);" % (child.inst_name, child.address_offset, self.getRegAccessType(child))) - - def add_build_block_content(self, parentNode, child): - if child.is_array: - self.add_uvm_block_content(self.indent*2, "foreach (this.%s[i]) begin" %child.inst_name) - self.add_uvm_block_content(self.indent*3, "%s[i] = %s::type_id::create($psprintf(\"%s[%%d]\",i), , get_full_name());" % (child.inst_name, self.get_class_name(parentNode, child), child.inst_name)) - self.add_uvm_block_content(self.indent*3, "%s[i].configure(this, \"\");" % (child.inst_name)) - self.add_uvm_block_content(self.indent*3, "%s[i].build();" %(child.inst_name)) - self.add_uvm_block_content(self.indent*3, "default_map.add_submap(%s[i].default_map, `UVM_REG_ADDR_WIDTH'h%x+i*`UVM_REG_ADDR_WIDTH'h%x);" % (child.inst_name, child.raw_address_offset, child.array_stride)) - self.add_uvm_block_content(self.indent*2, "end") - else: - self.add_uvm_block_content(self.indent*2, "%s = %s::type_id::create(\"%s\",,get_full_name());" %(child.inst_name, self.get_class_name(parentNode, child), child.inst_name)) - self.add_uvm_block_content(self.indent*2, "%s.configure(this, \"\");" %(child.inst_name)) - self.add_uvm_block_content(self.indent*2, "%s.build();" %(child.inst_name)) - self.add_uvm_block_content(self.indent*2, "default_map.add_submap(%s.default_map, `UVM_REG_ADDR_WIDTH'h%x);" % (child.inst_name, child.address_offset)) - - def add_build_mem_content(self, parentNode, child): - self.add_uvm_block_content(self.indent*2, "%s = %s::type_id::create(\"%s\",,get_full_name());" % (child.inst_name, self.get_class_name(parentNode, child), child.inst_name)) - self.add_uvm_block_content(self.indent*2, "%s.configure(this, \"%s\");" %(child.inst_name, child.inst_name)) - self.add_uvm_block_content(self.indent*2, "default_map.add_mem(%s.default_map, `UVM_REG_ADDR_WIDTH'h%x, \"%s\");" % (child.inst_name, child.address_offset, typemaps.access_from_sw(child.get_property("sw")))) - - def add_memFile(self, parent, node): - self.add_uvm_mem_content(content = "class %s extends uvm_reg;" % self.get_class_name(parent, node)) - self.add_uvm_mem_content(''' - function new(string name = \"%s\"); - super.new(name, 'h%x, %0d, "%s", UVM_NO_COVERAGE); - endfunction - - `uvm_object_utils(%s) -endclass\n''' % (self.get_class_name(parent, node), node.get_property("mementries"), node.get_property("memwidth"), typemaps.access_from_sw(node.get_property("sw")), self.get_class_name(parent, node))) - - - #--------------------------------------------------------------------------- - # utilities function - #--------------------------------------------------------------------------- - #--------------------------------------------------------------------------- - def get_class_name(self, parent, node): - regBlockName = parent.inst_name - regName = node.inst_name - prefixString = "reg_" - if isinstance(node, RegNode): - prefixString = "reg_" - elif isinstance(node, (AddrmapNode, RegfileNode)): - prefixString = "block_" - elif isinstance(node, MemNode): - prefixString = "mem_" - return prefixString + regBlockName.lower() + "_" + regName.lower() - - def resetStr(self, node): - reset = node.get_property("reset") - if reset is not None: - return "'h%x, " % reset + "1" - else: - return "0, 0" - - def isOnlyField(self, node): - i = 0; - for field in node.fields(): - i += 1; - return "1" if (i == 1) else "0" - - #set other sw read/write properties (these override sw= setting) - def setSwRdWrProperty(self, node): - self.isRclr = False - self.isRset = False - self.isWoclr = False - self.isWoset = False - if node.get_property("rclr"): - self.isSwReadable = True - self.isRclr = True - elif node.get_property("rset"): - self.isSwReadable = True - self.isRset = True - elif node.get_property("woclr"): - self.isSwWriteable = True - self.isWoclr = True - elif node.get_property("woset"): - self.isSwWriteable = True - self.isWoset = True - - def getFieldAccessType(self, node): - accessMode = "RO" - if self.isRclr: - if self.isWoset: - accessMode = "W1SRC" - elif node.is_sw_writable: - accessMode = "WRC" - else: - accessMode = "RC" - elif self.isRset: - if self.isWoclr: - accessMode = "W1CRS" - elif node.is_sw_writable: - accessMode = "WRS" - else: - accessMode = "RS" - else: - if self.isWoclr: - accessMode = "W1C" - elif self.isWoset: - accessMode = "W1S" - elif node.is_sw_writable: - if node.is_sw_readable: - accessMode = "RW" - else: - accessMode = "WO" - return accessMode - - def getRegAccessType(self, node): - accessMode = "RO" - if node.has_sw_writable: - if node.has_sw_readable: - accessMode = "RW" - else: - accessMode = "WO" - else: - accessMode = "RO" - return accessMode diff --git a/ralbot/uvmgen/typemaps.py b/ralbot/uvmgen/typemaps.py deleted file mode 100644 index 1dbb467..0000000 --- a/ralbot/uvmgen/typemaps.py +++ /dev/null @@ -1,66 +0,0 @@ -from systemrdl import rdltypes - -# sw <-->:access -ACCESS_MAP = [ - (rdltypes.AccessType.r, "RO"), - (rdltypes.AccessType.rw, "RW"), - (rdltypes.AccessType.w, "WO"), - (rdltypes.AccessType.w1, "W1"), -] - -def access_from_sw(sw): - for sw_entry, access_entry in ACCESS_MAP: - if sw == sw_entry: - return access_entry - return None - -def sw_from_access(access): - for sw_entry, access_entry in ACCESS_MAP: - if access == access_entry: - return sw_entry - return None - -#------------------------------------------------------------------------------- -# onwrite <-->:modifiedWriteValue -MWV_MAP = [ - (rdltypes.OnWriteType.wclr, "WC"), - (rdltypes.OnWriteType.woclr, "W1C"), - (rdltypes.OnWriteType.woset, "W1S"), - (rdltypes.OnWriteType.wot, "W1T"), - (rdltypes.OnWriteType.wset, "WS"), - (rdltypes.OnWriteType.wzc, "W0C"), - (rdltypes.OnWriteType.wzs, "W0S"), - (rdltypes.OnWriteType.wzt, "W0T"), -] - -def mwv_from_onwrite(onwrite): - for onwrite_entry, mwv_entry in MWV_MAP: - if onwrite == onwrite_entry: - return mwv_entry - return None - -def onwrite_from_mwv(mwv): - for onwrite_entry, mwv_entry in MWV_MAP: - if mwv == mwv_entry: - return onwrite_entry - return None - -#------------------------------------------------------------------------------- -# onread <-->:readAction -READ_ACTION_MAP = [ - (rdltypes.OnReadType.rclr, "RC"), - (rdltypes.OnReadType.rset, "RS"), -] - -def readaction_from_onread(onread): - for onread_entry, read_action_entry in READ_ACTION_MAP: - if onread == onread_entry: - return read_action_entry - return None - -def onread_from_readaction(readaction): - for onread_entry, read_action_entry in READ_ACTION_MAP: - if readaction == read_action_entry: - return onread_entry - return None - diff --git a/setup.py b/setup.py index 24076ff..870a16a 100644 --- a/setup.py +++ b/setup.py @@ -5,31 +5,29 @@ long_description = fh.read() -with open(os.path.join("ralbot/uvmgen", "__about__.py")) as f: +with open(os.path.join("peakrdl/uvm", "__about__.py")) as f: v_dict = {} exec(f.read(), v_dict) version = v_dict['__version__'] setuptools.setup( - name="ralbot-uvm", + name="peakrdl-uvm", version=version, - author="Jude Zhang", - author_email="564193687@qq.com", + author="Alex Mykyta, Jude Zhang", description="Generate UVM register model from compiled SystemRDL input", long_description=long_description, long_description_content_type="text/markdown", - url="https://github.com/SystemRDL/RALBot-uvm", - packages=['ralbot.uvmgen'], + url="https://github.com/SystemRDL/PeakRDL-uvm", + packages=['peakrdl.uvm'], include_package_data=True, - python_requires='>=3.4', install_requires=[ - "systemrdl-compiler>=1.5.0", + "systemrdl-compiler>=1.12.0", + "jinja2", ], classifiers=( "Development Status :: 5 - Production/Stable", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", @@ -41,8 +39,7 @@ "Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)", ), project_urls={ - #"Documentation": "TBD", - "Source": "https://github.com/SystemRDL/RALBot-uvm", - "Tracker": "https://github.com/SystemRDL/RALBot-uvm/issues" + "Source": "https://github.com/SystemRDL/PeakRDL-uvm", + "Tracker": "https://github.com/SystemRDL/PeakRDL-uvm/issues" }, ) diff --git a/test/.gitignore b/test/.gitignore new file mode 100644 index 0000000..9e9c9dd --- /dev/null +++ b/test/.gitignore @@ -0,0 +1,3 @@ +transcript +work +*.wlf \ No newline at end of file diff --git a/test/accelera-generic_example.rdl b/test/accelera-generic_example.rdl deleted file mode 100644 index 12eec80..0000000 --- a/test/accelera-generic_example.rdl +++ /dev/null @@ -1,190 +0,0 @@ -// ============================================================================ -// -// Program : generic_example.rdl -// Language : Register Description Language (RDL) -// Purpose : This is a generic example designed to show a number of the -// RDL Language Features... -// -// ============================================================================ - -// Revision: $Revision:$ -// Date: $Date:$ -// -// Copyright (c) 2009 The SPIRIT Consortium. -// -// This work forms part of a deliverable of The SPIRIT Consortium. -// -// Use of these materials are governed by the legal terms and conditions -// outlined in the disclaimer available from www.spiritconsortium.org. -// -// This source file is provided on an AS IS basis. The SPIRIT -// Consortium disclaims any warranty express or implied including -// any warranty of merchantability and fitness for use for a -// particular purpose. -// -// The user of the source file shall indemnify and hold The SPIRIT -// Consortium and its members harmless from any damages or liability. -// Users are requested to provide feedback to The SPIRIT Consortium -// using either mailto:feedback@lists.spiritconsortium.org or the forms at -// http://www.spiritconsortium.org/about/contact_us/ -// -// This file may be copied, and distributed, with or without -// modifications; this notice must be included on any copy. -// -signal gen_reset_signal_type { // Define a generic reset signal type - name="Generic Reset Signal"; - desc="This is a generic reset signal used to reset"; -}; - -gen_reset_signal_type generic_reset; // Instance the Generic Reset Signal - -// -// This example shows the concept of a register file -// A register file is a group of registers that belong together... -// Now we can easily instance multiple fifo status registers very easily... -// - -regfile fifoRfile { - reg pointerReg { field { we;} data[31:0]; }; - - reg fifoStatusReg { - field {} full; - field {} empty; - field {} almost_empty[4:4]; - field {} almost_full[5:5]; - - full->reset = 1'b0; - full->resetsignal = generic_reset; // Just the full signal uses generic reset. Others use reset... - empty->reset = 1'b1; - almost_empty->reset = 1'b1; - almost_full ->reset = 1'b0; - }; - - pointerReg head; // Define a register pointing to the head of the fifo - head.data->resetsignal = generic_reset; // Assign an alternate reset to register head - - pointerReg tail; // Define a register pointing to the tail of a fifo - fifoStatusReg status; // Define a register for the Fifo's Status -}; - - -// -// This example shows using perl to do anything you desire -// - -field myField { - desc = "My example 2bit status field"; - rclr; // Read to Clear -}; - - -// An example of Apache's ASP standard for embedding Perl -reg myReg { - <% $num_fields = 16; - for( $i = 0; $i < $num_fields*2; $i += 2 ) { %> - myField data<%=$i/2%> [<%=$i+1%>:<%=$i%>]; - data<%=$i/2%>->reset = 2'd<%=$i/2%4%>; - <% } %> -}; - - -// -// Enumeration Example -// -enum link_status_enum { - not_present = 4'd0 { desc = "No link peer is currently detected"; }; - training = 4'd1 { desc = "Link is currently training"; }; - snooze = 4'd5 { desc = "Link is in a partial low power state"; }; - sleep = 4'd6 { desc = "Link is a Full low power state"; }; - wake = 4'd7 { desc = "Link is waking up from snooze or sleep state"; }; - active = 4'd10 { desc = "Link is opertating normally"; }; -}; - -field link_status_field { - hw = rw; - sw = r; - desc = "Status of a Serdes Link"; - encode = link_status_enum; - fieldwidth = 4; -}; - -reg serdes_link_status_reg { - link_status_field port0; // Instance 4 ports of Link Status - link_status_field port1; - link_status_field port2; - link_status_field port3; -}; - - -// -// Counter Example -// - -field count_field { // Anonymous Generic Counter definition. - hw = r; sw = rw; rclr; counter; - desc = "Number of certain packet type seen"; -}; - -reg gige_pkt_count_reg { - count_field port0[31:24]; - count_field port1[23:16]; - count_field port2[15:8]; - count_field port3[7:0]; -}; - -reg spi4_pkt_count_reg { - count_field port0[31:16]; - count_field port1[15:0]; - port0->threshold = 16'hCFFF; - port1->threshold = 16'hCFFF; -}; - -reg vc_pkt_count_reg { - count_field vc_count[30:0]; - field { desc="VC is Active"; stickybit; } active; - active->reset = 1'b1; - vc_count->reset = 31'h0; -}; - - -addrmap some_register_map { - - name = "RDL Example Registers"; - desc = "This address map contains some example registers to show - how RDL can be utilized in various situations."; - - // - // This register is a inline register definition. - // It defines a simple ID register. No flip-flop is implimented - // - reg chip_id { - name = "This chip part number and revision #"; - desc = "This register cotains the part # and revision # for XYZ ASIC"; - - field { - hw = w; // This combination of attributes creates an input port for - sw = r; // hardware to set the part num external to the reg block - desc = "This field represents the chips part number"; - } part_num[31:4] = 28'h12_34_56_7; // Verilog Style number with _'s - - field { - hw = na; // This combination creates the ID num as a constant interal - sw = r; // to the reg block - desc = "This field represents the chips revision number"; - } rev_num[3:0] = 4'b00_01; // Verilog Style number with _'s - }; // End chip_id register definition - - - external chip_id chip_id_reg @0x0000; // Create an Instance of CHIP_ID type called chip_id_reg at Addr=0; - - serdes_link_status_reg link_status; // Instance a reg. Auto Address - - myReg myRegInst @0x0010; // This instance starts at 0x10 - - spi4_pkt_count_reg spi4_pkt_count @0x0020; - gige_pkt_count_reg gige_pkt_count_reg; - - fifoRfile fifo_port[8] @0x100 += 0x10; // Create 8 Instances of Fifo Reg File Starting at Address=0x100 - vc_pkt_count_reg vc_pkt_count[16] @0x1000 +=0x10; - -}; // End some_register_map diff --git a/test/generate_testcase_data.py b/test/generate_testcase_data.py new file mode 100644 index 0000000..8258ff9 --- /dev/null +++ b/test/generate_testcase_data.py @@ -0,0 +1,108 @@ +#!/usr/bin/env python3 + +import sys +import os + +import jinja2 as jj + +from systemrdl import RDLCompiler, AddrmapNode, RegfileNode, MemNode, RegNode, FieldNode +from peakrdl.uvm import UVMExporter + +#------------------------------------------------------------------------------- +testcase_name = sys.argv[1] +rdl_file = sys.argv[2] +output_dir = os.path.dirname(rdl_file) +#------------------------------------------------------------------------------- +# Generate UVM model +#------------------------------------------------------------------------------- +rdlc = RDLCompiler() +rdlc.compile_file(rdl_file) +root = rdlc.elaborate().top + +uvm_exportname = os.path.join(output_dir, testcase_name + "_uvm.sv") + +uvm_file = os.path.join(output_dir, testcase_name + "_uvm_nofac_reuse_pkg.sv") +UVMExporter().export( + root, uvm_exportname, + use_uvm_factory=False, + reuse_class_definitions=True, + export_as_package=True, +) +os.rename(uvm_exportname, uvm_file) + +uvm_file = os.path.join(output_dir, testcase_name + "_uvm_fac_reuse_pkg.sv") +UVMExporter().export( + root, uvm_exportname, + use_uvm_factory=True, + reuse_class_definitions=True, + export_as_package=True, +) +os.rename(uvm_exportname, uvm_file) + +uvm_file = os.path.join(output_dir, testcase_name + "_uvm_nofac_noreuse_pkg.sv") +UVMExporter().export( + root, uvm_exportname, + use_uvm_factory=True, + reuse_class_definitions=False, + export_as_package=True, +) +os.rename(uvm_exportname, uvm_file) + +#------------------------------------------------------------------------------- +# Generate test logic +#------------------------------------------------------------------------------- +context = { + 'testcase_name': testcase_name, + 'root': root, + 'rn': root.inst_name, + 'isinstance': isinstance, + 'AddrmapNode': AddrmapNode, + 'RegfileNode': RegfileNode, + 'MemNode': MemNode, + 'RegNode': RegNode, + 'FieldNode': FieldNode, +} + +template = jj.Template(""" +module top(); + import uvm_pkg::*; + `include "uvm_macros.svh" + `define ASSERT_EQ_STR(a,b) assert(a == b) else $error("%s != %s", a, b) + `define ASSERT_EQ_INT(a,b) assert(a == b) else $error("0x%x != 0x%x", a, b) + + initial begin + {{testcase_name}}_uvm::{{testcase_name}} {{rn}}; + {{rn}} = new("{{rn}}"); + {{rn}}.build(); + {{rn}}.lock_model(); + + {% for node in root.descendants(unroll=True) %} + // {{node}} + {%- if isinstance(node, (AddrmapNode, RegfileNode, MemNode)) %} + `ASSERT_EQ_STR({{node.get_path()}}.get_full_name(), "{{node.get_path()}}"); + {%- endif %} + {%- if isinstance(node, RegNode) %} + {%- if node.is_virtual %} + `ASSERT_EQ_STR({{node.parent.get_path() + "." + node.inst_name}}.get_full_name(), "{{node.parent.get_path() + "." + node.inst_name}}"); + `ASSERT_EQ_INT({{node.parent.get_path() + "." + node.inst_name}}.get_size(), {{node.inst.n_elements}}); + {%- else %} + `ASSERT_EQ_STR({{node.get_path()}}.get_full_name(), "{{node.get_path()}}"); + `ASSERT_EQ_INT({{node.get_path()}}.get_address(), {{"'h%x" % node.absolute_address}}); + `ASSERT_EQ_INT({{node.get_path()}}.get_n_bits(), {{node.get_property("regwidth")}}); + {%- endif %} + {%- endif %} + {%- if isinstance(node, FieldNode) %} + {%- if node.is_virtual %} + `ASSERT_EQ_STR({{node.parent.parent.get_path() + "." + node.parent.inst_name + "." + node.inst_name}}.get_full_name(), "{{node.parent.parent.get_path() + "." + node.parent.inst_name + "." + node.inst_name}}"); + {%- else %} + `ASSERT_EQ_STR({{node.get_path()}}.get_full_name(), "{{node.get_path()}}"); + `ASSERT_EQ_INT({{node.get_path()}}.get_lsb_pos(), {{node.lsb}}); + `ASSERT_EQ_INT({{node.get_path()}}.get_n_bits(), {{node.width}}); + {%- endif %} + {%- endif %} + {%- endfor %} + end +endmodule +""") + +template.stream(context).dump(os.path.join(output_dir, testcase_name + "_test.sv")) diff --git a/test/hwa_wrapper.rdl b/test/hwa_wrapper.rdl deleted file mode 100644 index a0c38db..0000000 --- a/test/hwa_wrapper.rdl +++ /dev/null @@ -1,85 +0,0 @@ - -field swcfg_field { - hw = r; sw = rw; - desc = "SW Configuration field"; -}; - -regfile regs_ral_demo{ - default regwidth=32; - - reg { - name = "Control Register"; - desc = "////Convolution MAC array Registers"; - field { - sw = r; - hw = w; - reset = 0x0; - } PRESCALER[1:0] = 0; - PRESCALER->desc=" -//// Status of configuration register group 0"; - field { - sw = r; - hw = w; - reset = 0x0; - } MODE[3:2] = 0; - - field { - hw=rw; we; - } MASTER[4:4] = 0; - MASTER->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"; - 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 - bit (lsb) of the data byte is transmitted first, and when DORD is - 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 @ 0x4; - - reg { - field { - sw=r; hw=rw; we; - } WRCOL[6:6] = 0; - - field { - sw=r; hw=rw; we; - } IF[7:7] = 0; - } STATUS @0x8; - - reg { - swcfg_field en[31:31] = 1'd1; - field {rclr; hw=w; we; } subch[30:26] = 5'd0; - field {rclr; hw=w; we; - } str[25:16] = 10'd0; - } RCNT_SAT @0xc; - -}; - -addrmap addrmap_PICO{ - name = "ATXMEGA SPI controller"; - desc = "Register description of Atmel XMEGA AU's SPI controller - 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"; - signal { activelow; field_reset; cpuif_reset;} reset_n; - regs_ral_demo ral_regs_demo @0x100; -}; diff --git a/test/pylint.rc b/test/pylint.rc new file mode 100644 index 0000000..829f881 --- /dev/null +++ b/test/pylint.rc @@ -0,0 +1,590 @@ +[MASTER] + +# A comma-separated list of package or module names from where C extensions may +# be loaded. Extensions are loading into the active Python interpreter and may +# run arbitrary code. +extension-pkg-whitelist= + +# Add files or directories to the blacklist. They should be base names, not +# paths. +ignore=CVS, parser, docs, test + +# Add files or directories matching the regex patterns to the blacklist. The +# regex matches against base names, not paths. +ignore-patterns= + +# Python code to execute, usually for sys.path manipulation such as +# pygtk.require(). +#init-hook= + +# Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the +# number of processors available to use. +jobs=0 + +# Control the amount of potential inferred values when inferring a single +# object. This can help the performance when dealing with large functions or +# complex, nested conditions. +limit-inference-results=100 + +# List of plugins (as comma separated values of python module names) to load, +# usually to register additional checkers. +load-plugins= + +# Pickle collected data for later comparisons. +persistent=yes + +# Specify a configuration file. +#rcfile= + +# When enabled, pylint would attempt to guess common misconfiguration and emit +# user-friendly hints instead of false-positive error messages. +suggestion-mode=yes + +# Allow loading of arbitrary C extensions. Extensions are imported into the +# active Python interpreter and may run arbitrary code. +unsafe-load-any-extension=no + + +[MESSAGES CONTROL] + +# Only show warnings with the listed confidence levels. Leave empty to show +# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED. +confidence= + +# Disable the message, report, category or checker with the given id(s). You +# can either give multiple identifiers separated by comma (,) or put this +# option multiple times (only on the command line, not in the configuration +# file where it should appear only once). You can also use "--disable=all" to +# disable everything first and then reenable specific checks. For example, if +# you want to run only the similarities checker, you can use "--disable=all +# --enable=similarities". If you want to run only the classes checker, but have +# no Warning level messages displayed, use "--disable=all --enable=classes +# --disable=W". +disable= + parameter-unpacking, + unpacking-in-except, + backtick, + long-suffix, + import-star-module-level, + raw-checker-failed, + bad-inline-option, + locally-disabled, + locally-enabled, + file-ignored, + suppressed-message, + useless-suppression, + deprecated-pragma, + apply-builtin, + basestring-builtin, + buffer-builtin, + cmp-builtin, + coerce-builtin, + execfile-builtin, + file-builtin, + long-builtin, + raw_input-builtin, + reduce-builtin, + standarderror-builtin, + unicode-builtin, + xrange-builtin, + coerce-method, + delslice-method, + getslice-method, + setslice-method, + no-absolute-import, + dict-iter-method, + dict-view-method, + next-method-called, + metaclass-assignment, + indexing-exception, + raising-string, + reload-builtin, + oct-method, + hex-method, + nonzero-method, + cmp-method, + input-builtin, + round-builtin, + intern-builtin, + unichr-builtin, + map-builtin-not-iterating, + zip-builtin-not-iterating, + range-builtin-not-iterating, + filter-builtin-not-iterating, + using-cmp-argument, + eq-without-hash, + div-method, + idiv-method, + rdiv-method, + exception-message-attribute, + invalid-str-codec, + sys-max-int, + bad-python3-import, + + # Disable for now during development + fixme, + + # User ignored limits + too-many-locals, + too-many-branches, + too-many-return-statements, + too-few-public-methods, + too-many-public-methods, + too-many-statements, + too-many-instance-attributes, + line-too-long, + + useless-return, + no-else-return, + inconsistent-return-statements, + cyclic-import, + no-self-use, + unused-variable, + + bad-continuation, + invalid-name, + missing-docstring, + + unidiomatic-typecheck, + abstract-method, + protected-access + +# Enable the message, report, category or checker with the given id(s). You can +# either give multiple identifier separated by comma (,) or put this option +# multiple time (only on the command line, not in the configuration file where +# it should appear only once). See also the "--disable" option for examples. +enable=c-extension-no-member + + +[REPORTS] + +# Python expression which should return a score less than or equal to 10. You +# have access to the variables 'error', 'warning', 'refactor', and 'convention' +# which contain the number of messages in each category, as well as 'statement' +# which is the total number of statements analyzed. This score is used by the +# global evaluation report (RP0004). +evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) + +# Template used to display messages. This is a python new-style format string +# used to format the message information. See doc for all details. +#msg-template= + +# Set the output format. Available formats are text, parseable, colorized, json +# and msvs (visual studio). You can also give a reporter class, e.g. +# mypackage.mymodule.MyReporterClass. +output-format=text + +# Tells whether to display a full report or only the messages. +reports=no + +# Activate the evaluation score. +score=yes + + +[REFACTORING] + +# Maximum number of nested blocks for function / method body +max-nested-blocks=5 + +# Complete name of functions that never returns. When checking for +# inconsistent-return-statements if a never returning function is called then +# it will be considered as an explicit return statement and no message will be +# printed. +never-returning-functions=sys.exit + + +[STRING] + +# This flag controls whether the implicit-str-concat-in-sequence should +# generate a warning on implicit string concatenation in sequences defined over +# several lines. +check-str-concat-over-line-jumps=no + + +[SPELLING] + +# Limits count of emitted suggestions for spelling mistakes. +max-spelling-suggestions=4 + +# Spelling dictionary name. Available dictionaries: none. To make it work, +# install the python-enchant package. +spelling-dict= + +# List of comma separated words that should not be checked. +spelling-ignore-words= + +# A path to a file that contains the private dictionary; one word per line. +spelling-private-dict-file= + +# Tells whether to store unknown words to the private dictionary (see the +# --spelling-private-dict-file option) instead of raising a message. +spelling-store-unknown-words=no + + +[LOGGING] + +# Format style used to check logging format string. `old` means using % +# formatting, `new` is for `{}` formatting,and `fstr` is for f-strings. +logging-format-style=old + +# Logging modules to check that the string format arguments are in logging +# function parameter format. +logging-modules=logging + + +[VARIABLES] + +# List of additional names supposed to be defined in builtins. Remember that +# you should avoid defining new builtins when possible. +additional-builtins= + +# Tells whether unused global variables should be treated as a violation. +allow-global-unused-variables=yes + +# List of strings which can identify a callback function by name. A callback +# name must start or end with one of those strings. +callbacks=cb_, + _cb + +# A regular expression matching the name of dummy variables (i.e. expected to +# not be used). +dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ + +# Argument names that match this expression will be ignored. Default to name +# with leading underscore. +ignored-argument-names=_.*|^ignored_|^unused_ + +# Tells whether we should check for unused import in __init__ files. +init-import=no + +# List of qualified module names which can have objects that can redefine +# builtins. +redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io + + +[SIMILARITIES] + +# Ignore comments when computing similarities. +ignore-comments=yes + +# Ignore docstrings when computing similarities. +ignore-docstrings=yes + +# Ignore imports when computing similarities. +ignore-imports=no + +# Minimum lines number of a similarity. +min-similarity-lines=10 + + +[TYPECHECK] + +# List of decorators that produce context managers, such as +# contextlib.contextmanager. Add to this list to register other decorators that +# produce valid context managers. +contextmanager-decorators=contextlib.contextmanager + +# List of members which are set dynamically and missed by pylint inference +# system, and so shouldn't trigger E1101 when accessed. Python regular +# expressions are accepted. +generated-members= + +# Tells whether missing members accessed in mixin class should be ignored. A +# mixin class is detected if its name ends with "mixin" (case insensitive). +ignore-mixin-members=yes + +# Tells whether to warn about missing members when the owner of the attribute +# is inferred to be None. +ignore-none=yes + +# This flag controls whether pylint should warn about no-member and similar +# checks whenever an opaque object is returned when inferring. The inference +# can return multiple potential results while evaluating a Python object, but +# some branches might not be evaluated, which results in partial inference. In +# that case, it might be useful to still emit no-member and other checks for +# the rest of the inferred objects. +ignore-on-opaque-inference=yes + +# List of class names for which member attributes should not be checked (useful +# for classes with dynamically set attributes). This supports the use of +# qualified names. +ignored-classes=optparse.Values,thread._local,_thread._local + +# List of module names for which member attributes should not be checked +# (useful for modules/projects where namespaces are manipulated during runtime +# and thus existing member attributes cannot be deduced by static analysis). It +# supports qualified module names, as well as Unix pattern matching. +ignored-modules= + +# Show a hint with possible names when a member name was not found. The aspect +# of finding the hint is based on edit distance. +missing-member-hint=yes + +# The minimum edit distance a name should have in order to be considered a +# similar match for a missing member name. +missing-member-hint-distance=1 + +# The total number of similar names that should be taken in consideration when +# showing a hint for a missing member. +missing-member-max-choices=1 + +# List of decorators that change the signature of a decorated function. +signature-mutators= + + +[FORMAT] + +# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. +expected-line-ending-format= + +# Regexp for a line that is allowed to be longer than the limit. +ignore-long-lines=^\s*(# )??$ + +# Number of spaces of indent required inside a hanging or continued line. +indent-after-paren=4 + +# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 +# tab). +indent-string=' ' + +# Maximum number of characters on a single line. +max-line-length=110 + +# Maximum number of lines in a module. +max-module-lines=2000 + +# List of optional constructs for which whitespace checking is disabled. `dict- +# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}. +# `trailing-comma` allows a space between comma and closing bracket: (a, ). +# `empty-line` allows space-only lines. +no-space-check=trailing-comma, + dict-separator + +# Allow the body of a class to be on the same line as the declaration if body +# contains single statement. +single-line-class-stmt=no + +# Allow the body of an if to be on the same line as the test if there is no +# else. +single-line-if-stmt=no + + +[MISCELLANEOUS] + +# List of note tags to take in consideration, separated by a comma. +notes=FIXME, + XXX, + TODO + + +[BASIC] + +# Naming style matching correct argument names. +argument-naming-style=snake_case + +# Regular expression matching correct argument names. Overrides argument- +# naming-style. +#argument-rgx= + +# Naming style matching correct attribute names. +attr-naming-style=snake_case + +# Regular expression matching correct attribute names. Overrides attr-naming- +# style. +#attr-rgx= + +# Bad variable names which should always be refused, separated by a comma. +bad-names=foo, + bar, + baz, + toto, + tutu, + tata + +# Naming style matching correct class attribute names. +class-attribute-naming-style=any + +# Regular expression matching correct class attribute names. Overrides class- +# attribute-naming-style. +#class-attribute-rgx= + +# Naming style matching correct class names. +class-naming-style=PascalCase + +# Regular expression matching correct class names. Overrides class-naming- +# style. +#class-rgx= + +# Naming style matching correct constant names. +const-naming-style=UPPER_CASE + +# Regular expression matching correct constant names. Overrides const-naming- +# style. +#const-rgx= + +# Minimum line length for functions/classes that require docstrings, shorter +# ones are exempt. +docstring-min-length=-1 + +# Naming style matching correct function names. +function-naming-style=snake_case + +# Regular expression matching correct function names. Overrides function- +# naming-style. +#function-rgx= + +# Good variable names which should always be accepted, separated by a comma. +good-names=i, + j, + k, + ex, + Run, + _ + +# Include a hint for the correct naming format with invalid-name. +include-naming-hint=no + +# Naming style matching correct inline iteration names. +inlinevar-naming-style=any + +# Regular expression matching correct inline iteration names. Overrides +# inlinevar-naming-style. +#inlinevar-rgx= + +# Naming style matching correct method names. +method-naming-style=snake_case + +# Regular expression matching correct method names. Overrides method-naming- +# style. +#method-rgx= + +# Naming style matching correct module names. +module-naming-style=snake_case + +# Regular expression matching correct module names. Overrides module-naming- +# style. +#module-rgx= + +# Colon-delimited sets of names that determine each other's naming style when +# the name regexes allow several styles. +name-group= + +# Regular expression which should only match function or class names that do +# not require a docstring. +no-docstring-rgx=^_ + +# List of decorators that produce properties, such as abc.abstractproperty. Add +# to this list to register other decorators that produce valid properties. +# These decorators are taken in consideration only for invalid-name. +property-classes=abc.abstractproperty + +# Naming style matching correct variable names. +variable-naming-style=snake_case + +# Regular expression matching correct variable names. Overrides variable- +# naming-style. +#variable-rgx= + + +[CLASSES] + +# List of method names used to declare (i.e. assign) instance attributes. +defining-attr-methods=__init__, + __new__, + setUp, + __post_init__ + +# List of member names, which should be excluded from the protected access +# warning. +exclude-protected=_asdict, + _fields, + _replace, + _source, + _make + +# List of valid names for the first argument in a class method. +valid-classmethod-first-arg=cls + +# List of valid names for the first argument in a metaclass class method. +valid-metaclass-classmethod-first-arg=cls + + +[DESIGN] + +# Maximum number of arguments for function / method. +max-args=8 + +# Maximum number of attributes for a class (see R0902). +max-attributes=7 + +# Maximum number of boolean expressions in an if statement (see R0916). +max-bool-expr=5 + +# Maximum number of branch for function / method body. +max-branches=12 + +# Maximum number of locals for function / method body. +max-locals=15 + +# Maximum number of parents for a class (see R0901). +max-parents=7 + +# Maximum number of public methods for a class (see R0904). +max-public-methods=20 + +# Maximum number of return / yield for function / method body. +max-returns=6 + +# Maximum number of statements in function / method body. +max-statements=50 + +# Minimum number of public methods for a class (see R0903). +min-public-methods=2 + + +[IMPORTS] + +# List of modules that can be imported at any level, not just the top level +# one. +allow-any-import-level= + +# Allow wildcard imports from modules that define __all__. +allow-wildcard-with-all=no + +# Analyse import fallback blocks. This can be used to support both Python 2 and +# 3 compatible code, which means that the block might have code that exists +# only in one or another interpreter, leading to false positives when analysed. +analyse-fallback-blocks=no + +# Deprecated modules which should not be used, separated by a comma. +deprecated-modules=optparse,tkinter.tix + +# Create a graph of external dependencies in the given file (report RP0402 must +# not be disabled). +ext-import-graph= + +# Create a graph of every (i.e. internal and external) dependencies in the +# given file (report RP0402 must not be disabled). +import-graph= + +# Create a graph of internal dependencies in the given file (report RP0402 must +# not be disabled). +int-import-graph= + +# Force import order to recognize a module as part of the standard +# compatibility libraries. +known-standard-library= + +# Force import order to recognize a module as part of a third party library. +known-third-party=enchant + +# Couples of modules and preferred modules, separated by a comma. +preferred-modules= + + +[EXCEPTIONS] + +# Exceptions that will emit a warning when being caught. Defaults to +# "BaseException, Exception". +overgeneral-exceptions=BaseException, + Exception diff --git a/test/run.sh b/test/run.sh new file mode 100755 index 0000000..73e60f1 --- /dev/null +++ b/test/run.sh @@ -0,0 +1,45 @@ +#!/bin/bash + +set -e + +this_dir="$( cd "$(dirname "$0")" ; pwd -P )" + +exists () { + type "$1" >/dev/null 2>/dev/null +} + +# Initialize venv +venv_bin=$this_dir/.venv/bin +python3 -m venv $this_dir/.venv + +#tools +python=$venv_bin/python +pylint=$venv_bin/pylint + + +# Install test dependencies +$python -m pip install -U pylint setuptools pip + + +# Install dut +cd $this_dir/.. +$python setup.py install +cd $this_dir + + +# Generate testcase verilog files +$python generate_testcase_data.py basic testcases/basic.rdl + + +# Run modelsim testcases +if exists vsim; then + ./vsim_test.sh testcases/basic_uvm_nofac_reuse_pkg.sv testcases/basic_test.sv + ./vsim_test.sh testcases/basic_uvm_fac_reuse_pkg.sv testcases/basic_test.sv + ./vsim_test.sh testcases/basic_uvm_nofac_noreuse_pkg.sv testcases/basic_test.sv +fi + + +# Run lint +cd $this_dir/.. +$pylint --rcfile $this_dir/pylint.rc peakrdl | tee $this_dir/lint.rpt +cd $this_dir diff --git a/test/test_uvm_gen.py b/test/test_uvm_gen.py deleted file mode 100755 index 187708d..0000000 --- a/test/test_uvm_gen.py +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/env python3 - -import sys -import os - -# Ignore this. Only needed for this example -this_dir = os.path.dirname(os.path.realpath(__file__)) -sys.path.insert(0, os.path.join(this_dir, "../")) - - -from systemrdl import RDLCompiler, RDLListener, RDLWalker, RDLCompileError -from systemrdl.node import FieldNode, RegNode, AddrmapNode, SignalNode -from ralbot.uvmgen import uvmGenExporter - -# Collect input files from the command line arguments -input_files = sys.argv[1:] - -# Create an instance of the compiler -rdlc = RDLCompiler() - -try: - # Compile all the files provided - for input_file in input_files: - rdlc.compile_file(input_file) - - # Elaborate the design - root = rdlc.elaborate() -except RDLCompileError: - # A compilation error occurred. Exit with error code - sys.exit(1) - - -file = "test.svh" -exporter = uvmGenExporter() -exporter.export(root, file) -exporter.export(root, "output/test_uvmgen") diff --git a/test/test_write_enable.rdl b/test/test_write_enable.rdl deleted file mode 100644 index 39f24d4..0000000 --- a/test/test_write_enable.rdl +++ /dev/null @@ -1,50 +0,0 @@ -// parallel interface write enable example -// -// parameter defines for this example -// -// define a state reg -reg state_reg{ - name = "State reg"; - field { - name = "Some state bits"; - sw=r; hw=w; - } lsb_field[16] = 16'hffff; -}; - -mem fixed_mem #(longint unsigned word_size = 32, - longint unsigned memory_size = word_size * 4096) { - mementries = memory_size / word_size ; - memwidth = word_size ; - sw =rw ; -}; -addrmap foo{ - default reset=0; - reg { - field {sw=rw; hw=r; counter; hwset; hwclr; swwel;} count_fld[8] = 8'h55; - field {sw=rw; intr; } intr1; - field {sw=rw; intr;} intr2; - } intr_reg; - - reg{ - field {sw=rw;hw=w;} log_err[8] =8'h4; - field {sw=r;hw=w;} log_value[8] =8'h0; - } log_reg; - - reg { - regwidth=64; - field{sw=rw; hw=na;} field64[64]; - } bigreg; - - external fixed_mem mem32; - - state_reg state_regs[8] @0x200; - state_regs->name = "State regs"; - - addrmap { - reg { - field{sw=rw; hw=na;} field32[32]; - } array_reg[8] @0x10; - } childmap_32b @0x100; -} ; - - diff --git a/test/testcases/.gitignore b/test/testcases/.gitignore new file mode 100644 index 0000000..027a298 --- /dev/null +++ b/test/testcases/.gitignore @@ -0,0 +1,2 @@ +*.sv +*.svh diff --git a/test/testcases/basic.rdl b/test/testcases/basic.rdl new file mode 100644 index 0000000..e4adfdc --- /dev/null +++ b/test/testcases/basic.rdl @@ -0,0 +1,50 @@ + +addrmap basic { + regfile { + reg reg_t{ + field {sw=rw; hw=r;} f1[15:0] = 1234; + field f2_t {sw=rw; hw=r;}; + f2_t f2[16:16] = 0; + f2_t f3[17:17] = 0; + field { + sw=rw; hw=r; + hdl_path_slice = '{"f4"}; + hdl_path_gate_slice = '{"f4_31_gate", "f4_30_gate"}; + } f4[31:30] = 0; + field { + sw=rw; hw=r; + hdl_path_slice = '{"f5_29", "f5_28"}; + hdl_path_gate_slice = '{"f5_gate"}; + } f5[29:28] = 0; + }; + reg_t r1 @ 0x0; + reg_t r2 @ 0x4; + reg_t r3 @ 0x8; + + r3.f1->sw=w; + r3.f1->reset=200; + } foo @ 0x0; + + regfile bar_t { + reg { + field {sw=rw; hw=r;} f[15:0] = 1234; + hdl_path = "r1"; + hdl_path_gate = "r1_gate"; + } r1[4] @ 0x0 += 4; + hdl_path = "bar"; + hdl_path_gate = "bar_gate"; + }; + + bar_t bar[4][3] @ 0x1000 += 0x100; + bar_t bar2 @ 0x8000; + + bar2.r1.f->sw=w; + bar2.r1.f->reset=200; + + external mem { + reg { + field {sw=rw; hw=r;} f[15:0] = 1234; + } r1[8] @ 0x0 += 4; + mementries = 0x100; + } xxx @ 0x10000; +}; diff --git a/test/vsim_test.sh b/test/vsim_test.sh new file mode 100755 index 0000000..5801814 --- /dev/null +++ b/test/vsim_test.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +set -e + +vlog_args="-quiet -sv -L questa_uvm_pkg +incdir+$UVM_SRC" +vsim_args="-quiet -L questa_uvm_pkg +UVM_NO_RELNOTES" +do_script=" + run -all; + exit; +" + +rm -rf work transcript *.wlf +vlog $vlog_args $@ +vsim $vsim_args -c top -do "$do_script" + +if grep -Pq "(Error:|Fatal:|UVM_ERROR|UVM_FATAL)" transcript; then + echo Sim had errors + exit 1 +fi