regblock -> busdecoder

This commit is contained in:
Arnav Sacheti
2025-10-10 22:30:59 -07:00
parent 9bf5cd1e68
commit b4f9eaff71
78 changed files with 904 additions and 705 deletions

View File

@@ -7,7 +7,7 @@ assignees: ''
---
- [ ] I have reviewed this project's [contribution guidelines](https://github.com/SystemRDL/PeakRDL-regblock/blob/main/CONTRIBUTING.md)
- [ ] I have reviewed this project's [contribution guidelines](https://github.com/SystemRDL/PeakRDL-busdecoder/blob/main/CONTRIBUTING.md)
**Describe the bug**
A clear and concise description of what the bug is.

View File

@@ -7,7 +7,7 @@ assignees: ''
---
- [ ] I have reviewed this project's [contribution guidelines](https://github.com/SystemRDL/PeakRDL-regblock/blob/main/CONTRIBUTING.md)
- [ ] I have reviewed this project's [contribution guidelines](https://github.com/SystemRDL/PeakRDL-busdecoder/blob/main/CONTRIBUTING.md)
**Describe the problem/limitation you think should be addressed**
A clear and concise description of what the problem is.

View File

@@ -6,6 +6,6 @@ this change.
# Checklist
- [ ] I have reviewed this project's [contribution guidelines](https://github.com/SystemRDL/PeakRDL-regblock/blob/main/CONTRIBUTING.md)
- [ ] I have reviewed this project's [contribution guidelines](https://github.com/SystemRDL/PeakRDL-busdecoder/blob/main/CONTRIBUTING.md)
- [ ] This change has been tested and does not break any of the existing unit tests. (if unable to run the tests, let us know)
- [ ] If this change adds new features, I have added new unit tests that cover them.

View File

@@ -56,7 +56,7 @@ jobs:
- name: Test
run: |
cd tests
pytest --cov=peakrdl_regblock --synth-tool skip --sim-tool stub
pytest --cov=peakrdl_busdecoder --synth-tool skip --sim-tool stub
- name: Coveralls
env:
@@ -103,7 +103,7 @@ jobs:
- name: Run Lint
run: |
pylint --rcfile tests/pylint.rc peakrdl_regblock
pylint --rcfile tests/pylint.rc peakrdl_busdecoder
#-------------------------------------------------------------------------------
mypy:
@@ -125,7 +125,7 @@ jobs:
- name: Type Check
run: |
mypy --config-file tests/mypy.ini src/peakrdl_regblock
mypy --config-file tests/mypy.ini src/peakrdl_busdecoder
#-------------------------------------------------------------------------------
build:

View File

