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

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::
:hidden:
@@ -11,4 +56,3 @@ Welcome to PeakRDL-regblock's documentation!
props/addrmap
props/signal
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
supported properties, see the appropriate property listing page in the previous
supported properties, see the appropriate property listing page in the following
sections.

View File

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

View File

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

View File

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

View File

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

View File

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

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:
# 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

View File

@@ -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;",
]

View File

@@ -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",
},

View File

@@ -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/<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 #(
.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;

View File

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

View File

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

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;
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");

View File

@@ -1,3 +1,4 @@
pytest
parameterized
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" %}
{% block seq %}
{% sv_line_anchor %}
cb.hwif_in.r3.f.wel <= 1;
##1;
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" %}
{%- 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;

View File

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

View File

@@ -14,4 +14,12 @@ addrmap top {
swmod;
} f[8] = 20;
} 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" %}
{% 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 %}

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()