More testcases & documentation

This commit is contained in:
Alex Mykyta
2021-12-04 17:24:19 -08:00
parent f70bdf774c
commit 3adf7e1328
44 changed files with 827 additions and 63 deletions

View File

@@ -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 # PeakRDL-regblock
Generate SystemVerilog RTL that implements a register block from compiled SystemRDL input. Generate SystemVerilog RTL that implements a register block from compiled SystemRDL input.
## Installing ## Documentation
(Not published to PyPi yet) See the [PeakRDL-regblock Documentation](http://peakrdl-regblock.readthedocs.io) for more details

26
doc/architecture.rst Normal file
View File

@@ -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

7
doc/cpuif/advanced.rst Normal file
View File

@@ -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

11
doc/cpuif/apb3.rst Normal file
View File

@@ -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

8
doc/hwif.rst Normal file
View File

@@ -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

View File

@@ -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 <https://github.com/SystemRDL/PeakRDL-regblock>`_
- `Release Notes <https://github.com/SystemRDL/PeakRDL-regblock/releases>`_
- `Issue tracker <https://github.com/SystemRDL/PeakRDL-regblock/issues>`_
- `PyPi <https://pypi.org/project/peakrdl-regblock>`_
- `SystemRDL Specification <http://accellera.org/downloads/standards/systemrdl>`_
.. toctree::
:hidden:
self
architecture
hwif
limitations
.. toctree::
:hidden:
:caption: CPU Interfaces
cpuif/apb3
cpuif/advanced
.. toctree:: .. toctree::
:hidden: :hidden:
@@ -11,4 +56,3 @@ Welcome to PeakRDL-regblock's documentation!
props/addrmap props/addrmap
props/signal props/signal
props/rhs_props props/rhs_props
limitations

View File

@@ -2,7 +2,7 @@ Known Issues & Limitations
========================== ==========================
Not all SystemRDL features are supported by this exporter. For a listing of 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. sections.

View File

@@ -1,8 +1,8 @@
Addrmap/Regfile Properties Addrmap/Regfile Properties
========================== ==========================
.. note:: Any properties not explicitly listed here are either implicitly supported, .. note:: Any properties not explicitly listed here are either implicitly
or are not relevant to the regblock exporter and are ignored. supported, or are not relevant to the regblock exporter and are ignored.
errextbus errextbus
@@ -14,6 +14,7 @@ sharedextbus
|NO| |NO|
--------------------------------------------------------------------------------
Addrmap Properties Addrmap Properties
================== ==================

View File

@@ -1,15 +1,15 @@
Field Properties Field Properties
================ ================
.. note:: Any properties not explicitly listed here are either implicitly supported, .. note:: Any properties not explicitly listed here are either implicitly
or are not relevant to the regblock exporter and are ignored. supported, or are not relevant to the regblock exporter and are ignored.
Software Access Properties Software Access Properties
-------------------------- --------------------------
onread/onwrite onread/onwrite
^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^
|EX| |OK|
rclr/rset rclr/rset
^^^^^^^^^ ^^^^^^^^^
@@ -25,9 +25,11 @@ sw
swacc 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:: .. wavedrom::
@@ -40,9 +42,10 @@ If true, infers an output signal ``swacc`` that is asserted as the field is samp
swmod 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:: .. wavedrom::
@@ -56,28 +59,34 @@ If true, infers an output signal ``swmod`` that is asserted as the field is bein
swwe/swwel swwe/swwel
^^^^^^^^^^ ^^^^^^^^^^
TODO: Describe result Provides a mechanism that allows hardware to override whether the field is
writable by software.
boolean boolean
|NO| |OK|
bit If True, infers an input signal ``hwif_in..swwe`` or ``hwif_in..swwel``.
|NO|
reference reference
|NO| |OK|
woclr/woset woclr/woset
^^^^^^^^^^^ ^^^^^^^^^^^
See ``onwrite`` See ``onwrite``
--------------------------------------------------------------------------------
Hardware Access Properties Hardware Access Properties
-------------------------- --------------------------
anded/ored/xored anded/ored/xored
^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^
|EX| |OK|
If true, infers the existence of output signal: ``hwif_out..anded``,
``hwif_out..ored``, ``hwif_out..xored``
hw hw
@@ -86,19 +95,27 @@ hw
hwclr/hwset 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 boolean
|EX| |OK|
If true, infers the existence of input signal: ``hwif_in..hwclr``, ``hwif_in..hwset``
reference reference
|EX| |OK|
hwenable/hwmask hwenable/hwmask
^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^
|EX| |OK|
we/wel 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:: .. wavedrom::
@@ -113,12 +130,14 @@ Write-enable control from hardware interface
boolean boolean
|OK| |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 reference
|EX| |OK|
--------------------------------------------------------------------------------
Counter Properties Counter Properties
------------------ ------------------
@@ -212,6 +231,8 @@ underflow
|NO| |NO|
--------------------------------------------------------------------------------
Interrupt Properties Interrupt Properties
-------------------- --------------------
@@ -244,6 +265,8 @@ stickybit
|NO| |NO|
--------------------------------------------------------------------------------
Misc Misc
---- ----

View File

@@ -1,8 +1,8 @@
Register Properties Register Properties
=================== ===================
.. note:: Any properties not explicitly listed here are either implicitly supported, .. note:: Any properties not explicitly listed here are either implicitly
or are not relevant to the regblock exporter and are ignored. supported, or are not relevant to the regblock exporter and are ignored.
accesswidth accesswidth
----------- -----------

View File

@@ -1,6 +1,20 @@
RHS Property References 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 Field
----- -----
@@ -8,30 +22,68 @@ swacc
^^^^^ ^^^^^
|EX| |EX|
Single-cycle strobe that indicates the field is being sampled during a software
read operation.
swmod swmod
^^^^^ ^^^^^
|EX| |EX|
Single-cycle strobe that indicates the field is being modified during a software
access operation.
swwe/swwel swwe/swwel
^^^^^^^^^^ ^^^^^^^^^^
|EX| |OK|
Represents the signal that controls the owning field's swwe/swwel behavior.
anded/ored/xored anded/ored/xored
^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^
|EX| |EX|
Represents the current and/or/xor reduction of the owning field's value.
hwclr/hwset hwclr/hwset
^^^^^^^^^^^ ^^^^^^^^^^^
|EX| |EX|
Represents the signal that controls the owning field's hwclr/hwset behavior.
hwenable/hwmask hwenable/hwmask
^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^
|EX| |EX|
Represents the signal that controls the owning field's hwenable/hwmask behavior.
we/wel we/wel
^^^^^^ ^^^^^^
|EX| |EX|
next
^^^^
|EX|
reset
^^^^^
|EX|
resetsignal
^^^^^^^^^^^
|EX|
--------------------------------------------------------------------------------
Field Counter Properties
------------------------
Represents the signal that controls the owning field's we/wel behavior.
decr decr
^^^^ ^^^^
|NO| |NO|
@@ -72,6 +124,11 @@ underflow
^^^^^^^^^ ^^^^^^^^^
|NO| |NO|
--------------------------------------------------------------------------------
Field Interrupt Properties
--------------------------
enable enable
^^^^^^ ^^^^^^
|EX| |EX|
@@ -88,19 +145,8 @@ mask
^^^^ ^^^^
|EX| |EX|
next
^^^^
|EX|
reset
^^^^^
|EX|
resetsignal
^^^^^^^^^^^
|EX|
--------------------------------------------------------------------------------
Register Register
-------- --------

View File

@@ -1,8 +1,8 @@
Signal Properties Signal Properties
================= =================
.. note:: Any properties not explicitly listed here are either implicitly supported, .. note:: Any properties not explicitly listed here are either implicitly
or are not relevant to the regblock exporter and are ignored. supported, or are not relevant to the regblock exporter and are ignored.
activehigh/activelow activehigh/activelow

View File

@@ -1 +1 @@
__version__ = "0.1.0" __version__ = "0.1.0-a1"

View File

@@ -81,12 +81,14 @@ class Dereferencer:
def get_field_propref_value(self, field: FieldNode, prop_name: str) -> str: def get_field_propref_value(self, field: FieldNode, prop_name: str) -> str:
# Value reduction properties. # Value reduction properties.
# Wrap with the appropriate Verilog reduction operator # Wrap with the appropriate Verilog reduction operator
val = self.get_value(field)
if prop_name == "anded": if prop_name == "anded":
val = self.get_value(field)
return f"&({val})" return f"&({val})"
elif prop_name == "ored": elif prop_name == "ored":
val = self.get_value(field)
return f"|({val})" return f"|({val})"
elif prop_name == "xored": elif prop_name == "xored":
val = self.get_value(field)
return f"^({val})" return f"^({val})"
# references that directly access a property value # references that directly access a property value
@@ -159,7 +161,7 @@ class Dereferencer:
prop_value = field.get_property(complementary_pairs[prop_name]) prop_value = field.get_property(complementary_pairs[prop_name])
if prop_value is True: if prop_value is True:
# Points to inferred hwif input # 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: elif prop_value is False:
# This should never happen, as this is checked by the compiler's validator # This should never happen, as this is checked by the compiler's validator
raise RuntimeError raise RuntimeError

View File

@@ -8,7 +8,6 @@ if TYPE_CHECKING:
from systemrdl.node import FieldNode from systemrdl.node import FieldNode
# TODO: implement sw=w1 "write once" fields # TODO: implement sw=w1 "write once" fields
# TODO: Implement swwe/swwel masking properties
class _OnWrite(NextStateConditional): class _OnWrite(NextStateConditional):
onwritetype = None onwritetype = None
@@ -17,8 +16,15 @@ class _OnWrite(NextStateConditional):
def get_predicate(self, field: 'FieldNode') -> str: def get_predicate(self, field: 'FieldNode') -> str:
strb = self.exp.dereferencer.get_access_strobe(field) 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" return f"{strb} && decoded_req_is_wr"
def _wr_data(self, field: 'FieldNode') -> str: def _wr_data(self, field: 'FieldNode') -> str:
if field.msb < field.lsb: if field.msb < field.lsb:
# Field gets bitswapped since it is in [low:high] orientation # 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]: def get_assignments(self, field: 'FieldNode') -> List[str]:
field_path = self.get_field_path(field) field_path = self.get_field_path(field)
return [ 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;", f"field_combo.{field_path}.load_next = '1;",
] ]

