testcase framework

This commit is contained in:
Alex Mykyta
2021-11-21 19:00:47 -08:00
parent d3c876a491
commit f70bdf774c
69 changed files with 1730 additions and 403 deletions

4
.gitignore vendored
View File

@@ -3,6 +3,10 @@
**/.venv
**/.coverage
**/*.rpt
**/.pytest_cache
**/_build
**/*.out
**/transcript
build/
dist/

View File

@@ -1,9 +1,5 @@
[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/peakrdl-regblock.svg)](https://pypi.org/project/peakrdl-regblock)
# PeakRDL-regblock
Generate SystemVerilog RTL that implements a register block from compiled SystemRDL input.
## Installing
Install from [PyPi](https://pypi.org/project/peakrdl-regblock) using pip:
python3 -m pip install peakrdl-regblock
(Not published to PyPi yet)

View File

@@ -29,7 +29,9 @@ author = 'Alex Mykyta'
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
"sphinxcontrib.wavedrom",
]
render_using_wavedrompy = True
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
@@ -50,4 +52,26 @@ html_theme = "sphinx_rtd_theme"
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
html_static_path = []
rst_epilog = """
.. |iNO| image:: /img/err.svg
:width: 18px
:class: no-scaled-link
.. |iEX| image:: /img/warn.svg
:width: 18px
:class: no-scaled-link
.. |iOK| image:: /img/ok.svg
:width: 18px
:class: no-scaled-link
.. |NO| replace:: |iNO| Not Supported
.. |EX| replace:: |iEX| Experimental
.. |OK| replace:: |iOK| Supported
"""

53
doc/img/err.svg Normal file
View File

@@ -0,0 +1,53 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
viewBox="0 0 512 512"
version="1.1"
id="svg4"
sodipodi:docname="times-circle.svg"
inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)">
<metadata
id="metadata10">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs8" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="983"
id="namedview6"
showgrid="false"
inkscape:zoom="0.4609375"
inkscape:cx="18.440678"
inkscape:cy="245.15254"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1"
inkscape:current-layer="svg4" />
<!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) -->
<path
d="M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm121.6 313.1c4.7 4.7 4.7 12.3 0 17L338 377.6c-4.7 4.7-12.3 4.7-17 0L256 312l-65.1 65.6c-4.7 4.7-12.3 4.7-17 0L134.4 338c-4.7-4.7-4.7-12.3 0-17l65.6-65-65.6-65.1c-4.7-4.7-4.7-12.3 0-17l39.6-39.6c4.7-4.7 12.3-4.7 17 0l65 65.7 65.1-65.6c4.7-4.7 12.3-4.7 17 0l39.6 39.6c4.7 4.7 4.7 12.3 0 17L312 256l65.6 65.1z"
id="path2"
style="fill:#b40000;fill-opacity:1" />
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

53
doc/img/ok.svg Normal file
View File

@@ -0,0 +1,53 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
viewBox="0 0 512 512"
version="1.1"
id="svg4"
sodipodi:docname="check-circle.svg"
inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)">
<metadata
id="metadata10">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs8" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="983"
id="namedview6"
showgrid="false"
inkscape:zoom="0.4609375"
inkscape:cx="-402.44068"
inkscape:cy="247.32203"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1"
inkscape:current-layer="svg4" />
<!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) -->
<path
d="M504 256c0 136.967-111.033 248-248 248S8 392.967 8 256 119.033 8 256 8s248 111.033 248 248zM227.314 387.314l184-184c6.248-6.248 6.248-16.379 0-22.627l-22.627-22.627c-6.248-6.249-16.379-6.249-22.628 0L216 308.118l-70.059-70.059c-6.248-6.248-16.379-6.248-22.628 0l-22.627 22.627c-6.248 6.248-6.248 16.379 0 22.627l104 104c6.249 6.249 16.379 6.249 22.628.001z"
id="path2"
style="fill:#00b405;fill-opacity:1" />
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

53
doc/img/warn.svg Normal file
View File

@@ -0,0 +1,53 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
viewBox="0 0 576 512"
version="1.1"
id="svg4"
sodipodi:docname="exclamation-triangle.svg"
inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)">
<metadata
id="metadata10">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs8" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="983"
id="namedview6"
showgrid="false"
inkscape:zoom="0.4609375"
inkscape:cx="46.101695"
inkscape:cy="256"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1"
inkscape:current-layer="svg4" />
<!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) -->
<path
d="M569.517 440.013C587.975 472.007 564.806 512 527.94 512H48.054c-36.937 0-59.999-40.055-41.577-71.987L246.423 23.985c18.467-32.009 64.72-31.951 83.154 0l239.94 416.028zM288 354c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z"
id="path2"
style="fill:#ffa705;fill-opacity:1" />
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -1,20 +1,14 @@
.. PeakRDL-regblock documentation master file, created by
sphinx-quickstart on Tue Nov 16 23:25:58 2021.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Welcome to PeakRDL-regblock's documentation!
============================================
.. toctree::
:maxdepth: 2
:caption: Contents:
:hidden:
:caption: Property Support
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
props/field
props/reg
props/addrmap
props/signal
props/rhs_props
limitations

27
doc/limitations.rst Normal file
View File

@@ -0,0 +1,27 @@
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
sections.
External Components
-------------------
Regfiles, registers & fields instantiated using the ``external`` keyword are not supported yet.
Alias Registers
---------------
Registers instantiated using the ``alias`` keyword are not supported yet.
Unaligned Registers
-------------------
All address offsets & strides shall be a multiple of the regwidth used. Specifically:
* Each register's address and array stride shall be aligned to it's regwidth.
* Each regfile or addrmap shall use an offset and stride that is a multiple of the largest regwidth it encloses.

View File

