From 3adf7e132809e25f1e94b95cc1efedd573b42b65 Mon Sep 17 00:00:00 2001 From: Alex Mykyta Date: Sat, 4 Dec 2021 17:24:19 -0800 Subject: [PATCH] More testcases & documentation --- README.md | 11 ++- doc/architecture.rst | 26 +++++ doc/cpuif/advanced.rst | 7 ++ doc/cpuif/apb3.rst | 11 +++ doc/hwif.rst | 8 ++ doc/index.rst | 50 +++++++++- doc/limitations.rst | 2 +- doc/props/addrmap.rst | 5 +- doc/props/field.rst | 61 ++++++++---- doc/props/reg.rst | 4 +- doc/props/rhs_props.rst | 72 +++++++++++--- doc/props/signal.rst | 4 +- peakrdl/regblock/__about__.py | 2 +- peakrdl/regblock/dereferencer.py | 6 +- peakrdl/regblock/field_logic/sw_onwrite.py | 10 +- setup.py | 4 +- test/README.md | 75 +++++++++++++-- test/lib/cpuifs/apb3/tb_inst.sv | 2 + test/lib/cpuifs/base.py | 18 +++- test/lib/regblock_testcase.py | 10 +- test/lib/sv_line_anchor.py | 10 ++ test/lib/templates/tb_base.sv | 4 + test/requirements.txt | 1 + test/test_bitwise_reduce/__init__.py | 0 test/test_bitwise_reduce/regblock.rdl | 8 ++ test/test_bitwise_reduce/tb_template.sv | 44 +++++++++ test/test_bitwise_reduce/testcase.py | 5 + .../{tb.sv => tb_template.sv} | 1 + test/test_hw_access/__init__.py | 0 test/test_hw_access/regblock.rdl | 63 +++++++++++++ test/test_hw_access/tb_template.sv | 94 +++++++++++++++++++ test/test_hw_access/testcase.py | 5 + test/test_onread_onwrite/__init__.py | 0 test/test_onread_onwrite/regblock.rdl | 61 ++++++++++++ test/test_onread_onwrite/tb_template.sv | 30 ++++++ test/test_onread_onwrite/testcase.py | 5 + .../test_read_fanin/{tb.sv => tb_template.sv} | 3 + .../{tb.sv => tb_template.sv} | 1 + test/test_swacc_swmod/regblock.rdl | 8 ++ .../{tb.sv => tb_template.sv} | 25 ++++- test/test_swwe/__init__.py | 0 test/test_swwe/regblock.rdl | 66 +++++++++++++ test/test_swwe/tb_template.sv | 63 +++++++++++++ test/test_swwe/testcase.py | 5 + 44 files changed, 827 insertions(+), 63 deletions(-) create mode 100644 doc/architecture.rst create mode 100644 doc/cpuif/advanced.rst create mode 100644 doc/cpuif/apb3.rst create mode 100644 doc/hwif.rst create mode 100644 test/lib/sv_line_anchor.py create mode 100644 test/test_bitwise_reduce/__init__.py create mode 100644 test/test_bitwise_reduce/regblock.rdl create mode 100644 test/test_bitwise_reduce/tb_template.sv create mode 100644 test/test_bitwise_reduce/testcase.py rename test/test_field_types/{tb.sv => tb_template.sv} (99%) create mode 100644 test/test_hw_access/__init__.py create mode 100644 test/test_hw_access/regblock.rdl create mode 100644 test/test_hw_access/tb_template.sv create mode 100644 test/test_hw_access/testcase.py create mode 100644 test/test_onread_onwrite/__init__.py create mode 100644 test/test_onread_onwrite/regblock.rdl create mode 100644 test/test_onread_onwrite/tb_template.sv create mode 100644 test/test_onread_onwrite/testcase.py rename test/test_read_fanin/{tb.sv => tb_template.sv} (93%) rename test/test_structural_sw_rw/{tb.sv => tb_template.sv} (98%) rename test/test_swacc_swmod/{tb.sv => tb_template.sv} (72%) create mode 100644 test/test_swwe/__init__.py create mode 100644 test/test_swwe/regblock.rdl create mode 100644 test/test_swwe/tb_template.sv create mode 100644 test/test_swwe/testcase.py diff --git a/README.md b/README.md index c751e28..2e8642c 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,12 @@ +[![Documentation Status](https://readthedocs.org/projects/peakrdl-regblock/badge/?version=latest)](http://peakrdl-regblock.readthedocs.io) +[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/peakrdl-regblock.svg)](https://pypi.org/project/peakrdl-regblock) + +# IMPORTANT + +This project has no official releases yet and is still under active development! + # PeakRDL-regblock Generate SystemVerilog RTL that implements a register block from compiled SystemRDL input. -## Installing -(Not published to PyPi yet) +## Documentation +See the [PeakRDL-regblock Documentation](http://peakrdl-regblock.readthedocs.io) for more details diff --git a/doc/architecture.rst b/doc/architecture.rst new file mode 100644 index 0000000..2ea9ad7 --- /dev/null +++ b/doc/architecture.rst @@ -0,0 +1,26 @@ +Register Block Architecture +=========================== + +TODO: Add full block diagram + + +CPU Interface +------------- + +TODO: describe boundary signals. Timing diagrams + +Address Decode +-------------- + +TODO: describe boundary signals. Timing diagrams + +Field Logic +----------- + +TODO: describe boundary signals. Timing diagrams + +Readback +-------- + +TODO: describe boundary signals. Timing diagrams +Retiming options diff --git a/doc/cpuif/advanced.rst b/doc/cpuif/advanced.rst new file mode 100644 index 0000000..b56496f --- /dev/null +++ b/doc/cpuif/advanced.rst @@ -0,0 +1,7 @@ +Advanced Topics +=============== + +TODO: + +* How to override an interface's name, modport, signal names, whatever +* Creating your own custom CPU interface definition diff --git a/doc/cpuif/apb3.rst b/doc/cpuif/apb3.rst new file mode 100644 index 0000000..4c44e71 --- /dev/null +++ b/doc/cpuif/apb3.rst @@ -0,0 +1,11 @@ +AMBA APB3 +========= + +TODO: Describe the following + +* List of interface signals + + * interface name & modports (link to advanced topics in case user wants to override) + * flattened equivalents + +* Download link to SV interface definition diff --git a/doc/hwif.rst b/doc/hwif.rst new file mode 100644 index 0000000..c464832 --- /dev/null +++ b/doc/hwif.rst @@ -0,0 +1,8 @@ +Hardware Interface +------------------ + +TODO: Describe the following + +* hwif_in / hwif_out structs and their contents +* shorthand notation used in this reference: ``hwif_in..xyz`` +* Example of how to peel back a sub-hierarchy struct diff --git a/doc/index.rst b/doc/index.rst index 147bc38..a967987 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -1,6 +1,51 @@ +PeakRDL-regblock +================ -Welcome to PeakRDL-regblock's documentation! -============================================ +.. important:: + + This project has no official releases yet and is still under active development! + + +TODO: Intro text + + +Installing +---------- + +Install from `PyPi`_ using pip + +.. code-block:: bash + + python3 -m pip install peakrdl-regblock + +.. _PyPi: https://pypi.org/project/peakrdl-regblock + + +Links +----- + +- `Source repository `_ +- `Release Notes `_ +- `Issue tracker `_ +- `PyPi `_ +- `SystemRDL Specification `_ + + + +.. toctree:: + :hidden: + + self + architecture + hwif + limitations + +.. toctree:: + :hidden: + :caption: CPU Interfaces + + cpuif/apb3 + cpuif/advanced .. toctree:: :hidden: @@ -11,4 +56,3 @@ Welcome to PeakRDL-regblock's documentation! props/addrmap props/signal props/rhs_props - limitations diff --git a/doc/limitations.rst b/doc/limitations.rst index 7543746..ebfc9ea 100644 --- a/doc/limitations.rst +++ b/doc/limitations.rst @@ -2,7 +2,7 @@ Known Issues & Limitations ========================== Not all SystemRDL features are supported by this exporter. For a listing of -supported properties, see the appropriate property listing page in the previous +supported properties, see the appropriate property listing page in the following sections. diff --git a/doc/props/addrmap.rst b/doc/props/addrmap.rst index 41d2964..614711d 100644 --- a/doc/props/addrmap.rst +++ b/doc/props/addrmap.rst @@ -1,8 +1,8 @@ Addrmap/Regfile Properties ========================== -.. note:: Any properties not explicitly listed here are either implicitly supported, - or are not relevant to the regblock exporter and are ignored. +.. note:: Any properties not explicitly listed here are either implicitly + supported, or are not relevant to the regblock exporter and are ignored. errextbus @@ -14,6 +14,7 @@ sharedextbus |NO| +-------------------------------------------------------------------------------- Addrmap Properties ================== diff --git a/doc/props/field.rst b/doc/props/field.rst index 0af7c70..6da785c 100644 --- a/doc/props/field.rst +++ b/doc/props/field.rst @@ -1,15 +1,15 @@ Field Properties ================ -.. note:: Any properties not explicitly listed here are either implicitly supported, - or are not relevant to the regblock exporter and are ignored. +.. note:: Any properties not explicitly listed here are either implicitly + supported, or are not relevant to the regblock exporter and are ignored. Software Access Properties -------------------------- onread/onwrite ^^^^^^^^^^^^^^ -|EX| +|OK| rclr/rset ^^^^^^^^^ @@ -25,9 +25,11 @@ sw swacc ^^^^^ -|EX| +|OK| -If true, infers an output signal ``swacc`` that is asserted as the field is sampled for a software read operation. +If true, infers an output signal ``hwif_out..swacc`` that is asserted on the +same clock cycle that the field is being sampled during a software read +operation. .. wavedrom:: @@ -40,9 +42,10 @@ If true, infers an output signal ``swacc`` that is asserted as the field is samp swmod ^^^^^ -|EX| +|OK| -If true, infers an output signal ``swmod`` that is asserted as the field is being modified by software. +If true, infers an output signal ``hwif_out..swmod`` that is asserted as the +field is being modified by software. .. wavedrom:: @@ -56,28 +59,34 @@ If true, infers an output signal ``swmod`` that is asserted as the field is bein swwe/swwel ^^^^^^^^^^ -TODO: Describe result +Provides a mechanism that allows hardware to override whether the field is +writable by software. boolean - |NO| + |OK| -bit - |NO| + If True, infers an input signal ``hwif_in..swwe`` or ``hwif_in..swwel``. reference - |NO| + |OK| + woclr/woset ^^^^^^^^^^^ See ``onwrite`` +-------------------------------------------------------------------------------- + Hardware Access Properties -------------------------- anded/ored/xored ^^^^^^^^^^^^^^^^ -|EX| +|OK| + +If true, infers the existence of output signal: ``hwif_out..anded``, +``hwif_out..ored``, ``hwif_out..xored`` hw @@ -86,19 +95,27 @@ hw hwclr/hwset ^^^^^^^^^^^ + +If both ``hwclr`` and ``hwset`` properties are used, and both are asserted at +the same clock cycle, then ``hwset`` will take precedence. + boolean - |EX| + |OK| + + If true, infers the existence of input signal: ``hwif_in..hwclr``, ``hwif_in..hwset`` reference - |EX| + |OK| hwenable/hwmask ^^^^^^^^^^^^^^^ -|EX| +|OK| we/wel ^^^^^^ -Write-enable control from hardware interface +Write-enable control from hardware interface. + +If true, infers the existence of input signal: ``hwif_in..we``, ``hwif_in..wel`` .. wavedrom:: @@ -113,12 +130,14 @@ Write-enable control from hardware interface boolean |OK| - if set, infers the existence of input signal ``hwif_in..we`` or ``hwif_in..wel`` + If true, infers the existence of input signal ``hwif_in..we`` or ``hwif_in..wel`` reference - |EX| + |OK| +-------------------------------------------------------------------------------- + Counter Properties ------------------ @@ -212,6 +231,8 @@ underflow |NO| +-------------------------------------------------------------------------------- + Interrupt Properties -------------------- @@ -244,6 +265,8 @@ stickybit |NO| +-------------------------------------------------------------------------------- + Misc ---- diff --git a/doc/props/reg.rst b/doc/props/reg.rst index 98e287f..cb094fa 100644 --- a/doc/props/reg.rst +++ b/doc/props/reg.rst @@ -1,8 +1,8 @@ Register Properties =================== -.. note:: Any properties not explicitly listed here are either implicitly supported, - or are not relevant to the regblock exporter and are ignored. +.. note:: Any properties not explicitly listed here are either implicitly + supported, or are not relevant to the regblock exporter and are ignored. accesswidth ----------- diff --git a/doc/props/rhs_props.rst b/doc/props/rhs_props.rst index 10a6779..8c1f1b8 100644 --- a/doc/props/rhs_props.rst +++ b/doc/props/rhs_props.rst @@ -1,6 +1,20 @@ RHS Property References ======================= +SystemRDL allows some properties to be referenced in the righthand-side of +property assignment expressions: + + .. code-block:: systemrdl + + some_property = my_reg.my_field->some_property; + +The official SystemRDL spec refers to these as "Ref targets" in Table G1, but +unfortunately does not describe their semantics in much detail. + +The text below describes the interpretations used for this exporter. + +-------------------------------------------------------------------------------- + Field ----- @@ -8,30 +22,68 @@ swacc ^^^^^ |EX| +Single-cycle strobe that indicates the field is being sampled during a software +read operation. + + swmod ^^^^^ |EX| +Single-cycle strobe that indicates the field is being modified during a software +access operation. + + swwe/swwel ^^^^^^^^^^ -|EX| +|OK| + +Represents the signal that controls the owning field's swwe/swwel behavior. + anded/ored/xored ^^^^^^^^^^^^^^^^ |EX| +Represents the current and/or/xor reduction of the owning field's value. + + hwclr/hwset ^^^^^^^^^^^ |EX| +Represents the signal that controls the owning field's hwclr/hwset behavior. + + hwenable/hwmask ^^^^^^^^^^^^^^^ |EX| +Represents the signal that controls the owning field's hwenable/hwmask behavior. + we/wel ^^^^^^ |EX| +next +^^^^ +|EX| + +reset +^^^^^ +|EX| + +resetsignal +^^^^^^^^^^^ +|EX| + +-------------------------------------------------------------------------------- + +Field Counter Properties +------------------------ + +Represents the signal that controls the owning field's we/wel behavior. + decr ^^^^ |NO| @@ -72,6 +124,11 @@ underflow ^^^^^^^^^ |NO| +-------------------------------------------------------------------------------- + +Field Interrupt Properties +-------------------------- + enable ^^^^^^ |EX| @@ -88,19 +145,8 @@ mask ^^^^ |EX| -next -^^^^ -|EX| - -reset -^^^^^ -|EX| - -resetsignal -^^^^^^^^^^^ -|EX| - +-------------------------------------------------------------------------------- Register -------- diff --git a/doc/props/signal.rst b/doc/props/signal.rst index c644f08..4850288 100644 --- a/doc/props/signal.rst +++ b/doc/props/signal.rst @@ -1,8 +1,8 @@ Signal Properties ================= -.. note:: Any properties not explicitly listed here are either implicitly supported, - or are not relevant to the regblock exporter and are ignored. +.. note:: Any properties not explicitly listed here are either implicitly + supported, or are not relevant to the regblock exporter and are ignored. activehigh/activelow diff --git a/peakrdl/regblock/__about__.py b/peakrdl/regblock/__about__.py index 3dc1f76..b1e19fe 100644 --- a/peakrdl/regblock/__about__.py +++ b/peakrdl/regblock/__about__.py @@ -1 +1 @@ -__version__ = "0.1.0" +__version__ = "0.1.0-a1" diff --git a/peakrdl/regblock/dereferencer.py b/peakrdl/regblock/dereferencer.py index 2fdea7b..9953889 100644 --- a/peakrdl/regblock/dereferencer.py +++ b/peakrdl/regblock/dereferencer.py @@ -81,12 +81,14 @@ class Dereferencer: def get_field_propref_value(self, field: FieldNode, prop_name: str) -> str: # Value reduction properties. # Wrap with the appropriate Verilog reduction operator - val = self.get_value(field) if prop_name == "anded": + val = self.get_value(field) return f"&({val})" elif prop_name == "ored": + val = self.get_value(field) return f"|({val})" elif prop_name == "xored": + val = self.get_value(field) return f"^({val})" # references that directly access a property value @@ -159,7 +161,7 @@ class Dereferencer: prop_value = field.get_property(complementary_pairs[prop_name]) if prop_value is True: # Points to inferred hwif input - return f"!({self.hwif.get_implied_prop_input_identifier(field, prop_name)})" + return f"!({self.hwif.get_implied_prop_input_identifier(field, complementary_pairs[prop_name])})" elif prop_value is False: # This should never happen, as this is checked by the compiler's validator raise RuntimeError diff --git a/peakrdl/regblock/field_logic/sw_onwrite.py b/peakrdl/regblock/field_logic/sw_onwrite.py index b204ceb..4211a63 100644 --- a/peakrdl/regblock/field_logic/sw_onwrite.py +++ b/peakrdl/regblock/field_logic/sw_onwrite.py @@ -8,7 +8,6 @@ if TYPE_CHECKING: from systemrdl.node import FieldNode # TODO: implement sw=w1 "write once" fields -# TODO: Implement swwe/swwel masking properties class _OnWrite(NextStateConditional): onwritetype = None @@ -17,8 +16,15 @@ class _OnWrite(NextStateConditional): def get_predicate(self, field: 'FieldNode') -> str: strb = self.exp.dereferencer.get_access_strobe(field) + + if field.get_property('swwe') or field.get_property('swwel'): + # dereferencer will wrap swwel complement if necessary + qualifier = self.exp.dereferencer.get_field_propref_value(field, 'swwe') + return f"{strb} && decoded_req_is_wr && {qualifier}" + return f"{strb} && decoded_req_is_wr" + def _wr_data(self, field: 'FieldNode') -> str: if field.msb < field.lsb: # Field gets bitswapped since it is in [low:high] orientation @@ -34,7 +40,7 @@ class WriteOneSet(_OnWrite): def get_assignments(self, field: 'FieldNode') -> List[str]: field_path = self.get_field_path(field) return [ - f"field_combo.{field_path}.next = field_storage.{field_path} | {self._wr_data(field)}];", + f"field_combo.{field_path}.next = field_storage.{field_path} | {self._wr_data(field)};", f"field_combo.{field_path}.load_next = '1;", ] diff --git a/setup.py b/setup.py index 8ad327e..96cfe24 100644 --- a/setup.py +++ b/setup.py @@ -21,6 +21,7 @@ setuptools.setup( url="https://github.com/SystemRDL/PeakRDL-regblock", packages=['peakrdl.regblock'], include_package_data=True, + python_requires='>=3.6', install_requires=[ "systemrdl-compiler>=1.21.0", "Jinja2>=2.11", @@ -30,11 +31,11 @@ setuptools.setup( "Development Status :: 3 - Alpha", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3 :: Only", "Intended Audience :: Developers", "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", @@ -42,6 +43,7 @@ setuptools.setup( "Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)", ), project_urls={ + "Documentation": "http://peakrdl-regblock.readthedocs.io", "Source": "https://github.com/SystemRDL/PeakRDL-regblock", "Tracker": "https://github.com/SystemRDL/PeakRDL-regblock/issues", }, diff --git a/test/README.md b/test/README.md index 7a11e2b..1c0133f 100644 --- a/test/README.md +++ b/test/README.md @@ -1,17 +1,80 @@ -ModelSim --------- +# Test Dependencies + +## ModelSim Testcases require an installation of ModelSim/QuestaSim, and for `vlog` & `vsim` commands to be visible via the PATH environment variable. ModelSim - Intel FPGA Edition can be downloaded for free from https://fpgasoftware.intel.com/ and is sufficient to run unit tests. -Running tests -------------- +## Python Packages +Install dependencies required for running tests + +```bash +python3 -m pip install test/requirements.txt ``` + +# Running tests + +Tests can be launched from the test directory using `pytest``. +Use ``pytest -n auto`` to run tests in parallel. + +To run all tests: +```bash cd test/ -python3 -m pip install requirements.txt -pytest -n auto +pytest ``` + +You can also run a specific testcase. For example: +```bash +cd test/test_hw_access +pytest +``` + + +# Test organization + +The goal for this test infrastructre is to make it easy to add small-standalone +testcases, with minimal repetition/boilerplate code that is usually present in +SystemVerilog testbenches. + +To accomplish this, Jinja templates are used extensively to generate the +resulting ``tb.sv`` file, as well as assist in dynamic testcase parameterization. + + + +## CPU Interfaces +Each CPU interface type is described in its own folder as follows: + +`lib/cpuifs//__init__.py` +: Definitions for CPU Interface test mode classes. + +`lib/cpuifs//tb_inst.sv` +: Jinja template that defines how the CPU interface is declared & instantiated in the testbench file. + +`lib/cpuifs//*.sv` +: Any other files required for compilation. + + + +## Testcase +Each testcase group has its own folder and contains the following: + +`test_*/__init__.py` +: Empty file required for test discovery. + +`test_*/regblock.rdl` +: Testcase RDL file. Testcase infrastructure will automatically compile this and generate the regblock output SystemVerilog. + +`test_*/tb_template.sv` +: Jinja template that defines the testcase-specific sequence. + +`test_*/testcase.py` +: Defines Python unittest testcase entry point. + + + +## Parameterization +Testcase classes can be parameterized using the [parameterized](https://github.com/wolever/parameterized) extension. This allows the same testcase to be run against multiple permutations of regblock export modes such as CPU interfaces, retiming flop stages, or even RDL parameterizations. diff --git a/test/lib/cpuifs/apb3/tb_inst.sv b/test/lib/cpuifs/apb3/tb_inst.sv index 6504608..7c3662d 100644 --- a/test/lib/cpuifs/apb3/tb_inst.sv +++ b/test/lib/cpuifs/apb3/tb_inst.sv @@ -1,3 +1,4 @@ +{% sv_line_anchor %} apb3_intf #( .DATA_WIDTH({{exporter.cpuif.data_width}}), .ADDR_WIDTH({{exporter.cpuif.addr_width}}) @@ -11,6 +12,7 @@ apb3_intf_driver #( .m_apb(s_apb) ); {% if type(cpuif).__name__.startswith("Flat") %} +{% sv_line_anchor %} wire s_apb_psel; wire s_apb_penable; wire s_apb_pwrite; diff --git a/test/lib/cpuifs/base.py b/test/lib/cpuifs/base.py index ad5de69..52280bb 100644 --- a/test/lib/cpuifs/base.py +++ b/test/lib/cpuifs/base.py @@ -6,6 +6,8 @@ import jinja2 as jj from peakrdl.regblock.cpuif.base import CpuifBase +from ..sv_line_anchor import SVLineAnchor + if TYPE_CHECKING: from peakrdl.regblock import RegblockExporter from ..regblock_testcase import RegblockTestCase @@ -30,13 +32,17 @@ class CpuifTestMode: def get_tb_inst(self, tb_cls: 'RegblockTestCase', exporter: 'RegblockExporter') -> str: - class_dir = os.path.dirname(inspect.getfile(self.__class__)) + + # For consistency, make the template root path relative to the test dir + template_root_path = os.path.join(os.path.dirname(__file__), "../..") + loader = jj.FileSystemLoader( - os.path.join(class_dir) + template_root_path ) jj_env = jj.Environment( loader=loader, undefined=jj.StrictUndefined, + extensions=[SVLineAnchor], ) context = { @@ -46,5 +52,11 @@ class CpuifTestMode: "type": type, } - template = jj_env.get_template(self.tb_template) + # template paths are relative to their class. + # transform to be relative to the root path + class_dir = os.path.dirname(inspect.getfile(self.__class__)) + template_local_path = os.path.join(class_dir, self.tb_template) + template_path = os.path.relpath(template_local_path, template_root_path) + template = jj_env.get_template(template_path) + return template.render(context) diff --git a/test/lib/regblock_testcase.py b/test/lib/regblock_testcase.py index 547ba7b..b426fed 100644 --- a/test/lib/regblock_testcase.py +++ b/test/lib/regblock_testcase.py @@ -5,11 +5,14 @@ import glob import shutil import subprocess import inspect +import pathlib import pytest import jinja2 as jj from systemrdl import RDLCompiler +from .sv_line_anchor import SVLineAnchor + from peakrdl.regblock import RegblockExporter from .cpuifs.base import CpuifTestMode from .cpuifs.apb3 import APB3 @@ -53,7 +56,7 @@ class RegblockTestCase(unittest.TestCase): @classmethod def get_build_dir(cls) -> str: this_dir = cls.get_testcase_dir() - build_dir = os.path.join(this_dir, cls.__name__ + ".out") + build_dir = os.path.join(this_dir, "run.out", cls.__name__) return build_dir @classmethod @@ -113,6 +116,7 @@ class RegblockTestCase(unittest.TestCase): jj_env = jj.Environment( loader=loader, undefined=jj.StrictUndefined, + extensions=[SVLineAnchor], ) context = { @@ -121,7 +125,7 @@ class RegblockTestCase(unittest.TestCase): } # template path needs to be relative to the Jinja loader root - template_path = os.path.join(cls.get_testcase_dir(), "tb.sv") + template_path = os.path.join(cls.get_testcase_dir(), "tb_template.sv") template_path = os.path.relpath(template_path, template_root_path) template = jj_env.get_template(template_path) @@ -173,7 +177,7 @@ class RegblockTestCase(unittest.TestCase): build_dir = cls.get_build_dir() if os.path.exists(build_dir): shutil.rmtree(build_dir) - os.mkdir(build_dir) + pathlib.Path(build_dir).mkdir(parents=True, exist_ok=True) cls._write_params() diff --git a/test/lib/sv_line_anchor.py b/test/lib/sv_line_anchor.py new file mode 100644 index 0000000..1f09ec1 --- /dev/null +++ b/test/lib/sv_line_anchor.py @@ -0,0 +1,10 @@ +from jinja2_simple_tags import StandaloneTag + +class SVLineAnchor(StandaloneTag): + """ + Define a custom Jinja tag that emits a SystemVerilog `line directive so that + assertion messages can get properly back-annotated + """ + tags = {"sv_line_anchor"} + def render(self): + return f'`line {self.lineno + 1} "{self.template}" 0' diff --git a/test/lib/templates/tb_base.sv b/test/lib/templates/tb_base.sv index 151c382..69d8363 100644 --- a/test/lib/templates/tb_base.sv +++ b/test/lib/templates/tb_base.sv @@ -1,3 +1,4 @@ +{% sv_line_anchor %} module tb; timeunit 1ns; timeprecision 1ps; @@ -51,9 +52,11 @@ module tb; //-------------------------------------------------------------------------- // DUT //-------------------------------------------------------------------------- + {% sv_line_anchor %} regblock dut (.*); {%- if exporter.hwif.has_output_struct %} + {% sv_line_anchor %} initial forever begin ##1; if(!rst) assert(!$isunknown({>>{hwif_out}})) else $error("hwif_out has X's!"); end @@ -82,6 +85,7 @@ module tb; //-------------------------------------------------------------------------- // Monitor for timeout //-------------------------------------------------------------------------- + {% sv_line_anchor %} initial begin ##{{cls.timeout_clk_cycles}}; $fatal(1, "Test timed out after {{cls.timeout_clk_cycles}} clock cycles"); diff --git a/test/requirements.txt b/test/requirements.txt index 28ef6f4..04be52b 100644 --- a/test/requirements.txt +++ b/test/requirements.txt @@ -1,3 +1,4 @@ pytest parameterized pytest-xdist +jinja2-simple-tags diff --git a/test/test_bitwise_reduce/__init__.py b/test/test_bitwise_reduce/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/test_bitwise_reduce/regblock.rdl b/test/test_bitwise_reduce/regblock.rdl new file mode 100644 index 0000000..6d770e1 --- /dev/null +++ b/test/test_bitwise_reduce/regblock.rdl @@ -0,0 +1,8 @@ +addrmap top { + reg { + field { + sw=rw; hw=r; + anded; ored; xored; + } f[7:0] = 0; + } r1; +}; diff --git a/test/test_bitwise_reduce/tb_template.sv b/test/test_bitwise_reduce/tb_template.sv new file mode 100644 index 0000000..85752ab --- /dev/null +++ b/test/test_bitwise_reduce/tb_template.sv @@ -0,0 +1,44 @@ +{% extends "lib/templates/tb_base.sv" %} + +{% block seq %} + {% sv_line_anchor %} + ##1; + cb.rst <= '0; + ##1; + + cpuif.write('h0, 'h00); + @cb; + assert(cb.hwif_out.r1.f.anded == 1'b0); + assert(cb.hwif_out.r1.f.ored == 1'b0); + assert(cb.hwif_out.r1.f.xored == 1'b0); + + cpuif.write('h0, 'h01); + @cb; + assert(cb.hwif_out.r1.f.anded == 1'b0); + assert(cb.hwif_out.r1.f.ored == 1'b1); + assert(cb.hwif_out.r1.f.xored == 1'b1); + + cpuif.write('h0, 'h02); + @cb; + assert(cb.hwif_out.r1.f.anded == 1'b0); + assert(cb.hwif_out.r1.f.ored == 1'b1); + assert(cb.hwif_out.r1.f.xored == 1'b1); + + cpuif.write('h0, 'h03); + @cb; + assert(cb.hwif_out.r1.f.anded == 1'b0); + assert(cb.hwif_out.r1.f.ored == 1'b1); + assert(cb.hwif_out.r1.f.xored == 1'b0); + + cpuif.write('h0, 'hFE); + @cb; + assert(cb.hwif_out.r1.f.anded == 1'b0); + assert(cb.hwif_out.r1.f.ored == 1'b1); + assert(cb.hwif_out.r1.f.xored == 1'b1); + + cpuif.write('h0, 'hFF); + @cb; + assert(cb.hwif_out.r1.f.anded == 1'b1); + assert(cb.hwif_out.r1.f.ored == 1'b1); + assert(cb.hwif_out.r1.f.xored == 1'b0); +{% endblock %} diff --git a/test/test_bitwise_reduce/testcase.py b/test/test_bitwise_reduce/testcase.py new file mode 100644 index 0000000..b458d3c --- /dev/null +++ b/test/test_bitwise_reduce/testcase.py @@ -0,0 +1,5 @@ +from ..lib.regblock_testcase import RegblockTestCase + +class Test(RegblockTestCase): + def test_dut(self): + self.run_test() diff --git a/test/test_field_types/tb.sv b/test/test_field_types/tb_template.sv similarity index 99% rename from test/test_field_types/tb.sv rename to test/test_field_types/tb_template.sv index 1d8dd00..3a5c728 100644 --- a/test/test_field_types/tb.sv +++ b/test/test_field_types/tb_template.sv @@ -1,6 +1,7 @@ {% extends "lib/templates/tb_base.sv" %} {% block seq %} + {% sv_line_anchor %} cb.hwif_in.r3.f.wel <= 1; ##1; cb.rst <= '0; diff --git a/test/test_hw_access/__init__.py b/test/test_hw_access/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/test_hw_access/regblock.rdl b/test/test_hw_access/regblock.rdl new file mode 100644 index 0000000..365dccd --- /dev/null +++ b/test/test_hw_access/regblock.rdl @@ -0,0 +1,63 @@ +addrmap top { + reg { + field { + sw=rw; hw=na; + } hw_enable[7:0] = 0xFF; + + field { + sw=rw; hw=na; + } hw_mask[15:8] = 0x00; + + field { + sw=rw; hw=na; + } hw_clr[16:16] = 0; + + field { + sw=rw; hw=na; + } hw_set[17:17] = 0; + + field { + sw=rw; hw=na; + } hw_we[18:18] = 0; + + field { + sw=rw; hw=na; + } hw_wel[20:20] = 1; + } hw_ctrl; + + + reg { + field { + sw=r; hw=w; + we; hwclr; hwset; + } f[7:0] = 0x11; + } r1; + r1.f->hwenable = hw_ctrl.hw_enable; + + + reg { + field { + sw=r; hw=w; + we; hwclr; hwset; + } f[7:0] = 0x22; + } r2; + r2.f->hwmask = hw_ctrl.hw_mask; + + + reg { + field { + sw=rw; hw=w; + } f[7:0] = 0x33; + } r3; + r3.f->hwenable = hw_ctrl.hw_enable; + r3.f->hwclr = hw_ctrl.hw_clr; + r3.f->hwset = hw_ctrl.hw_set; + r3.f->we = hw_ctrl.hw_we; + + reg { + field { + sw=rw; hw=w; + } f[7:0] = 0x44; + } r4; + r4.f->wel = hw_ctrl.hw_wel; +}; diff --git a/test/test_hw_access/tb_template.sv b/test/test_hw_access/tb_template.sv new file mode 100644 index 0000000..6128039 --- /dev/null +++ b/test/test_hw_access/tb_template.sv @@ -0,0 +1,94 @@ +{% extends "lib/templates/tb_base.sv" %} + +{% block seq %} + {% sv_line_anchor %} + ##1; + cb.rst <= '0; + ##1; + + // check initial conditions + cpuif.assert_read('h4, 'h11); + cpuif.assert_read('h8, 'h22); + cpuif.assert_read('hC, 'h33); + + //--------------------------------- + // set hwenable = F0 + cpuif.write('h0, 'h00_F0); + + // test hwenable + we + cb.hwif_in.r1.f.value <= 'hAB; + cb.hwif_in.r1.f.we <= '1; + @cb; + cb.hwif_in.r1.f.we <= '0; + cpuif.assert_read('h4, 'hA1); + + // test hwenable + hwclr + cb.hwif_in.r1.f.hwclr <= '1; + @cb; + cb.hwif_in.r1.f.hwclr <= '0; + cpuif.assert_read('h4, 'h01); + + // test hwenable + hwset + cb.hwif_in.r1.f.hwset <= '1; + @cb; + cb.hwif_in.r1.f.hwset <= '0; + cpuif.assert_read('h4, 'hF1); + + + //--------------------------------- + // set hwmask = F0 + cpuif.write('h0, 'hF0_00); + + // test hwmask + we + cb.hwif_in.r2.f.value <= 'hAB; + cb.hwif_in.r2.f.we <= '1; + @cb; + cb.hwif_in.r2.f.we <= '0; + cpuif.assert_read('h8, 'h2B); + + // test hwmask + hwclr + cb.hwif_in.r2.f.hwclr <= '1; + @cb; + cb.hwif_in.r2.f.hwclr <= '0; + cpuif.assert_read('h8, 'h20); + + // test hwmask + hwset + cb.hwif_in.r2.f.hwset <= '1; + @cb; + cb.hwif_in.r2.f.hwset <= '0; + cpuif.assert_read('h8, 'h2F); + + //--------------------------------- + // test hwenable + hwclr via reference + // toggle hwenable = F0, hwclr=1 + cpuif.write('h0, 'h1_00_F0); + cpuif.write('h0, 'h0_00_00); + cpuif.assert_read('hC, 'h03); + + // test hwenable + hwset via reference + // toggle hwenable = 0F, hwset=1 + cpuif.write('h0, 'h2_00_0F); + cpuif.write('h0, 'h0_00_00); + cpuif.assert_read('hC, 'h0F); + + // test hwenable + we via reference + cb.hwif_in.r3.f.value <= 'hAA; + // toggle hwenable = 0F, we=1 + cpuif.write('h0, 'h4_00_0F); + cpuif.write('h0, 'h0_00_00); + cpuif.assert_read('hC, 'h0A); + + //--------------------------------- + // test wel via reference + cb.hwif_in.r4.f.value <= 'hBB; + // toggle wel + cpuif.write('h0, 'h10_00_00); + cpuif.write('h0, 'h00_00_00); + cpuif.assert_read('h10, 'hBB); + + cb.hwif_in.r4.f.value <= 'hCC; + // toggle wel + cpuif.write('h0, 'h10_00_00); + cpuif.write('h0, 'h00_00_00); + cpuif.assert_read('h10, 'hCC); +{% endblock %} diff --git a/test/test_hw_access/testcase.py b/test/test_hw_access/testcase.py new file mode 100644 index 0000000..b458d3c --- /dev/null +++ b/test/test_hw_access/testcase.py @@ -0,0 +1,5 @@ +from ..lib.regblock_testcase import RegblockTestCase + +class Test(RegblockTestCase): + def test_dut(self): + self.run_test() diff --git a/test/test_onread_onwrite/__init__.py b/test/test_onread_onwrite/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/test_onread_onwrite/regblock.rdl b/test/test_onread_onwrite/regblock.rdl new file mode 100644 index 0000000..de196e5 --- /dev/null +++ b/test/test_onread_onwrite/regblock.rdl @@ -0,0 +1,61 @@ +addrmap top { + + reg { + field { + sw=rw; hw=na; + onread = rclr; + } f1[7:0] = 0xF0; + + field { + sw=rw; hw=na; + onread = rset; + } f2[15:8] = 0x0F; + } r1; + + + reg { + field { + sw=rw; hw=na; + onwrite = woset; + } f1[3:0] = 0x0; + + field { + sw=rw; hw=na; + onwrite = woclr; + } f2[7:4] = 0xF; + + field { + sw=rw; hw=na; + onwrite = wot; + } f3[11:8] = 0x0; + } r2; + + reg { + field { + sw=rw; hw=na; + onwrite = wzs; + } f1[3:0] = 0x0; + + field { + sw=rw; hw=na; + onwrite = wzc; + } f2[7:4] = 0xF; + + field { + sw=rw; hw=na; + onwrite = wzt; + } f3[11:8] = 0x0; + } r3; + + reg { + field { + sw=rw; hw=na; + onwrite = wclr; + } f1[7:0] = 0xF0; + + field { + sw=rw; hw=na; + onwrite = wset; + } f2[15:8] = 0x0F; + } r4; +}; diff --git a/test/test_onread_onwrite/tb_template.sv b/test/test_onread_onwrite/tb_template.sv new file mode 100644 index 0000000..3d5731f --- /dev/null +++ b/test/test_onread_onwrite/tb_template.sv @@ -0,0 +1,30 @@ +{% extends "lib/templates/tb_base.sv" %} + +{% block seq %} + {% sv_line_anchor %} + ##1; + cb.rst <= '0; + ##1; + + cpuif.assert_read('h0, 'h0F_F0); + cpuif.assert_read('h0, 'hFF_00); + cpuif.write ('h0, 'h00_FF); + cpuif.assert_read('h0, 'h00_FF); + cpuif.assert_read('h0, 'hFF_00); + + cpuif.assert_read('h4, 'h0_F_0); + cpuif.write ('h4, 'h1_1_1); + cpuif.assert_read('h4, 'h1_E_1); + cpuif.write ('h4, 'h1_2_2); + cpuif.assert_read('h4, 'h0_C_3); + + cpuif.assert_read('h8, 'h0_F_0); + cpuif.write ('h8, 'hE_E_E); + cpuif.assert_read('h8, 'h1_E_1); + cpuif.write ('h8, 'hE_D_D); + cpuif.assert_read('h8, 'h0_C_3); + + cpuif.assert_read('hC, 'h0F_F0); + cpuif.write ('hC, 'h12_34); + cpuif.assert_read('hC, 'hFF_00); +{% endblock %} diff --git a/test/test_onread_onwrite/testcase.py b/test/test_onread_onwrite/testcase.py new file mode 100644 index 0000000..b458d3c --- /dev/null +++ b/test/test_onread_onwrite/testcase.py @@ -0,0 +1,5 @@ +from ..lib.regblock_testcase import RegblockTestCase + +class Test(RegblockTestCase): + def test_dut(self): + self.run_test() diff --git a/test/test_read_fanin/tb.sv b/test/test_read_fanin/tb_template.sv similarity index 93% rename from test/test_read_fanin/tb.sv rename to test/test_read_fanin/tb_template.sv index 07d7531..3497728 100644 --- a/test/test_read_fanin/tb.sv +++ b/test/test_read_fanin/tb_template.sv @@ -1,12 +1,15 @@ {% extends "lib/templates/tb_base.sv" %} {%- block declarations %} + {% sv_line_anchor %} localparam REGWIDTH = {{cls.regwidth}}; localparam STRIDE = REGWIDTH/8; localparam N_REGS = {{cls.n_regs}}; {%- endblock %} + {% block seq %} + {% sv_line_anchor %} bit [REGWIDTH-1:0] data[N_REGS]; ##1; diff --git a/test/test_structural_sw_rw/tb.sv b/test/test_structural_sw_rw/tb_template.sv similarity index 98% rename from test/test_structural_sw_rw/tb.sv rename to test/test_structural_sw_rw/tb_template.sv index a51581c..f494278 100644 --- a/test/test_structural_sw_rw/tb.sv +++ b/test/test_structural_sw_rw/tb_template.sv @@ -1,6 +1,7 @@ {% extends "lib/templates/tb_base.sv" %} {% block seq %} + {% sv_line_anchor %} ##1; cb.rst <= '0; ##1; diff --git a/test/test_swacc_swmod/regblock.rdl b/test/test_swacc_swmod/regblock.rdl index 0bccf2c..73d9324 100644 --- a/test/test_swacc_swmod/regblock.rdl +++ b/test/test_swacc_swmod/regblock.rdl @@ -14,4 +14,12 @@ addrmap top { swmod; } f[8] = 20; } r2; + + reg { + field { + sw=rw; hw=r; + swmod; + rclr; + } f[8] = 30; + } r3; }; diff --git a/test/test_swacc_swmod/tb.sv b/test/test_swacc_swmod/tb_template.sv similarity index 72% rename from test/test_swacc_swmod/tb.sv rename to test/test_swacc_swmod/tb_template.sv index 28d1c2a..6b73d61 100644 --- a/test/test_swacc_swmod/tb.sv +++ b/test/test_swacc_swmod/tb_template.sv @@ -1,6 +1,7 @@ {% extends "lib/templates/tb_base.sv" %} {% block seq %} + {% sv_line_anchor %} logic [7:0] rd_data; logic [7:0] latched_data; int event_count; @@ -61,6 +62,28 @@ join_any disable fork; - // TODO: verify some other atypical swmod (onread actions) + // Verify that hwif changes 1 cycle after swmod + fork + begin + ##0; + forever begin + assert(hwif_out.r3.f.value == 30); + if(hwif_out.r3.f.swmod) break; + @cb; + end + @cb; + forever begin + assert(hwif_out.r3.f.value == 0); + assert(hwif_out.r3.f.swmod == 0); + @cb; + end + end + + begin + cpuif.assert_read('h2, 30); + @cb; + end + join_any + disable fork; {% endblock %} diff --git a/test/test_swwe/__init__.py b/test/test_swwe/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/test_swwe/regblock.rdl b/test/test_swwe/regblock.rdl new file mode 100644 index 0000000..c31fc29 --- /dev/null +++ b/test/test_swwe/regblock.rdl @@ -0,0 +1,66 @@ +addrmap top { + default regwidth = 8; + + reg { + field { + sw=rw; hw=na; + } r3_swwe[0:0] = 1; + + field { + sw=rw; hw=na; + } r4_swwel[1:1] = 0; + } lock; + + //--------------------------------- + // via inferred signal + //--------------------------------- + + reg { + field { + sw=rw; hw=na; + swwe; + } f[8] = 0x11; + } r1; + + reg { + field { + sw=rw; hw=na; + swwel; + } f[8] = 0x22; + } r2; + + //--------------------------------- + // via lock register + //--------------------------------- + + reg { + field { + sw=rw; hw=na; + } f[8] = 0x33; + } r3; + r3.f->swwe = lock.r3_swwe; + + reg { + field { + sw=rw; hw=na; + } f[8] = 0x44; + } r4; + r4.f->swwel = lock.r4_swwel; + + //--------------------------------- + // via prop ref chaining + //--------------------------------- + reg { + field { + sw=rw; hw=na; + } f[8] = 0x55; + } r5; + r5.f->swwe = r3.f->swwe; + + reg { + field { + sw=rw; hw=na; + } f[8] = 0x66; + } r6; + r6.f->swwe = r4.f->swwel; // intentionally opposite! +}; diff --git a/test/test_swwe/tb_template.sv b/test/test_swwe/tb_template.sv new file mode 100644 index 0000000..ac89f98 --- /dev/null +++ b/test/test_swwe/tb_template.sv @@ -0,0 +1,63 @@ +{% extends "lib/templates/tb_base.sv" %} + +{% block seq %} + {% sv_line_anchor %} + ##1; + cb.rst <= '0; + ##1; + + // r1 swwe = true + cpuif.assert_read('h1, 'h11); + cb.hwif_in.r1.f.swwe <= '0; + cpuif.write ('h1, 'h12); + cpuif.assert_read('h1, 'h11); + cb.hwif_in.r1.f.swwe <= '1; + cpuif.write ('h1, 'h13); + cpuif.assert_read('h1, 'h13); + + // r2 swwel = true + cpuif.assert_read('h2, 'h22); + cb.hwif_in.r2.f.swwel <= '1; + cpuif.write ('h2, 'h23); + cpuif.assert_read('h2, 'h22); + cb.hwif_in.r2.f.swwel <= '0; + cpuif.write ('h2, 'h24); + cpuif.assert_read('h2, 'h24); + + // r3 swwe = lock.r3_swwe + cpuif.assert_read('h3, 'h33); + cpuif.write ('h0, 'h0); + cpuif.write ('h3, 'h32); + cpuif.assert_read('h3, 'h33); + cpuif.write ('h0, 'h1); + cpuif.write ('h3, 'h34); + cpuif.assert_read('h3, 'h34); + + // r4 swwel = lock.r4_swwel + cpuif.assert_read('h4, 'h44); + cpuif.write ('h0, 'h2); + cpuif.write ('h4, 'h40); + cpuif.assert_read('h4, 'h44); + cpuif.write ('h0, 'h0); + cpuif.write ('h4, 'h45); + cpuif.assert_read('h4, 'h45); + + // r5 swwe = r3->swwe = lock.r3_swwe + cpuif.assert_read('h5, 'h55); + cpuif.write ('h0, 'h0); + cpuif.write ('h5, 'h52); + cpuif.assert_read('h5, 'h55); + cpuif.write ('h0, 'h1); + cpuif.write ('h5, 'h54); + cpuif.assert_read('h5, 'h54); + + // r6 swwe = r4->swwel = lock.r4_swwel + cpuif.assert_read('h6, 'h66); + cpuif.write ('h0, 'h0); + cpuif.write ('h6, 'h60); + cpuif.assert_read('h6, 'h66); + cpuif.write ('h0, 'h2); + cpuif.write ('h6, 'h65); + cpuif.assert_read('h6, 'h65); + +{% endblock %} diff --git a/test/test_swwe/testcase.py b/test/test_swwe/testcase.py new file mode 100644 index 0000000..b458d3c --- /dev/null +++ b/test/test_swwe/testcase.py @@ -0,0 +1,5 @@ +from ..lib.regblock_testcase import RegblockTestCase + +class Test(RegblockTestCase): + def test_dut(self): + self.run_test()