View File

@@ -21,6 +21,7 @@ setuptools.setup(
url="https://github.com/SystemRDL/PeakRDL-regblock", url="https://github.com/SystemRDL/PeakRDL-regblock",
packages=['peakrdl.regblock'], packages=['peakrdl.regblock'],
include_package_data=True, include_package_data=True,
python_requires='>=3.6',
install_requires=[ install_requires=[
"systemrdl-compiler>=1.21.0", "systemrdl-compiler>=1.21.0",
"Jinja2>=2.11", "Jinja2>=2.11",
@@ -30,11 +31,11 @@ setuptools.setup(
"Development Status :: 3 - Alpha", "Development Status :: 3 - Alpha",
"Programming Language :: Python", "Programming Language :: Python",
"Programming Language :: Python :: 3", "Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3 :: Only",
"Intended Audience :: Developers", "Intended Audience :: Developers",
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)", "License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
@@ -42,6 +43,7 @@ setuptools.setup(
"Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)", "Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)",
), ),
project_urls={ project_urls={
"Documentation": "http://peakrdl-regblock.readthedocs.io",
"Source": "https://github.com/SystemRDL/PeakRDL-regblock", "Source": "https://github.com/SystemRDL/PeakRDL-regblock",
"Tracker": "https://github.com/SystemRDL/PeakRDL-regblock/issues", "Tracker": "https://github.com/SystemRDL/PeakRDL-regblock/issues",
}, },

View File

@@ -1,17 +1,80 @@
ModelSim # Test Dependencies
--------
## ModelSim
Testcases require an installation of ModelSim/QuestaSim, and for `vlog` & `vsim` Testcases require an installation of ModelSim/QuestaSim, and for `vlog` & `vsim`
commands to be visible via the PATH environment variable. 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. 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/ cd test/
python3 -m pip install requirements.txt pytest
pytest -n auto
``` ```
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/<type>/__init__.py`
: Definitions for CPU Interface test mode classes.
`lib/cpuifs/<type>/tb_inst.sv`
: Jinja template that defines how the CPU interface is declared & instantiated in the testbench file.
`lib/cpuifs/<type>/*.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.

View File

@@ -1,3 +1,4 @@
{% sv_line_anchor %}
apb3_intf #( apb3_intf #(
.DATA_WIDTH({{exporter.cpuif.data_width}}), .DATA_WIDTH({{exporter.cpuif.data_width}}),
.ADDR_WIDTH({{exporter.cpuif.addr_width}}) .ADDR_WIDTH({{exporter.cpuif.addr_width}})
@@ -11,6 +12,7 @@ apb3_intf_driver #(
.m_apb(s_apb) .m_apb(s_apb)
); );
{% if type(cpuif).__name__.startswith("Flat") %} {% if type(cpuif).__name__.startswith("Flat") %}
{% sv_line_anchor %}
wire s_apb_psel; wire s_apb_psel;
wire s_apb_penable; wire s_apb_penable;
wire s_apb_pwrite; wire s_apb_pwrite;

View File

@@ -6,6 +6,8 @@ import jinja2 as jj
from peakrdl.regblock.cpuif.base import CpuifBase from peakrdl.regblock.cpuif.base import CpuifBase
from ..sv_line_anchor import SVLineAnchor
if TYPE_CHECKING: if TYPE_CHECKING:
from peakrdl.regblock import RegblockExporter from peakrdl.regblock import RegblockExporter
from ..regblock_testcase import RegblockTestCase from ..regblock_testcase import RegblockTestCase
@@ -30,13 +32,17 @@ class CpuifTestMode:
def get_tb_inst(self, tb_cls: 'RegblockTestCase', exporter: 'RegblockExporter') -> str: 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( loader = jj.FileSystemLoader(
os.path.join(class_dir) template_root_path
) )
jj_env = jj.Environment( jj_env = jj.Environment(
loader=loader, loader=loader,
undefined=jj.StrictUndefined, undefined=jj.StrictUndefined,
extensions=[SVLineAnchor],
) )
context = { context = {
@@ -46,5 +52,11 @@ class CpuifTestMode:
"type": type, "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) return template.render(context)

View File

@@ -5,11 +5,14 @@ import glob
import shutil import shutil
import subprocess import subprocess
import inspect import inspect
import pathlib
import pytest import pytest
import jinja2 as jj import jinja2 as jj
from systemrdl import RDLCompiler from systemrdl import RDLCompiler
from .sv_line_anchor import SVLineAnchor
from peakrdl.regblock import RegblockExporter from peakrdl.regblock import RegblockExporter
from .cpuifs.base import CpuifTestMode from .cpuifs.base import CpuifTestMode
from .cpuifs.apb3 import APB3 from .cpuifs.apb3 import APB3
@@ -53,7 +56,7 @@ class RegblockTestCase(unittest.TestCase):
@classmethod @classmethod
def get_build_dir(cls) -> str: def get_build_dir(cls) -> str:
this_dir = cls.get_testcase_dir() 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 return build_dir
@classmethod @classmethod
@@ -113,6 +116,7 @@ class RegblockTestCase(unittest.TestCase):
jj_env = jj.Environment( jj_env = jj.Environment(
loader=loader, loader=loader,
undefined=jj.StrictUndefined, undefined=jj.StrictUndefined,
extensions=[SVLineAnchor],
) )
context = { context = {
@@ -121,7 +125,7 @@ class RegblockTestCase(unittest.TestCase):
} }
# template path needs to be relative to the Jinja loader root # 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_path = os.path.relpath(template_path, template_root_path)
template = jj_env.get_template(template_path) template = jj_env.get_template(template_path)
@@ -173,7 +177,7 @@ class RegblockTestCase(unittest.TestCase):
build_dir = cls.get_build_dir() build_dir = cls.get_build_dir()
if os.path.exists(build_dir): if os.path.exists(build_dir):
shutil.rmtree(build_dir) shutil.rmtree(build_dir)
os.mkdir(build_dir) pathlib.Path(build_dir).mkdir(parents=True, exist_ok=True)
cls._write_params() cls._write_params()

View File

@@ -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'

View File

@@ -1,3 +1,4 @@
{% sv_line_anchor %}
module tb; module tb;
timeunit 1ns; timeunit 1ns;
timeprecision 1ps; timeprecision 1ps;
@@ -51,9 +52,11 @@ module tb;
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
// DUT // DUT
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
{% sv_line_anchor %}
regblock dut (.*); regblock dut (.*);
{%- if exporter.hwif.has_output_struct %} {%- if exporter.hwif.has_output_struct %}
{% sv_line_anchor %}
initial forever begin initial forever begin
##1; if(!rst) assert(!$isunknown({>>{hwif_out}})) else $error("hwif_out has X's!"); ##1; if(!rst) assert(!$isunknown({>>{hwif_out}})) else $error("hwif_out has X's!");
end end
@@ -82,6 +85,7 @@ module tb;
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
// Monitor for timeout // Monitor for timeout
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
{% sv_line_anchor %}
initial begin initial begin
##{{cls.timeout_clk_cycles}}; ##{{cls.timeout_clk_cycles}};
$fatal(1, "Test timed out after {{cls.timeout_clk_cycles}} clock cycles"); $fatal(1, "Test timed out after {{cls.timeout_clk_cycles}} clock cycles");

View File

@@ -1,3 +1,4 @@
pytest pytest
parameterized parameterized
pytest-xdist pytest-xdist
jinja2-simple-tags

View File

View File

@@ -0,0 +1,8 @@
addrmap top {
reg {
field {
sw=rw; hw=r;
anded; ored; xored;
} f[7:0] = 0;
} r1;
};

View File

@@ -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 %}

View File

@@ -0,0 +1,5 @@
from ..lib.regblock_testcase import RegblockTestCase
class Test(RegblockTestCase):
def test_dut(self):
self.run_test()

View File

@@ -1,6 +1,7 @@
{% extends "lib/templates/tb_base.sv" %} {% extends "lib/templates/tb_base.sv" %}
{% block seq %} {% block seq %}
{% sv_line_anchor %}
cb.hwif_in.r3.f.wel <= 1; cb.hwif_in.r3.f.wel <= 1;
##1; ##1;
cb.rst <= '0; cb.rst <= '0;

View File

View File

@@ -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;
};

View File

@@ -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 %}

View File

@@ -0,0 +1,5 @@
from ..lib.regblock_testcase import RegblockTestCase
class Test(RegblockTestCase):
def test_dut(self):
self.run_test()

View File

View File

@@ -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;
};

View File

@@ -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 %}

View File

@@ -0,0 +1,5 @@
from ..lib.regblock_testcase import RegblockTestCase
class Test(RegblockTestCase):
def test_dut(self):
self.run_test()

View File

@@ -1,12 +1,15 @@
{% extends "lib/templates/tb_base.sv" %} {% extends "lib/templates/tb_base.sv" %}
{%- block declarations %} {%- block declarations %}
{% sv_line_anchor %}
localparam REGWIDTH = {{cls.regwidth}}; localparam REGWIDTH = {{cls.regwidth}};
localparam STRIDE = REGWIDTH/8; localparam STRIDE = REGWIDTH/8;
localparam N_REGS = {{cls.n_regs}}; localparam N_REGS = {{cls.n_regs}};
{%- endblock %} {%- endblock %}
{% block seq %} {% block seq %}
{% sv_line_anchor %}
bit [REGWIDTH-1:0] data[N_REGS]; bit [REGWIDTH-1:0] data[N_REGS];
##1; ##1;

View File

@@ -1,6 +1,7 @@
{% extends "lib/templates/tb_base.sv" %} {% extends "lib/templates/tb_base.sv" %}
{% block seq %} {% block seq %}
{% sv_line_anchor %}
##1; ##1;
cb.rst <= '0; cb.rst <= '0;
##1; ##1;

View File

@@ -14,4 +14,12 @@ addrmap top {
swmod; swmod;
} f[8] = 20; } f[8] = 20;
} r2; } r2;
reg {
field {
sw=rw; hw=r;
swmod;
rclr;
} f[8] = 30;
} r3;
}; };

View File

@@ -1,6 +1,7 @@
{% extends "lib/templates/tb_base.sv" %} {% extends "lib/templates/tb_base.sv" %}
{% block seq %} {% block seq %}
{% sv_line_anchor %}
logic [7:0] rd_data; logic [7:0] rd_data;
logic [7:0] latched_data; logic [7:0] latched_data;
int event_count; int event_count;
@@ -61,6 +62,28 @@
join_any join_any
disable fork; 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 %} {% endblock %}

View File

View File

@@ -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!
};

View File

@@ -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 %}

View File

@@ -0,0 +1,5 @@
from ..lib.regblock_testcase import RegblockTestCase
class Test(RegblockTestCase):
def test_dut(self):
self.run_test()