@@ -1,4 +1,4 @@
# Contributing to the PeakRDL-regblock code generator
# Contributing to the PeakRDL-busdecoder code generator
We love your input! We want to make contributing to this project as easy and
transparent as possible, whether it's:
@@ -9,9 +9,9 @@ transparent as possible, whether it's:
- Becoming a maintainer
## Open an issue using the [Issue Tracker](https://github.com/SystemRDL/PeakRDL-regblock/issues)
## Open an issue using the [Issue Tracker](https://github.com/SystemRDL/PeakRDL-busdecoder/issues)
Talking to us is the easiest way to contribute! Report a bug or feature request by
[opening a new issue](https://github.com/SystemRDL/PeakRDL-regblock/issues).
[opening a new issue](https://github.com/SystemRDL/PeakRDL-busdecoder/issues).
Issue submission expectations:
* Please keep each issue submission limited to one topic. This helps us stay organized.

View File

@@ -1,2 +1,2 @@
recursive-include src/peakrdl_regblock *.sv
recursive-include src/peakrdl_busdecoder *.sv
prune tests

View File

@@ -1,12 +1,12 @@
[![Documentation Status](https://readthedocs.org/projects/peakrdl-regblock/badge/?version=latest)](http://peakrdl-regblock.readthedocs.io)
[![build](https://github.com/SystemRDL/PeakRDL-regblock/workflows/build/badge.svg)](https://github.com/SystemRDL/PeakRDL-regblock/actions?query=workflow%3Abuild+branch%3Amain)
[![Coverage Status](https://coveralls.io/repos/github/SystemRDL/PeakRDL-regblock/badge.svg?branch=main)](https://coveralls.io/github/SystemRDL/PeakRDL-regblock?branch=main)
[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/peakrdl-regblock.svg)](https://pypi.org/project/peakrdl-regblock)
[![Documentation Status](https://readthedocs.org/projects/peakrdl-busdecoder/badge/?version=latest)](http://peakrdl-busdecoder.readthedocs.io)
[![build](https://github.com/SystemRDL/PeakRDL-busdecoder/workflows/build/badge.svg)](https://github.com/SystemRDL/PeakRDL-busdecoder/actions?query=workflow%3Abuild+branch%3Amain)
[![Coverage Status](https://coveralls.io/repos/github/SystemRDL/PeakRDL-busdecoder/badge.svg?branch=main)](https://coveralls.io/github/SystemRDL/PeakRDL-busdecoder?branch=main)
[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/peakrdl-busdecoder.svg)](https://pypi.org/project/peakrdl-busdecoder)
# PeakRDL-regblock
# PeakRDL-busdecoder
Compile SystemRDL into a SystemVerilog control/status register (CSR) block.
For the command line tool, see the [PeakRDL project](https://peakrdl.readthedocs.io).
## Documentation
See the [PeakRDL-regblock Documentation](https://peakrdl-regblock.readthedocs.io) for more details
See the [PeakRDL-busdecoder Documentation](https://peakrdl-busdecoder.readthedocs.io) for more details

View File

@@ -2,9 +2,9 @@ Exporter API
============
If you are not using the `PeakRDL command-line tool <https://peakrdl.readthedocs.io>`_,
you can still generate regblocks programmatically using the exporter API:
you can still generate busdecoders programmatically using the exporter API:
.. autoclass:: peakrdl_regblock.RegblockExporter
.. autoclass:: peakrdl_busdecoder.BusDecoderExporter
:members:
Example
@@ -16,9 +16,9 @@ implementation from SystemRDL source.
:emphasize-lines: 2-4, 29-33
from systemrdl import RDLCompiler, RDLCompileError
from peakrdl_regblock import RegblockExporter
from peakrdl_regblock.cpuif.axi4lite import AXI4Lite_Cpuif
from peakrdl_regblock.udps import ALL_UDPS
from peakrdl_busdecoder import BusDecoderExporter
from peakrdl_busdecoder.cpuif.axi4lite import AXI4Lite_Cpuif
from peakrdl_busdecoder.udps import ALL_UDPS
input_files = [
"PATH/TO/my_register_block.rdl"
@@ -27,7 +27,7 @@ implementation from SystemRDL source.
# Create an instance of the compiler
rdlc = RDLCompiler()
# Register all UDPs that 'regblock' requires
# Register all UDPs that 'busdecoder' requires
for udp in ALL_UDPS:
rdlc.register_udp(udp)
@@ -43,7 +43,7 @@ implementation from SystemRDL source.
sys.exit(1)
# Export a SystemVerilog implementation
exporter = RegblockExporter()
exporter = BusDecoderExporter()
exporter.export(
root, "path/to/output_dir",
cpuif_cls=AXI4Lite_Cpuif

View File

@@ -12,15 +12,16 @@
#
import os
import sys
sys.path.insert(0, os.path.abspath('../src/'))
sys.path.insert(0, os.path.abspath("../src/"))
import datetime
# -- Project information -----------------------------------------------------
project = 'PeakRDL-regblock'
copyright = '%d, Alex Mykyta' % datetime.datetime.now().year
author = 'Alex Mykyta'
project = "PeakRDL-busdecoder"
copyright = "%d, Alex Mykyta" % datetime.datetime.now().year
author = "Alex Mykyta"
# -- General configuration ---------------------------------------------------
@@ -29,19 +30,19 @@ author = 'Alex Mykyta'
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.napoleon',
"sphinx.ext.autodoc",
"sphinx.ext.napoleon",
"sphinxcontrib.wavedrom",
]
render_using_wavedrompy = True
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
templates_path = ["_templates"]
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
# -- Options for HTML output -------------------------------------------------
@@ -52,7 +53,7 @@ exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
html_theme = "sphinx_book_theme"
html_theme_options = {
"repository_url": "https://github.com/SystemRDL/PeakRDL-regblock",
"repository_url": "https://github.com/SystemRDL/PeakRDL-busdecoder",
"path_to_docs": "docs",
"use_download_button": False,
"use_source_button": True,

View File

@@ -1,13 +1,13 @@
.. _peakrdl_cfg:
Configuring PeakRDL-regblock
Configuring PeakRDL-busdecoder
============================
If using the `PeakRDL command line tool <https://peakrdl.readthedocs.io/>`_,
some aspects of the ``regblock`` command have additional configuration options
some aspects of the ``busdecoder`` command have additional configuration options
available via the PeakRDL TOML file.
All regblock-specific options are defined under the ``[regblock]`` TOML heading.
All busdecoder-specific options are defined under the ``[busdecoder]`` TOML heading.
.. data:: cpuifs
@@ -20,7 +20,7 @@ All regblock-specific options are defined under the ``[regblock]`` TOML heading.
.. code-block:: toml
[regblock]
[busdecoder]
cpuifs.my-cpuif-name = "my_cpuif_module:MyCPUInterfaceClass"
@@ -41,5 +41,5 @@ All regblock-specific options are defined under the ``[regblock]`` TOML heading.
.. code-block:: toml
[regblock]
[busdecoder]
default_reset = "arst"

View File

@@ -29,13 +29,13 @@ The APB3 CPU interface comes in two i/o port flavors:
SystemVerilog Interface
* Command line: ``--cpuif apb3``
* Interface Definition: :download:`apb3_intf.sv <../../hdl-src/apb3_intf.sv>`
* Class: :class:`peakrdl_regblock.cpuif.apb3.APB3_Cpuif`
* Class: :class:`peakrdl_busdecoder.cpuif.apb3.APB3_Cpuif`
Flattened inputs/outputs
Flattens the interface into discrete input and output ports.
* Command line: ``--cpuif apb3-flat``
* Class: :class:`peakrdl_regblock.cpuif.apb3.APB3_Cpuif_flattened`
* Class: :class:`peakrdl_busdecoder.cpuif.apb3.APB3_Cpuif_flattened`
APB4
@@ -50,10 +50,10 @@ The APB4 CPU interface comes in two i/o port flavors:
SystemVerilog Interface
* Command line: ``--cpuif apb4``
* Interface Definition: :download:`apb4_intf.sv <../../hdl-src/apb4_intf.sv>`
* Class: :class:`peakrdl_regblock.cpuif.apb4.APB4_Cpuif`
* Class: :class:`peakrdl_busdecoder.cpuif.apb4.APB4_Cpuif`
Flattened inputs/outputs
Flattens the interface into discrete input and output ports.
* Command line: ``--cpuif apb4-flat``
* Class: :class:`peakrdl_regblock.cpuif.apb4.APB4_Cpuif_flattened`
* Class: :class:`peakrdl_busdecoder.cpuif.apb4.APB4_Cpuif_flattened`

View File

@@ -10,13 +10,13 @@ The Avalon interface comes in two i/o port flavors:
SystemVerilog Interface
* Command line: ``--cpuif avalon-mm``
* Interface Definition: :download:`avalon_mm_intf.sv <../../hdl-src/avalon_mm_intf.sv>`
* Class: :class:`peakrdl_regblock.cpuif.avalon.Avalon_Cpuif`
* Class: :class:`peakrdl_busdecoder.cpuif.avalon.Avalon_Cpuif`
Flattened inputs/outputs
Flattens the interface into discrete input and output ports.
* Command line: ``--cpuif avalon-mm-flat``
* Class: :class:`peakrdl_regblock.cpuif.avalon.Avalon_Cpuif_flattened`
* Class: :class:`peakrdl_busdecoder.cpuif.avalon.Avalon_Cpuif_flattened`
Implementation Details

View File

@@ -12,13 +12,13 @@ The AXI4-Lite CPU interface comes in two i/o port flavors:
SystemVerilog Interface
* Command line: ``--cpuif axi4-lite``
* Interface Definition: :download:`axi4lite_intf.sv <../../hdl-src/axi4lite_intf.sv>`
* Class: :class:`peakrdl_regblock.cpuif.axi4lite.AXI4Lite_Cpuif`
* Class: :class:`peakrdl_busdecoder.cpuif.axi4lite.AXI4Lite_Cpuif`
Flattened inputs/outputs
Flattens the interface into discrete input and output ports.
* Command line: ``--cpuif axi4-lite-flat``
* Class: :class:`peakrdl_regblock.cpuif.axi4lite.AXI4Lite_Cpuif_flattened`
* Class: :class:`peakrdl_busdecoder.cpuif.axi4lite.AXI4Lite_Cpuif_flattened`
Pipelined Performance

View File

@@ -29,7 +29,7 @@ Rather than rewriting a new CPU interface definition, you can extend and adjust
.. code-block:: python
from peakrdl_regblock.cpuif.axi4lite import AXI4Lite_Cpuif
from peakrdl_busdecoder.cpuif.axi4lite import AXI4Lite_Cpuif
class My_AXI4Lite(AXI4Lite_Cpuif):
@property
@@ -45,7 +45,7 @@ Then use your custom CPUIF during export:
.. code-block:: python
exporter = RegblockExporter()
exporter = BusDecoderExporter()
exporter.export(
root, "path/to/output_dir",
cpuif_cls=My_AXI4Lite
@@ -72,7 +72,7 @@ you can define your own.
2. Create a Python class that defines your CPUIF
Extend your class from :class:`peakrdl_regblock.cpuif.CpuifBase`.
Extend your class from :class:`peakrdl_busdecoder.cpuif.CpuifBase`.
Define the port declaration string, and provide a reference to your template file.
3. Use your new CPUIF definition when exporting.
@@ -94,17 +94,17 @@ Via a package's entry point definition
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
If you are publishing a collection of PeakRDL plugins as an installable Python
package, you can advertise them to PeakRDL using an entry point.
This advertises your custom CPUIF class to the PeakRDL-regblock tool as a plugin
This advertises your custom CPUIF class to the PeakRDL-busdecoder tool as a plugin
that should be loaded, and made available as a command-line option in PeakRDL.
.. code-block:: toml
[project.entry-points."peakrdl_regblock.cpuif"]
[project.entry-points."peakrdl_busdecoder.cpuif"]
my-cpuif = "my_package.my_module:MyCPUIF"
* ``my_package``: The name of your installable Python module
* ``peakrdl-regblock.cpuif``: This is the namespace that PeakRDL-regblock will
* ``peakrdl-busdecoder.cpuif``: This is the namespace that PeakRDL-busdecoder will
search. Any cpuif plugins you create must be enclosed in this namespace in
order to be discovered.
* ``my_package.my_module:MyCPUIF``: This is the import path that

View File

@@ -3,9 +3,9 @@
Internal CPUIF Protocol
=======================
Internally, the regblock generator uses a common CPU interface handshake
Internally, the busdecoder generator uses a common CPU interface handshake
protocol. This strobe-based protocol is designed to add minimal overhead to the
regblock implementation, while also being flexible enough to support advanced
busdecoder implementation, while also being flexible enough to support advanced
features of a variety of bus interface standards.
@@ -205,7 +205,7 @@ request until the stall is cleared.
For non-pipelined CPU interfaces that only allow one outstanding transaction at a time,
these stall signals can be safely ignored.
In the following example, the regblock is configured such that:
In the following example, the busdecoder is configured such that:
* A read transaction takes 1 clock cycle to complete
* A write transaction takes 0 clock cycles to complete

View File

@@ -17,9 +17,9 @@ encountered in the design.
Addressing
^^^^^^^^^^
The regblock exporter will always generate its address decoding logic using local
The busdecoder exporter will always generate its address decoding logic using local
address offsets. The absolute address offset of your device shall be
handled by your system interconnect, and present addresses to the regblock that
handled by your system interconnect, and present addresses to the busdecoder that
only include the local offset.
For example, consider a fictional AXI4-Lite device that:

View File

@@ -5,6 +5,6 @@ This CPUIF mode bypasses the protocol converter stage and directly exposes the
internal CPUIF handshake signals to the user.
* Command line: ``--cpuif passthrough``
* Class: :class:`peakrdl_regblock.cpuif.passthrough.PassthroughCpuif`
* Class: :class:`peakrdl_busdecoder.cpuif.passthrough.PassthroughCpuif`
For more details on the protocol itself, see: :ref:`cpuif_protocol`.

View File

@@ -42,14 +42,14 @@ Naming Scheme
================================================================================
hwif_out
.my_regblock
.my_busdecoder
.my_reg[X][Y]
.my_field
.value
.anded
hwif_in
.my_regblock
.my_busdecoder
.my_reg[X][Y]
.my_field
.value

View File

@@ -101,7 +101,7 @@ The generated output does not match our organization's coding style
SystemVerilog coding styles vary wildly, and unfortunately there is little
consensus on this topic within the digital design community.
The output generated by PeakRDL-regblock strives to be as human-readable as possible,
The output generated by PeakRDL-busdecoder strives to be as human-readable as possible,
and follow consistent indentation and styling. We do our best to use the most
widely accepted coding style, but since this is a very opinionated space, it is
impossible to satisfy everyone.
@@ -127,5 +127,5 @@ complexity to the tool.
If you encounter a lint violation, please carefully review and consider waiving
it if it does not pose an actual danger. If you still believe it is a problem,
please let us know by `submitting an issue <https://github.com/SystemRDL/PeakRDL-regblock/issues>`_
please let us know by `submitting an issue <https://github.com/SystemRDL/PeakRDL-busdecoder/issues>`_
that describes the problem.

View File

@@ -1,7 +1,7 @@
Introduction
============
PeakRDL-regblock is a free and open-source control & status register (CSR) compiler.
PeakRDL-busdecoder is a free and open-source control & status register (CSR) compiler.
This code generator translates your SystemRDL register description into
a synthesizable SystemVerilog RTL module that can be easily instantiated into
your hardware design.
@@ -14,31 +14,31 @@ your hardware design.
Quick Start
-----------
The easiest way to use PeakRDL-regblock is via the `PeakRDL command line tool <https://peakrdl.readthedocs.io/>`_:
The easiest way to use PeakRDL-busdecoder is via the `PeakRDL command line tool <https://peakrdl.readthedocs.io/>`_:
.. code-block:: bash
# Install PeakRDL-regblock along with the command-line tool
python3 -m pip install peakrdl-regblock[cli]
# Install PeakRDL-busdecoder along with the command-line tool
python3 -m pip install peakrdl-busdecoder[cli]
# Export!
peakrdl regblock atxmega_spi.rdl -o regblock/ --cpuif axi4-lite
peakrdl busdecoder atxmega_spi.rdl -o busdecoder/ --cpuif axi4-lite
Looking for VHDL?
-----------------
This project generates SystemVerilog RTL. If you prefer using VHDL, check out
the sister project which aims to be a feature-equivalent fork of
PeakRDL-regblock: `PeakRDL-regblock-VHDL <https://peakrdl-regblock-vhdl.readthedocs.io>`_
PeakRDL-busdecoder: `PeakRDL-busdecoder-VHDL <https://peakrdl-busdecoder-vhdl.readthedocs.io>`_
Links
-----
- `Source repository <https://github.com/SystemRDL/PeakRDL-regblock>`_
- `Release Notes <https://github.com/SystemRDL/PeakRDL-regblock/releases>`_
- `Issue tracker <https://github.com/SystemRDL/PeakRDL-regblock/issues>`_
- `PyPi <https://pypi.org/project/peakrdl-regblock>`_
- `Source repository <https://github.com/SystemRDL/PeakRDL-busdecoder>`_
- `Release Notes <https://github.com/SystemRDL/PeakRDL-busdecoder/releases>`_
- `Issue tracker <https://github.com/SystemRDL/PeakRDL-busdecoder/issues>`_
- `PyPi <https://pypi.org/project/peakrdl-busdecoder>`_
- `SystemRDL Specification <http://accellera.org/downloads/standards/systemrdl>`_

View File

@@ -1,9 +1,9 @@
Licensing
=========
Re-distribution of the PeakRDL-regblock code generator tool shall adhere to the
Re-distribution of the PeakRDL-busdecoder code generator tool shall adhere to the
terms outlined by the GNU LGPL v3 license. For a copy of the license, see:
https://github.com/SystemRDL/PeakRDL-regblock/blob/main/LICENSE
https://github.com/SystemRDL/PeakRDL-busdecoder/blob/main/LICENSE
Why LGPLv3?
@@ -23,19 +23,19 @@ explicitly mentioned in the exemptions below.
What is exempt from the LGPLv3 license?
---------------------------------------
Don't worry. Not everything that the PeakRDL-regblock project touches is
Don't worry. Not everything that the PeakRDL-busdecoder project touches is
considered LGPLv3 code.
The following are exempt and are free to use with no restrictions:
* Any code that is generated using PeakRDL-regblock is 100% yours. Since it
was derived from your regblock definition, it remains yours. You can
* Any code that is generated using PeakRDL-busdecoder is 100% yours. Since it
was derived from your busdecoder definition, it remains yours. You can
distribute it freely, use it in a proprietary ASIC, sell it as part of an
IP, whatever.
* Any code snippets in this documentation can be freely copy/pasted. These are
examples that are intended for this purpose.
* All reference files that are downloadable from this documentation, which are
also available in the `hdl-src folder in the repository <https://github.com/SystemRDL/PeakRDL-regblock/tree/main/hdl-src>`_
also available in the `hdl-src folder in the repository <https://github.com/SystemRDL/PeakRDL-busdecoder/tree/main/hdl-src>`_
Can I use this as part of my company's internally developed tools?

View File

@@ -15,7 +15,7 @@ Unaligned Registers
-------------------
All address offsets & strides shall be a multiple of the cpuif bus width used. Specifically:
* Bus width is inferred by the maximum accesswidth used in the regblock.
* Bus width is inferred by the maximum accesswidth used in the busdecoder.
* Each component's address and array stride shall be aligned to the bus width.

View File

@@ -2,7 +2,7 @@ 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.
supported, or are not relevant to the busdecoder exporter and are ignored.
errextbus

View File

@@ -2,7 +2,7 @@ Field Properties
================
.. note:: Any properties not explicitly listed here are either implicitly
supported, or are not relevant to the regblock exporter and are ignored.
supported, or are not relevant to the busdecoder exporter and are ignored.
Software Access Properties
--------------------------

View File

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

View File

@@ -2,7 +2,7 @@ Signal Properties
=================
.. note:: Any properties not explicitly listed here are either implicitly
supported, or are not relevant to the regblock exporter and are ignored.
supported, or are not relevant to the busdecoder exporter and are ignored.
activehigh/activelow
@@ -19,7 +19,7 @@ Ignored in all other contexts.
cpuif_reset
-----------
Specify that this signal shall be used as alternate reset signal for the CPU
interface for this regblock.
interface for this busdecoder.
field_reset

View File

@@ -1,9 +1,9 @@
External Components
===================
SystemRDL allows some component instances to be defined as "external" elements
of an address space definition. In the context of this regblock generator,
of an address space definition. In the context of this busdecoder generator,
the implementation of an external component is left up to the designer. When
generating the RTL for a regblock, the implementations of external components
generating the RTL for a busdecoder, the implementations of external components
are omitted and instead a user-interface is presented on the
``hwif_in``/``hwif_out`` i/o structs.
@@ -16,7 +16,7 @@ Things you should know
* By default external ``hwif_out`` signals are driven combinationally. An
optional output retiming stage can be enabled if needed.
* Due to the uncertain access latency of external components, the regblock will
* Due to the uncertain access latency of external components, the busdecoder will
only issue one outstanding transaction to an external component at a time.
This is enforced even if the CPUIF is capable of pipelined accesses such as
AXI4-Lite.
@@ -109,7 +109,7 @@ Broader external address regions can be represented by external block-like
components such as ``addrmap``, ``regfile`` or ``mem`` elements.
To ensure address decoding for external blocks is simple (only requires simple bit-pruning),
blocks that are external to an exported regblock shall be aligned to their size.
blocks that are external to an exported busdecoder shall be aligned to their size.
Request
^^^^^^^

View File

@@ -15,9 +15,9 @@ functionality.
Properties
----------
These UDP definitions, along with others supported by PeakRDL-regblock can be
These UDP definitions, along with others supported by PeakRDL-busdecoder can be
enabled by compiling the following file along with your design:
:download:`regblock_udps.rdl <../../hdl-src/regblock_udps.rdl>`.
:download:`busdecoder_udps.rdl <../../hdl-src/busdecoder_udps.rdl>`.
.. describe:: rd_swacc

View File

@@ -11,21 +11,21 @@ for each number, unlike for floating-point numbers.
For this SystemVerilog exporter, these properties only affect the signal type in
the the ``hwif`` structs. There is no special handling in the internals of
the regblock.
the busdecoder.
Properties
----------
Fields can be declared as fixed-point numbers using the following two properties:
.. literalinclude:: ../../hdl-src/regblock_udps.rdl
.. literalinclude:: ../../hdl-src/busdecoder_udps.rdl
:lines: 46-54
The :ref:`is_signed<signed>` property can be used in conjunction with these
properties to declare signed fixed-point fields.
These UDP definitions, along with others supported by PeakRDL-regblock, can be
These UDP definitions, along with others supported by PeakRDL-busdecoder, can be
enabled by compiling the following file along with your design:
:download:`regblock_udps.rdl <../../hdl-src/regblock_udps.rdl>`.
:download:`busdecoder_udps.rdl <../../hdl-src/busdecoder_udps.rdl>`.
.. describe:: intwidth

View File

@@ -5,11 +5,11 @@ Although the official SystemRDL spec defines numerous properties that allow you
to define complex register map structures, sometimes they are not enough to
accurately describe a necessary feature. Fortunately the SystemRDL spec allows
the language to be extended using "User Defined Properties" (UDPs). The
PeakRDL-regblock tool understands several UDPs that are described in this
PeakRDL-busdecoder tool understands several UDPs that are described in this
section.
To enable these UDPs, compile this RDL file prior to the rest of your design:
:download:`regblock_udps.rdl <../../hdl-src/regblock_udps.rdl>`.
:download:`busdecoder_udps.rdl <../../hdl-src/busdecoder_udps.rdl>`.
.. list-table:: Summary of UDPs
:header-rows: 1

View File

@@ -24,12 +24,12 @@ Properties
The behavior of read-buffered registers is defined using the following two
properties:
.. literalinclude:: ../../hdl-src/regblock_udps.rdl
.. literalinclude:: ../../hdl-src/busdecoder_udps.rdl
:lines: 10-18
These UDP definitions, along with others supported by PeakRDL-regblock can be
These UDP definitions, along with others supported by PeakRDL-busdecoder can be
enabled by compiling the following file along with your design:
:download:`regblock_udps.rdl <../../hdl-src/regblock_udps.rdl>`.
:download:`busdecoder_udps.rdl <../../hdl-src/busdecoder_udps.rdl>`.
.. describe:: buffer_reads

View File

@@ -8,18 +8,18 @@ The ``is_signed`` user-defined property fills this need.
For this SystemVerilog exporter, marking a field as signed only affects the
signal type in the ``hwif`` structs. There is no special handling in the internals
of the regblock.
of the busdecoder.
Properties
----------
A field can be marked as signed using the following user-defined property:
.. literalinclude:: ../../hdl-src/regblock_udps.rdl
.. literalinclude:: ../../hdl-src/busdecoder_udps.rdl
:lines: 40-44
This UDP definition, along with others supported by PeakRDL-regblock, can be
This UDP definition, along with others supported by PeakRDL-busdecoder, can be
enabled by compiling the following file along with your design:
:download:`regblock_udps.rdl <../../hdl-src/regblock_udps.rdl>`.
:download:`busdecoder_udps.rdl <../../hdl-src/busdecoder_udps.rdl>`.
.. describe:: is_signed

View File

@@ -4,8 +4,8 @@ Write-buffered Registers
========================
In order to support larger software write accesses that are atomic, the
regblock generator understands several UDPs that implement write-buffering to
specific registers. This causes the regblock to delay the effect of a software
busdecoder generator understands several UDPs that implement write-buffering to
specific registers. This causes the busdecoder to delay the effect of a software
write operation until a defined trigger event.
Some examples of when this is useful:
@@ -30,12 +30,12 @@ Properties
The behavior of write-buffered registers is defined using the following two
properties:
.. literalinclude:: ../../hdl-src/regblock_udps.rdl
.. literalinclude:: ../../hdl-src/busdecoder_udps.rdl
:lines: 20-28
These UDP definitions, along with others supported by PeakRDL-regblock can be
These UDP definitions, along with others supported by PeakRDL-busdecoder can be
enabled by compiling the following file along with your design:
:download:`regblock_udps.rdl <../../hdl-src/regblock_udps.rdl>`.
:download:`busdecoder_udps.rdl <../../hdl-src/busdecoder_udps.rdl>`.
.. describe:: buffer_writes

View File

@@ -1,10 +1,10 @@
/*
* This file defines several property extensions that are understood by the
* PeakRDL-Regblock SystemVerilog code generator.
* PeakRDL-BusDecoder SystemVerilog code generator.
*
* Compile this file prior to your other SystemRDL sources.
*
* For more details, see: https://peakrdl-regblock.readthedocs.io/en/latest/udps/intro.html
* For more details, see: https://peakrdl-busdecoder.readthedocs.io/en/latest/udps/intro.html
*/
property buffer_reads {

View File

@@ -3,24 +3,28 @@ requires = ["setuptools", "setuptools-scm"]
build-backend = "setuptools.build_meta"
[project]
name = "peakrdl-regblock"
name = "peakrdl-busdecoder"
dynamic = ["version"]
requires-python = ">=3.7"
dependencies = [
"systemrdl-compiler ~= 1.29",
"Jinja2>=2.11",
]
dependencies = ["systemrdl-compiler ~= 1.29", "Jinja2>=2.11"]
authors = [
{name="Alex Mykyta"},
]
authors = [{ name = "Alex Mykyta" }]
description = "Compile SystemRDL into a SystemVerilog control/status register (CSR) block"
readme = "README.md"
license = {text = "LGPLv3"}
license = { text = "LGPLv3" }
keywords = [
"SystemRDL", "PeakRDL", "CSR", "compiler", "tool", "registers", "generator",
"Verilog", "SystemVerilog", "register abstraction layer",
"FPGA", "ASIC",
"SystemRDL",
"PeakRDL",
"CSR",
"compiler",
"tool",
"registers",
"generator",
"Verilog",
"SystemVerilog",
"register abstraction layer",
"FPGA",
"ASIC",
]
classifiers = [
"Development Status :: 5 - Production/Stable",
@@ -34,18 +38,16 @@ classifiers = [
]
[project.optional-dependencies]
cli = [
"peakrdl-cli >= 1.2.3",
]
cli = ["peakrdl-cli >= 1.2.3"]
[project.urls]
Source = "https://github.com/SystemRDL/PeakRDL-regblock"
Tracker = "https://github.com/SystemRDL/PeakRDL-regblock/issues"
Changelog = "https://github.com/SystemRDL/PeakRDL-regblock/releases"
Documentation = "https://peakrdl-regblock.readthedocs.io/"
Source = "https://github.com/SystemRDL/PeakRDL-busdecoder"
Tracker = "https://github.com/SystemRDL/PeakRDL-busdecoder/issues"
Changelog = "https://github.com/SystemRDL/PeakRDL-busdecoder/releases"
Documentation = "https://peakrdl-busdecoder.readthedocs.io/"
[tool.setuptools.dynamic]
version = {attr = "peakrdl_regblock.__about__.__version__"}
version = { attr = "peakrdl_busdecoder.__about__.__version__" }
[project.entry-points."peakrdl.exporters"]
regblock = "peakrdl_regblock.__peakrdl__:Exporter"
busdecoder = "peakrdl_busdecoder.__peakrdl__:Exporter"

View File

@@ -1,3 +1,3 @@
from .__about__ import __version__
from .exporter import RegblockExporter
from .exporter import BusDecoderExporter

View File

@@ -6,7 +6,7 @@ from peakrdl.plugins.exporter import ExporterSubcommandPlugin
from peakrdl.config import schema
from peakrdl.plugins.entry_points import get_entry_points
from .exporter import RegblockExporter
from .exporter import BusDecoderExporter
from .cpuif import CpuifBase, apb3, apb4, axi4lite, passthrough, avalon
from .udps import ALL_UDPS
@@ -14,6 +14,7 @@ if TYPE_CHECKING:
import argparse
from systemrdl.node import AddrmapNode
class Exporter(ExporterSubcommandPlugin):
short_desc = "Generate a SystemVerilog control/status register (CSR) block"
@@ -26,7 +27,6 @@ class Exporter(ExporterSubcommandPlugin):
@functools.lru_cache()
def get_cpuifs(self) -> Dict[str, Type[CpuifBase]]:
# All built-in CPUIFs
cpuifs = {
"passthrough": passthrough.PassthroughCpuif,
@@ -41,67 +41,74 @@ class Exporter(ExporterSubcommandPlugin):
}
# Load any cpuifs specified via entry points
for ep, dist in get_entry_points("peakrdl_regblock.cpuif"):
for ep, dist in get_entry_points("peakrdl_busdecoder.cpuif"):
name = ep.name
cpuif = ep.load()
if name in cpuifs:
raise RuntimeError(f"A plugin for 'peakrdl-regblock' tried to load cpuif '{name}' but it already exists")
raise RuntimeError(
f"A plugin for 'peakrdl-busdecoder' tried to load cpuif '{name}' but it already exists"
)
if not issubclass(cpuif, CpuifBase):
raise RuntimeError(f"A plugin for 'peakrdl-regblock' tried to load cpuif '{name}' but it not a CpuifBase class")
raise RuntimeError(
f"A plugin for 'peakrdl-busdecoder' tried to load cpuif '{name}' but it not a CpuifBase class"
)
cpuifs[name] = cpuif
# Load any CPUIFs via config import
for name, cpuif in self.cfg['cpuifs'].items():
for name, cpuif in self.cfg["cpuifs"].items():
if name in cpuifs:
raise RuntimeError(f"A plugin for 'peakrdl-regblock' tried to load cpuif '{name}' but it already exists")
raise RuntimeError(
f"A plugin for 'peakrdl-busdecoder' tried to load cpuif '{name}' but it already exists"
)
if not issubclass(cpuif, CpuifBase):
raise RuntimeError(f"A plugin for 'peakrdl-regblock' tried to load cpuif '{name}' but it not a CpuifBase class")
raise RuntimeError(
f"A plugin for 'peakrdl-busdecoder' tried to load cpuif '{name}' but it not a CpuifBase class"
)
cpuifs[name] = cpuif
return cpuifs
def add_exporter_arguments(self, arg_group: 'argparse._ActionsContainer') -> None:
def add_exporter_arguments(self, arg_group: "argparse._ActionsContainer") -> None:
cpuifs = self.get_cpuifs()
arg_group.add_argument(
"--cpuif",
choices=cpuifs.keys(),
default="apb3",
help="Select the CPU interface protocol to use [apb3]"
help="Select the CPU interface protocol to use [apb3]",
)
arg_group.add_argument(
"--module-name",
metavar="NAME",
default=None,
help="Override the SystemVerilog module name"
help="Override the SystemVerilog module name",
)
arg_group.add_argument(
"--package-name",
metavar="NAME",
default=None,
help="Override the SystemVerilog package name"
help="Override the SystemVerilog package name",
)
arg_group.add_argument(
"--type-style",
dest="type_style",
choices=['lexical', 'hier'],
choices=["lexical", "hier"],
default="lexical",
help="""Choose how HWIF struct type names are generated.
The 'lexical' style will use RDL lexical scope & type names where
possible and attempt to re-use equivalent type definitions.
The 'hier' style uses component's hierarchy as the struct type name. [lexical]
"""
""",
)
arg_group.add_argument(
"--hwif-report",
action="store_true",
default=False,
help="Generate a HWIF report file"
help="Generate a HWIF report file",
)
arg_group.add_argument(
@@ -109,25 +116,25 @@ class Exporter(ExporterSubcommandPlugin):
type=int,
default=None,
help="""Override the CPU interface's address width. By default,
address width is sized to the contents of the regblock.
"""
address width is sized to the contents of the busdecoder.
""",
)
arg_group.add_argument(
"--rt-read-fanin",
action="store_true",
default=False,
help="Enable additional read path retiming. Good for register blocks with large readback fan-in"
help="Enable additional read path retiming. Good for register blocks with large readback fan-in",
)
arg_group.add_argument(
"--rt-read-response",
action="store_true",
default=False,
help="Enable additional retiming stage between readback fan-in and cpu interface"
help="Enable additional retiming stage between readback fan-in and cpu interface",
)
arg_group.add_argument(
"--rt-external",
help="Retime outputs to external components. Specify a comma-separated list of: reg,regfile,mem,addrmap,all"
help="Retime outputs to external components. Specify a comma-separated list of: reg,regfile,mem,addrmap,all",
)
arg_group.add_argument(
@@ -136,11 +143,10 @@ class Exporter(ExporterSubcommandPlugin):
default=None,
help="""Choose the default style of reset signal if not explicitly
specified by the SystemRDL design. If unspecified, the default reset
is active-high and synchronous [rst]"""
is active-high and synchronous [rst]""",
)
def do_export(self, top_node: 'AddrmapNode', options: 'argparse.Namespace') -> None:
def do_export(self, top_node: "AddrmapNode", options: "argparse.Namespace") -> None:
cpuifs = self.get_cpuifs()
retime_external_reg = False
@@ -164,10 +170,13 @@ class Exporter(ExporterSubcommandPlugin):
retime_external_mem = True
retime_external_addrmap = True
else:
print("error: invalid option for --rt-external: '%s'" % key, file=sys.stderr)
print(
"error: invalid option for --rt-external: '%s'" % key,
file=sys.stderr,
)
# Get default reset. Favor command-line over cfg. Fall back to 'rst'
default_rst = options.default_reset or self.cfg['default_reset'] or "rst"
default_rst = options.default_reset or self.cfg["default_reset"] or "rst"
if default_rst == "rst":
default_reset_activelow = False
default_reset_async = False
@@ -183,8 +192,7 @@ class Exporter(ExporterSubcommandPlugin):
else:
raise RuntimeError
x = RegblockExporter()
x = BusDecoderExporter()
x.export(
top_node,
options.output,

View File

@@ -10,16 +10,17 @@ from .identifier_filter import kw_filter as kwf
from .sv_int import SVInt
if TYPE_CHECKING:
from .exporter import RegblockExporter
from .exporter import BusDecoderExporter
from systemrdl.node import AddrmapNode, AddressableNode
from systemrdl.node import RegfileNode, MemNode
class AddressDecode:
def __init__(self, exp:'RegblockExporter'):
def __init__(self, exp: "BusDecoderExporter"):
self.exp = exp
@property
def top_node(self) -> 'AddrmapNode':
def top_node(self) -> "AddrmapNode":
return self.exp.ds.top_node
def get_strobe_struct(self) -> str:
@@ -34,7 +35,9 @@ class AddressDecode:
assert s is not None
return s
def get_access_strobe(self, node: Union[RegNode, FieldNode], reduce_substrobes: bool=True) -> str:
def get_access_strobe(
self, node: Union[RegNode, FieldNode], reduce_substrobes: bool = True
) -> str:
"""
Returns the Verilog string that represents the register/field's access strobe.
"""
@@ -42,8 +45,8 @@ class AddressDecode:
field = node
path = get_indexed_path(self.top_node, node.parent)
regwidth = node.parent.get_property('regwidth')
accesswidth = node.parent.get_property('accesswidth')
regwidth = node.parent.get_property("regwidth")
accesswidth = node.parent.get_property("accesswidth")
if regwidth > accesswidth:
# Is wide register.
# Determine the substrobe(s) relevant to this field
@@ -63,7 +66,7 @@ class AddressDecode:
return "decoded_reg_strb." + path
def get_external_block_access_strobe(self, node: 'AddressableNode') -> str:
def get_external_block_access_strobe(self, node: "AddressableNode") -> str:
assert node.external
assert not isinstance(node, RegNode)
path = get_indexed_path(self.top_node, node)
@@ -71,42 +74,41 @@ class AddressDecode:
class DecodeStructGenerator(RDLStructGenerator):
def _enter_external_block(self, node: 'AddressableNode') -> None:
def _enter_external_block(self, node: "AddressableNode") -> None:
self.add_member(
kwf(node.inst_name),
array_dimensions=node.array_dimensions,
)
def enter_Addrmap(self, node: 'AddrmapNode') -> Optional[WalkerAction]:
def enter_Addrmap(self, node: "AddrmapNode") -> Optional[WalkerAction]:
assert node.external
self._enter_external_block(node)
return WalkerAction.SkipDescendants
def exit_Addrmap(self, node: 'AddrmapNode') -> None:
def exit_Addrmap(self, node: "AddrmapNode") -> None:
assert node.external
def enter_Regfile(self, node: 'RegfileNode') -> Optional[WalkerAction]:
def enter_Regfile(self, node: "RegfileNode") -> Optional[WalkerAction]:
if node.external:
self._enter_external_block(node)
return WalkerAction.SkipDescendants
super().enter_Regfile(node)
return WalkerAction.Continue
def exit_Regfile(self, node: 'RegfileNode') -> None:
def exit_Regfile(self, node: "RegfileNode") -> None:
if node.external:
return
super().exit_Regfile(node)
def enter_Mem(self, node: 'MemNode') -> Optional[WalkerAction]:
def enter_Mem(self, node: "MemNode") -> Optional[WalkerAction]:
assert node.external
self._enter_external_block(node)
return WalkerAction.SkipDescendants
def exit_Mem(self, node: 'MemNode') -> None:
def exit_Mem(self, node: "MemNode") -> None:
assert node.external
def enter_Reg(self, node: 'RegNode') -> None:
def enter_Reg(self, node: "RegNode") -> None:
# if register is "wide", expand the strobe to be able to access the sub-words
n_subwords = node.get_property("regwidth") // node.get_property("accesswidth")
@@ -117,14 +119,14 @@ class DecodeStructGenerator(RDLStructGenerator):
)
# Stub out
def exit_Reg(self, node: 'RegNode') -> None:
def exit_Reg(self, node: "RegNode") -> None:
pass
def enter_Field(self, node: 'FieldNode') -> None:
def enter_Field(self, node: "FieldNode") -> None:
pass
class DecodeLogicGenerator(RDLForLoopGenerator):
def __init__(self, addr_decode: AddressDecode) -> None:
self.addr_decode = addr_decode
super().__init__()
@@ -132,8 +134,9 @@ class DecodeLogicGenerator(RDLForLoopGenerator):
# List of address strides for each dimension
self._array_stride_stack = [] # type: List[int]
def enter_AddressableComponent(self, node: 'AddressableNode') -> Optional[WalkerAction]:
def enter_AddressableComponent(
self, node: "AddressableNode"
) -> Optional[WalkerAction]:
super().enter_AddressableComponent(node)
if node.array_dimensions:
@@ -158,21 +161,23 @@ class DecodeLogicGenerator(RDLForLoopGenerator):
return WalkerAction.Continue
def _get_address_str(self, node: 'AddressableNode', subword_offset: int=0) -> str:
def _get_address_str(self, node: "AddressableNode", subword_offset: int = 0) -> str:
expr_width = self.addr_decode.exp.ds.addr_width
a = str(SVInt(
node.raw_absolute_address - self.addr_decode.top_node.raw_absolute_address + subword_offset,
expr_width
))
a = str(
SVInt(
node.raw_absolute_address
- self.addr_decode.top_node.raw_absolute_address
+ subword_offset,
expr_width,
)
)
for i, stride in enumerate(self._array_stride_stack):
a += f" + ({expr_width})'(i{i}) * {SVInt(stride, expr_width)}"
return a
def enter_Reg(self, node: RegNode) -> None:
regwidth = node.get_property('regwidth')
accesswidth = node.get_property('accesswidth')
regwidth = node.get_property("regwidth")
accesswidth = node.get_property("accesswidth")
if regwidth == accesswidth:
rhs = f"cpuif_req_masked & (cpuif_addr == {self._get_address_str(node)})"
@@ -194,7 +199,7 @@ class DecodeLogicGenerator(RDLForLoopGenerator):
n_subwords = regwidth // accesswidth
subword_stride = accesswidth // 8
for i in range(n_subwords):
rhs = f"cpuif_req_masked & (cpuif_addr == {self._get_address_str(node, subword_offset=(i*subword_stride))})"
rhs = f"cpuif_req_masked & (cpuif_addr == {self._get_address_str(node, subword_offset=(i * subword_stride))})"
s = f"{self.addr_decode.get_access_strobe(node)}[{i}] = {rhs};"
self.add_content(s)
if node.external:
@@ -209,7 +214,7 @@ class DecodeLogicGenerator(RDLForLoopGenerator):
else:
raise RuntimeError
def exit_AddressableComponent(self, node: 'AddressableNode') -> None:
def exit_AddressableComponent(self, node: "AddressableNode") -> None:
super().exit_AddressableComponent(node)
if not node.array_dimensions:

View File

@@ -1,5 +1,6 @@
from ..base import CpuifBase
class AXI4Lite_Cpuif(CpuifBase):
template_path = "axi4lite_tmpl.sv"
is_interface = True
@@ -8,11 +9,11 @@ class AXI4Lite_Cpuif(CpuifBase):
def port_declaration(self) -> str:
return "axi4lite_intf.slave s_axil"
def signal(self, name:str) -> str:
def signal(self, name: str) -> str:
return "s_axil." + name.upper()
@property
def regblock_latency(self) -> int:
def busdecoder_latency(self) -> int:
return max(self.exp.ds.min_read_latency, self.exp.ds.min_write_latency)
@property
@@ -23,7 +24,7 @@ class AXI4Lite_Cpuif(CpuifBase):
Anything beyond that does not have any effect, aside from adding unnecessary
logic and additional buffer-bloat latency.
"""
return self.regblock_latency + 2
return self.busdecoder_latency + 2
@property
def resp_buffer_size(self) -> int:
@@ -42,29 +43,25 @@ class AXI4Lite_Cpuif_flattened(AXI4Lite_Cpuif):
lines = [
"output logic " + self.signal("awready"),
"input wire " + self.signal("awvalid"),
f"input wire [{self.addr_width-1}:0] " + self.signal("awaddr"),
f"input wire [{self.addr_width - 1}:0] " + self.signal("awaddr"),
"input wire [2:0] " + self.signal("awprot"),
"output logic " + self.signal("wready"),
"input wire " + self.signal("wvalid"),
f"input wire [{self.data_width-1}:0] " + self.signal("wdata"),
f"input wire [{self.data_width_bytes-1}:0]" + self.signal("wstrb"),
f"input wire [{self.data_width - 1}:0] " + self.signal("wdata"),
f"input wire [{self.data_width_bytes - 1}:0]" + self.signal("wstrb"),
"input wire " + self.signal("bready"),
"output logic " + self.signal("bvalid"),
"output logic [1:0] " + self.signal("bresp"),
"output logic " + self.signal("arready"),
"input wire " + self.signal("arvalid"),
f"input wire [{self.addr_width-1}:0] " + self.signal("araddr"),
f"input wire [{self.addr_width - 1}:0] " + self.signal("araddr"),
"input wire [2:0] " + self.signal("arprot"),
"input wire " + self.signal("rready"),
"output logic " + self.signal("rvalid"),
f"output logic [{self.data_width-1}:0] " + self.signal("rdata"),
f"output logic [{self.data_width - 1}:0] " + self.signal("rdata"),
"output logic [1:0] " + self.signal("rresp"),
]
return ",\n".join(lines)
def signal(self, name:str) -> str:
def signal(self, name: str) -> str:
return "s_axil_" + name

View File

@@ -7,14 +7,14 @@ import jinja2 as jj
from ..utils import clog2, is_pow2, roundup_pow2
if TYPE_CHECKING:
from ..exporter import RegblockExporter
from ..exporter import BusDecoderExporter
class CpuifBase:
# Path is relative to the location of the class that assigns this variable
template_path = ""
def __init__(self, exp:'RegblockExporter'):
def __init__(self, exp: "BusDecoderExporter"):
self.exp = exp
self.reset = exp.ds.top_node.cpuif_reset
@@ -53,7 +53,6 @@ class CpuifBase:
return class_dir
raise RuntimeError
def get_implementation(self) -> str:
class_dir = self._get_template_path_class_dir()
loader = jj.FileSystemLoader(class_dir)

View File

@@ -5,40 +5,46 @@ from systemrdl.rdltypes import PropertyReference
from .sv_int import SVInt
if TYPE_CHECKING:
from .exporter import RegblockExporter, DesignState
from .exporter import BusDecoderExporter, DesignState
from .hwif import Hwif
from .field_logic import FieldLogic
from .addr_decode import AddressDecode
class Dereferencer:
"""
This class provides an interface to convert conceptual SystemRDL references
into Verilog identifiers
"""
def __init__(self, exp:'RegblockExporter'):
def __init__(self, exp: "BusDecoderExporter"):
self.exp = exp
@property
def hwif(self) -> 'Hwif':
def hwif(self) -> "Hwif":
return self.exp.hwif
@property
def address_decode(self) -> 'AddressDecode':
def address_decode(self) -> "AddressDecode":
return self.exp.address_decode
@property
def field_logic(self) -> 'FieldLogic':
def field_logic(self) -> "FieldLogic":
return self.exp.field_logic
@property
def ds(self) -> 'DesignState':
def ds(self) -> "DesignState":
return self.exp.ds
@property
def top_node(self) -> AddrmapNode:
return self.exp.ds.top_node
def get_value(self, obj: Union[int, FieldNode, SignalNode, PropertyReference], width: Optional[int] = None) -> Union[SVInt, str]:
def get_value(
self,
obj: Union[int, FieldNode, SignalNode, PropertyReference],
width: Optional[int] = None,
) -> Union[SVInt, str]:
"""
Returns the Verilog string that represents the readable value associated
with the object.
@@ -63,14 +69,14 @@ class Dereferencer:
# Field does not have a storage element, nor does it have a HW input
# must be a constant value as defined by its reset value
reset_value = obj.get_property('reset')
reset_value = obj.get_property("reset")
if reset_value is not None:
return self.get_value(reset_value, obj.width)
else:
# No reset value defined!
obj.env.msg.warning(
f"Field '{obj.inst_name}' is a constant but does not have a known value (missing reset). Assigning it a value of X.",
obj.inst.inst_src_ref
obj.inst.inst_src_ref,
)
return "'X"
@@ -88,7 +94,6 @@ class Dereferencer:
raise RuntimeError(f"Unhandled reference to: {obj}")
def get_field_propref_value(
self,
field: FieldNode,
@@ -109,16 +114,16 @@ class Dereferencer:
# references that directly access a property value
if prop_name in {
'decrvalue',
'enable',
'haltenable',
'haltmask',
'hwenable',
'hwmask',
'incrvalue',
'mask',
'reset',
'resetsignal',
"decrvalue",
"enable",
"haltenable",
"haltmask",
"hwenable",
"hwmask",
"incrvalue",
"mask",
"reset",
"resetsignal",
}:
return self.get_value(field.get_property(prop_name), width)
@@ -132,7 +137,7 @@ class Dereferencer:
return self.get_value(prop_value, width)
# References to another component value, or an implied input
if prop_name in {'hwclr', 'hwset'}:
if prop_name in {"hwclr", "hwset"}:
prop_value = field.get_property(prop_name)
if prop_value is True:
# Points to inferred hwif input
@@ -175,7 +180,6 @@ class Dereferencer:
if prop_name == "swmod":
return self.field_logic.get_swmod_identifier(field)
# translate aliases
aliases = {
"saturate": "incrsaturate",
@@ -184,37 +188,37 @@ class Dereferencer:
prop_name = aliases.get(prop_name, prop_name)
# Counter properties
if prop_name == 'incr':
if prop_name == "incr":
return self.field_logic.get_counter_incr_strobe(field)
if prop_name == 'decr':
if prop_name == "decr":
return self.field_logic.get_counter_decr_strobe(field)
if prop_name in {
'decrsaturate',
'decrthreshold',
'incrsaturate',
'incrthreshold',
'overflow',
'underflow',
"decrsaturate",
"decrthreshold",
"incrsaturate",
"incrthreshold",
"overflow",
"underflow",
}:
return self.field_logic.get_field_combo_identifier(field, prop_name)
raise RuntimeError(f"Unhandled reference to: {field}->{prop_name}")
def get_reg_propref_value(self, reg: RegNode, prop_name: str) -> str:
if prop_name in {'halt', 'intr'}:
if prop_name in {"halt", "intr"}:
return self.hwif.get_implied_prop_output_identifier(reg, prop_name)
raise NotImplementedError
def get_access_strobe(self, obj: Union[RegNode, FieldNode], reduce_substrobes: bool=True) -> str:
def get_access_strobe(
self, obj: Union[RegNode, FieldNode], reduce_substrobes: bool = True
) -> str:
"""
Returns the Verilog string that represents the register's access strobe
"""
return self.address_decode.get_access_strobe(obj, reduce_substrobes)
def get_external_block_access_strobe(self, obj: 'AddressableNode') -> str:
def get_external_block_access_strobe(self, obj: "AddressableNode") -> str:
"""
Returns the Verilog string that represents the external block's access strobe
"""
@@ -229,14 +233,13 @@ class Dereferencer:
s = f"{s}_n"
return s
def get_resetsignal(self, obj: Optional[SignalNode] = None) -> str:
"""
Returns a normalized active-high reset signal
"""
if isinstance(obj, SignalNode):
s = self.get_value(obj)
if obj.get_property('activehigh'):
if obj.get_property("activehigh"):
return str(s)
else:
return f"~{s}"
@@ -257,8 +260,12 @@ class Dereferencer:
return f"@(posedge clk or posedge {self.default_resetsignal_name})"
else:
return "@(posedge clk)"
elif resetsignal.get_property('async') and resetsignal.get_property('activehigh'):
elif resetsignal.get_property("async") and resetsignal.get_property(
"activehigh"
):
return f"@(posedge clk or posedge {self.get_value(resetsignal)})"
elif resetsignal.get_property('async') and not resetsignal.get_property('activehigh'):
elif resetsignal.get_property("async") and not resetsignal.get_property(
"activehigh"
):
return f"@(posedge clk or negedge {self.get_value(resetsignal)})"
return "@(posedge clk)"

View File

@@ -26,7 +26,8 @@ if TYPE_CHECKING:
from systemrdl.node import SignalNode
from systemrdl.rdltypes import UserEnum
class RegblockExporter:
class BusDecoderExporter:
hwif: Hwif
cpuif: CpuifBase
address_decode: AddressDecode
@@ -35,28 +36,35 @@ class RegblockExporter:
write_buffering: WriteBuffering
read_buffering: ReadBuffering
dereferencer: Dereferencer
ds: 'DesignState'
ds: "DesignState"
def __init__(self, **kwargs: Any) -> None:
# Check for stray kwargs
if kwargs:
raise TypeError(f"got an unexpected keyword argument '{list(kwargs.keys())[0]}'")
raise TypeError(
f"got an unexpected keyword argument '{list(kwargs.keys())[0]}'"
)
loader = jj.ChoiceLoader([
loader = jj.ChoiceLoader(
[
jj.FileSystemLoader(os.path.dirname(__file__)),
jj.PrefixLoader({
'base': jj.FileSystemLoader(os.path.dirname(__file__)),
}, delimiter=":")
])
jj.PrefixLoader(
{
"base": jj.FileSystemLoader(os.path.dirname(__file__)),
},
delimiter=":",
),
]
)
self.jj_env = jj.Environment(
loader=loader,
undefined=jj.StrictUndefined,
)
def export(self, node: Union[RootNode, AddrmapNode], output_dir:str, **kwargs: Any) -> None:
def export(
self, node: Union[RootNode, AddrmapNode], output_dir: str, **kwargs: Any
) -> None:
"""
Parameters
----------
@@ -65,7 +73,7 @@ class RegblockExporter:
output_dir: str
Path to the output directory where generated SystemVerilog will be written.
Output includes two files: a module definition and package definition.
cpuif_cls: :class:`peakrdl_regblock.cpuif.CpuifBase`
cpuif_cls: :class:`peakrdl_busdecoder.cpuif.CpuifBase`
Specify the class type that implements the CPU interface of your choice.
Defaults to AMBA APB4.
module_name: str
@@ -115,7 +123,7 @@ class RegblockExporter:
the contents of the ``hwif_in`` and ``hwif_out`` structures.
address_width: int
Override the CPU interface's address width. By default, address width
is sized to the contents of the regblock.
is sized to the contents of the busdecoder.
default_reset_activelow: bool
If overriden to True, default reset is active-low instead of active-high.
default_reset_async: bool
@@ -134,11 +142,13 @@ class RegblockExporter:
# Check for stray kwargs
if kwargs:
raise TypeError(f"got an unexpected keyword argument '{list(kwargs.keys())[0]}'")
raise TypeError(
f"got an unexpected keyword argument '{list(kwargs.keys())[0]}'"
)
if generate_hwif_report:
path = os.path.join(output_dir, f"{self.ds.module_name}_hwif.rpt")
hwif_report_file = open(path, "w", encoding='utf-8') # pylint: disable=consider-using-with
hwif_report_file = open(path, "w", encoding="utf-8") # pylint: disable=consider-using-with
else:
hwif_report_file = None
@@ -181,7 +191,7 @@ class RegblockExporter:
"get_always_ff_event": self.dereferencer.get_always_ff_event,
"ds": self.ds,
"kwf": kwf,
"SVInt" : SVInt,
"SVInt": SVInt,
}
# Write out design
@@ -210,12 +220,16 @@ class DesignState:
self.top_node = top_node
msg = top_node.env.msg
#------------------------
# ------------------------
# Extract compiler args
#------------------------
# ------------------------
self.reuse_hwif_typedefs = kwargs.pop("reuse_hwif_typedefs", True) # type: bool
self.module_name = kwargs.pop("module_name", None) or kwf(self.top_node.inst_name) # type: str
self.package_name = kwargs.pop("package_name", None) or (self.module_name + "_pkg") # type: str
self.module_name = kwargs.pop("module_name", None) or kwf(
self.top_node.inst_name
) # type: str
self.package_name = kwargs.pop("package_name", None) or (
self.module_name + "_pkg"
) # type: str
user_addr_width = kwargs.pop("address_width", None) # type: Optional[int]
# Pipelining options
@@ -230,9 +244,9 @@ class DesignState:
self.default_reset_activelow = kwargs.pop("default_reset_activelow", False) # type: bool
self.default_reset_async = kwargs.pop("default_reset_async", False) # type: bool
#------------------------
# ------------------------
# Info about the design
#------------------------
# ------------------------
self.cpuif_data_width = 0
# Collections of signals that were actually referenced by the design
@@ -260,17 +274,21 @@ class DesignState:
# Assume 32-bits
msg.warning(
"Addrmap being exported only contains external components. Unable to infer the CPUIF bus width. Assuming 32-bits.",
self.top_node.inst.def_src_ref
self.top_node.inst.def_src_ref,
)
self.cpuif_data_width = 32
#------------------------
# ------------------------
# Min address width encloses the total size AND at least 1 useful address bit
self.addr_width = max(clog2(self.top_node.size), clog2(self.cpuif_data_width//8) + 1)
self.addr_width = max(
clog2(self.top_node.size), clog2(self.cpuif_data_width // 8) + 1
)
if user_addr_width is not None:
if user_addr_width < self.addr_width:
msg.fatal(f"User-specified address width shall be greater than or equal to {self.addr_width}.")
msg.fatal(
f"User-specified address width shall be greater than or equal to {self.addr_width}."
)
self.addr_width = user_addr_width
@property

View File

@@ -6,12 +6,12 @@ from systemrdl.node import RegNode
from .forloop_generator import RDLForLoopGenerator
if TYPE_CHECKING:
from .exporter import RegblockExporter
from .exporter import BusDecoderExporter
from systemrdl.node import AddressableNode
class ExternalWriteAckGenerator(RDLForLoopGenerator):
def __init__(self, exp: 'RegblockExporter') -> None:
def __init__(self, exp: "BusDecoderExporter") -> None:
super().__init__()
self.exp = exp
@@ -21,19 +21,21 @@ class ExternalWriteAckGenerator(RDLForLoopGenerator):
return ""
return content
def enter_AddressableComponent(self, node: 'AddressableNode') -> WalkerAction:
def enter_AddressableComponent(self, node: "AddressableNode") -> WalkerAction:
super().enter_AddressableComponent(node)
if node.external:
if not isinstance(node, RegNode) or node.has_sw_writable:
self.add_content(f"wr_ack |= {self.exp.hwif.get_external_wr_ack(node)};")
self.add_content(
f"wr_ack |= {self.exp.hwif.get_external_wr_ack(node)};"
)
return WalkerAction.SkipDescendants
return WalkerAction.Continue
class ExternalReadAckGenerator(RDLForLoopGenerator):
def __init__(self, exp: 'RegblockExporter') -> None:
def __init__(self, exp: "BusDecoderExporter") -> None:
super().__init__()
self.exp = exp
@@ -43,12 +45,14 @@ class ExternalReadAckGenerator(RDLForLoopGenerator):
return ""
return content
def enter_AddressableComponent(self, node: 'AddressableNode') -> WalkerAction:
def enter_AddressableComponent(self, node: "AddressableNode") -> WalkerAction:
super().enter_AddressableComponent(node)
if node.external:
if not isinstance(node, RegNode) or node.has_sw_readable:
self.add_content(f"rd_ack |= {self.exp.hwif.get_external_rd_ack(node)};")
self.add_content(
f"rd_ack |= {self.exp.hwif.get_external_rd_ack(node)};"
)
return WalkerAction.SkipDescendants
return WalkerAction.Continue

View File

@@ -14,15 +14,20 @@ from . import hw_interrupts_with_write
from ..utils import get_indexed_path
from ..sv_int import SVInt
from .generators import CombinationalStructGenerator, FieldStorageStructGenerator, FieldLogicGenerator
from .generators import (
CombinationalStructGenerator,
FieldStorageStructGenerator,
FieldLogicGenerator,
)
if TYPE_CHECKING:
from typing import Dict, List
from systemrdl.node import AddrmapNode, FieldNode
from ..exporter import RegblockExporter, DesignState
from ..exporter import BusDecoderExporter, DesignState
class FieldLogic:
def __init__(self, exp:'RegblockExporter'):
def __init__(self, exp: "BusDecoderExporter"):
self.exp = exp
self._hw_conditionals = {} # type: Dict[int, List[NextStateConditional]]
@@ -31,11 +36,11 @@ class FieldLogic:
self.init_conditionals()
@property
def ds(self) -> 'DesignState':
def ds(self) -> "DesignState":
return self.exp.ds
@property
def top_node(self) -> 'AddrmapNode':
def top_node(self) -> "AddrmapNode":
return self.exp.ds.top_node
def get_storage_struct(self) -> str:
@@ -65,10 +70,10 @@ class FieldLogic:
return ""
return s
#---------------------------------------------------------------------------
# ---------------------------------------------------------------------------
# Field utility functions
#---------------------------------------------------------------------------
def get_storage_identifier(self, field: 'FieldNode') -> str:
# ---------------------------------------------------------------------------
def get_storage_identifier(self, field: "FieldNode") -> str:
"""
Returns the Verilog string that represents the storage register element
for the referenced field
@@ -77,7 +82,7 @@ class FieldLogic:
path = get_indexed_path(self.top_node, field)
return f"field_storage.{path}.value"
def get_next_q_identifier(self, field: 'FieldNode') -> str:
def get_next_q_identifier(self, field: "FieldNode") -> str:
"""
Returns the Verilog string that represents the storage register element
for the delayed 'next' input value
@@ -86,7 +91,7 @@ class FieldLogic:
path = get_indexed_path(self.top_node, field)
return f"field_storage.{path}.next_q"
def get_field_combo_identifier(self, field: 'FieldNode', name: str) -> str:
def get_field_combo_identifier(self, field: "FieldNode", name: str) -> str:
"""
Returns a Verilog string that represents a field's internal combinational
signal.
@@ -95,94 +100,94 @@ class FieldLogic:
path = get_indexed_path(self.top_node, field)
return f"field_combo.{path}.{name}"
def get_counter_incr_strobe(self, field: 'FieldNode') -> str:
def get_counter_incr_strobe(self, field: "FieldNode") -> str:
"""
Return the Verilog string that represents the field's incr strobe signal.
"""
prop_value = field.get_property('incr')
prop_value = field.get_property("incr")
if prop_value:
return str(self.exp.dereferencer.get_value(prop_value))
# unset by the user, points to the implied input signal
return self.exp.hwif.get_implied_prop_input_identifier(field, "incr")
def get_counter_incrvalue(self, field: 'FieldNode') -> Union[SVInt, str]:
def get_counter_incrvalue(self, field: "FieldNode") -> Union[SVInt, str]:
"""
Return the string that represents the field's increment value
"""
incrvalue = field.get_property('incrvalue')
incrvalue = field.get_property("incrvalue")
if incrvalue is not None:
return self.exp.dereferencer.get_value(incrvalue, field.width)
if field.get_property('incrwidth'):
if field.get_property("incrwidth"):
return self.exp.hwif.get_implied_prop_input_identifier(field, "incrvalue")
return "1'b1"
def get_counter_incrsaturate_value(self, field: 'FieldNode') -> Union[SVInt, str]:
prop_value = field.get_property('incrsaturate')
def get_counter_incrsaturate_value(self, field: "FieldNode") -> Union[SVInt, str]:
prop_value = field.get_property("incrsaturate")
if prop_value is True:
return self.exp.dereferencer.get_value(2**field.width - 1, field.width)
return self.exp.dereferencer.get_value(prop_value, field.width)
def counter_incrsaturates(self, field: 'FieldNode') -> bool:
def counter_incrsaturates(self, field: "FieldNode") -> bool:
"""
Returns True if the counter saturates
"""
return field.get_property('incrsaturate') is not False
return field.get_property("incrsaturate") is not False
def get_counter_incrthreshold_value(self, field: 'FieldNode') -> Union[SVInt, str]:
prop_value = field.get_property('incrthreshold')
def get_counter_incrthreshold_value(self, field: "FieldNode") -> Union[SVInt, str]:
prop_value = field.get_property("incrthreshold")
if isinstance(prop_value, bool):
# No explicit value set. use max
return self.exp.dereferencer.get_value(2**field.width - 1, field.width)
return self.exp.dereferencer.get_value(prop_value, field.width)
def get_counter_decr_strobe(self, field: 'FieldNode') -> str:
def get_counter_decr_strobe(self, field: "FieldNode") -> str:
"""
Return the Verilog string that represents the field's incr strobe signal.
"""
prop_value = field.get_property('decr')
prop_value = field.get_property("decr")
if prop_value:
return str(self.exp.dereferencer.get_value(prop_value))
# unset by the user, points to the implied input signal
return self.exp.hwif.get_implied_prop_input_identifier(field, "decr")
def get_counter_decrvalue(self, field: 'FieldNode') -> Union[SVInt, str]:
def get_counter_decrvalue(self, field: "FieldNode") -> Union[SVInt, str]:
"""
Return the string that represents the field's decrement value
"""
decrvalue = field.get_property('decrvalue')
decrvalue = field.get_property("decrvalue")
if decrvalue is not None:
return self.exp.dereferencer.get_value(decrvalue, field.width)
if field.get_property('decrwidth'):
if field.get_property("decrwidth"):
return self.exp.hwif.get_implied_prop_input_identifier(field, "decrvalue")
return "1'b1"
def get_counter_decrsaturate_value(self, field: 'FieldNode') -> Union[SVInt, str]:
prop_value = field.get_property('decrsaturate')
def get_counter_decrsaturate_value(self, field: "FieldNode") -> Union[SVInt, str]:
prop_value = field.get_property("decrsaturate")
if prop_value is True:
return f"{field.width}'d0"
return self.exp.dereferencer.get_value(prop_value, field.width)
def counter_decrsaturates(self, field: 'FieldNode') -> bool:
def counter_decrsaturates(self, field: "FieldNode") -> bool:
"""
Returns True if the counter saturates
"""
return field.get_property('decrsaturate') is not False
return field.get_property("decrsaturate") is not False
def get_counter_decrthreshold_value(self, field: 'FieldNode') -> Union[SVInt, str]:
prop_value = field.get_property('decrthreshold')
def get_counter_decrthreshold_value(self, field: "FieldNode") -> Union[SVInt, str]:
prop_value = field.get_property("decrthreshold")
if isinstance(prop_value, bool):
# No explicit value set. use min
return f"{field.width}'d0"
return self.exp.dereferencer.get_value(prop_value, field.width)
def get_swacc_identifier(self, field: 'FieldNode') -> str:
def get_swacc_identifier(self, field: "FieldNode") -> str:
"""
Asserted when field is software accessed (read or write)
"""
buffer_reads = field.parent.get_property('buffer_reads')
buffer_writes = field.parent.get_property('buffer_writes')
buffer_reads = field.parent.get_property("buffer_reads")
buffer_writes = field.parent.get_property("buffer_writes")
if buffer_reads and buffer_writes:
rstrb = self.exp.read_buffering.get_trigger(field.parent)
wstrb = self.exp.write_buffering.get_write_strobe(field)
@@ -199,11 +204,11 @@ class FieldLogic:
strb = self.exp.dereferencer.get_access_strobe(field)
return strb
def get_rd_swacc_identifier(self, field: 'FieldNode') -> str:
def get_rd_swacc_identifier(self, field: "FieldNode") -> str:
"""
Asserted when field is software accessed (read)
"""
buffer_reads = field.parent.get_property('buffer_reads')
buffer_reads = field.parent.get_property("buffer_reads")
if buffer_reads:
rstrb = self.exp.read_buffering.get_trigger(field.parent)
return rstrb
@@ -211,11 +216,11 @@ class FieldLogic:
strb = self.exp.dereferencer.get_access_strobe(field)
return f"{strb} && !decoded_req_is_wr"
def get_wr_swacc_identifier(self, field: 'FieldNode') -> str:
def get_wr_swacc_identifier(self, field: "FieldNode") -> str:
"""
Asserted when field is software accessed (write)
"""
buffer_writes = field.parent.get_property('buffer_writes')
buffer_writes = field.parent.get_property("buffer_writes")
if buffer_writes:
wstrb = self.exp.write_buffering.get_write_strobe(field)
return wstrb
@@ -223,18 +228,17 @@ class FieldLogic:
strb = self.exp.dereferencer.get_access_strobe(field)
return f"{strb} && decoded_req_is_wr"
def get_swmod_identifier(self, field: 'FieldNode') -> str:
def get_swmod_identifier(self, field: "FieldNode") -> str:
"""
Asserted when field is modified by software (written or read with a
set or clear side effect).
"""
w_modifiable = field.is_sw_writable
r_modifiable = field.get_property('onread') is not None
buffer_writes = field.parent.get_property('buffer_writes')
buffer_reads = field.parent.get_property('buffer_reads')
r_modifiable = field.get_property("onread") is not None
buffer_writes = field.parent.get_property("buffer_writes")
buffer_reads = field.parent.get_property("buffer_reads")
accesswidth = field.parent.get_property("accesswidth")
astrb = self.exp.dereferencer.get_access_strobe(field)
conditions = []
@@ -267,43 +271,42 @@ class FieldLogic:
else:
return " || ".join(conditions)
def get_parity_identifier(self, field: 'FieldNode') -> str:
def get_parity_identifier(self, field: "FieldNode") -> str:
"""
Returns the identifier for the stored 'golden' parity value of the field
"""
path = get_indexed_path(self.top_node, field)
return f"field_storage.{path}.parity"
def get_parity_error_identifier(self, field: 'FieldNode') -> str:
def get_parity_error_identifier(self, field: "FieldNode") -> str:
"""
Returns the identifier for whether the field currently has a parity error
"""
path = get_indexed_path(self.top_node, field)
return f"field_combo.{path}.parity_error"
def has_next_q(self, field: 'FieldNode') -> bool:
def has_next_q(self, field: "FieldNode") -> bool:
"""
Some fields require a delayed version of their 'next' input signal in
order to do edge-detection.
Returns True if this is the case.
"""
if field.get_property('intr type') in {
if field.get_property("intr type") in {
InterruptType.posedge,
InterruptType.negedge,
InterruptType.bothedge
InterruptType.bothedge,
}:
return True
return False
def get_wbus_bitslice(self, field: 'FieldNode', subword_idx: int = 0) -> str:
def get_wbus_bitslice(self, field: "FieldNode", subword_idx: int = 0) -> str:
"""
Get the bitslice range string of the internal cpuif's data/biten bus
that corresponds to this field
"""
if field.parent.get_property('buffer_writes'):
if field.parent.get_property("buffer_writes"):
# register is buffered.
# write buffer is the full width of the register. no need to deal with subwords
high = field.high
@@ -311,7 +314,7 @@ class FieldLogic:
if field.msb < field.lsb:
# slice is for an msb0 field.
# mirror it
regwidth = field.parent.get_property('regwidth')
regwidth = field.parent.get_property("regwidth")
low = regwidth - 1 - low
high = regwidth - 1 - high
low, high = high, low
@@ -321,7 +324,7 @@ class FieldLogic:
# values unchanged.
# For fields within a wide register (accesswidth < regwidth), low/high
# may be shifted down and clamped depending on which sub-word is being accessed
accesswidth = field.parent.get_property('accesswidth')
accesswidth = field.parent.get_property("accesswidth")
# Shift based on subword
high = field.high - (subword_idx * accesswidth)
@@ -341,11 +344,11 @@ class FieldLogic:
return f"[{high}:{low}]"
def get_wr_biten(self, field: 'FieldNode', subword_idx: int=0) -> str:
def get_wr_biten(self, field: "FieldNode", subword_idx: int = 0) -> str:
"""
Get the bit-enable slice that corresponds to this field
"""
if field.parent.get_property('buffer_writes'):
if field.parent.get_property("buffer_writes"):
# Is buffered. Use value from write buffer
# No need to check msb0 ordering. Bus is pre-swapped, and bitslice
# accounts for it
@@ -363,11 +366,11 @@ class FieldLogic:
value = "decoded_wr_biten" + bslice
return value
def get_wr_data(self, field: 'FieldNode', subword_idx: int=0) -> str:
def get_wr_data(self, field: "FieldNode", subword_idx: int = 0) -> str:
"""
Get the write data slice that corresponds to this field
"""
if field.parent.get_property('buffer_writes'):
if field.parent.get_property("buffer_writes"):
# Is buffered. Use value from write buffer
# No need to check msb0 ordering. Bus is pre-swapped, and bitslice
# accounts for it
@@ -385,10 +388,12 @@ class FieldLogic:
value = "decoded_wr_data" + bslice
return value
#---------------------------------------------------------------------------
# ---------------------------------------------------------------------------
# Field Logic Conditionals
#---------------------------------------------------------------------------
def add_hw_conditional(self, conditional: NextStateConditional, precedence: AssignmentPrecedence) -> None:
# ---------------------------------------------------------------------------
def add_hw_conditional(
self, conditional: NextStateConditional, precedence: AssignmentPrecedence
) -> None:
"""
Register a NextStateConditional action for hardware-triggered field updates.
Categorizing conditionals correctly by hw/sw ensures that the RDL precedence
@@ -404,8 +409,9 @@ class FieldLogic:
self._hw_conditionals[precedence] = []
self._hw_conditionals[precedence].append(conditional)
def add_sw_conditional(self, conditional: NextStateConditional, precedence: AssignmentPrecedence) -> None:
def add_sw_conditional(
self, conditional: NextStateConditional, precedence: AssignmentPrecedence
) -> None:
"""
Register a NextStateConditional action for software-triggered field updates.
Categorizing conditionals correctly by hw/sw ensures that the RDL precedence
@@ -421,7 +427,6 @@ class FieldLogic:
self._sw_conditionals[precedence] = []
self._sw_conditionals[precedence].append(conditional)
def init_conditionals(self) -> None:
"""
Initialize all possible conditionals here.
@@ -430,46 +435,117 @@ class FieldLogic:
same assignment precedence.
"""
self.add_sw_conditional(sw_onread.ClearOnRead(self.exp), AssignmentPrecedence.SW_ONREAD)
self.add_sw_conditional(sw_onread.SetOnRead(self.exp), AssignmentPrecedence.SW_ONREAD)
self.add_sw_conditional(
sw_onread.ClearOnRead(self.exp), AssignmentPrecedence.SW_ONREAD
)
self.add_sw_conditional(
sw_onread.SetOnRead(self.exp), AssignmentPrecedence.SW_ONREAD
)
self.add_sw_conditional(sw_onwrite.Write(self.exp), AssignmentPrecedence.SW_ONWRITE)
self.add_sw_conditional(sw_onwrite.WriteSet(self.exp), AssignmentPrecedence.SW_ONWRITE)
self.add_sw_conditional(sw_onwrite.WriteClear(self.exp), AssignmentPrecedence.SW_ONWRITE)
self.add_sw_conditional(sw_onwrite.WriteZeroToggle(self.exp), AssignmentPrecedence.SW_ONWRITE)
self.add_sw_conditional(sw_onwrite.WriteZeroClear(self.exp), AssignmentPrecedence.SW_ONWRITE)
self.add_sw_conditional(sw_onwrite.WriteZeroSet(self.exp), AssignmentPrecedence.SW_ONWRITE)
self.add_sw_conditional(sw_onwrite.WriteOneToggle(self.exp), AssignmentPrecedence.SW_ONWRITE)
self.add_sw_conditional(sw_onwrite.WriteOneClear(self.exp), AssignmentPrecedence.SW_ONWRITE)
self.add_sw_conditional(sw_onwrite.WriteOneSet(self.exp), AssignmentPrecedence.SW_ONWRITE)
self.add_sw_conditional(
sw_onwrite.Write(self.exp), AssignmentPrecedence.SW_ONWRITE
)
self.add_sw_conditional(
sw_onwrite.WriteSet(self.exp), AssignmentPrecedence.SW_ONWRITE
)
self.add_sw_conditional(
sw_onwrite.WriteClear(self.exp), AssignmentPrecedence.SW_ONWRITE
)
self.add_sw_conditional(
sw_onwrite.WriteZeroToggle(self.exp), AssignmentPrecedence.SW_ONWRITE
)
self.add_sw_conditional(
sw_onwrite.WriteZeroClear(self.exp), AssignmentPrecedence.SW_ONWRITE
)
self.add_sw_conditional(
sw_onwrite.WriteZeroSet(self.exp), AssignmentPrecedence.SW_ONWRITE
)
self.add_sw_conditional(
sw_onwrite.WriteOneToggle(self.exp), AssignmentPrecedence.SW_ONWRITE
)
self.add_sw_conditional(
sw_onwrite.WriteOneClear(self.exp), AssignmentPrecedence.SW_ONWRITE
)
self.add_sw_conditional(
sw_onwrite.WriteOneSet(self.exp), AssignmentPrecedence.SW_ONWRITE
)
self.add_sw_conditional(sw_singlepulse.Singlepulse(self.exp), AssignmentPrecedence.SW_SINGLEPULSE)
self.add_sw_conditional(
sw_singlepulse.Singlepulse(self.exp), AssignmentPrecedence.SW_SINGLEPULSE
)
self.add_hw_conditional(hw_interrupts_with_write.PosedgeStickybitWE(self.exp), AssignmentPrecedence.HW_WRITE)
self.add_hw_conditional(hw_interrupts_with_write.PosedgeStickybitWEL(self.exp), AssignmentPrecedence.HW_WRITE)
self.add_hw_conditional(hw_interrupts_with_write.NegedgeStickybitWE(self.exp), AssignmentPrecedence.HW_WRITE)
self.add_hw_conditional(hw_interrupts_with_write.NegedgeStickybitWEL(self.exp), AssignmentPrecedence.HW_WRITE)
self.add_hw_conditional(hw_interrupts_with_write.BothedgeStickybitWE(self.exp), AssignmentPrecedence.HW_WRITE)
self.add_hw_conditional(hw_interrupts_with_write.BothedgeStickybitWEL(self.exp), AssignmentPrecedence.HW_WRITE)
self.add_hw_conditional(hw_interrupts_with_write.StickyWE(self.exp), AssignmentPrecedence.HW_WRITE)
self.add_hw_conditional(hw_interrupts_with_write.StickyWEL(self.exp), AssignmentPrecedence.HW_WRITE)
self.add_hw_conditional(hw_interrupts_with_write.StickybitWE(self.exp), AssignmentPrecedence.HW_WRITE)
self.add_hw_conditional(hw_interrupts_with_write.StickybitWEL(self.exp), AssignmentPrecedence.HW_WRITE)
self.add_hw_conditional(hw_interrupts.PosedgeStickybit(self.exp), AssignmentPrecedence.HW_WRITE)
self.add_hw_conditional(hw_interrupts.NegedgeStickybit(self.exp), AssignmentPrecedence.HW_WRITE)
self.add_hw_conditional(hw_interrupts.BothedgeStickybit(self.exp), AssignmentPrecedence.HW_WRITE)
self.add_hw_conditional(hw_interrupts.Sticky(self.exp), AssignmentPrecedence.HW_WRITE)
self.add_hw_conditional(hw_interrupts.Stickybit(self.exp), AssignmentPrecedence.HW_WRITE)
self.add_hw_conditional(hw_write.WEWrite(self.exp), AssignmentPrecedence.HW_WRITE)
self.add_hw_conditional(hw_write.WELWrite(self.exp), AssignmentPrecedence.HW_WRITE)
self.add_hw_conditional(hw_write.AlwaysWrite(self.exp), AssignmentPrecedence.HW_WRITE)
self.add_hw_conditional(
hw_interrupts_with_write.PosedgeStickybitWE(self.exp),
AssignmentPrecedence.HW_WRITE,
)
self.add_hw_conditional(
hw_interrupts_with_write.PosedgeStickybitWEL(self.exp),
AssignmentPrecedence.HW_WRITE,
)
self.add_hw_conditional(
hw_interrupts_with_write.NegedgeStickybitWE(self.exp),
AssignmentPrecedence.HW_WRITE,
)
self.add_hw_conditional(
hw_interrupts_with_write.NegedgeStickybitWEL(self.exp),
AssignmentPrecedence.HW_WRITE,
)
self.add_hw_conditional(
hw_interrupts_with_write.BothedgeStickybitWE(self.exp),
AssignmentPrecedence.HW_WRITE,
)
self.add_hw_conditional(
hw_interrupts_with_write.BothedgeStickybitWEL(self.exp),
AssignmentPrecedence.HW_WRITE,
)
self.add_hw_conditional(
hw_interrupts_with_write.StickyWE(self.exp), AssignmentPrecedence.HW_WRITE
)
self.add_hw_conditional(
hw_interrupts_with_write.StickyWEL(self.exp), AssignmentPrecedence.HW_WRITE
)
self.add_hw_conditional(
hw_interrupts_with_write.StickybitWE(self.exp),
AssignmentPrecedence.HW_WRITE,
)
self.add_hw_conditional(
hw_interrupts_with_write.StickybitWEL(self.exp),
AssignmentPrecedence.HW_WRITE,
)
self.add_hw_conditional(
hw_interrupts.PosedgeStickybit(self.exp), AssignmentPrecedence.HW_WRITE
)
self.add_hw_conditional(
hw_interrupts.NegedgeStickybit(self.exp), AssignmentPrecedence.HW_WRITE
)
self.add_hw_conditional(
hw_interrupts.BothedgeStickybit(self.exp), AssignmentPrecedence.HW_WRITE
)
self.add_hw_conditional(
hw_interrupts.Sticky(self.exp), AssignmentPrecedence.HW_WRITE
)
self.add_hw_conditional(
hw_interrupts.Stickybit(self.exp), AssignmentPrecedence.HW_WRITE
)
self.add_hw_conditional(
hw_write.WEWrite(self.exp), AssignmentPrecedence.HW_WRITE
)
self.add_hw_conditional(
hw_write.WELWrite(self.exp), AssignmentPrecedence.HW_WRITE
)
self.add_hw_conditional(
hw_write.AlwaysWrite(self.exp), AssignmentPrecedence.HW_WRITE
)
self.add_hw_conditional(hw_set_clr.HWClear(self.exp), AssignmentPrecedence.HWCLR)
self.add_hw_conditional(
hw_set_clr.HWClear(self.exp), AssignmentPrecedence.HWCLR
)
self.add_hw_conditional(hw_set_clr.HWSet(self.exp), AssignmentPrecedence.HWSET)
def _get_X_conditionals(self, conditionals: 'Dict[int, List[NextStateConditional]]', field: 'FieldNode') -> 'List[NextStateConditional]':
def _get_X_conditionals(
self, conditionals: "Dict[int, List[NextStateConditional]]", field: "FieldNode"
) -> "List[NextStateConditional]":
result = []
precedences = sorted(conditionals.keys(), reverse=True)
for precedence in precedences:
@@ -479,15 +555,14 @@ class FieldLogic:
break
return result
def get_conditionals(self, field: 'FieldNode') -> 'List[NextStateConditional]':
def get_conditionals(self, field: "FieldNode") -> "List[NextStateConditional]":
"""
Get a list of NextStateConditional objects that apply to the given field.
The returned list is sorted in priority order - the conditional with highest
precedence is first in the list.
"""
sw_precedence = field.get_property('precedence') == PrecedenceType.sw
sw_precedence = field.get_property("precedence") == PrecedenceType.sw
result = []
if sw_precedence:

View File

@@ -6,7 +6,8 @@ from ..utils import get_indexed_path
if TYPE_CHECKING:
from systemrdl.node import FieldNode
from ..exporter import RegblockExporter
from ..exporter import BusDecoderExporter
class AssignmentPrecedence(enum.IntEnum):
"""
@@ -33,12 +34,11 @@ class AssignmentPrecedence(enum.IntEnum):
COUNTER_INCR_DECR = 0
class SVLogic:
"""
Represents a SystemVerilog logic signal
"""
def __init__(self, name: str, width: int, default_assignment: str) -> None:
self.name = name
self.width = width
@@ -67,10 +67,10 @@ class NextStateConditional:
# Optional comment to emit next to the conditional
comment = ""
def __init__(self, exp:'RegblockExporter'):
def __init__(self, exp: "BusDecoderExporter"):
self.exp = exp
def is_match(self, field: 'FieldNode') -> bool:
def is_match(self, field: "FieldNode") -> bool:
"""
Returns True if this conditional is relevant to the field. If so,
it instructs the FieldBuilder that Verilog for this conditional shall
@@ -78,17 +78,16 @@ class NextStateConditional:
"""
raise NotImplementedError
def get_field_path(self, field:'FieldNode') -> str:
def get_field_path(self, field: "FieldNode") -> str:
return get_indexed_path(self.exp.ds.top_node, field)
def get_predicate(self, field: 'FieldNode') -> str:
def get_predicate(self, field: "FieldNode") -> str:
"""
Returns the rendered conditional text
"""
raise NotImplementedError
def get_assignments(self, field: 'FieldNode') -> List[str]:
def get_assignments(self, field: "FieldNode") -> List[str]:
"""
Returns a list of rendered assignment strings
This will basically always be two:
@@ -97,13 +96,14 @@ class NextStateConditional:
"""
raise NotImplementedError
def get_extra_combo_signals(self, field: 'FieldNode') -> List[SVLogic]:
def get_extra_combo_signals(self, field: "FieldNode") -> List[SVLogic]:
"""
Return any additional combinational signals that this conditional
will assign if present.
"""
return []
class NextStateUnconditional(NextStateConditional):
"""
Use this class if predicate can never evaluate to false.

View File

@@ -12,7 +12,8 @@ from .generators import InputStructGenerator_TypeScope, OutputStructGenerator_Ty
from .generators import EnumGenerator
if TYPE_CHECKING:
from ..exporter import RegblockExporter, DesignState
from ..exporter import BusDecoderExporter, DesignState
class Hwif:
"""
@@ -22,10 +23,7 @@ class Hwif:
- Signal inputs (except those that are promoted to the top)
"""
def __init__(
self, exp: 'RegblockExporter',
hwif_report_file: Optional[TextIO]
):
def __init__(self, exp: "BusDecoderExporter", hwif_report_file: Optional[TextIO]):
self.exp = exp
self.has_input_struct = False
@@ -41,31 +39,25 @@ class Hwif:
self._gen_out_cls = OutputStructGenerator_TypeScope
@property
def ds(self) -> 'DesignState':
def ds(self) -> "DesignState":
return self.exp.ds
@property
def top_node(self) -> AddrmapNode:
return self.exp.ds.top_node
def get_extra_package_params(self) -> str:
lines = [""]
for param in self.top_node.inst.parameters:
value = param.get_value()
if isinstance(value, int):
lines.append(
f"localparam {param.name} = {SVInt(value)};"
)
lines.append(f"localparam {param.name} = {SVInt(value)};")
elif isinstance(value, str):
lines.append(
f"localparam {param.name} = {value};"
)
lines.append(f"localparam {param.name} = {value};")
return "\n".join(lines)
def get_package_contents(self) -> str:
"""
If this hwif requires a package, generate the string
@@ -74,8 +66,7 @@ class Hwif:
gen_in = self._gen_in_cls(self)
structs_in = gen_in.get_struct(
self.top_node,
f"{self.top_node.inst_name}__in_t"
self.top_node, f"{self.top_node.inst_name}__in_t"
)
if structs_in is not None:
self.has_input_struct = True
@@ -85,8 +76,7 @@ class Hwif:
gen_out = self._gen_out_cls(self)
structs_out = gen_out.get_struct(
self.top_node,
f"{self.top_node.inst_name}__out_t"
self.top_node, f"{self.top_node.inst_name}__out_t"
)
if structs_out is not None:
self.has_output_struct = True
@@ -101,7 +91,6 @@ class Hwif:
return "\n\n".join(lines)
@property
def port_declaration(self) -> str:
"""
@@ -122,9 +111,9 @@ class Hwif:
return ",\n".join(lines)
#---------------------------------------------------------------------------
# ---------------------------------------------------------------------------
# hwif utility functions
#---------------------------------------------------------------------------
# ---------------------------------------------------------------------------
def has_value_input(self, obj: Union[FieldNode, SignalNode]) -> bool:
"""
Returns True if the object infers an input wire in the hwif
@@ -137,14 +126,12 @@ class Hwif:
else:
raise RuntimeError
def has_value_output(self, obj: FieldNode) -> bool:
"""
Returns True if the object infers an output wire in the hwif
"""
return obj.is_hw_readable
def get_input_identifier(
self,
obj: Union[FieldNode, SignalNode, PropertyReference],
@@ -162,7 +149,7 @@ class Hwif:
raises an exception if obj is invalid
"""
if isinstance(obj, FieldNode):
next_value = obj.get_property('next')
next_value = obj.get_property("next")
if next_value is not None:
# 'next' property replaces the inferred input signal
return self.exp.dereferencer.get_value(next_value, width)
@@ -203,13 +190,20 @@ class Hwif:
def get_implied_prop_input_identifier(self, field: FieldNode, prop: str) -> str:
assert prop in {
'hwclr', 'hwset', 'swwe', 'swwel', 'we', 'wel',
'incr', 'decr', 'incrvalue', 'decrvalue'
"hwclr",
"hwset",
"swwe",
"swwel",
"we",
"wel",
"incr",
"decr",
"incrvalue",
"decrvalue",
}
path = get_indexed_path(self.top_node, field)
return "hwif_in." + path + "." + prop
def get_output_identifier(self, obj: Union[FieldNode, PropertyReference]) -> str:
"""
Returns the identifier string that best represents the output object.
@@ -233,17 +227,27 @@ class Hwif:
raise RuntimeError(f"Unhandled reference to: {obj}")
def get_implied_prop_output_identifier(self, node: Union[FieldNode, RegNode], prop: str) -> str:
def get_implied_prop_output_identifier(
self, node: Union[FieldNode, RegNode], prop: str
) -> str:
if isinstance(node, FieldNode):
assert prop in {
"anded", "ored", "xored", "swmod", "swacc",
"incrthreshold", "decrthreshold", "overflow", "underflow",
"rd_swacc", "wr_swacc",
"anded",
"ored",
"xored",
"swmod",
"swacc",
"incrthreshold",
"decrthreshold",
"overflow",
"underflow",
"rd_swacc",
"wr_swacc",
}
elif isinstance(node, RegNode):
assert prop in {
"intr", "halt",
"intr",
"halt",
}
path = get_indexed_path(self.top_node, node)
return "hwif_out." + path + "." + prop

View File

@@ -1,5 +1,5 @@
// Generated by PeakRDL-regblock - A free and open-source SystemVerilog generator
// https://github.com/SystemRDL/PeakRDL-regblock
// Generated by PeakRDL-busdecoder - A free and open-source SystemVerilog generator
// https://github.com/SystemRDL/PeakRDL-busdecoder
module {{ds.module_name}}
{%- if cpuif.parameters %} #(

View File

@@ -1,5 +1,5 @@
// Generated by PeakRDL-regblock - A free and open-source SystemVerilog generator
// https://github.com/SystemRDL/PeakRDL-regblock
// Generated by PeakRDL-busdecoder - A free and open-source SystemVerilog generator
// https://github.com/SystemRDL/PeakRDL-busdecoder
package {{ds.package_name}};

View File

@@ -6,12 +6,12 @@ from systemrdl.walker import WalkerAction
from .forloop_generator import RDLForLoopGenerator
if TYPE_CHECKING:
from .exporter import RegblockExporter
from .exporter import BusDecoderExporter
from systemrdl.node import FieldNode, AddressableNode
class ParityErrorReduceGenerator(RDLForLoopGenerator):
def __init__(self, exp: 'RegblockExporter') -> None:
def __init__(self, exp: "BusDecoderExporter") -> None:
super().__init__()
self.exp = exp
@@ -21,14 +21,14 @@ class ParityErrorReduceGenerator(RDLForLoopGenerator):
return ""
return content
def enter_AddressableComponent(self, node: 'AddressableNode') -> WalkerAction:
def enter_AddressableComponent(self, node: "AddressableNode") -> WalkerAction:
super().enter_AddressableComponent(node)
if node.external:
return WalkerAction.SkipDescendants
return WalkerAction.Continue
def enter_Field(self, node: 'FieldNode') -> None:
if node.get_property('paritycheck') and node.implements_storage:
def enter_Field(self, node: "FieldNode") -> None:
if node.get_property("paritycheck") and node.implements_storage:
self.add_content(
f"err |= {self.exp.field_logic.get_parity_error_identifier(node)};"
)

View File

@@ -8,15 +8,15 @@ from ..utils import get_indexed_path
from ..sv_int import SVInt
if TYPE_CHECKING:
from ..exporter import RegblockExporter
from ..exporter import BusDecoderExporter
class ReadBuffering:
def __init__(self, exp:'RegblockExporter'):
def __init__(self, exp: "BusDecoderExporter"):
self.exp = exp
@property
def top_node(self) -> 'AddrmapNode':
def top_node(self) -> "AddrmapNode":
return self.exp.ds.top_node
def get_storage_struct(self) -> str:
@@ -32,14 +32,16 @@ class ReadBuffering:
return s
def get_trigger(self, node: RegNode) -> str:
trigger = node.get_property('rbuffer_trigger')
trigger = node.get_property("rbuffer_trigger")
if isinstance(trigger, RegNode):
# Trigger is a register.
# trigger when lowermost address of the register is written
regwidth = trigger.get_property('regwidth')
accesswidth = trigger.get_property('accesswidth')
strb_prefix = self.exp.dereferencer.get_access_strobe(trigger, reduce_substrobes=False)
regwidth = trigger.get_property("regwidth")
accesswidth = trigger.get_property("accesswidth")
strb_prefix = self.exp.dereferencer.get_access_strobe(
trigger, reduce_substrobes=False
)
if accesswidth < regwidth:
return f"{strb_prefix}[0] && !decoded_req_is_wr"
@@ -47,7 +49,7 @@ class ReadBuffering:
return f"{strb_prefix} && !decoded_req_is_wr"
elif isinstance(trigger, SignalNode):
s = self.exp.dereferencer.get_value(trigger)
if trigger.get_property('activehigh'):
if trigger.get_property("activehigh"):
return str(s)
else:
return f"~{s}"

View File

@@ -4,19 +4,20 @@ import math
from .generators import ReadbackAssignmentGenerator
if TYPE_CHECKING:
from ..exporter import RegblockExporter, DesignState
from ..exporter import BusDecoderExporter, DesignState
from systemrdl.node import AddrmapNode
class Readback:
def __init__(self, exp:'RegblockExporter'):
def __init__(self, exp: "BusDecoderExporter"):
self.exp = exp
@property
def ds(self) -> 'DesignState':
def ds(self) -> "DesignState":
return self.exp.ds
@property
def top_node(self) -> 'AddrmapNode':
def top_node(self) -> "AddrmapNode":
return self.exp.ds.top_node
def get_implementation(self) -> str:
@@ -30,10 +31,10 @@ class Readback:
self.ds.retime_read_fanin = False
context = {
"array_assignments" : array_assignments,
"array_size" : array_size,
'get_always_ff_event': self.exp.dereferencer.get_always_ff_event,
'get_resetsignal': self.exp.dereferencer.get_resetsignal,
"array_assignments": array_assignments,
"array_size": array_size,
"get_always_ff_event": self.exp.dereferencer.get_always_ff_event,
"get_resetsignal": self.exp.dereferencer.get_resetsignal,
"cpuif": self.exp.cpuif,
"ds": self.ds,
}
@@ -61,12 +62,10 @@ class Readback:
else:
fanin_loop_iter = fanin_array_size
context['fanin_stride'] = fanin_stride
context['fanin_array_size'] = fanin_array_size
context['fanin_residual_stride'] = fanin_residual_stride
context['fanin_loop_iter'] = fanin_loop_iter
context["fanin_stride"] = fanin_stride
context["fanin_array_size"] = fanin_array_size
context["fanin_residual_stride"] = fanin_residual_stride
context["fanin_loop_iter"] = fanin_loop_iter
template = self.exp.jj_env.get_template(
"readback/templates/readback.sv"
)
template = self.exp.jj_env.get_template("readback/templates/readback.sv")
return template.render(context)

View File

@@ -8,7 +8,8 @@ from ..forloop_generator import RDLForLoopGenerator, LoopBody
from ..utils import do_bitswap, do_slice
if TYPE_CHECKING:
from ..exporter import RegblockExporter
from ..exporter import BusDecoderExporter
class ReadbackLoopBody(LoopBody):
def __init__(self, dim: int, iterator: str, i_type: str) -> None:
@@ -22,11 +23,12 @@ class ReadbackLoopBody(LoopBody):
s = s.replace(token, str(self.n_regs))
return s
class ReadbackAssignmentGenerator(RDLForLoopGenerator):
i_type = "genvar"
loop_body_cls = ReadbackLoopBody
def __init__(self, exp:'RegblockExporter') -> None:
def __init__(self, exp: "BusDecoderExporter") -> None:
super().__init__()
self.exp = exp
@@ -79,15 +81,16 @@ class ReadbackAssignmentGenerator(RDLForLoopGenerator):
# Advance current scope's offset to account for loop's contents
self.current_offset = start_offset + n_regs * dim
def enter_AddressableComponent(self, node: 'AddressableNode') -> WalkerAction:
def enter_AddressableComponent(self, node: "AddressableNode") -> WalkerAction:
super().enter_AddressableComponent(node)
if node.external and not isinstance(node, RegNode):
# External block
strb = self.exp.hwif.get_external_rd_ack(node)
data = self.exp.hwif.get_external_rd_data(node)
self.add_content(f"assign readback_array[{self.current_offset_str}] = {strb} ? {data} : '0;")
self.add_content(
f"assign readback_array[{self.current_offset_str}] = {strb} ? {data} : '0;"
)
self.current_offset += 1
return WalkerAction.SkipDescendants
@@ -101,12 +104,12 @@ class ReadbackAssignmentGenerator(RDLForLoopGenerator):
self.process_external_reg(node)
return WalkerAction.SkipDescendants
accesswidth = node.get_property('accesswidth')
regwidth = node.get_property('regwidth')
rbuf = node.get_property('buffer_reads')
accesswidth = node.get_property("accesswidth")
regwidth = node.get_property("regwidth")
rbuf = node.get_property("buffer_reads")
if rbuf:
trigger = node.get_property('rbuffer_trigger')
is_own_trigger = (isinstance(trigger, RegNode) and trigger == node)
trigger = node.get_property("rbuffer_trigger")
is_own_trigger = isinstance(trigger, RegNode) and trigger == node
if is_own_trigger:
if accesswidth < regwidth:
self.process_buffered_reg_with_bypass(node, regwidth, accesswidth)
@@ -125,18 +128,26 @@ class ReadbackAssignmentGenerator(RDLForLoopGenerator):
def process_external_reg(self, node: RegNode) -> None:
strb = self.exp.hwif.get_external_rd_ack(node)
data = self.exp.hwif.get_external_rd_data(node)
regwidth = node.get_property('regwidth')
regwidth = node.get_property("regwidth")
if regwidth < self.exp.cpuif.data_width:
self.add_content(f"assign readback_array[{self.current_offset_str}][{self.exp.cpuif.data_width-1}:{regwidth}] = '0;")
self.add_content(f"assign readback_array[{self.current_offset_str}][{regwidth-1}:0] = {strb} ? {data} : '0;")
self.add_content(
f"assign readback_array[{self.current_offset_str}][{self.exp.cpuif.data_width - 1}:{regwidth}] = '0;"
)
self.add_content(
f"assign readback_array[{self.current_offset_str}][{regwidth - 1}:0] = {strb} ? {data} : '0;"
)
else:
self.add_content(f"assign readback_array[{self.current_offset_str}] = {strb} ? {data} : '0;")
self.add_content(
f"assign readback_array[{self.current_offset_str}] = {strb} ? {data} : '0;"
)
self.current_offset += 1
def process_reg(self, node: RegNode) -> None:
current_bit = 0
rd_strb = f"({self.exp.dereferencer.get_access_strobe(node)} && !decoded_req_is_wr)"
rd_strb = (
f"({self.exp.dereferencer.get_access_strobe(node)} && !decoded_req_is_wr)"
)
# Fields are sorted by ascending low bit
for field in node.fields():
if not field.is_sw_readable:
@@ -144,51 +155,67 @@ class ReadbackAssignmentGenerator(RDLForLoopGenerator):
# insert reserved assignment before this field if needed
if field.low != current_bit:
self.add_content(f"assign readback_array[{self.current_offset_str}][{field.low-1}:{current_bit}] = '0;")
self.add_content(
f"assign readback_array[{self.current_offset_str}][{field.low - 1}:{current_bit}] = '0;"
)
value = self.exp.dereferencer.get_value(field)
if field.msb < field.lsb:
# Field gets bitswapped since it is in [low:high] orientation
value = do_bitswap(value)
self.add_content(f"assign readback_array[{self.current_offset_str}][{field.high}:{field.low}] = {rd_strb} ? {value} : '0;")
self.add_content(
f"assign readback_array[{self.current_offset_str}][{field.high}:{field.low}] = {rd_strb} ? {value} : '0;"
)
current_bit = field.high + 1
# Insert final reserved assignment if needed
bus_width = self.exp.cpuif.data_width
if current_bit < bus_width:
self.add_content(f"assign readback_array[{self.current_offset_str}][{bus_width-1}:{current_bit}] = '0;")
self.add_content(
f"assign readback_array[{self.current_offset_str}][{bus_width - 1}:{current_bit}] = '0;"
)
self.current_offset += 1
def process_buffered_reg(self, node: RegNode, regwidth: int, accesswidth: int) -> None:
def process_buffered_reg(
self, node: RegNode, regwidth: int, accesswidth: int
) -> None:
rbuf = self.exp.read_buffering.get_rbuf_data(node)
if accesswidth < regwidth:
# Is wide reg
n_subwords = regwidth // accesswidth
astrb = self.exp.dereferencer.get_access_strobe(node, reduce_substrobes=False)
astrb = self.exp.dereferencer.get_access_strobe(
node, reduce_substrobes=False
)
for i in range(n_subwords):
rd_strb = f"({astrb}[{i}] && !decoded_req_is_wr)"
bslice = f"[{(i + 1) * accesswidth - 1}:{i*accesswidth}]"
self.add_content(f"assign readback_array[{self.current_offset_str}] = {rd_strb} ? {rbuf}{bslice} : '0;")
bslice = f"[{(i + 1) * accesswidth - 1}:{i * accesswidth}]"
self.add_content(
f"assign readback_array[{self.current_offset_str}] = {rd_strb} ? {rbuf}{bslice} : '0;"
)
self.current_offset += 1
else:
# Is regular reg
rd_strb = f"({self.exp.dereferencer.get_access_strobe(node)} && !decoded_req_is_wr)"
self.add_content(f"assign readback_array[{self.current_offset_str}][{regwidth-1}:0] = {rd_strb} ? {rbuf} : '0;")
self.add_content(
f"assign readback_array[{self.current_offset_str}][{regwidth - 1}:0] = {rd_strb} ? {rbuf} : '0;"
)
bus_width = self.exp.cpuif.data_width
if regwidth < bus_width:
self.add_content(f"assign readback_array[{self.current_offset_str}][{bus_width-1}:{regwidth}] = '0;")
self.add_content(
f"assign readback_array[{self.current_offset_str}][{bus_width - 1}:{regwidth}] = '0;"
)
self.current_offset += 1
def process_buffered_reg_with_bypass(self, node: RegNode, regwidth: int, accesswidth: int) -> None:
def process_buffered_reg_with_bypass(
self, node: RegNode, regwidth: int, accesswidth: int
) -> None:
"""
Special case for a buffered register when the register is its own trigger.
First sub-word shall bypass the read buffer and assign directly.
@@ -210,7 +237,9 @@ class ReadbackAssignmentGenerator(RDLForLoopGenerator):
if bidx < field.low:
# insert padding before
self.add_content(f"assign readback_array[{self.current_offset_str}][{field.low - 1}:{bidx}] = '0;")
self.add_content(
f"assign readback_array[{self.current_offset_str}][{field.low - 1}:{bidx}] = '0;"
)
if field.high >= accesswidth:
# field gets truncated
@@ -225,11 +254,17 @@ class ReadbackAssignmentGenerator(RDLForLoopGenerator):
f_low = field.width - 1 - f_low
f_high = field.width - 1 - f_high
f_low, f_high = f_high, f_low
value = do_bitswap(do_slice(self.exp.dereferencer.get_value(field), f_high, f_low))
value = do_bitswap(
do_slice(self.exp.dereferencer.get_value(field), f_high, f_low)
)
else:
value = do_slice(self.exp.dereferencer.get_value(field), f_high, f_low)
value = do_slice(
self.exp.dereferencer.get_value(field), f_high, f_low
)
self.add_content(f"assign readback_array[{self.current_offset_str}][{r_high}:{r_low}] = {rd_strb} ? {value} : '0;")
self.add_content(
f"assign readback_array[{self.current_offset_str}][{r_high}:{r_low}] = {rd_strb} ? {value} : '0;"
)
bidx = accesswidth
else:
# field fits in subword
@@ -237,12 +272,16 @@ class ReadbackAssignmentGenerator(RDLForLoopGenerator):
if field.msb < field.lsb:
# Field gets bitswapped since it is in [low:high] orientation
value = do_bitswap(value)
self.add_content(f"assign readback_array[{self.current_offset_str}][{field.high}:{field.low}] = {rd_strb} ? {value} : '0;")
self.add_content(
f"assign readback_array[{self.current_offset_str}][{field.high}:{field.low}] = {rd_strb} ? {value} : '0;"
)
bidx = field.high + 1
# pad up remainder of subword
if bidx < accesswidth:
self.add_content(f"assign readback_array[{self.current_offset_str}][{accesswidth-1}:{bidx}] = '0;")
self.add_content(
f"assign readback_array[{self.current_offset_str}][{accesswidth - 1}:{bidx}] = '0;"
)
self.current_offset += 1
# Assign remainder of subwords from read buffer
@@ -250,8 +289,10 @@ class ReadbackAssignmentGenerator(RDLForLoopGenerator):
rbuf = self.exp.read_buffering.get_rbuf_data(node)
for i in range(1, n_subwords):
rd_strb = f"({astrb}[{i}] && !decoded_req_is_wr)"
bslice = f"[{(i + 1) * accesswidth - 1}:{i*accesswidth}]"
self.add_content(f"assign readback_array[{self.current_offset_str}] = {rd_strb} ? {rbuf}{bslice} : '0;")
bslice = f"[{(i + 1) * accesswidth - 1}:{i * accesswidth}]"
self.add_content(
f"assign readback_array[{self.current_offset_str}] = {rd_strb} ? {rbuf}{bslice} : '0;"
)
self.current_offset += 1
def process_wide_reg(self, node: RegNode, accesswidth: int) -> None:
@@ -259,20 +300,24 @@ class ReadbackAssignmentGenerator(RDLForLoopGenerator):
subword_idx = 0
current_bit = 0 # Bit-offset within the wide register
access_strb = self.exp.dereferencer.get_access_strobe(node, reduce_substrobes=False)
access_strb = self.exp.dereferencer.get_access_strobe(
node, reduce_substrobes=False
)
# Fields are sorted by ascending low bit
for field in node.fields():
if not field.is_sw_readable:
continue
# insert zero assignment before this field if needed
if field.low >= accesswidth*(subword_idx+1):
if field.low >= accesswidth * (subword_idx + 1):
# field does not start in this subword
if current_bit > accesswidth * subword_idx:
# current subword had content. Assign remainder
low = current_bit % accesswidth
high = bus_width - 1
self.add_content(f"assign readback_array[{self.current_offset_str}][{high}:{low}] = '0;")
self.add_content(
f"assign readback_array[{self.current_offset_str}][{high}:{low}] = '0;"
)
self.current_offset += 1
# Advance to subword that contains the start of the field
@@ -283,17 +328,20 @@ class ReadbackAssignmentGenerator(RDLForLoopGenerator):
# assign zero up to start of this field
low = current_bit % accesswidth
high = (field.low % accesswidth) - 1
self.add_content(f"assign readback_array[{self.current_offset_str}][{high}:{low}] = '0;")
self.add_content(
f"assign readback_array[{self.current_offset_str}][{high}:{low}] = '0;"
)
current_bit = field.low
# Assign field
# loop until the entire field's assignments have been generated
field_pos = field.low
while current_bit <= field.high:
# Assign the field
rd_strb = f"({access_strb}[{subword_idx}] && !decoded_req_is_wr)"
if (field_pos == field.low) and (field.high < accesswidth*(subword_idx+1)):
if (field_pos == field.low) and (
field.high < accesswidth * (subword_idx + 1)
):
# entire field fits into this subword
low = field.low - accesswidth * subword_idx
high = field.high - accesswidth * subword_idx
@@ -303,15 +351,17 @@ class ReadbackAssignmentGenerator(RDLForLoopGenerator):
# Field gets bitswapped since it is in [low:high] orientation
value = do_bitswap(value)
self.add_content(f"assign readback_array[{self.current_offset_str}][{high}:{low}] = {rd_strb} ? {value} : '0;")
self.add_content(
f"assign readback_array[{self.current_offset_str}][{high}:{low}] = {rd_strb} ? {value} : '0;"
)
current_bit = field.high + 1
if current_bit == accesswidth*(subword_idx+1):
if current_bit == accesswidth * (subword_idx + 1):
# Field ends at the subword boundary
subword_idx += 1
self.current_offset += 1
elif field.high >= accesswidth*(subword_idx+1):
elif field.high >= accesswidth * (subword_idx + 1):
# only a subset of the field can fit into this subword
# high end gets truncated
@@ -330,11 +380,19 @@ class ReadbackAssignmentGenerator(RDLForLoopGenerator):
f_high = field.width - 1 - f_high
f_low, f_high = f_high, f_low
value = do_bitswap(do_slice(self.exp.dereferencer.get_value(field), f_high, f_low))
value = do_bitswap(
do_slice(
self.exp.dereferencer.get_value(field), f_high, f_low
)
)
else:
value = do_slice(self.exp.dereferencer.get_value(field), f_high, f_low)
value = do_slice(
self.exp.dereferencer.get_value(field), f_high, f_low
)
self.add_content(f"assign readback_array[{self.current_offset_str}][{r_high}:{r_low}] = {rd_strb} ? {value} : '0;")
self.add_content(
f"assign readback_array[{self.current_offset_str}][{r_high}:{r_low}] = {rd_strb} ? {value} : '0;"
)
# advance to the next subword
subword_idx += 1
@@ -360,14 +418,22 @@ class ReadbackAssignmentGenerator(RDLForLoopGenerator):
f_high = field.width - 1 - f_high
f_low, f_high = f_high, f_low
value = do_bitswap(do_slice(self.exp.dereferencer.get_value(field), f_high, f_low))
value = do_bitswap(
do_slice(
self.exp.dereferencer.get_value(field), f_high, f_low
)
)
else:
value = do_slice(self.exp.dereferencer.get_value(field), f_high, f_low)
value = do_slice(
self.exp.dereferencer.get_value(field), f_high, f_low
)
self.add_content(f"assign readback_array[{self.current_offset_str}][{r_high}:{r_low}] = {rd_strb} ? {value} : '0;")
self.add_content(
f"assign readback_array[{self.current_offset_str}][{r_high}:{r_low}] = {rd_strb} ? {value} : '0;"
)
current_bit = field.high + 1
if current_bit == accesswidth*(subword_idx+1):
if current_bit == accesswidth * (subword_idx + 1):
# Field ends at the subword boundary
subword_idx += 1
self.current_offset += 1
@@ -377,5 +443,7 @@ class ReadbackAssignmentGenerator(RDLForLoopGenerator):
# current subword had content. Assign remainder
low = current_bit % accesswidth
high = bus_width - 1
self.add_content(f"assign readback_array[{self.current_offset_str}][{high}:{low}] = '0;")
self.add_content(
f"assign readback_array[{self.current_offset_str}][{high}:{low}] = '0;"
)
self.current_offset += 1

View File

@@ -15,12 +15,13 @@ class DesignScanner(RDLListener):
Also collects any information that is required prior to the start of the export process.
"""
def __init__(self, ds:'DesignState') -> None:
def __init__(self, ds: "DesignState") -> None:
self.ds = ds
self.msg = self.top_node.env.msg
@property
def top_node(self) -> 'AddrmapNode':
def top_node(self) -> "AddrmapNode":
return self.ds.top_node
def _get_out_of_hier_field_reset(self) -> None:
@@ -28,7 +29,7 @@ class DesignScanner(RDLListener):
current_node = self.top_node.parent
while current_node is not None:
for signal in current_node.signals():
if signal.get_property('field_reset'):
if signal.get_property("field_reset"):
path = signal.get_path()
self.ds.out_of_hier_signals[path] = signal
return
@@ -50,19 +51,19 @@ class DesignScanner(RDLListener):
# Ensure addrmap is not a bridge. This concept does not make sense for
# terminal components.
if self.top_node.get_property('bridge'):
if self.top_node.get_property("bridge"):
self.msg.error(
"Regblock generator does not support exporting bridge address maps",
self.top_node.inst.property_src_ref.get('bridge', self.top_node.inst.inst_src_ref)
"BusDecoder generator does not support exporting bridge address maps",
self.top_node.inst.property_src_ref.get(
"bridge", self.top_node.inst.inst_src_ref
),
)
RDLWalker().walk(self.top_node, self)
if self.msg.had_error:
self.msg.fatal(
"Unable to export due to previous errors"
)
self.msg.fatal("Unable to export due to previous errors")
def enter_Component(self, node: 'Node') -> Optional[WalkerAction]:
def enter_Component(self, node: "Node") -> Optional[WalkerAction]:
if node.external and (node != self.top_node):
# Do not inspect external components. None of my business
return WalkerAction.SkipDescendants
@@ -84,36 +85,42 @@ class DesignScanner(RDLListener):
return WalkerAction.Continue
def enter_AddressableComponent(self, node: 'AddressableNode') -> None:
def enter_AddressableComponent(self, node: "AddressableNode") -> None:
if node.external and node != self.top_node:
self.ds.has_external_addressable = True
if not isinstance(node, RegNode):
self.ds.has_external_block = True
def enter_Reg(self, node: 'RegNode') -> None:
def enter_Reg(self, node: "RegNode") -> None:
# The CPUIF's bus width is sized according to the largest accesswidth in the design
accesswidth = node.get_property('accesswidth')
accesswidth = node.get_property("accesswidth")
self.ds.cpuif_data_width = max(self.ds.cpuif_data_width, accesswidth)
self.ds.has_buffered_write_regs = self.ds.has_buffered_write_regs or bool(node.get_property('buffer_writes'))
self.ds.has_buffered_read_regs = self.ds.has_buffered_read_regs or bool(node.get_property('buffer_reads'))
self.ds.has_buffered_write_regs = self.ds.has_buffered_write_regs or bool(
node.get_property("buffer_writes")
)
self.ds.has_buffered_read_regs = self.ds.has_buffered_read_regs or bool(
node.get_property("buffer_reads")
)
def enter_Signal(self, node: 'SignalNode') -> None:
if node.get_property('field_reset'):
def enter_Signal(self, node: "SignalNode") -> None:
if node.get_property("field_reset"):
path = node.get_path()
self.ds.in_hier_signal_paths.add(path)
def enter_Field(self, node: 'FieldNode') -> None:
def enter_Field(self, node: "FieldNode") -> None:
if node.is_sw_writable and (node.msb < node.lsb):
self.ds.has_writable_msb0_fields = True
if node.get_property('paritycheck') and node.implements_storage:
if node.get_property("paritycheck") and node.implements_storage:
self.ds.has_paritycheck = True
if node.get_property('reset') is None:
if node.get_property("reset") is None:
self.msg.warning(
f"Field '{node.inst_name}' includes parity check logic, but "
"its reset value was not defined. Will result in an undefined "
"value on the module's 'parity_error' output.",
self.top_node.inst.property_src_ref.get('paritycheck', self.top_node.inst.inst_src_ref)
self.top_node.inst.property_src_ref.get(
"paritycheck", self.top_node.inst.inst_src_ref
),
)

View File

@@ -10,14 +10,16 @@ from .utils import roundup_pow2, is_pow2
from .utils import ref_is_internal
if TYPE_CHECKING:
from .exporter import RegblockExporter
from .exporter import BusDecoderExporter
class DesignValidator(RDLListener):
"""
Performs additional rule-checks on the design that check for limitations
imposed by this exporter.
"""
def __init__(self, exp:'RegblockExporter') -> None:
def __init__(self, exp: "BusDecoderExporter") -> None:
self.exp = exp
self.ds = exp.ds
self.msg = self.top_node.env.msg
@@ -26,17 +28,15 @@ class DesignValidator(RDLListener):
self.contains_external_block = False
@property
def top_node(self) -> 'AddrmapNode':
def top_node(self) -> "AddrmapNode":
return self.exp.ds.top_node
def do_validate(self) -> None:
RDLWalker().walk(self.top_node, self)
if self.msg.had_error:
self.msg.fatal(
"Unable to export due to previous errors"
)
self.msg.fatal("Unable to export due to previous errors")
def enter_Component(self, node: 'Node') -> Optional[WalkerAction]:
def enter_Component(self, node: "Node") -> Optional[WalkerAction]:
if node.external and (node != self.top_node):
# Do not inspect external components. None of my business
return WalkerAction.SkipDescendants
@@ -49,39 +49,41 @@ class DesignValidator(RDLListener):
if isinstance(value, PropertyReference):
src_ref = value.src_ref
else:
src_ref = node.inst.property_src_ref.get(prop_name, node.inst.inst_src_ref)
src_ref = node.inst.property_src_ref.get(
prop_name, node.inst.inst_src_ref
)
self.msg.error(
"Property is assigned a reference that points to a component not internal to the regblock being exported.",
src_ref
"Property is assigned a reference that points to a component not internal to the busdecoder being exported.",
src_ref,
)
return None
def enter_Signal(self, node: 'SignalNode') -> None:
def enter_Signal(self, node: "SignalNode") -> None:
# If encountering a CPUIF reset that is nested within the register model,
# warn that it will be ignored.
# Only cpuif resets in the top-level node or above will be honored
if node.get_property('cpuif_reset') and (node.parent != self.top_node):
if node.get_property("cpuif_reset") and (node.parent != self.top_node):
self.msg.warning(
"Only cpuif_reset signals that are instantiated in the top-level "
"addrmap or above will be honored. Any cpuif_reset signals nested "
"within children of the addrmap being exported will be ignored.",
node.inst.inst_src_ref
node.inst.inst_src_ref,
)
def enter_AddressableComponent(self, node: 'AddressableNode') -> None:
def enter_AddressableComponent(self, node: "AddressableNode") -> None:
# All registers must be aligned to the internal data bus width
alignment = self.exp.cpuif.data_width_bytes
if (node.raw_address_offset % alignment) != 0:
self.msg.error(
"Unaligned registers are not supported. Address offset of "
f"instance '{node.inst_name}' must be a multiple of {alignment}",
node.inst.inst_src_ref
node.inst.inst_src_ref,
)
if node.is_array and (node.array_stride % alignment) != 0: # type: ignore # is_array implies stride is not none
self.msg.error(
"Unaligned registers are not supported. Address stride of "
f"instance array '{node.inst_name}' must be a multiple of {alignment}",
node.inst.inst_src_ref
node.inst.inst_src_ref,
)
if not isinstance(node, RegNode):
@@ -99,49 +101,49 @@ class DesignValidator(RDLListener):
self._check_sharedextbus(node)
def _check_sharedextbus(self, node: Union[RegfileNode, AddrmapNode]) -> None:
if node.get_property('sharedextbus'):
if node.get_property("sharedextbus"):
self.msg.error(
"This exporter does not support enabling the 'sharedextbus' property yet.",
node.inst.property_src_ref.get('sharedextbus', node.inst.inst_src_ref)
node.inst.property_src_ref.get("sharedextbus", node.inst.inst_src_ref),
)
def enter_Reg(self, node: 'RegNode') -> None:
def enter_Reg(self, node: "RegNode") -> None:
# accesswidth of wide registers must be consistent within the register block
accesswidth = node.get_property('accesswidth')
regwidth = node.get_property('regwidth')
accesswidth = node.get_property("accesswidth")
regwidth = node.get_property("regwidth")
if accesswidth < regwidth:
# register is 'wide'
if accesswidth != self.exp.cpuif.data_width:
self.msg.error(
f"Multi-word registers that have an accesswidth ({accesswidth}) "
"that are inconsistent with this regblock's CPU bus width "
"that are inconsistent with this busdecoder's CPU bus width "
f"({self.exp.cpuif.data_width}) are not supported.",
node.inst.inst_src_ref
node.inst.inst_src_ref,
)
def enter_Field(self, node: 'FieldNode') -> None:
parent_accesswidth = node.parent.get_property('accesswidth')
parent_regwidth = node.parent.get_property('regwidth')
if (
(parent_accesswidth < parent_regwidth)
and (node.lsb // parent_accesswidth) != (node.msb // parent_accesswidth)
):
def enter_Field(self, node: "FieldNode") -> None:
parent_accesswidth = node.parent.get_property("accesswidth")
parent_regwidth = node.parent.get_property("regwidth")
if (parent_accesswidth < parent_regwidth) and (
node.lsb // parent_accesswidth
) != (node.msb // parent_accesswidth):
# field spans multiple sub-words
if node.is_sw_writable and not node.parent.get_property('buffer_writes'):
if node.is_sw_writable and not node.parent.get_property("buffer_writes"):
# ... and is writable without the protection of double-buffering
# Enforce 10.6.1-f
self.msg.error(
f"Software-writable field '{node.inst_name}' shall not span"
" multiple software-accessible subwords. Consider enabling"
" write double-buffering.\n"
"For more details, see: https://peakrdl-regblock.readthedocs.io/en/latest/udps/write_buffering.html",
node.inst.inst_src_ref
"For more details, see: https://peakrdl-busdecoder.readthedocs.io/en/latest/udps/write_buffering.html",
node.inst.inst_src_ref,
)
if node.get_property('onread') is not None and not node.parent.get_property('buffer_reads'):
if node.get_property("onread") is not None and not node.parent.get_property(
"buffer_reads"
):
# ... is modified by an onread action without the atomicity of read buffering
# Enforce 10.6.1-f
self.msg.error(
@@ -149,8 +151,8 @@ class DesignValidator(RDLListener):
" subwords and is modified on-read, making it impossible to"
" access its value correctly. Consider enabling read"
" double-buffering. \n"
"For more details, see: https://peakrdl-regblock.readthedocs.io/en/latest/udps/read_buffering.html",
node.inst.inst_src_ref
"For more details, see: https://peakrdl-busdecoder.readthedocs.io/en/latest/udps/read_buffering.html",
node.inst.inst_src_ref,
)
# Check for unsynthesizable reset
@@ -166,10 +168,9 @@ class DesignValidator(RDLListener):
if is_async_reset:
self.msg.error(
"A field that uses an asynchronous reset cannot use a dynamic reset value. This is not synthesizable.",
node.inst.inst_src_ref
node.inst.inst_src_ref,
)
def exit_AddressableComponent(self, node: AddressableNode) -> None:
if not isinstance(node, RegNode):
# Exiting block-like node
@@ -194,14 +195,14 @@ class DesignValidator(RDLListener):
if (node.raw_address_offset % req_align) != 0:
self.msg.error(
f"Address offset +0x{node.raw_address_offset:x} of instance '{node.inst_name}' is not a power of 2 multiple of its size 0x{node.size:x}. "
f"This is required by the regblock exporter if a component {err_suffix}.",
node.inst.inst_src_ref
f"This is required by the busdecoder exporter if a component {err_suffix}.",
node.inst.inst_src_ref,
)
if node.is_array:
assert node.array_stride is not None
if not is_pow2(node.array_stride):
self.msg.error(
f"Address stride of instance array '{node.inst_name}' is not a power of 2"
f"This is required by the regblock exporter if a component {err_suffix}.",
node.inst.inst_src_ref
f"This is required by the busdecoder exporter if a component {err_suffix}.",
node.inst.inst_src_ref,
)

View File

@@ -8,25 +8,23 @@ from ..utils import get_indexed_path
from ..sv_int import SVInt
if TYPE_CHECKING:
from ..exporter import RegblockExporter
from ..exporter import BusDecoderExporter
class WriteBuffering:
def __init__(self, exp:'RegblockExporter'):
def __init__(self, exp: "BusDecoderExporter"):
self.exp = exp
@property
def top_node(self) -> 'AddrmapNode':
def top_node(self) -> "AddrmapNode":
return self.exp.ds.top_node
def get_storage_struct(self) -> str:
struct_gen = WBufStorageStructGenerator(self)
s = struct_gen.get_struct(self.top_node, "wbuf_storage_t")
assert s is not None
return s + "\nwbuf_storage_t wbuf_storage;"
def get_implementation(self) -> str:
gen = WBufLogicGenerator(self)
s = gen.get_content(self.top_node)
@@ -43,24 +41,26 @@ class WriteBuffering:
prefix = self.get_wbuf_prefix(node)
return f"{prefix}.pending && {self.get_trigger(node)}"
def get_raw_trigger(self, node: 'RegNode') -> Union[SVInt, str]:
trigger = node.get_property('wbuffer_trigger')
def get_raw_trigger(self, node: "RegNode") -> Union[SVInt, str]:
trigger = node.get_property("wbuffer_trigger")
if isinstance(trigger, RegNode):
# Trigger is a register.
# trigger when uppermost address of the register is written
regwidth = trigger.get_property('regwidth')
accesswidth = trigger.get_property('accesswidth')
strb_prefix = self.exp.dereferencer.get_access_strobe(trigger, reduce_substrobes=False)
regwidth = trigger.get_property("regwidth")
accesswidth = trigger.get_property("accesswidth")
strb_prefix = self.exp.dereferencer.get_access_strobe(
trigger, reduce_substrobes=False
)
if accesswidth < regwidth:
n_subwords = regwidth // accesswidth
return f"{strb_prefix}[{n_subwords-1}] && decoded_req_is_wr"
return f"{strb_prefix}[{n_subwords - 1}] && decoded_req_is_wr"
else:
return f"{strb_prefix} && decoded_req_is_wr"
elif isinstance(trigger, SignalNode):
s = self.exp.dereferencer.get_value(trigger)
if trigger.get_property('activehigh'):
if trigger.get_property("activehigh"):
return s
else:
return f"~{s}"
@@ -71,7 +71,7 @@ class WriteBuffering:
def get_trigger(self, node: Union[RegNode, FieldNode]) -> Union[SVInt, str]:
if isinstance(node, FieldNode):
node = node.parent
trigger = node.get_property('wbuffer_trigger')
trigger = node.get_property("wbuffer_trigger")
if isinstance(trigger, RegNode) and trigger == node:
# register is its own trigger

View File

@@ -8,9 +8,9 @@ omit =
[paths]
source =
../src/peakrdl_regblock/
*/site-packages/*/peakrdl_regblock
*/site-packages/peakrdl_regblock
../src/peakrdl_busdecoder/
*/site-packages/*/peakrdl_busdecoder
*/site-packages/peakrdl_busdecoder
[report]
exclude_lines =

View File

@@ -104,8 +104,8 @@ Each testcase group has its own folder and contains the following:
`test_*/__init__.py`
: Empty file required for test discovery.
`test_*/regblock.rdl`
: Testcase RDL file. Testcase infrastructure will automatically compile this and generate the regblock output SystemVerilog.
`test_*/busdecoder.rdl`
: Testcase RDL file. Testcase infrastructure will automatically compile this and generate the busdecoder output SystemVerilog.
`test_*/tb_template.sv`
: Jinja template that defines the testcase-specific sequence.
@@ -116,4 +116,4 @@ Each testcase group has its own folder and contains the following:
## Parameterization
Testcase classes can be parameterized using the [parameterized](https://github.com/wolever/parameterized) extension. This allows the same testcase to be run against multiple permutations of regblock export modes such as CPU interfaces, retiming flop stages, or even RDL parameterizations.
Testcase classes can be parameterized using the [parameterized](https://github.com/wolever/parameterized) extension. This allows the same testcase to be run against multiple permutations of busdecoder export modes such as CPU interfaces, retiming flop stages, or even RDL parameterizations.

View File

@@ -9,7 +9,7 @@ def pytest_addoption(parser):
stub: run the testcase using a no-op simulator stub
skip: skip all the simulation tests
auto: choose the best simulator based on what is installed
"""
""",
)
parser.addoption(
@@ -20,19 +20,18 @@ def pytest_addoption(parser):
Launch sim tool in GUI mode
Only use this option when running a single test
"""
""",
)
parser.addoption(
"--rerun",
default=False,
action="store_true",
help=""",
Re-run simulation in-place without re-exporting regblock
Re-run simulation in-place without re-exporting busdecoder
Useful if hand-editing a testcase interactively.
"""
""",
)
parser.addoption(
@@ -44,5 +43,5 @@ def pytest_addoption(parser):
skip: skip all the simulation tests
auto: choose the best tool based on what is installed
"""
""",
)

View File

@@ -9,8 +9,8 @@ import pathlib
import pytest
from systemrdl import RDLCompiler
from peakrdl_regblock import RegblockExporter
from peakrdl_regblock.udps import ALL_UDPS
from peakrdl_busdecoder import BusDecoderExporter
from peakrdl_busdecoder.udps import ALL_UDPS
from .cpuifs.base import CpuifTestMode
from .cpuifs.apb4 import APB4
@@ -43,7 +43,7 @@ class BaseTestCase(unittest.TestCase):
#: this gets auto-loaded via the _load_request autouse fixture
request = None # type: pytest.FixtureRequest
exporter = RegblockExporter()
exporter = BusDecoderExporter()
@pytest.fixture(autouse=True)
def _load_request(self, request):
@@ -72,16 +72,15 @@ class BaseTestCase(unittest.TestCase):
"""
path = os.path.join(self.get_run_dir(), "params.txt")
with open(path, 'w') as f:
with open(path, "w") as f:
for k, v in self.__class__.__dict__.items():
if k.startswith("_") or callable(v):
continue
f.write(f"{k}: {repr(v)}\n")
def export_regblock(self):
def export_busdecoder(self):
"""
Call the peakrdl_regblock exporter to generate the DUT
Call the peakrdl_busdecoder exporter to generate the DUT
"""
this_dir = self.get_testcase_dir()
@@ -97,17 +96,17 @@ class BaseTestCase(unittest.TestCase):
for udp in ALL_UDPS:
rdlc.register_udp(udp)
# ... including the definition
udp_file = os.path.join(this_dir, "../../hdl-src/regblock_udps.rdl")
udp_file = os.path.join(this_dir, "../../hdl-src/busdecoder_udps.rdl")
rdlc.compile_file(udp_file)
rdlc.compile_file(rdl_file)
root = rdlc.elaborate(self.rdl_elab_target, "regblock", self.rdl_elab_params)
root = rdlc.elaborate(self.rdl_elab_target, "busdecoder", self.rdl_elab_params)
self.exporter.export(
root,
self.get_run_dir(),
module_name="regblock",
package_name="regblock_pkg",
module_name="busdecoder",
package_name="busdecoder_pkg",
cpuif_cls=self.cpuif.cpuif_cls,
retime_read_fanin=self.retime_read_fanin,
retime_read_response=self.retime_read_response,
@@ -137,4 +136,4 @@ class BaseTestCase(unittest.TestCase):
self._write_params()
# Convert testcase RDL file --> SV
self.export_regblock()
self.export_busdecoder()

View File

@@ -1,6 +1,7 @@
from ..base import CpuifTestMode
from peakrdl_regblock.cpuif.apb3 import APB3_Cpuif, APB3_Cpuif_flattened
from peakrdl_busdecoder.cpuif.apb3 import APB3_Cpuif, APB3_Cpuif_flattened
class APB3(CpuifTestMode):
cpuif_cls = APB3_Cpuif
@@ -13,6 +14,7 @@ class APB3(CpuifTestMode):
]
tb_template = "tb_inst.sv"
class FlatAPB3(APB3):
cpuif_cls = APB3_Cpuif_flattened
rtl_files = []

View File

@@ -1,6 +1,7 @@
from ..base import CpuifTestMode
from peakrdl_regblock.cpuif.apb4 import APB4_Cpuif, APB4_Cpuif_flattened
from peakrdl_busdecoder.cpuif.apb4 import APB4_Cpuif, APB4_Cpuif_flattened
class APB4(CpuifTestMode):
cpuif_cls = APB4_Cpuif
@@ -13,6 +14,7 @@ class APB4(CpuifTestMode):
]
tb_template = "tb_inst.sv"
class FlatAPB4(APB4):
cpuif_cls = APB4_Cpuif_flattened
rtl_files = []

View File

@@ -1,6 +1,7 @@
from ..base import CpuifTestMode
from peakrdl_regblock.cpuif.avalon import Avalon_Cpuif, Avalon_Cpuif_flattened
from peakrdl_busdecoder.cpuif.avalon import Avalon_Cpuif, Avalon_Cpuif_flattened
class Avalon(CpuifTestMode):
cpuif_cls = Avalon_Cpuif
@@ -13,6 +14,7 @@ class Avalon(CpuifTestMode):
]
tb_template = "tb_inst.sv"
class FlatAvalon(Avalon):
cpuif_cls = Avalon_Cpuif_flattened
rtl_files = []

View File

@@ -1,6 +1,7 @@
from ..base import CpuifTestMode
from peakrdl_regblock.cpuif.axi4lite import AXI4Lite_Cpuif, AXI4Lite_Cpuif_flattened
from peakrdl_busdecoder.cpuif.axi4lite import AXI4Lite_Cpuif, AXI4Lite_Cpuif_flattened
class AXI4Lite(CpuifTestMode):
cpuif_cls = AXI4Lite_Cpuif
@@ -13,6 +14,7 @@ class AXI4Lite(CpuifTestMode):
]
tb_template = "tb_inst.sv"
class FlatAXI4Lite(AXI4Lite):
cpuif_cls = AXI4Lite_Cpuif_flattened
rtl_files = []

View File

@@ -4,14 +4,15 @@ import inspect
import jinja2 as jj
from peakrdl_regblock.cpuif.base import CpuifBase
from peakrdl_busdecoder.cpuif.base import CpuifBase
from ..sv_line_anchor import SVLineAnchor
if TYPE_CHECKING:
from peakrdl_regblock import RegblockExporter
from peakrdl_busdecoder import BusDecoderExporter
from ..sim_testcase import SimTestCase
class CpuifTestMode:
cpuif_cls = None # type: CpuifBase
@@ -26,8 +27,7 @@ class CpuifTestMode:
# Path is relative to the class that assigns this
tb_template = ""
def _get_class_dir_of_variable(self, varname:str) -> str:
def _get_class_dir_of_variable(self, varname: str) -> str:
"""
Traverse up the MRO and find the first class that explicitly assigns
the variable of name varname. Returns the directory that contains the
@@ -39,34 +39,29 @@ class CpuifTestMode:
return class_dir
raise RuntimeError
def _get_file_paths(self, varname:str) -> List[str]:
def _get_file_paths(self, varname: str) -> List[str]:
class_dir = self._get_class_dir_of_variable(varname)
files = getattr(self, varname)
cwd = os.getcwd()
new_files = []
for file in files:
relpath = os.path.relpath(
os.path.join(class_dir, file),
cwd
)
relpath = os.path.relpath(os.path.join(class_dir, file), cwd)
new_files.append(relpath)
return new_files
def get_sim_files(self) -> List[str]:
files = self._get_file_paths("rtl_files") + self._get_file_paths("tb_files")
unique_files = []
[unique_files.append(f) for f in files if f not in unique_files]
return unique_files
def get_synth_files(self) -> List[str]:
return self._get_file_paths("rtl_files")
def get_tb_inst(self, testcase: 'SimTestCase', exporter: 'RegblockExporter') -> str:
def get_tb_inst(
self, testcase: "SimTestCase", exporter: "BusDecoderExporter"
) -> str:
class_dir = self._get_class_dir_of_variable("tb_template")
loader = jj.FileSystemLoader(class_dir)
jj_env = jj.Environment(

View File

@@ -1,6 +1,7 @@
from ..base import CpuifTestMode
from peakrdl_regblock.cpuif.passthrough import PassthroughCpuif
from peakrdl_busdecoder.cpuif.passthrough import PassthroughCpuif
class Passthrough(CpuifTestMode):
cpuif_cls = PassthroughCpuif

View File

@@ -3,6 +3,7 @@ from typing import TYPE_CHECKING, List
if TYPE_CHECKING:
from ..sim_testcase import SimTestCase
class Simulator:
name = ""
@@ -10,7 +11,7 @@ class Simulator:
def is_installed(cls) -> bool:
raise NotImplementedError
def __init__(self, testcase: 'SimTestCase' = None) -> None:
def __init__(self, testcase: "SimTestCase" = None) -> None:
self.testcase = testcase
@property
@@ -22,8 +23,8 @@ class Simulator:
files = []
files.extend(self.testcase.cpuif.get_sim_files())
files.extend(self.testcase.get_extra_tb_files())
files.append("regblock_pkg.sv")
files.append("regblock.sv")
files.append("busdecoder_pkg.sv")
files.append("busdecoder.sv")
files.append("tb.sv")
return files
@@ -31,5 +32,5 @@ class Simulator:
def compile(self) -> None:
raise NotImplementedError
def run(self, plusargs:List[str] = None) -> None:
def run(self, plusargs: List[str] = None) -> None:
raise NotImplementedError

View File

@@ -6,13 +6,13 @@ import pytest
from .base_testcase import BaseTestCase
from .synthesizers import get_synthesizer_cls
class SynthTestCase(BaseTestCase):
class SynthTestCase(BaseTestCase):
def _get_synth_files(self) -> List[str]:
files = []
files.extend(self.cpuif.get_synth_files())
files.append("regblock_pkg.sv")
files.append("regblock.sv")
files.append("busdecoder_pkg.sv")
files.append("busdecoder.sv")
return files

View File

@@ -25,7 +25,7 @@ set_msg_config -severity {CRITICAL WARNING} -new_severity "ERROR"
set_part [lindex [get_parts] 0]
read_verilog -sv $files
read_xdc $this_dir/constr.xdc
synth_design -top regblock -mode out_of_context
synth_design -top busdecoder -mode out_of_context
#write_checkpoint -force synth.dcp

View File

@@ -28,11 +28,11 @@ module tb;
// DUT Signal declarations
//--------------------------------------------------------------------------
{%- if exporter.hwif.has_input_struct %}
regblock_pkg::regblock__in_t hwif_in;
busdecoder_pkg::busdecoder__in_t hwif_in;
{%- endif %}
{%- if exporter.hwif.has_output_struct %}
regblock_pkg::regblock__out_t hwif_out;
busdecoder_pkg::busdecoder__out_t hwif_out;
{%- endif %}
{%- if exporter.ds.has_paritycheck %}
@@ -76,7 +76,7 @@ module tb;
// DUT
//--------------------------------------------------------------------------
{% sv_line_anchor %}
regblock dut (.*);
busdecoder dut (.*);
{%- if exporter.hwif.has_output_struct %}
{% sv_line_anchor %}

View File

@@ -16,13 +16,13 @@ pip install -r requirements.txt
pip install -e "../[cli]"
# Run lint
pylint --rcfile pylint.rc ../src/peakrdl_regblock
pylint --rcfile pylint.rc ../src/peakrdl_busdecoder
# Run static type checking
mypy ../src/peakrdl_regblock
mypy ../src/peakrdl_busdecoder
# Run unit tests
pytest --workers auto --cov=peakrdl_regblock --synth-tool skip
pytest --workers auto --cov=peakrdl_busdecoder --synth-tool skip
# Generate coverage report
coverage html -i -d htmlcov

View File

@@ -7,16 +7,16 @@
##1;
// check enum values
assert(regblock_pkg::top__my_enum__val_1 == 'd3);
assert(regblock_pkg::top__my_enum__val_2 == 'd4);
assert(busdecoder_pkg::top__my_enum__val_1 == 'd3);
assert(busdecoder_pkg::top__my_enum__val_2 == 'd4);
// check initial conditions
cpuif.assert_read('h0, regblock_pkg::top__my_enum__val_2);
cpuif.assert_read('h0, busdecoder_pkg::top__my_enum__val_2);
//---------------------------------
// set r0 = val_1
cpuif.write('h0, regblock_pkg::top__my_enum__val_1);
cpuif.write('h0, busdecoder_pkg::top__my_enum__val_1);
cpuif.assert_read('h0, regblock_pkg::top__my_enum__val_1);
cpuif.assert_read('h0, busdecoder_pkg::top__my_enum__val_1);
{% endblock %}

View File

@@ -296,8 +296,8 @@
// Check register struct bit-order
repeat(32) begin
regblock_pkg::top__my_reg_alt__external__fields__in_t fields_in;
regblock_pkg::top__my_reg_alt__external__fields__out_t fields_out;
busdecoder_pkg::top__my_reg_alt__external__fields__in_t fields_in;
busdecoder_pkg::top__my_reg_alt__external__fields__out_t fields_out;
fields_in = $urandom();
fields_out = $urandom();

View File

@@ -7,6 +7,6 @@
##1;
// check block size
assert(regblock_pkg::REGBLOCK_SIZE == {{exporter.ds.top_node.size}});
assert(busdecoder_pkg::REGBLOCK_SIZE == {{exporter.ds.top_node.size}});
{% endblock %}

View File

@@ -1,4 +1,4 @@
addrmap regblock {
addrmap busdecoder {
default sw=rw;
default hw=r;

View File

@@ -2,7 +2,7 @@
{% block seq %}
{% sv_line_anchor %}
assert(regblock_pkg::N_REGS == {{testcase.n_regs}});
assert(regblock_pkg::REGWIDTH == {{testcase.regwidth}});
assert(regblock_pkg::NAME == "{{testcase.name}}");
assert(busdecoder_pkg::N_REGS == {{testcase.n_regs}});
assert(busdecoder_pkg::REGWIDTH == {{testcase.regwidth}});
assert(busdecoder_pkg::NAME == "{{testcase.name}}");
{% endblock %}

View File

@@ -1,4 +1,4 @@
addrmap regblock {
addrmap busdecoder {
default sw=rw;
default hw=r;

View File

@@ -1,17 +1,19 @@
import os
from peakrdl_regblock.cpuif.apb3 import APB3_Cpuif
from peakrdl_busdecoder.cpuif.apb3 import APB3_Cpuif
from ..lib.cpuifs.apb3 import APB3
from ..lib.base_testcase import BaseTestCase
#-------------------------------------------------------------------------------
# -------------------------------------------------------------------------------
class ClassOverride_Cpuif(APB3_Cpuif):
@property
def port_declaration(self) -> str:
return "user_apb3_intf.slave s_apb"
class ClassOverride_cpuiftestmode(APB3):
cpuif_cls = ClassOverride_Cpuif
@@ -20,19 +22,19 @@ class Test_class_override(BaseTestCase):
cpuif = ClassOverride_cpuiftestmode()
def test_override_success(self):
output_file = os.path.join(self.get_run_dir(), "regblock.sv")
output_file = os.path.join(self.get_run_dir(), "busdecoder.sv")
with open(output_file, "r") as f:
self.assertIn(
"user_apb3_intf.slave s_apb",
f.read()
)
self.assertIn("user_apb3_intf.slave s_apb", f.read())
# -------------------------------------------------------------------------------
#-------------------------------------------------------------------------------
class TemplateOverride_Cpuif(APB3_Cpuif):
# contains the text "USER TEMPLATE OVERRIDE"
template_path = "user_apb3_tmpl.sv"
class TemplateOverride_cpuiftestmode(APB3):
cpuif_cls = TemplateOverride_Cpuif
@@ -41,9 +43,6 @@ class Test_template_override(BaseTestCase):
cpuif = TemplateOverride_cpuiftestmode()
def test_override_success(self):
output_file = os.path.join(self.get_run_dir(), "regblock.sv")
output_file = os.path.join(self.get_run_dir(), "busdecoder.sv")
with open(output_file, "r") as f:
self.assertIn(
"USER TEMPLATE OVERRIDE",
f.read()
)
self.assertIn("USER TEMPLATE OVERRIDE", f.read())

View File

@@ -5,6 +5,7 @@ from systemrdl.messages import RDLCompileError
from ..lib.base_testcase import BaseTestCase
class TestValidationErrors(BaseTestCase):
def setUp(self) -> None:
# Stub usual pre-test setup
@@ -19,11 +20,10 @@ class TestValidationErrors(BaseTestCase):
f = io.StringIO()
with contextlib.redirect_stderr(f):
with self.assertRaises(RDLCompileError):
self.export_regblock()
self.export_busdecoder()
stderr = f.getvalue()
self.assertRegex(stderr, err_regex)
def test_unaligned_reg(self) -> None:
self.assert_validate_error(
"unaligned_reg.rdl",
@@ -39,7 +39,7 @@ class TestValidationErrors(BaseTestCase):
def test_bad_external_ref(self) -> None:
self.assert_validate_error(
"external_ref.rdl",
"Property is assigned a reference that points to a component not internal to the regblock being exported",
"Property is assigned a reference that points to a component not internal to the busdecoder being exported",
)
def test_sharedextbus_not_supported(self) -> None:
@@ -51,7 +51,7 @@ class TestValidationErrors(BaseTestCase):
def test_inconsistent_accesswidth(self) -> None:
self.assert_validate_error(
"inconsistent_accesswidth.rdl",
r"Multi-word registers that have an accesswidth \(16\) that are inconsistent with this regblock's CPU bus width \(32\) are not supported",
r"Multi-word registers that have an accesswidth \(16\) that are inconsistent with this busdecoder's CPU bus width \(32\) are not supported",
)
def test_unbuffered_wide_w_fields(self) -> None:
@@ -117,5 +117,5 @@ class TestValidationErrors(BaseTestCase):
def test_signed_enum(self) -> None:
self.assert_validate_error(
"signed_enum.rdl",
"The property is_signed=true is not supported for fields encoded as an enum."
"The property is_signed=true is not supported for fields encoded as an enum.",
)