@@ -76,6 +76,60 @@ Write about this in the SystemRDL errata?
Maybe add some words to the "clarifications" section
================================================================================
Unit Testing
================================================================================
I NEED to start building a suite of unit-tests!
Goal:
- Small easy-to-understand testcases
- Parameterized testcases to rerun testcases with different cpuifs, etc.
- coverage
Split it into the following components:
Common testbench SV infrastructure
Make a generic SV framework that can be re-used everywhere
Use SV interfaces/classes and even `include preprocessor tricks to make it possible
to swap out for specific testcases:
- cpuif abstraction layer
Need to be able to swap out to different CPU interfaces easily
- Clocks/resets
- DUT instantiation
Will need to account for minor variations in port list somehow
Maybe a good time to use the .* method?
- Helper functions, assertion library, etc.
Testcase-specific
SV sequence file that issues transactions and asserts things
Dispatch tests completely through pytest
- Each testcase has its own folder with:
testcase-specific SV file(s)
RDL file
pytest entry point .py file
- build up py utility functions that will:
Export the testcase-specific RDL --> SV
Compile and run the simulation
need to deal with timeouts if the RTL deadlocks somehow. Limit of how many uS to run?
Query sim result for pass/fail
- Each testcase folder will likely have multiple subtests
- Variations to RDL export:
- different cpuif
- pipe stages
- etc.
- Different test sequences
may be necessary to test the same compilation in different ways
I can imagine it may not be possible to do everything from a single test sequence.
May require the sim to reset to T-0 for fresh-slate.
- Handle these variations using pytest testcases & parameterizations as appropriate.
Possibly something like:
- Each pytest class --> unique compilation/elaboration
pytest parameters to expand this for export variations
- Each pytest class method --> simulation sequence
- Collect coverage!
install the tool in a venv, collect exporter coverage, etc.
TBD if i want to deal with SV coverage (is that even allowed in modelsim free?)
================================================================================
Dev Todo list
================================================================================

View File

@@ -4,7 +4,7 @@ to stuff via a normalized interface.
For example, if RDL defines:
my_field->next = my_other_field
Then in Python (or a template) I could do:
x = my_field.get_property("next")
x = my_field.get_property('next')
y = dereferencer.get(x)
and trust that I'll get a value/identifier/whatever that accurately represents
the value being referenced

View File

@@ -82,7 +82,7 @@ X If a node ispresent=true, and any of it's properties are a reference,
then those references' ispresent shall also be true
This is an explicit clause in the spec: 5.3.1-i
! Flag illegal sw actions if not readable/writable
X Flag illegal sw actions if not readable/writable
The following combinations dont get flagged currently:
sw=w; rclr;
sw=w; rset;
@@ -91,15 +91,15 @@ X If a node ispresent=true, and any of it's properties are a reference,
their counterparts do get flagged. such as:
sw=w; onread=rclr;
X Signals marked as field_reset or cpuif_reset need to have activehigh/activelow
specified. (8.2.1-d states that activehigh/low does not have an implied default state if unset!)
Also aplies to signals referenced by resetsignal
! hwclr/hwset/we/wel probably shouldn't be able to reference itself
y->hwclr = y;
y->we = y;
... it works, but should it be allowed? Seems like user-error
! Signals marked as field_reset or cpuif_reset need to have activehigh/activelow
specified. (8.2.1-d states that activehigh/low does not have an implied default state if unset!)
Also aplies to signals referenced by fieldreset
@@ -126,7 +126,7 @@ X Warn/error on any signal with cpuif_reset set, that is not in the top-level
! async data signals
Only supporting async signals if they are exclusively used in resets.
Anyhting else declared as "async" shall be an error
Anyhting else declared as "async" shall emit a warning that it is ignored
I have zero interest in implementing resynchronizers
! Error if a property references a non-signal component, or property reference from

31
doc/props/addrmap.rst Normal file
View File

@@ -0,0 +1,31 @@
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.
errextbus
---------
|NO|
sharedextbus
------------
|NO|
Addrmap Properties
==================
bigendian/littleendian
----------------------
|NO|
bridge
------
|NO|
rsvdset
-------
|NO|

276
doc/props/field.rst Normal file
View File

@@ -0,0 +1,276 @@
Field Properties
================
.. 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|
rclr/rset
^^^^^^^^^
See ``onread``
singlepulse
^^^^^^^^^^^
|NO|
sw
^^^
|OK|
swacc
^^^^^
|EX|
If true, infers an output signal ``swacc`` that is asserted as the field is sampled for a software read operation.
.. wavedrom::
{signal: [
{name: 'clk', wave: 'p....'},
{name: 'hwif_in..value', wave: 'x.=x.', data: ['D']},
{name: 'hwif_out..swacc', wave: '0.10.'}
]}
swmod
^^^^^
|EX|
If true, infers an output signal ``swmod`` that is asserted as the field is being modified by software.
.. wavedrom::
{signal: [
{name: 'clk', wave: 'p.....'},
{name: 'hwif_out..value', wave: '=..=..', data: ['old', 'new']},
{name: 'hwif_out..swmod', wave: '0.10..'}
]}
swwe/swwel
^^^^^^^^^^
TODO: Describe result
boolean
|NO|
bit
|NO|
reference
|NO|
woclr/woset
^^^^^^^^^^^
See ``onwrite``
Hardware Access Properties
--------------------------
anded/ored/xored
^^^^^^^^^^^^^^^^
|EX|
hw
^^^
|OK|
hwclr/hwset
^^^^^^^^^^^
boolean
|EX|
reference
|EX|
hwenable/hwmask
^^^^^^^^^^^^^^^
|EX|
we/wel
^^^^^^
Write-enable control from hardware interface
.. wavedrom::
{signal: [
{name: 'clk', wave: 'p....'},
{name: 'hwif_in..value', wave: 'x.=x.', data: ['D']},
{name: 'hwif_in..we', wave: '0.10.',},
{name: 'hwif_in..wel', wave: '1.01.',},
{name: '<field value>', wave: 'x..=.', data: ['D']}
]}
boolean
|OK|
if set, infers the existence of input signal ``hwif_in..we`` or ``hwif_in..wel``
reference
|EX|
Counter Properties
------------------
counter
^^^^^^^
|NO|
decr
^^^^
reference
|NO|
decrthreshold
^^^^^^^^^^^^^
boolean
|NO|
bit
|NO|
reference
|NO|
decrsaturate
^^^^^^^^^^^^
boolean
|NO|
bit
|NO|
reference
|NO|
decrvalue
^^^^^^^^^
bit
|NO|
reference
|NO|
decrwidth
^^^^^^^^^
|NO|
incr
^^^^
|NO|
incrsaturate/saturate
^^^^^^^^^^^^^^^^^^^^^
boolean
|NO|
bit
|NO|
reference
|NO|
incrthreshold/threshold
^^^^^^^^^^^^^^^^^^^^^^^
boolean
|NO|
bit
|NO|
reference
|NO|
incrvalue
^^^^^^^^^
bit
|NO|
reference
|NO|
incrwidth
^^^^^^^^^
|NO|
overflow
^^^^^^^^
|NO|
underflow
^^^^^^^^^
|NO|
Interrupt Properties
--------------------
enable
^^^^^^
|NO|
haltenable
^^^^^^^^^^
|NO|
haltmask
^^^^^^^^
|NO|
intr
^^^^
|NO|
mask
^^^^
|NO|
sticky
^^^^^^
|NO|
stickybit
^^^^^^^^^
|NO|
Misc
----
encode
^^^^^^
|NO|
next
^^^^
|NO|
paritycheck
^^^^^^^^^^^
|NO|
precedence
^^^^^^^^^^
|EX|
reset
^^^^^
bit
|OK|
reference
|EX|
resetsignal
^^^^^^^^^^^
|EX|

19
doc/props/reg.rst Normal file
View File

@@ -0,0 +1,19 @@
Register Properties
===================
.. note:: Any properties not explicitly listed here are either implicitly supported,
or are not relevant to the regblock exporter and are ignored.
accesswidth
-----------
|NO|
Only ``accesswidth`` that is equal to the ``regwidth`` is supported (default if unset)
regwidth
--------
|OK|
shared
------
|NO|

114
doc/props/rhs_props.rst Normal file
View File

@@ -0,0 +1,114 @@
RHS Property References
=======================
Field
-----
swacc
^^^^^
|EX|
swmod
^^^^^
|EX|
swwe/swwel
^^^^^^^^^^
|EX|
anded/ored/xored
^^^^^^^^^^^^^^^^
|EX|
hwclr/hwset
^^^^^^^^^^^
|EX|
hwenable/hwmask
^^^^^^^^^^^^^^^
|EX|
we/wel
^^^^^^
|EX|
decr
^^^^
|NO|
decrthreshold
^^^^^^^^^^^^^
|NO|
decrsaturate
^^^^^^^^^^^^
|NO|
decrvalue
^^^^^^^^^
|EX|
incr
^^^^
|NO|
incrsaturate/saturate
^^^^^^^^^^^^^^^^^^^^^
|NO|
incrthreshold/threshold
^^^^^^^^^^^^^^^^^^^^^^^
|NO|
incrvalue
^^^^^^^^^
|EX|
overflow
^^^^^^^^
|NO|
underflow
^^^^^^^^^
|NO|
enable
^^^^^^
|EX|
haltenable
^^^^^^^^^^
|EX|
haltmask
^^^^^^^^
|EX|
mask
^^^^
|EX|
next
^^^^
|EX|
reset
^^^^^
|EX|
resetsignal
^^^^^^^^^^^
|EX|
Register
--------
intr
^^^^
|NO|
halt
^^^^
|NO|

25
doc/props/signal.rst Normal file
View File

@@ -0,0 +1,25 @@
Signal Properties
=================
.. note:: Any properties not explicitly listed here are either implicitly supported,
or are not relevant to the regblock exporter and are ignored.
activehigh/activelow
--------------------
|EX|
sync/async
----------
|EX|
Only supported for signals used as resets to infer edge-sensitive reset.
Ignored in all other contexts.
cpuif_reset
-----------
|EX|
field_reset
-----------
|EX|

View File

@@ -1 +1,2 @@
pygments-systemrdl
sphinxcontrib-wavedrom

View File

@@ -1,21 +0,0 @@
#!/usr/bin/env python3
import sys
from systemrdl import RDLCompiler, RDLCompileError
from peakrdl.regblock import RegblockExporter
input_files = sys.argv[1:]
rdlc = RDLCompiler()
try:
for input_file in input_files:
rdlc.compile_file(input_file)
root = rdlc.elaborate()
except RDLCompileError:
sys.exit(1)
R = RegblockExporter()
R.export(root, ".")

View File

@@ -14,7 +14,6 @@ class APB3_Cpuif(CpuifBase):
class APB3_Cpuif_flattened(APB3_Cpuif):
@property
def port_declaration(self) -> str:
# TODO: Reference data/addr width from verilog parameter perhaps?
lines = [
"input wire " + self.signal("psel"),
"input wire " + self.signal("penable"),

View File

@@ -57,11 +57,11 @@ class Dereferencer:
# must be a constant value as defined by its reset value
reset_value = obj.get_property('reset')
if reset_value is not None:
return f"'h{reset_value:x}"
return self.get_value(reset_value)
else:
# No reset value defined!
# Fall back to a value of 0
return "'h0"
# Callers shall ensure this is impossible
raise RuntimeError
if isinstance(obj, SignalNode):
# Signals are always inputs from the hwif

View File

@@ -72,8 +72,8 @@ class RegblockExporter:
package_name = kwargs.pop("package_name", module_name + "_pkg")
# Pipelining options
retime_read_response = kwargs.pop("retime_read_response", True)
retime_read_fanin = kwargs.pop("retime_read_fanin", False)
retime_read_response = kwargs.pop("retime_read_response", True)
# Check for stray kwargs
if kwargs:

View File

@@ -109,7 +109,7 @@ class FieldLogic:
set or clear side effect).
"""
w_modifiable = field.is_sw_writable
r_modifiable = (field.get_property("onread") is not None)
r_modifiable = (field.get_property('onread') is not None)
strb = self.exp.dereferencer.get_access_strobe(field)
if w_modifiable and not r_modifiable:

View File

@@ -74,13 +74,13 @@ class FieldLogicGenerator(RDLForLoopGenerator):
for signal in conditional.get_extra_combo_signals(node):
extra_combo_signals[signal.name] = signal
sig = node.get_property("resetsignal")
sig = node.get_property('resetsignal')
if sig is not None:
resetsignal = RDLSignal(sig)
else:
resetsignal = self.exp.default_resetsignal
reset_value = node.get_property("reset")
reset_value = node.get_property('reset')
if reset_value is not None:
reset_value_str = self.exp.dereferencer.get_value(reset_value)
else:
@@ -106,38 +106,39 @@ class FieldLogicGenerator(RDLForLoopGenerator):
# Field value output
if self.exp.hwif.has_value_output(node):
output_identifier = self.exp.hwif.get_output_identifier(node)
value = self.exp.dereferencer.get_value(node)
self.add_content(
f"assign {output_identifier} = field_storage.{field_path};"
f"assign {output_identifier} = {value};"
)
# Inferred logical reduction outputs
if node.get_property("anded"):
if node.get_property('anded'):
output_identifier = self.exp.hwif.get_implied_prop_output_identifier(node, "anded")
value = self.exp.dereferencer.get_field_propref_value(node, "anded")
self.add_content(
f"assign {output_identifier} = {value};"
)
if node.get_property("ored"):
if node.get_property('ored'):
output_identifier = self.exp.hwif.get_implied_prop_output_identifier(node, "ored")
value = self.exp.dereferencer.get_field_propref_value(node, "ored")
self.add_content(
f"assign {output_identifier} = {value};"
)
if node.get_property("xored"):
if node.get_property('xored'):
output_identifier = self.exp.hwif.get_implied_prop_output_identifier(node, "xored")
value = self.exp.dereferencer.get_field_propref_value(node, "xored")
self.add_content(
f"assign {output_identifier} = {value};"
)
if node.get_property("swmod"):
if node.get_property('swmod'):
output_identifier = self.exp.hwif.get_implied_prop_output_identifier(node, "swmod")
value = self.field_logic.get_swmod_identifier(node)
self.add_content(
f"assign {output_identifier} = {value};"
)
if node.get_property("swacc"):
if node.get_property('swacc'):
output_identifier = self.exp.hwif.get_implied_prop_output_identifier(node, "swacc")
value = self.field_logic.get_swacc_identifier(node)
self.add_content(

View File

@@ -10,7 +10,7 @@ if TYPE_CHECKING:
class _OnRead(NextStateConditional):
onreadtype = None
def is_match(self, field: 'FieldNode') -> bool:
return field.get_property("onread") == self.onreadtype
return field.get_property('onread') == self.onreadtype
def get_predicate(self, field: 'FieldNode') -> str:
strb = self.exp.dereferencer.get_access_strobe(field)

View File

@@ -13,7 +13,7 @@ if TYPE_CHECKING:
class _OnWrite(NextStateConditional):
onwritetype = None
def is_match(self, field: 'FieldNode') -> bool:
return field.get_property("onwrite") == self.onwritetype
return field.is_sw_writable and field.get_property('onwrite') == self.onwritetype
def get_predicate(self, field: 'FieldNode') -> str:
strb = self.exp.dereferencer.get_access_strobe(field)

View File

@@ -80,7 +80,7 @@ class Hwif:
empty_array_suffix="x"
)
if is_input:
return f'{base}_in_t'
return f'{base}__in_t'
return f'{base}__out_t'
def _do_struct_addressable(self, lines:list, node:AddressableNode, is_input:bool = True) -> bool:
@@ -159,22 +159,22 @@ class Hwif:
contents.append(f"logic {prop_name};")
# Generate any implied counter inputs
if node.get_property("counter"):
if not node.get_property("incr"):
if node.get_property('counter'):
if not node.get_property('incr'):
# User did not provide their own incr component reference.
# Imply an input
contents.append("logic incr;")
if not node.get_property("decr"):
if not node.get_property('decr'):
# User did not provide their own decr component reference.
# Imply an input
contents.append("logic decr;")
width = node.get_property("incrwidth")
width = node.get_property('incrwidth')
if width:
# Implies a corresponding incrvalue input
contents.append(f"logic [{width-1}:0] incrvalue;")
width = node.get_property("decrwidth")
width = node.get_property('decrwidth')
if width:
# Implies a corresponding decrvalue input
contents.append(f"logic [{width-1}:0] decrvalue;")

View File

@@ -104,4 +104,4 @@ class ReadbackAssignmentGenerator(RDLForLoopGenerator):
super().pop_loop()
# Advance current scope's offset to account for loop's contents
self.current_offset += n_regs * dim - 1
self.current_offset = start_offset + n_regs * dim

View File

@@ -5,10 +5,10 @@ logic [{{cpuif.data_width-1}}:0] readback_array[{{array_size}}];
{% if do_fanin_stage %}
// fanin stage
logic [31:0] readback_array_c[{{fanin_array_size}}];
logic [{{cpuif.data_width-1}}:0] readback_array_c[{{fanin_array_size}}];
for(genvar g=0; g<{{fanin_loop_iter}}; g++) begin
always_comb begin
automatic logic [31:0] readback_data_var;
automatic logic [{{cpuif.data_width-1}}:0] readback_data_var;
readback_data_var = '0;
for(int i=g*{{fanin_stride}}; i<((g+1)*{{fanin_stride}}); i++) readback_data_var |= readback_array[i];
readback_array_c[g] = readback_data_var;
@@ -18,14 +18,14 @@ end
assign readback_array_c[{{fanin_array_size-1}}] = readback_array[{{array_size-1}}];
{%- elif fanin_residual_stride > 1 %}
always_comb begin
automatic logic [31:0] readback_data_var;
automatic logic [{{cpuif.data_width-1}}:0] readback_data_var;
readback_data_var = '0;
for(int i={{(fanin_array_size-1) * fanin_stride}}; i<{{array_size-1}}; i++) readback_data_var |= readback_array[i];
for(int i={{(fanin_array_size-1) * fanin_stride}}; i<{{array_size}}; i++) readback_data_var |= readback_array[i];
readback_array_c[{{fanin_array_size-1}}] = readback_data_var;
end
{%- endif %}
logic [31:0] readback_array_r[{{fanin_array_size}}];
logic [{{cpuif.data_width-1}}:0] readback_array_r[{{fanin_array_size}}];
logic readback_done_r;
always_ff @(posedge clk) begin
if(rst) begin
@@ -39,8 +39,8 @@ end
// Reduce the array
always_comb begin
automatic logic [31:0] readback_data_var;
readback_done = decoded_req & ~decoded_req_is_wr;
automatic logic [{{cpuif.data_width-1}}:0] readback_data_var;
readback_done = readback_done_r;
readback_err = '0;
readback_data_var = '0;
for(int i=0; i<{{fanin_array_size}}; i++) readback_data_var |= readback_array_r[i];

View File

@@ -22,7 +22,7 @@ class DesignScanner(RDLListener):
def enter_Reg(self, node: 'RegNode') -> None:
# The CPUIF's bus width is sized according to the largest register in the design
self.cpuif_data_width = max(self.cpuif_data_width, node.get_property("regwidth"))
self.cpuif_data_width = max(self.cpuif_data_width, node.get_property('regwidth'))
# TODO: Collect any references to signals that lie outside of the hierarchy
# These will be added as top-level signals

View File

@@ -50,11 +50,11 @@ class RDLSignal(SignalBase):
@property
def is_async(self) -> bool:
return self.rdl_signal.get_property("async")
return self.rdl_signal.get_property('async')
@property
def is_activehigh(self) -> bool:
return self.rdl_signal.get_property("activehigh")
return self.rdl_signal.get_property('activehigh')
@property
def width(self) -> int:

3
test/.gitignore vendored
View File

@@ -1,3 +0,0 @@
work
transcript
*.wlf

17
test/README.md Normal file
View File

@@ -0,0 +1,17 @@
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
-------------
```
cd test/
python3 -m pip install requirements.txt
pytest -n auto
```

0
test/__init__.py Normal file
View File

0
test/lib/__init__.py Normal file
View File

View File

View File

@@ -0,0 +1,14 @@
from ..base import CpuifTestMode
from peakrdl.regblock.cpuif.apb3 import APB3_Cpuif, APB3_Cpuif_flattened
class APB3(CpuifTestMode):
cpuif_cls = APB3_Cpuif
tb_files = [
"apb3_intf.sv",
"apb3_intf_driver.sv",
]
tb_template = "tb_inst.sv"
class FlatAPB3(APB3):
cpuif_cls = APB3_Cpuif_flattened

View File

@@ -3,6 +3,7 @@ interface apb3_intf_driver #(
parameter ADDR_WIDTH = 32
)(
input wire clk,
input wire rst,
apb3_intf.master m_apb
);
@@ -84,12 +85,25 @@ interface apb3_intf_driver #(
// Wait for response
while(cb.PREADY !== 1'b1) @(cb);
assert(!$isunknown(cb.PRDATA)) else $error("Read from 0x%0x returned X's on PRDATA", addr);
assert(!$isunknown(cb.PSLVERR)) else $error("Read from 0x%0x returned X's on PSLVERR", addr);
data = cb.PRDATA;
reset();
endtask
task assert_read(logic [ADDR_WIDTH-1:0] addr, logic [DATA_WIDTH-1:0] expected_data);
logic [DATA_WIDTH-1:0] data;
read(addr, data);
assert(data == expected_data) else $error("Read from 0x%x returned 0x%x. Expected 0x%x", addr, data, expected_data);
endtask
initial begin
reset();
end
initial forever begin
@cb;
if(!rst) assert(!$isunknown(cb.PREADY)) else $error("Saw X on PREADY!");
end
endinterface

View File

@@ -0,0 +1,30 @@
apb3_intf #(
.DATA_WIDTH({{exporter.cpuif.data_width}}),
.ADDR_WIDTH({{exporter.cpuif.addr_width}})
) s_apb();
apb3_intf_driver #(
.DATA_WIDTH({{exporter.cpuif.data_width}}),
.ADDR_WIDTH({{exporter.cpuif.addr_width}})
) cpuif(
.clk(clk),
.rst(rst),
.m_apb(s_apb)
);
{% if type(cpuif).__name__.startswith("Flat") %}
wire s_apb_psel;
wire s_apb_penable;
wire s_apb_pwrite;
wire [{{exporter.cpuif.addr_width - 1}}:0] s_apb_paddr;
wire [{{exporter.cpuif.data_width - 1}}:0] s_apb_pwdata;
wire s_apb_pready;
wire [{{exporter.cpuif.data_width - 1}}:0] s_apb_prdata;
wire s_apb_pslverr;
assign s_apb_psel = s_apb.PSEL;
assign s_apb_penable = s_apb.PENABLE;
assign s_apb_pwrite = s_apb.PWRITE;
assign s_apb_paddr = s_apb.PADDR;
assign s_apb_pwdata = s_apb.PWDATA;
assign s_apb.PREADY = s_apb_pready;
assign s_apb.PRDATA = s_apb_prdata;
assign s_apb.PSLVERR = s_apb_pslverr;
{% endif %}

50
test/lib/cpuifs/base.py Normal file
View File

@@ -0,0 +1,50 @@
from typing import List, TYPE_CHECKING
import os
import inspect
import jinja2 as jj
from peakrdl.regblock.cpuif.base import CpuifBase
if TYPE_CHECKING:
from peakrdl.regblock import RegblockExporter
from ..regblock_testcase import RegblockTestCase
class CpuifTestMode:
cpuif_cls = None # type: CpuifBase
tb_files = [] # type: List[str]
tb_template = ""
def get_tb_files(self) -> List[str]:
class_dir = os.path.dirname(inspect.getfile(self.__class__))
cwd = os.getcwd()
tb_files = []
for file in self.tb_files:
relpath = os.path.relpath(
os.path.join(class_dir, file),
cwd
)
tb_files.append(relpath)
return tb_files
def get_tb_inst(self, tb_cls: 'RegblockTestCase', exporter: 'RegblockExporter') -> str:
class_dir = os.path.dirname(inspect.getfile(self.__class__))
loader = jj.FileSystemLoader(
os.path.join(class_dir)
)
jj_env = jj.Environment(
loader=loader,
undefined=jj.StrictUndefined,
)
context = {
"cpuif": self,
"cls": tb_cls,
"exporter": exporter,
"type": type,
}
template = jj_env.get_template(self.tb_template)
return template.render(context)

View File

@@ -0,0 +1,232 @@
from typing import Optional, List
import unittest
import os
import glob
import shutil
import subprocess
import inspect
import pytest
import jinja2 as jj
from systemrdl import RDLCompiler
from peakrdl.regblock import RegblockExporter
from .cpuifs.base import CpuifTestMode
from .cpuifs.apb3 import APB3
class RegblockTestCase(unittest.TestCase):
#: Path to the testcase's RDL file.
#: Relative to the testcase's dir. If unset, the first RDL file found in the
#: testcase dir will be used
rdl_file = None # type: Optional[str]
#: RDL type name to elaborate. If unset, compiler will automatically choose
#: the top.
rdl_elab_target = None # type: Optional[str]
#: Parameters to pass into RDL elaboration
rdl_elab_params = {}
#: Define what CPUIF to use for this testcase
cpuif = APB3() # type: CpuifTestMode
# Other exporter args:
retime_read_fanin = False
retime_read_response = False
#: Abort test if it exceeds this number of clock cycles
timeout_clk_cycles = 1000
#: this gets auto-loaded via the _load_request autouse fixture
request = None # type: pytest.FixtureRequest
@pytest.fixture(autouse=True)
def _load_request(self, request):
self.request = request
@classmethod
def get_testcase_dir(cls) -> str:
class_dir = os.path.dirname(inspect.getfile(cls))
return class_dir
@classmethod
def get_build_dir(cls) -> str:
this_dir = cls.get_testcase_dir()
build_dir = os.path.join(this_dir, cls.__name__ + ".out")
return build_dir
@classmethod
def _write_params(cls) -> None:
"""
Write out the class parameters to a file so that it is easier to debug
how a testcase was parameterized
"""
path = os.path.join(cls.get_build_dir(), "params.txt")
with open(path, 'w') as f:
for k, v in cls.__dict__.items():
if k.startswith("_") or callable(v):
continue
f.write(f"{k}: {repr(v)}\n")
@classmethod
def _export_regblock(cls) -> RegblockExporter:
"""
Call the peakrdl.regblock exporter to generate the DUT
"""
this_dir = cls.get_testcase_dir()
if cls.rdl_file:
rdl_file = cls.rdl_file
else:
# Find any *.rdl file in testcase dir
rdl_file = glob.glob(os.path.join(this_dir, "*.rdl"))[0]
rdlc = RDLCompiler()
rdlc.compile_file(rdl_file)
root = rdlc.elaborate(cls.rdl_elab_target, "regblock", cls.rdl_elab_params)
exporter = RegblockExporter()
exporter.export(
root,
cls.get_build_dir(),
module_name="regblock",
package_name="regblock_pkg",
cpuif_cls=cls.cpuif.cpuif_cls,
retime_read_fanin=cls.retime_read_fanin,
retime_read_response=cls.retime_read_response,
)
return exporter
@classmethod
def _generate_tb(cls, exporter: RegblockExporter):
"""
Render the testbench template into actual tb.sv
"""
template_root_path = os.path.join(os.path.dirname(__file__), "..")
loader = jj.FileSystemLoader(
template_root_path
)
jj_env = jj.Environment(
loader=loader,
undefined=jj.StrictUndefined,
)
context = {
"cls": cls,
"exporter": exporter,
}
# 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.relpath(template_path, template_root_path)
template = jj_env.get_template(template_path)
output_path = os.path.join(cls.get_build_dir(), "tb.sv")
stream = template.stream(context)
stream.dump(output_path)
@classmethod
def _compile_tb(cls):
# CD into the build directory
cwd = os.getcwd()
os.chdir(cls.get_build_dir())
cmd = [
"vlog", "-sv", "-quiet", "-l", "build.log",
# Free version of ModelSim throws errors if generate/endgenerate
# blocks are not used.
# These have been made optional long ago. Modern versions of SystemVerilog do
# not require them and I prefer not to add them.
"-suppress", "2720",
# Ignore noisy warning about vopt-time checking of always_comb/always_latch
"-suppress", "2583",
]
# Add CPUIF sources
cmd.extend(cls.cpuif.get_tb_files())
# Add DUT sources
cmd.append("regblock_pkg.sv")
cmd.append("regblock.sv")
# Add TB
cmd.append("tb.sv")
# Run command!
try:
subprocess.run(cmd, check=True)
finally:
# cd back
os.chdir(cwd)
@classmethod
def setUpClass(cls):
# Create fresh build dir
build_dir = cls.get_build_dir()
if os.path.exists(build_dir):
shutil.rmtree(build_dir)
os.mkdir(build_dir)
cls._write_params()
# Convert testcase RDL file --> SV
exporter = cls._export_regblock()
# Create testbench from template
cls._generate_tb(exporter)
cls._compile_tb()
def setUp(self) -> None:
# cd into the build directory
self.original_cwd = os.getcwd()
os.chdir(self.get_build_dir())
def run_test(self, plusargs:List[str] = None) -> None:
plusargs = plusargs or []
test_name = self.request.node.name
# call vsim
cmd = [
"vsim", "-quiet",
"-msgmode", "both",
"-do", "set WildcardFilter [lsearch -not -all -inline $WildcardFilter Memory]",
"-do", "log -r /*;",
"-do", "run -all; exit;",
"-c",
"-l", "%s.log" % test_name,
"-wlf", "%s.wlf" % test_name,
"tb",
]
for plusarg in plusargs:
cmd.append("+" + plusarg)
subprocess.run(cmd, check=True)
self.assertSimLogPass("%s.log" % test_name)
def tearDown(self) -> None:
# cd back
os.chdir(self.original_cwd)
def assertSimLogPass(self, path: str):
self.assertTrue(os.path.isfile(path))
with open(path, encoding="utf-8") as f:
for line in f:
if line.startswith("# ** Error"):
self.fail(line)
elif line.startswith("# ** Fatal"):
self.fail(line)

View File

@@ -0,0 +1,90 @@
module tb;
timeunit 1ns;
timeprecision 1ps;
logic rst = '1;
logic clk = '0;
initial forever begin
#10ns;
clk = ~clk;
end
//--------------------------------------------------------------------------
// DUT Signal declarations
//--------------------------------------------------------------------------
{%- if exporter.hwif.has_input_struct %}
regblock_pkg::regblock__in_t hwif_in;
{%- endif %}
{%- if exporter.hwif.has_output_struct %}
regblock_pkg::regblock__out_t hwif_out;
{%- endif %}
{%- block declarations %}
{%- endblock %}
//--------------------------------------------------------------------------
// Clocking
//--------------------------------------------------------------------------
default clocking cb @(posedge clk);
default input #1step output #1;
output rst;
{%- if exporter.hwif.has_input_struct %}
output hwif_in;
{%- endif %}
{%- if exporter.hwif.has_output_struct %}
input hwif_out;
{%- endif %}
{%- filter indent %}
{%- block clocking_dirs %}
{%- endblock %}
{%- endfilter %}
endclocking
//--------------------------------------------------------------------------
// CPUIF
//--------------------------------------------------------------------------
{{cls.cpuif.get_tb_inst(cls, exporter)|indent}}
//--------------------------------------------------------------------------
// DUT
//--------------------------------------------------------------------------
regblock dut (.*);
{%- if exporter.hwif.has_output_struct %}
initial forever begin
##1; if(!rst) assert(!$isunknown({>>{hwif_out}})) else $error("hwif_out has X's!");
end
{%- endif %}
//--------------------------------------------------------------------------
// Test Sequence
//--------------------------------------------------------------------------
initial begin
cb.rst <= '1;
{%- if exporter.hwif.has_input_struct %}
cb.hwif_in <= '{default: '0};
{%- endif %}
begin
{%- filter indent(8) %}
{%- block seq %}
{%- endblock %}
{%- endfilter %}
end
##5;
$finish();
end
//--------------------------------------------------------------------------
// Monitor for timeout
//--------------------------------------------------------------------------
initial begin
##{{cls.timeout_clk_cycles}};
$fatal(1, "Test timed out after {{cls.timeout_clk_cycles}} clock cycles");
end
endmodule

23
test/lib/test_params.py Normal file
View File

@@ -0,0 +1,23 @@
from itertools import product
from .cpuifs.apb3 import APB3, FlatAPB3
all_cpuif = [
APB3(),
FlatAPB3(),
]
def get_permutations(spec):
param_list = []
for v in product(*spec.values()):
param_list.append(dict(zip(spec, v)))
return param_list
#-------------------------------------------------------------------------------
# TODO: this wont scale well. Create groups of permutatuions. not necessary to permute everything all the time.
TEST_PARAMS = get_permutations({
"cpuif": all_cpuif,
"retime_read_fanin": [True, False],
"retime_read_response": [True, False],
})

2
test/pytest.ini Normal file
View File

@@ -0,0 +1,2 @@
[pytest]
python_files = test_*.py testcase.py

3
test/requirements.txt Normal file
View File

@@ -0,0 +1,3 @@
pytest
parameterized
pytest-xdist

View File

@@ -1,8 +0,0 @@
#!/bin/bash
set -e
../export.py test_regblock.rdl
vlog -sv -f vlog_args.f -f src.f
vsim -c -quiet tb -do "log -r /*; run -all; exit;"

View File

@@ -1,5 +0,0 @@
interfaces/apb3_intf.sv
drivers/apb3_intf_driver.sv
test_regblock_pkg.sv
test_regblock.sv
tb.sv

View File

@@ -1,51 +0,0 @@
module tb;
timeunit 1ns;
timeprecision 1ps;
logic rst = '1;
logic clk = '0;
initial forever begin
#10ns;
clk = ~clk;
end
apb3_intf apb();
apb3_intf_driver driver(
.clk(clk),
.m_apb(apb)
);
test_regblock dut (
.clk(clk),
.rst(rst),
.s_apb(apb),
.hwif_out()
);
initial begin
logic [31:0] rd_data;
repeat(5) @(posedge clk);
rst = '0;
repeat(5) @(posedge clk);
driver.read('h000, rd_data);
driver.write('h000, 'h0);
driver.read('h000, rd_data);
driver.read('h100, rd_data);
driver.write('h100, 'h0);
driver.read('h100, rd_data);
driver.read('h000, rd_data);
driver.write('h000, 'hFFFF_FFFF);
driver.read('h000, rd_data);
repeat(5) @(posedge clk);
$finish();
end
endmodule

View File

View File

@@ -0,0 +1,64 @@
addrmap top {
default regwidth = 8;
// All the valid combinations from Table 12
reg {
field {
sw=rw; hw=rw; we; // Storage element
} f[8] = 10;
} r1;
reg {
field {
sw=rw; hw=r; // Storage element
} f[8] = 20;
} r2;
reg {
field {
sw=rw; hw=w; wel; // Storage element
} f[8] = 30;
} r3;
reg {
field {
sw=rw; hw=na; // Storage element
} f[8] = 40;
} r4;
reg {
field {
sw=r; hw=rw; we; // Storage element
} f[8] = 50;
} r5;
reg {
field {
sw=r; hw=r; // Wire/Bus - constant value
} f[8] = 60;
} r6;
reg {
field {
sw=r; hw=w; // Wire/Bus - hardware assigns value
} f[8];
} r7;
reg {
field {
sw=r; hw=na; // Wire/Bus - constant value
} f[8] = 80;
} r8;
reg {
field {
sw=w; hw=rw; we; // Storage element
} f[8] = 90;
} r9;
reg {
field {
sw=w; hw=r; // Storage element
} f[8] = 100;
} r10;
};

130
test/test_field_types/tb.sv Normal file
View File

@@ -0,0 +1,130 @@
{% extends "lib/templates/tb_base.sv" %}
{% block seq %}
cb.hwif_in.r3.f.wel <= 1;
##1;
cb.rst <= '0;
##1;
// r1 - sw=rw; hw=rw; we; // Storage element
cpuif.assert_read('h0, 10);
assert(cb.hwif_out.r1.f.value == 10);
cpuif.write('h0, 11);
cpuif.assert_read('h0, 11);
assert(cb.hwif_out.r1.f.value == 11);
cb.hwif_in.r1.f.value <= 9;
cpuif.assert_read('h0, 11);
assert(cb.hwif_out.r1.f.value == 11);
cb.hwif_in.r1.f.value <= 12;
cb.hwif_in.r1.f.we <= 1;
@cb;
cb.hwif_in.r1.f.value <= 0;
cb.hwif_in.r1.f.we <= 0;
cpuif.assert_read('h0, 12);
assert(cb.hwif_out.r1.f.value == 12);
// r2 - sw=rw; hw=r; // Storage element
cpuif.assert_read('h1, 20);
assert(cb.hwif_out.r2.f.value == 20);
cpuif.write('h1, 21);
cpuif.assert_read('h1, 21);
assert(cb.hwif_out.r2.f.value == 21);
// r3 - sw=rw; hw=w; wel; // Storage element
cpuif.assert_read('h2, 30);
cpuif.write('h2, 31);
cpuif.assert_read('h2, 31);
cb.hwif_in.r3.f.value <= 29;
cpuif.assert_read('h2, 31);
cb.hwif_in.r3.f.value <= 32;
cb.hwif_in.r3.f.wel <= 0;
@cb;
cb.hwif_in.r3.f.value <= 0;
cb.hwif_in.r3.f.wel <= 1;
cpuif.assert_read('h2, 32);
// r4 - sw=rw; hw=na; // Storage element
cpuif.assert_read('h3, 40);
cpuif.write('h3, 41);
cpuif.assert_read('h3, 41);
// r5 - sw=r; hw=rw; we; // Storage element
cpuif.assert_read('h4, 50);
assert(cb.hwif_out.r5.f.value == 50);
cpuif.write('h4, 51);
cpuif.assert_read('h4, 50);
assert(cb.hwif_out.r5.f.value == 50);
cb.hwif_in.r5.f.value <= 9;
cpuif.assert_read('h4, 50);
assert(cb.hwif_out.r5.f.value == 50);
cb.hwif_in.r5.f.value <= 52;
cb.hwif_in.r5.f.we <= 1;
@cb;
cb.hwif_in.r5.f.value <= 0;
cb.hwif_in.r5.f.we <= 0;
cpuif.assert_read('h4, 52);
assert(cb.hwif_out.r5.f.value == 52);
// r6 - sw=r; hw=r; // Wire/Bus - constant value
cpuif.assert_read('h5, 60);
assert(cb.hwif_out.r6.f.value == 60);
cpuif.write('h5, 61);
cpuif.assert_read('h5, 60);
assert(cb.hwif_out.r6.f.value == 60);
// r7 - sw=r; hw=w; // Wire/Bus - hardware assigns value
cpuif.assert_read('h6, 0);
cb.hwif_in.r7.f.value = 70;
cpuif.assert_read('h6, 70);
cpuif.write('h6, 71);
cpuif.assert_read('h6, 70);
// r8 - sw=r; hw=na; // Wire/Bus - constant value
cpuif.assert_read('h7, 80);
cpuif.write('h7, 81);
cpuif.assert_read('h7, 80);
// r9 - sw=w; hw=rw; we; // Storage element
cpuif.assert_read('h8, 0);
assert(cb.hwif_out.r9.f.value == 90);
cpuif.write('h8, 91);
cpuif.assert_read('h8, 0);
assert(cb.hwif_out.r9.f.value == 91);
cb.hwif_in.r9.f.value <= 89;
cpuif.assert_read('h8, 0);
assert(cb.hwif_out.r9.f.value == 91);
cb.hwif_in.r9.f.value <= 92;
cb.hwif_in.r9.f.we <= 1;
@cb;
cb.hwif_in.r9.f.value <= 0;
cb.hwif_in.r9.f.we <= 0;
cpuif.assert_read('h8, 0);
assert(cb.hwif_out.r9.f.value == 92);
// r10 - sw=w; hw=r; // Storage element
cpuif.assert_read('h9, 0);
assert(cb.hwif_out.r10.f.value == 100);
cpuif.write('h9, 101);
cpuif.assert_read('h9, 0);
assert(cb.hwif_out.r10.f.value == 101);
{% 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,10 @@
addrmap top #(
longint N_REGS = 1,
longint REGWIDTH = 32
) {
reg reg_t {
regwidth = REGWIDTH;
field {sw=rw; hw=na;} f[REGWIDTH] = 1;
};
reg_t regs[N_REGS];
};

View File

@@ -0,0 +1,31 @@
{% extends "lib/templates/tb_base.sv" %}
{%- block declarations %}
localparam REGWIDTH = {{cls.regwidth}};
localparam STRIDE = REGWIDTH/8;
localparam N_REGS = {{cls.n_regs}};
{%- endblock %}
{% block seq %}
bit [REGWIDTH-1:0] data[N_REGS];
##1;
cb.rst <= '0;
##1;
foreach(data[i]) data[i] = {$urandom(), $urandom(), $urandom(), $urandom()};
for(int i=0; i<N_REGS; i++) begin
cpuif.assert_read(i*STRIDE, 'h1);
end
for(int i=0; i<N_REGS; i++) begin
cpuif.write(i*STRIDE, data[i]);
end
for(int i=0; i<N_REGS; i++) begin
cpuif.assert_read(i*STRIDE, data[i]);
end
assert($bits(dut.cpuif_wr_data) == REGWIDTH);
{% endblock %}

View File

@@ -0,0 +1,37 @@
from parameterized import parameterized_class
from ..lib.regblock_testcase import RegblockTestCase
from ..lib.test_params import get_permutations
PARAMS = get_permutations({
"regwidth" : [8, 16, 32, 64],
})
@parameterized_class(PARAMS)
class TestFanin(RegblockTestCase):
retime_read_fanin = False
n_regs = 20
regwidth = 32
@classmethod
def setUpClass(cls):
cls.rdl_elab_params = {
"N_REGS": cls.n_regs,
"REGWIDTH": cls.regwidth,
}
super().setUpClass()
def test_dut(self):
self.run_test()
PARAMS = get_permutations({
"n_regs" : [1, 4, 7, 9, 11],
"regwidth" : [8, 16, 32, 64],
})
@parameterized_class(PARAMS)
class TestRetimedFanin(TestFanin):
retime_read_fanin = True
def test_dut(self):
self.run_test()

View File

@@ -1,12 +0,0 @@
addrmap test_regblock {
reg my_reg {
field { sw=rw; hw=r; anded;} a[8] = 0x10;
//field { sw=rw; hw=r; ored;} b[8] = 0x20;
//field { sw=rw; hw=r; swmod;} c[8] = 0x30;
};
//my_reg r0 @0x000;
//my_reg r1 @0x100;
my_reg r2[112] @0x200 += 8;
};

View File

@@ -1,170 +0,0 @@
// TODO: Add a banner
module test_regblock (
input wire clk,
input wire rst,
apb3_intf.slave s_apb,
output test_regblock_pkg::test_regblock__out_t hwif_out
);
//--------------------------------------------------------------------------
// CPU Bus interface logic
//--------------------------------------------------------------------------
logic cpuif_req;
logic cpuif_req_is_wr;
logic [10:0] cpuif_addr;
logic [31:0] cpuif_wr_data;
logic [31:0] cpuif_wr_biten;
logic cpuif_rd_ack;
logic [31:0] cpuif_rd_data;
logic cpuif_rd_err;
logic cpuif_wr_ack;
logic cpuif_wr_err;
begin
// Request
logic is_active;
always_ff @(posedge clk) begin
if(rst) begin
is_active <= '0;
cpuif_req <= '0;
cpuif_req_is_wr <= '0;
cpuif_addr <= '0;
cpuif_wr_data <= '0;
end else begin
if(~is_active) begin
if(s_apb.PSEL) begin
is_active <= '1;
cpuif_req <= '1;
cpuif_req_is_wr <= s_apb.PWRITE;
cpuif_addr <= {s_apb.PADDR[10:2], 2'b0};
cpuif_wr_data <= s_apb.PWDATA;
end
end else begin
cpuif_req <= '0;
if(cpuif_rd_ack || cpuif_wr_ack) begin
is_active <= '0;
end
end
end
end
assign cpuif_wr_biten = '1;
// Response
assign s_apb.PREADY = cpuif_rd_ack | cpuif_wr_ack;
assign s_apb.PRDATA = cpuif_rd_data;
assign s_apb.PSLVERR = cpuif_rd_err | cpuif_wr_err;
end
//--------------------------------------------------------------------------
// Address Decode
//--------------------------------------------------------------------------
typedef struct {
logic r2[112];
} decoded_reg_strb_t;
decoded_reg_strb_t decoded_reg_strb;
logic decoded_req;
logic decoded_req_is_wr;
logic [31:0] decoded_wr_data;
logic [31:0] decoded_wr_biten;
always_comb begin
for(int i0=0; i0<112; i0++) begin
decoded_reg_strb.r2[i0] = cpuif_req & (cpuif_addr == 'h200 + i0*'h8);
end
end
// Writes are always granted with no error response
assign cpuif_wr_ack = cpuif_req & cpuif_req_is_wr;
assign cpuif_wr_err = '0;
// Pass down signals to next stage
assign decoded_req = cpuif_req;
assign decoded_req_is_wr = cpuif_req_is_wr;
assign decoded_wr_data = cpuif_wr_data;
assign decoded_wr_biten = cpuif_wr_biten;
//--------------------------------------------------------------------------
// Field logic
//--------------------------------------------------------------------------
typedef struct {
struct {
struct {
logic [7:0] next;
logic load_next;
} a;
} r2[112];
} field_combo_t;
field_combo_t field_combo;
typedef struct {
struct {
logic [7:0] a;
} r2[112];
} field_storage_t;
field_storage_t field_storage;
for(genvar i0=0; i0<112; i0++) begin
// Field: test_regblock.r2[].a
always_comb begin
field_combo.r2[i0].a.next = field_storage.r2[i0].a;
field_combo.r2[i0].a.load_next = '0;
if(decoded_reg_strb.r2[i0] && decoded_req_is_wr) begin // SW write
field_combo.r2[i0].a.next = decoded_wr_data[7:0];
field_combo.r2[i0].a.load_next = '1;
end
end
always_ff @(posedge clk) begin
if(rst) begin
field_storage.r2[i0].a <= 'h10;
end else if(field_combo.r2[i0].a.load_next) begin
field_storage.r2[i0].a <= field_combo.r2[i0].a.next;
end
end
assign hwif_out.r2[i0].a.value = field_storage.r2[i0].a;
assign hwif_out.r2[i0].a.anded = &(field_storage.r2[i0].a);
end
//--------------------------------------------------------------------------
// Readback
//--------------------------------------------------------------------------
logic readback_err;
logic readback_done;
logic [31:0] readback_data;
// Assign readback values to a flattened array
logic [31:0] readback_array[112];
for(genvar i0=0; i0<112; i0++) begin
assign readback_array[i0*1 + 0][7:0] = (decoded_reg_strb.r2[i0] && !decoded_req_is_wr) ? field_storage.r2[i0].a : '0;
assign readback_array[i0*1 + 0][31:8] = '0;
end
// Reduce the array
always_comb begin
automatic logic [31:0] readback_data_var;
readback_done = decoded_req & ~decoded_req_is_wr;
readback_err = '0;
readback_data_var = '0;
for(int i=0; i<112; i++) readback_data_var |= readback_array[i];
readback_data = readback_data_var;
end
always_ff @(posedge clk) begin
if(rst) begin
cpuif_rd_ack <= '0;
cpuif_rd_data <= '0;
cpuif_rd_err <= '0;
end else begin
cpuif_rd_ack <= readback_done;
cpuif_rd_data <= readback_data;
cpuif_rd_err <= readback_err;
end
end
endmodule

View File

@@ -1,19 +0,0 @@
// TODO: Add a banner
package test_regblock_pkg;
// test_regblock.r2[].a
typedef struct {
logic [7:0] value;
logic anded;
} test_regblock__r2x__a__out_t;
// test_regblock.r2[]
typedef struct {
test_regblock__r2x__a__out_t a;
} test_regblock__r2x__out_t;
// test_regblock
typedef struct {
test_regblock__r2x__out_t r2[112];
} test_regblock__out_t;
endpackage

View File

View File

@@ -0,0 +1,34 @@
addrmap regblock {
default sw=rw;
default hw=r;
reg my_reg {
field {} a[8] = 0x23;
field {} b = 0;
field {} c[31:31] = 1;
};
my_reg r0 @0x000;
r0.a->reset = 0x42;
my_reg r1[2][3][4] @0x10 += 8;
my_reg r2 @0x1000;
r2.a->reset = 0x11;
reg subreg {
field {} x[7:4] = 1;
};
regfile subrf {
subreg r1[4] @ 0x0 += 4;
regfile {
subreg r1 @ 0x0;
subreg r2[2] @ 0x4 += 4;
subreg r3 @ 0xc;
} sub[2] @ 0x10 += 0x10;
subreg r2[4] @ 0x30 += 4;
};
subrf sub2[2] @ 0x2000 += 0x40;
subreg r3 @ 0x2080;
};

View File

@@ -0,0 +1,63 @@
{% extends "lib/templates/tb_base.sv" %}
{% block seq %}
##1;
cb.rst <= '0;
##1;
// Assert value via frontdoor
cpuif.assert_read(0, 32'h8000_0042);
for(int i=0; i<2*3*4; i++) begin
cpuif.assert_read('h10+i*8, 32'h8000_0023);
end
cpuif.assert_read('h1000, 32'h8000_0011);
for(int i=0; i<33; i++) begin
cpuif.assert_read('h2000 +i*4, 32'h0000_0010);
end
// Assert via hwif
assert(hwif_out.r0.a.value == 'h42);
assert(hwif_out.r0.b.value == 'h0);
assert(hwif_out.r0.c.value == 'h1);
foreach(hwif_out.r1[x, y, z]) begin
assert(hwif_out.r1[x][y][z].a.value == 'h23);
assert(hwif_out.r1[x][y][z].b.value == 'h0);
assert(hwif_out.r1[x][y][z].c.value == 'h1);
end
assert(hwif_out.r2.a.value == 'h11);
assert(hwif_out.r2.b.value == 'h0);
assert(hwif_out.r2.c.value == 'h1);
// Write values
cpuif.write(0, 32'h8000_0002);
for(int i=0; i<2*3*4; i++) begin
cpuif.write('h10+i*8, i+'h110a);
end
cpuif.write('h1000, 32'h0000_0000);
for(int i=0; i<33; i++) begin
cpuif.write('h2000 +i*4, i << 4);
end
// Assert value via frontdoor
cpuif.assert_read(0, 32'h8000_0002);
for(int i=0; i<2*3*4; i++) begin
cpuif.assert_read('h10+i*8, i+'h10a);
end
cpuif.assert_read('h1000, 32'h0000_0000);
for(int i=0; i<33; i++) begin
cpuif.assert_read('h2000 +i*4, (i << 4) & 'hF0);
end
// Assert via hwif
assert(hwif_out.r0.a.value == 'h02);
assert(hwif_out.r0.b.value == 'h0);
assert(hwif_out.r0.c.value == 'h1);
foreach(hwif_out.r1[x, y, z]) begin
assert(hwif_out.r1[x][y][z].a.value == x*12+y*4+z+10);
assert(hwif_out.r1[x][y][z].b.value == 'h1);
assert(hwif_out.r1[x][y][z].c.value == 'h0);
end
assert(hwif_out.r2.a.value == 'h0);
assert(hwif_out.r2.b.value == 'h0);
assert(hwif_out.r2.c.value == 'h0);
{% endblock %}

View File

@@ -0,0 +1,9 @@
from parameterized import parameterized_class
from ..lib.regblock_testcase import RegblockTestCase
from ..lib.test_params import TEST_PARAMS
@parameterized_class(TEST_PARAMS)
class Test(RegblockTestCase):
def test_dut(self):
self.run_test()

View File

View File

@@ -0,0 +1,17 @@
addrmap top {
default regwidth = 8;
reg {
field {
sw=r; hw=w;
swacc;
} f[8];
} r1;
reg {
field {
sw=rw; hw=r;
swmod;
} f[8] = 20;
} r2;
};

View File

@@ -0,0 +1,66 @@
{% extends "lib/templates/tb_base.sv" %}
{% block seq %}
logic [7:0] rd_data;
logic [7:0] latched_data;
int event_count;
latched_data = 'x;
##1;
cb.rst <= '0;
##1;
// Verify that hwif gets sampled at the same cycle as swacc strobe
cb.hwif_in.r1.f.value <= 'h10;
@cb;
event_count = 0;
fork
begin
##0;
forever begin
cb.hwif_in.r1.f.value <= cb.hwif_in.r1.f.value + 1;
@cb;
if(cb.hwif_out.r1.f.swacc) begin
latched_data = cb.hwif_in.r1.f.value;
event_count++;
end
end
end
begin
cpuif.read('h0, rd_data);
@cb;
end
join_any
disable fork;
assert(rd_data == latched_data) else $error("Read returned 0x%0x but swacc strobed during 0x%0x", rd_data, latched_data);
assert(event_count == 1) else $error("Observed excess swacc events: %0d", event_count);
// Verify that hwif changes 1 cycle after swmod
fork
begin
##0;
forever begin
assert(hwif_out.r2.f.value == 20);
if(hwif_out.r2.f.swmod) break;
@cb;
end
@cb;
forever begin
assert(hwif_out.r2.f.value == 21);
assert(hwif_out.r2.f.swmod == 0);
@cb;
end
end
begin
cpuif.write('h1, 21);
@cb;
end
join_any
disable fork;
// TODO: verify some other atypical swmod (onread actions)
{% 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,9 +0,0 @@
-quiet
# Free version of ModelSim errors if generate statements are not used.
# These have been made optional long ago. Modern versions of SystemVerilog do
# not require them.
-suppress 2720
# Ignore warning about vopt-time checking of always_comb/always_latch
-suppress 2583

View File

@@ -1,45 +0,0 @@
onerror {resume}
quietly WaveActivateNextPane {} 0
add wave -noupdate /tb/rst
add wave -noupdate /tb/clk
add wave -noupdate /tb/apb/PSEL
add wave -noupdate /tb/apb/PENABLE
add wave -noupdate /tb/apb/PWRITE
add wave -noupdate /tb/apb/PADDR
add wave -noupdate /tb/apb/PWDATA
add wave -noupdate /tb/apb/PRDATA
add wave -noupdate /tb/apb/PREADY
add wave -noupdate /tb/apb/PSLVERR
add wave -noupdate -divider DUT
add wave -noupdate /tb/dut/cpuif_req
add wave -noupdate /tb/dut/cpuif_req_is_wr
add wave -noupdate /tb/dut/cpuif_addr
add wave -noupdate /tb/dut/cpuif_wr_data
add wave -noupdate /tb/dut/cpuif_wr_biten
add wave -noupdate /tb/dut/cpuif_rd_ack
add wave -noupdate /tb/dut/cpuif_rd_data
add wave -noupdate /tb/dut/cpuif_rd_err
add wave -noupdate /tb/dut/cpuif_wr_ack
add wave -noupdate /tb/dut/cpuif_wr_err
add wave -noupdate -divider Storage
add wave -noupdate -radix hexadecimal -childformat {{/tb/dut/field_storage.r0 -radix hexadecimal -childformat {{/tb/dut/field_storage.r0.a -radix hexadecimal} {/tb/dut/field_storage.r0.b -radix hexadecimal} {/tb/dut/field_storage.r0.c -radix hexadecimal}}} {/tb/dut/field_storage.r1 -radix hexadecimal -childformat {{/tb/dut/field_storage.r1.a -radix hexadecimal} {/tb/dut/field_storage.r1.b -radix hexadecimal} {/tb/dut/field_storage.r1.c -radix hexadecimal}}} {/tb/dut/field_storage.r2 -radix hexadecimal -childformat {{/tb/dut/field_storage.r2.a -radix hexadecimal} {/tb/dut/field_storage.r2.b -radix hexadecimal} {/tb/dut/field_storage.r2.c -radix hexadecimal}}}} -expand -subitemconfig {/tb/dut/field_storage.r0 {-height 17 -radix hexadecimal -childformat {{/tb/dut/field_storage.r0.a -radix hexadecimal} {/tb/dut/field_storage.r0.b -radix hexadecimal} {/tb/dut/field_storage.r0.c -radix hexadecimal}} -expand} /tb/dut/field_storage.r0.a {-height 17 -radix hexadecimal} /tb/dut/field_storage.r0.b {-height 17 -radix hexadecimal} /tb/dut/field_storage.r0.c {-height 17 -radix hexadecimal} /tb/dut/field_storage.r1 {-height 17 -radix hexadecimal -childformat {{/tb/dut/field_storage.r1.a -radix hexadecimal} {/tb/dut/field_storage.r1.b -radix hexadecimal} {/tb/dut/field_storage.r1.c -radix hexadecimal}} -expand} /tb/dut/field_storage.r1.a {-height 17 -radix hexadecimal} /tb/dut/field_storage.r1.b {-height 17 -radix hexadecimal} /tb/dut/field_storage.r1.c {-height 17 -radix hexadecimal} /tb/dut/field_storage.r2 {-height 17 -radix hexadecimal -childformat {{/tb/dut/field_storage.r2.a -radix hexadecimal} {/tb/dut/field_storage.r2.b -radix hexadecimal} {/tb/dut/field_storage.r2.c -radix hexadecimal}} -expand} /tb/dut/field_storage.r2.a {-height 17 -radix hexadecimal} /tb/dut/field_storage.r2.b {-height 17 -radix hexadecimal} /tb/dut/field_storage.r2.c {-height 17 -radix hexadecimal}} /tb/dut/field_storage
add wave -noupdate -divider HWIF
add wave -noupdate -radix hexadecimal -childformat {{/tb/dut/hwif_out.r0 -radix hexadecimal -childformat {{/tb/dut/hwif_out.r0.a -radix hexadecimal -childformat {{/tb/dut/hwif_out.r0.a.value -radix hexadecimal} {/tb/dut/hwif_out.r0.a.anded -radix hexadecimal}}} {/tb/dut/hwif_out.r0.b -radix hexadecimal -childformat {{/tb/dut/hwif_out.r0.b.value -radix hexadecimal} {/tb/dut/hwif_out.r0.b.ored -radix hexadecimal}}} {/tb/dut/hwif_out.r0.c -radix hexadecimal -childformat {{/tb/dut/hwif_out.r0.c.value -radix hexadecimal} {/tb/dut/hwif_out.r0.c.swmod -radix hexadecimal}}}}} {/tb/dut/hwif_out.r1 -radix hexadecimal -childformat {{/tb/dut/hwif_out.r1.a -radix hexadecimal} {/tb/dut/hwif_out.r1.b -radix hexadecimal} {/tb/dut/hwif_out.r1.c -radix hexadecimal}}} {/tb/dut/hwif_out.r2 -radix hexadecimal -childformat {{/tb/dut/hwif_out.r2.a -radix hexadecimal} {/tb/dut/hwif_out.r2.b -radix hexadecimal} {/tb/dut/hwif_out.r2.c -radix hexadecimal}}}} -expand -subitemconfig {/tb/dut/hwif_out.r0 {-height 17 -radix hexadecimal -childformat {{/tb/dut/hwif_out.r0.a -radix hexadecimal -childformat {{/tb/dut/hwif_out.r0.a.value -radix hexadecimal} {/tb/dut/hwif_out.r0.a.anded -radix hexadecimal}}} {/tb/dut/hwif_out.r0.b -radix hexadecimal -childformat {{/tb/dut/hwif_out.r0.b.value -radix hexadecimal} {/tb/dut/hwif_out.r0.b.ored -radix hexadecimal}}} {/tb/dut/hwif_out.r0.c -radix hexadecimal -childformat {{/tb/dut/hwif_out.r0.c.value -radix hexadecimal} {/tb/dut/hwif_out.r0.c.swmod -radix hexadecimal}}}} -expand} /tb/dut/hwif_out.r0.a {-height 17 -radix hexadecimal -childformat {{/tb/dut/hwif_out.r0.a.value -radix hexadecimal} {/tb/dut/hwif_out.r0.a.anded -radix hexadecimal}} -expand} /tb/dut/hwif_out.r0.a.value {-height 17 -radix hexadecimal} /tb/dut/hwif_out.r0.a.anded {-height 17 -radix hexadecimal} /tb/dut/hwif_out.r0.b {-height 17 -radix hexadecimal -childformat {{/tb/dut/hwif_out.r0.b.value -radix hexadecimal} {/tb/dut/hwif_out.r0.b.ored -radix hexadecimal}} -expand} /tb/dut/hwif_out.r0.b.value {-height 17 -radix hexadecimal} /tb/dut/hwif_out.r0.b.ored {-height 17 -radix hexadecimal} /tb/dut/hwif_out.r0.c {-height 17 -radix hexadecimal -childformat {{/tb/dut/hwif_out.r0.c.value -radix hexadecimal} {/tb/dut/hwif_out.r0.c.swmod -radix hexadecimal}} -expand} /tb/dut/hwif_out.r0.c.value {-height 17 -radix hexadecimal} /tb/dut/hwif_out.r0.c.swmod {-height 17 -radix hexadecimal} /tb/dut/hwif_out.r1 {-height 17 -radix hexadecimal -childformat {{/tb/dut/hwif_out.r1.a -radix hexadecimal} {/tb/dut/hwif_out.r1.b -radix hexadecimal} {/tb/dut/hwif_out.r1.c -radix hexadecimal}} -expand} /tb/dut/hwif_out.r1.a {-height 17 -radix hexadecimal} /tb/dut/hwif_out.r1.b {-height 17 -radix hexadecimal} /tb/dut/hwif_out.r1.c {-height 17 -radix hexadecimal} /tb/dut/hwif_out.r2 {-height 17 -radix hexadecimal -childformat {{/tb/dut/hwif_out.r2.a -radix hexadecimal} {/tb/dut/hwif_out.r2.b -radix hexadecimal} {/tb/dut/hwif_out.r2.c -radix hexadecimal}} -expand} /tb/dut/hwif_out.r2.a {-height 17 -radix hexadecimal} /tb/dut/hwif_out.r2.b {-height 17 -radix hexadecimal} /tb/dut/hwif_out.r2.c {-height 17 -radix hexadecimal}} /tb/dut/hwif_out
TreeUpdate [SetDefaultTree]
WaveRestoreCursors {{Cursor 1} {650000 ps} 0}
quietly wave cursor active 1
configure wave -namecolwidth 150
configure wave -valuecolwidth 100
configure wave -justifyvalue left
configure wave -signalnamewidth 0
configure wave -snapdistance 10
configure wave -datasetprefix 0
configure wave -rowmargin 4
configure wave -childrowmargin 2
configure wave -gridoffset 0
configure wave -gridperiod 1
configure wave -griddelta 40
configure wave -timeline 0
configure wave -timelineunits ps
update
WaveRestoreZoom {252900 ps} {755184 ps}