diff --git a/.gitignore b/.gitignore
index e222d71..d8c037d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,6 +3,10 @@
**/.venv
**/.coverage
**/*.rpt
+**/.pytest_cache
+**/_build
+**/*.out
+**/transcript
build/
dist/
diff --git a/README.md b/README.md
index fb060c2..c751e28 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,5 @@
-[](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)
diff --git a/doc/conf.py b/doc/conf.py
index 8704de7..d4eb3e3 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -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
+
+"""
diff --git a/doc/img/err.svg b/doc/img/err.svg
new file mode 100644
index 0000000..6ce297c
--- /dev/null
+++ b/doc/img/err.svg
@@ -0,0 +1,53 @@
+
+
diff --git a/doc/img/ok.svg b/doc/img/ok.svg
new file mode 100644
index 0000000..defc966
--- /dev/null
+++ b/doc/img/ok.svg
@@ -0,0 +1,53 @@
+
+
diff --git a/doc/img/warn.svg b/doc/img/warn.svg
new file mode 100644
index 0000000..1debe8a
--- /dev/null
+++ b/doc/img/warn.svg
@@ -0,0 +1,53 @@
+
+
diff --git a/doc/index.rst b/doc/index.rst
index d6bcb56..147bc38 100644
--- a/doc/index.rst
+++ b/doc/index.rst
@@ -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
diff --git a/doc/limitations.rst b/doc/limitations.rst
new file mode 100644
index 0000000..7543746
--- /dev/null
+++ b/doc/limitations.rst
@@ -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.
diff --git a/doc/logbooks/000-Main-Logbook b/doc/logbooks/000-Main-Logbook
index 825a364..a8d60d3 100644
--- a/doc/logbooks/000-Main-Logbook
+++ b/doc/logbooks/000-Main-Logbook
@@ -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
================================================================================
diff --git a/doc/logbooks/Signal Dereferencer b/doc/logbooks/Signal Dereferencer
index 1c626e1..2c427f8 100644
--- a/doc/logbooks/Signal Dereferencer
+++ b/doc/logbooks/Signal Dereferencer
@@ -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
diff --git a/doc/logbooks/Validation Needed b/doc/logbooks/Validation Needed
index 53fe48a..d252a31 100644
--- a/doc/logbooks/Validation Needed
+++ b/doc/logbooks/Validation Needed
@@ -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
diff --git a/doc/props/addrmap.rst b/doc/props/addrmap.rst
new file mode 100644
index 0000000..41d2964
--- /dev/null
+++ b/doc/props/addrmap.rst
@@ -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|
diff --git a/doc/props/field.rst b/doc/props/field.rst
new file mode 100644
index 0000000..0af7c70
--- /dev/null
+++ b/doc/props/field.rst
@@ -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: '', 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|
diff --git a/doc/props/reg.rst b/doc/props/reg.rst
new file mode 100644
index 0000000..98e287f
--- /dev/null
+++ b/doc/props/reg.rst
@@ -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|
diff --git a/doc/props/rhs_props.rst b/doc/props/rhs_props.rst
new file mode 100644
index 0000000..10a6779
--- /dev/null
+++ b/doc/props/rhs_props.rst
@@ -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|
diff --git a/doc/props/signal.rst b/doc/props/signal.rst
new file mode 100644
index 0000000..c644f08
--- /dev/null
+++ b/doc/props/signal.rst
@@ -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|
diff --git a/doc/requirements.txt b/doc/requirements.txt
index 60ba612..807facd 100644
--- a/doc/requirements.txt
+++ b/doc/requirements.txt
@@ -1 +1,2 @@
pygments-systemrdl
+sphinxcontrib-wavedrom
diff --git a/export.py b/export.py
deleted file mode 100755
index 3232fd0..0000000
--- a/export.py
+++ /dev/null
@@ -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, ".")
diff --git a/peakrdl/regblock/cpuif/apb3/__init__.py b/peakrdl/regblock/cpuif/apb3/__init__.py
index 8110a7b..6674d6c 100644
--- a/peakrdl/regblock/cpuif/apb3/__init__.py
+++ b/peakrdl/regblock/cpuif/apb3/__init__.py
@@ -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"),
diff --git a/peakrdl/regblock/dereferencer.py b/peakrdl/regblock/dereferencer.py
index 49852ec..2fdea7b 100644
--- a/peakrdl/regblock/dereferencer.py
+++ b/peakrdl/regblock/dereferencer.py
@@ -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
diff --git a/peakrdl/regblock/exporter.py b/peakrdl/regblock/exporter.py
index b03ba0e..9a4f191 100644
--- a/peakrdl/regblock/exporter.py
+++ b/peakrdl/regblock/exporter.py
@@ -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:
diff --git a/peakrdl/regblock/field_logic/__init__.py b/peakrdl/regblock/field_logic/__init__.py
index f8f0e18..463ed6d 100644
--- a/peakrdl/regblock/field_logic/__init__.py
+++ b/peakrdl/regblock/field_logic/__init__.py
@@ -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:
diff --git a/peakrdl/regblock/field_logic/generators.py b/peakrdl/regblock/field_logic/generators.py
index 8c819e5..c60b624 100644
--- a/peakrdl/regblock/field_logic/generators.py
+++ b/peakrdl/regblock/field_logic/generators.py
@@ -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(
diff --git a/peakrdl/regblock/field_logic/sw_onread.py b/peakrdl/regblock/field_logic/sw_onread.py
index b14b606..02e20b1 100644
--- a/peakrdl/regblock/field_logic/sw_onread.py
+++ b/peakrdl/regblock/field_logic/sw_onread.py
@@ -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)
diff --git a/peakrdl/regblock/field_logic/sw_onwrite.py b/peakrdl/regblock/field_logic/sw_onwrite.py
index 3d675e8..b204ceb 100644
--- a/peakrdl/regblock/field_logic/sw_onwrite.py
+++ b/peakrdl/regblock/field_logic/sw_onwrite.py
@@ -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)
diff --git a/peakrdl/regblock/hwif.py b/peakrdl/regblock/hwif.py
index 2e288fa..faafbe0 100644
--- a/peakrdl/regblock/hwif.py
+++ b/peakrdl/regblock/hwif.py
@@ -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;")
diff --git a/peakrdl/regblock/readback/generators.py b/peakrdl/regblock/readback/generators.py
index 9ccd55b..f772300 100644
--- a/peakrdl/regblock/readback/generators.py
+++ b/peakrdl/regblock/readback/generators.py
@@ -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
diff --git a/peakrdl/regblock/readback/templates/readback.sv b/peakrdl/regblock/readback/templates/readback.sv
index 2e8a770..fc706c8 100644
--- a/peakrdl/regblock/readback/templates/readback.sv
+++ b/peakrdl/regblock/readback/templates/readback.sv
@@ -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];
diff --git a/peakrdl/regblock/scan_design.py b/peakrdl/regblock/scan_design.py
index cde9135..8bb4e1c 100644
--- a/peakrdl/regblock/scan_design.py
+++ b/peakrdl/regblock/scan_design.py
@@ -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
diff --git a/peakrdl/regblock/signals.py b/peakrdl/regblock/signals.py
index 7706074..7abd586 100644
--- a/peakrdl/regblock/signals.py
+++ b/peakrdl/regblock/signals.py
@@ -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:
diff --git a/test/.gitignore b/test/.gitignore
deleted file mode 100644
index a7c92e9..0000000
--- a/test/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-work
-transcript
-*.wlf
diff --git a/test/README.md b/test/README.md
new file mode 100644
index 0000000..7a11e2b
--- /dev/null
+++ b/test/README.md
@@ -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
+```
diff --git a/test/__init__.py b/test/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/test/lib/__init__.py b/test/lib/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/test/lib/cpuifs/__init__.py b/test/lib/cpuifs/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/test/lib/cpuifs/apb3/__init__.py b/test/lib/cpuifs/apb3/__init__.py
new file mode 100644
index 0000000..e9d9ceb
--- /dev/null
+++ b/test/lib/cpuifs/apb3/__init__.py
@@ -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
diff --git a/test/interfaces/apb3_intf.sv b/test/lib/cpuifs/apb3/apb3_intf.sv
similarity index 100%
rename from test/interfaces/apb3_intf.sv
rename to test/lib/cpuifs/apb3/apb3_intf.sv
diff --git a/test/drivers/apb3_intf_driver.sv b/test/lib/cpuifs/apb3/apb3_intf_driver.sv
similarity index 76%
rename from test/drivers/apb3_intf_driver.sv
rename to test/lib/cpuifs/apb3/apb3_intf_driver.sv
index 240c5d1..082907b 100644
--- a/test/drivers/apb3_intf_driver.sv
+++ b/test/lib/cpuifs/apb3/apb3_intf_driver.sv
@@ -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
diff --git a/test/lib/cpuifs/apb3/tb_inst.sv b/test/lib/cpuifs/apb3/tb_inst.sv
new file mode 100644
index 0000000..6504608
--- /dev/null
+++ b/test/lib/cpuifs/apb3/tb_inst.sv
@@ -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 %}
diff --git a/test/lib/cpuifs/base.py b/test/lib/cpuifs/base.py
new file mode 100644
index 0000000..ad5de69
--- /dev/null
+++ b/test/lib/cpuifs/base.py
@@ -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)
diff --git a/test/lib/regblock_testcase.py b/test/lib/regblock_testcase.py
new file mode 100644
index 0000000..547ba7b
--- /dev/null
+++ b/test/lib/regblock_testcase.py
@@ -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)
diff --git a/test/lib/templates/tb_base.sv b/test/lib/templates/tb_base.sv
new file mode 100644
index 0000000..151c382
--- /dev/null
+++ b/test/lib/templates/tb_base.sv
@@ -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
diff --git a/test/lib/test_params.py b/test/lib/test_params.py
new file mode 100644
index 0000000..627e546
--- /dev/null
+++ b/test/lib/test_params.py
@@ -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],
+})
diff --git a/test/pytest.ini b/test/pytest.ini
new file mode 100644
index 0000000..afd91e9
--- /dev/null
+++ b/test/pytest.ini
@@ -0,0 +1,2 @@
+[pytest]
+python_files = test_*.py testcase.py
diff --git a/test/requirements.txt b/test/requirements.txt
new file mode 100644
index 0000000..28ef6f4
--- /dev/null
+++ b/test/requirements.txt
@@ -0,0 +1,3 @@
+pytest
+parameterized
+pytest-xdist
diff --git a/test/run.sh b/test/run.sh
deleted file mode 100755
index af3f8df..0000000
--- a/test/run.sh
+++ /dev/null
@@ -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;"
diff --git a/test/src.f b/test/src.f
deleted file mode 100644
index 6d6faa0..0000000
--- a/test/src.f
+++ /dev/null
@@ -1,5 +0,0 @@
-interfaces/apb3_intf.sv
-drivers/apb3_intf_driver.sv
-test_regblock_pkg.sv
-test_regblock.sv
-tb.sv
diff --git a/test/tb.sv b/test/tb.sv
deleted file mode 100644
index a130dee..0000000
--- a/test/tb.sv
+++ /dev/null
@@ -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
diff --git a/test/test_field_types/__init__.py b/test/test_field_types/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/test/test_field_types/regblock.rdl b/test/test_field_types/regblock.rdl
new file mode 100644
index 0000000..0557159
--- /dev/null
+++ b/test/test_field_types/regblock.rdl
@@ -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;
+};
diff --git a/test/test_field_types/tb.sv b/test/test_field_types/tb.sv
new file mode 100644
index 0000000..1d8dd00
--- /dev/null
+++ b/test/test_field_types/tb.sv
@@ -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 %}
diff --git a/test/test_field_types/testcase.py b/test/test_field_types/testcase.py
new file mode 100644
index 0000000..b458d3c
--- /dev/null
+++ b/test/test_field_types/testcase.py
@@ -0,0 +1,5 @@
+from ..lib.regblock_testcase import RegblockTestCase
+
+class Test(RegblockTestCase):
+ def test_dut(self):
+ self.run_test()
diff --git a/test/test_read_fanin/__init__.py b/test/test_read_fanin/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/test/test_read_fanin/regblock.rdl b/test/test_read_fanin/regblock.rdl
new file mode 100644
index 0000000..5d6bffd
--- /dev/null
+++ b/test/test_read_fanin/regblock.rdl
@@ -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];
+};
diff --git a/test/test_read_fanin/tb.sv b/test/test_read_fanin/tb.sv
new file mode 100644
index 0000000..07d7531
--- /dev/null
+++ b/test/test_read_fanin/tb.sv
@@ -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; ireset = 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;
+};
diff --git a/test/test_structural_sw_rw/tb.sv b/test/test_structural_sw_rw/tb.sv
new file mode 100644
index 0000000..a51581c
--- /dev/null
+++ b/test/test_structural_sw_rw/tb.sv
@@ -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 %}
diff --git a/test/test_structural_sw_rw/testcase.py b/test/test_structural_sw_rw/testcase.py
new file mode 100644
index 0000000..a0e39d0
--- /dev/null
+++ b/test/test_structural_sw_rw/testcase.py
@@ -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()
diff --git a/test/test_swacc_swmod/__init__.py b/test/test_swacc_swmod/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/test/test_swacc_swmod/regblock.rdl b/test/test_swacc_swmod/regblock.rdl
new file mode 100644
index 0000000..0bccf2c
--- /dev/null
+++ b/test/test_swacc_swmod/regblock.rdl
@@ -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;
+};
diff --git a/test/test_swacc_swmod/tb.sv b/test/test_swacc_swmod/tb.sv
new file mode 100644
index 0000000..28d1c2a
--- /dev/null
+++ b/test/test_swacc_swmod/tb.sv
@@ -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 %}
diff --git a/test/test_swacc_swmod/testcase.py b/test/test_swacc_swmod/testcase.py
new file mode 100644
index 0000000..b458d3c
--- /dev/null
+++ b/test/test_swacc_swmod/testcase.py
@@ -0,0 +1,5 @@
+from ..lib.regblock_testcase import RegblockTestCase
+
+class Test(RegblockTestCase):
+ def test_dut(self):
+ self.run_test()
diff --git a/test/vlog_args.f b/test/vlog_args.f
deleted file mode 100644
index 12ea952..0000000
--- a/test/vlog_args.f
+++ /dev/null
@@ -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
diff --git a/test/wave.do b/test/wave.do
deleted file mode 100644
index 28083b2..0000000
--- a/test/wave.do
+++ /dev/null
@@ -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}