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** **Describe the bug**
A clear and concise description of what the bug is. 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** **Describe the problem/limitation you think should be addressed**
A clear and concise description of what the problem is. A clear and concise description of what the problem is.

View File

@@ -6,6 +6,6 @@ this change.
# Checklist # 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) - [ ] 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. - [ ] If this change adds new features, I have added new unit tests that cover them.

View File

@@ -56,7 +56,7 @@ jobs:
- name: Test - name: Test
run: | run: |
cd tests cd tests
pytest --cov=peakrdl_regblock --synth-tool skip --sim-tool stub pytest --cov=peakrdl_busdecoder --synth-tool skip --sim-tool stub
- name: Coveralls - name: Coveralls
env: env:
@@ -103,7 +103,7 @@ jobs:
- name: Run Lint - name: Run Lint
run: | run: |
pylint --rcfile tests/pylint.rc peakrdl_regblock pylint --rcfile tests/pylint.rc peakrdl_busdecoder
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
mypy: mypy:
@@ -125,7 +125,7 @@ jobs:
- name: Type Check - name: Type Check
run: | run: |
mypy --config-file tests/mypy.ini src/peakrdl_regblock mypy --config-file tests/mypy.ini src/peakrdl_busdecoder
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
build: 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 We love your input! We want to make contributing to this project as easy and
transparent as possible, whether it's: transparent as possible, whether it's:
@@ -9,9 +9,9 @@ transparent as possible, whether it's:
- Becoming a maintainer - 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 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: Issue submission expectations:
* Please keep each issue submission limited to one topic. This helps us stay organized. * 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 prune tests

View File

@@ -1,12 +1,12 @@
[![Documentation Status](https://readthedocs.org/projects/peakrdl-regblock/badge/?version=latest)](http://peakrdl-regblock.readthedocs.io) [![Documentation Status](https://readthedocs.org/projects/peakrdl-busdecoder/badge/?version=latest)](http://peakrdl-busdecoder.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) [![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-regblock/badge.svg?branch=main)](https://coveralls.io/github/SystemRDL/PeakRDL-regblock?branch=main) [![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-regblock.svg)](https://pypi.org/project/peakrdl-regblock) [![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. Compile SystemRDL into a SystemVerilog control/status register (CSR) block.
For the command line tool, see the [PeakRDL project](https://peakrdl.readthedocs.io). For the command line tool, see the [PeakRDL project](https://peakrdl.readthedocs.io).
## Documentation ## 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>`_, 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: :members:
Example Example
@@ -16,9 +16,9 @@ implementation from SystemRDL source.
:emphasize-lines: 2-4, 29-33 :emphasize-lines: 2-4, 29-33
from systemrdl import RDLCompiler, RDLCompileError from systemrdl import RDLCompiler, RDLCompileError
from peakrdl_regblock import RegblockExporter from peakrdl_busdecoder import BusDecoderExporter
from peakrdl_regblock.cpuif.axi4lite import AXI4Lite_Cpuif from peakrdl_busdecoder.cpuif.axi4lite import AXI4Lite_Cpuif
from peakrdl_regblock.udps import ALL_UDPS from peakrdl_busdecoder.udps import ALL_UDPS
input_files = [ input_files = [
"PATH/TO/my_register_block.rdl" "PATH/TO/my_register_block.rdl"
@@ -27,7 +27,7 @@ implementation from SystemRDL source.
# Create an instance of the compiler # Create an instance of the compiler
rdlc = RDLCompiler() rdlc = RDLCompiler()
# Register all UDPs that 'regblock' requires # Register all UDPs that 'busdecoder' requires
for udp in ALL_UDPS: for udp in ALL_UDPS:
rdlc.register_udp(udp) rdlc.register_udp(udp)
@@ -43,7 +43,7 @@ implementation from SystemRDL source.
sys.exit(1) sys.exit(1)
# Export a SystemVerilog implementation # Export a SystemVerilog implementation
exporter = RegblockExporter() exporter = BusDecoderExporter()
exporter.export( exporter.export(
root, "path/to/output_dir", root, "path/to/output_dir",
cpuif_cls=AXI4Lite_Cpuif cpuif_cls=AXI4Lite_Cpuif

View File

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

View File

@@ -1,13 +1,13 @@
.. _peakrdl_cfg: .. _peakrdl_cfg:
Configuring PeakRDL-regblock Configuring PeakRDL-busdecoder
============================ ============================
If using the `PeakRDL command line tool <https://peakrdl.readthedocs.io/>`_, 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. 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 .. data:: cpuifs
@@ -20,7 +20,7 @@ All regblock-specific options are defined under the ``[regblock]`` TOML heading.
.. code-block:: toml .. code-block:: toml
[regblock] [busdecoder]
cpuifs.my-cpuif-name = "my_cpuif_module:MyCPUInterfaceClass" 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 .. code-block:: toml
[regblock] [busdecoder]
default_reset = "arst" default_reset = "arst"

View File

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

View File

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

View File

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

View File

@@ -3,9 +3,9 @@
Internal CPUIF Protocol 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 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. 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, For non-pipelined CPU interfaces that only allow one outstanding transaction at a time,
these stall signals can be safely ignored. 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 read transaction takes 1 clock cycle to complete
* A write transaction takes 0 clock cycles to complete * A write transaction takes 0 clock cycles to complete

View File

@@ -17,9 +17,9 @@ encountered in the design.
Addressing 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 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. only include the local offset.
For example, consider a fictional AXI4-Lite device that: 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. internal CPUIF handshake signals to the user.
* Command line: ``--cpuif passthrough`` * 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`. For more details on the protocol itself, see: :ref:`cpuif_protocol`.

View File

@@ -42,14 +42,14 @@ Naming Scheme
================================================================================ ================================================================================
hwif_out hwif_out
.my_regblock .my_busdecoder
.my_reg[X][Y] .my_reg[X][Y]
.my_field .my_field
.value .value
.anded .anded
hwif_in hwif_in
.my_regblock .my_busdecoder
.my_reg[X][Y] .my_reg[X][Y]
.my_field .my_field
.value .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 SystemVerilog coding styles vary wildly, and unfortunately there is little
consensus on this topic within the digital design community. 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 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 widely accepted coding style, but since this is a very opinionated space, it is
impossible to satisfy everyone. impossible to satisfy everyone.
@@ -127,5 +127,5 @@ complexity to the tool.
If you encounter a lint violation, please carefully review and consider waiving 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, 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. that describes the problem.

View File

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

View File

@@ -1,9 +1,9 @@
Licensing 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: 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? Why LGPLv3?
@@ -23,19 +23,19 @@ explicitly mentioned in the exemptions below.
What is exempt from the LGPLv3 license? 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. considered LGPLv3 code.
The following are exempt and are free to use with no restrictions: 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 * Any code that is generated using PeakRDL-busdecoder is 100% yours. Since it
was derived from your regblock definition, it remains yours. You can 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 distribute it freely, use it in a proprietary ASIC, sell it as part of an
IP, whatever. IP, whatever.
* Any code snippets in this documentation can be freely copy/pasted. These are * Any code snippets in this documentation can be freely copy/pasted. These are
examples that are intended for this purpose. examples that are intended for this purpose.
* All reference files that are downloadable from this documentation, which are * 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? 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: 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. * 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 .. 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 errextbus

View File

@@ -2,7 +2,7 @@ Field Properties
================ ================
.. note:: Any properties not explicitly listed here are either implicitly .. 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 Software Access Properties
-------------------------- --------------------------

View File

@@ -2,7 +2,7 @@ Register Properties
=================== ===================
.. note:: Any properties not explicitly listed here are either implicitly .. 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 accesswidth
----------- -----------

View File

@@ -2,7 +2,7 @@ Signal Properties
================= =================
.. note:: Any properties not explicitly listed here are either implicitly .. 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 activehigh/activelow
@@ -19,7 +19,7 @@ Ignored in all other contexts.
cpuif_reset cpuif_reset
----------- -----------
Specify that this signal shall be used as alternate reset signal for the CPU Specify that this signal shall be used as alternate reset signal for the CPU
interface for this regblock. interface for this busdecoder.
field_reset field_reset

View File

@@ -1,9 +1,9 @@
External Components External Components
=================== ===================
SystemRDL allows some component instances to be defined as "external" elements 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 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 are omitted and instead a user-interface is presented on the
``hwif_in``/``hwif_out`` i/o structs. ``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 * By default external ``hwif_out`` signals are driven combinationally. An
optional output retiming stage can be enabled if needed. 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. 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 This is enforced even if the CPUIF is capable of pipelined accesses such as
AXI4-Lite. 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. components such as ``addrmap``, ``regfile`` or ``mem`` elements.
To ensure address decoding for external blocks is simple (only requires simple bit-pruning), 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 Request
^^^^^^^ ^^^^^^^

View File

@@ -15,9 +15,9 @@ functionality.
Properties 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: 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 .. 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 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 the ``hwif`` structs. There is no special handling in the internals of
the regblock. the busdecoder.
Properties Properties
---------- ----------
Fields can be declared as fixed-point numbers using the following two 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 :lines: 46-54
The :ref:`is_signed<signed>` property can be used in conjunction with these The :ref:`is_signed<signed>` property can be used in conjunction with these
properties to declare signed fixed-point fields. 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: 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 .. 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 to define complex register map structures, sometimes they are not enough to
accurately describe a necessary feature. Fortunately the SystemRDL spec allows accurately describe a necessary feature. Fortunately the SystemRDL spec allows
the language to be extended using "User Defined Properties" (UDPs). The 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. section.
To enable these UDPs, compile this RDL file prior to the rest of your design: 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 .. list-table:: Summary of UDPs
:header-rows: 1 :header-rows: 1

View File

@@ -24,12 +24,12 @@ Properties
The behavior of read-buffered registers is defined using the following two The behavior of read-buffered registers is defined using the following two
properties: properties:
.. literalinclude:: ../../hdl-src/regblock_udps.rdl .. literalinclude:: ../../hdl-src/busdecoder_udps.rdl
:lines: 10-18 :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: 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 .. 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 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 signal type in the ``hwif`` structs. There is no special handling in the internals
of the regblock. of the busdecoder.
Properties Properties
---------- ----------
A field can be marked as signed using the following user-defined property: 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 :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: 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 .. describe:: is_signed

View File

@@ -4,8 +4,8 @@ Write-buffered Registers
======================== ========================
In order to support larger software write accesses that are atomic, the In order to support larger software write accesses that are atomic, the
regblock generator understands several UDPs that implement write-buffering to busdecoder generator understands several UDPs that implement write-buffering to
specific registers. This causes the regblock to delay the effect of a software specific registers. This causes the busdecoder to delay the effect of a software
write operation until a defined trigger event. write operation until a defined trigger event.
Some examples of when this is useful: Some examples of when this is useful:
@@ -30,12 +30,12 @@ Properties
The behavior of write-buffered registers is defined using the following two The behavior of write-buffered registers is defined using the following two
properties: properties:
.. literalinclude:: ../../hdl-src/regblock_udps.rdl .. literalinclude:: ../../hdl-src/busdecoder_udps.rdl
:lines: 20-28 :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: 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 .. describe:: buffer_writes

View File

@@ -1,10 +1,10 @@
/* /*
* This file defines several property extensions that are understood by the * 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. * 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 { property buffer_reads {

View File

@@ -3,24 +3,28 @@ requires = ["setuptools", "setuptools-scm"]
build-backend = "setuptools.build_meta" build-backend = "setuptools.build_meta"
[project] [project]
name = "peakrdl-regblock" name = "peakrdl-busdecoder"
dynamic = ["version"] dynamic = ["version"]
requires-python = ">=3.7" requires-python = ">=3.7"
dependencies = [ dependencies = ["systemrdl-compiler ~= 1.29", "Jinja2>=2.11"]
"systemrdl-compiler ~= 1.29",
"Jinja2>=2.11",
]
authors = [ authors = [{ name = "Alex Mykyta" }]
{name="Alex Mykyta"},
]
description = "Compile SystemRDL into a SystemVerilog control/status register (CSR) block" description = "Compile SystemRDL into a SystemVerilog control/status register (CSR) block"
readme = "README.md" readme = "README.md"
license = {text = "LGPLv3"} license = { text = "LGPLv3" }
keywords = [ keywords = [
"SystemRDL", "PeakRDL", "CSR", "compiler", "tool", "registers", "generator", "SystemRDL",
"Verilog", "SystemVerilog", "register abstraction layer", "PeakRDL",
"FPGA", "ASIC", "CSR",
"compiler",
"tool",
"registers",
"generator",
"Verilog",
"SystemVerilog",
"register abstraction layer",
"FPGA",
"ASIC",
] ]
classifiers = [ classifiers = [
"Development Status :: 5 - Production/Stable", "Development Status :: 5 - Production/Stable",
@@ -34,18 +38,16 @@ classifiers = [
] ]
[project.optional-dependencies] [project.optional-dependencies]
cli = [ cli = ["peakrdl-cli >= 1.2.3"]
"peakrdl-cli >= 1.2.3",
]
[project.urls] [project.urls]
Source = "https://github.com/SystemRDL/PeakRDL-regblock" Source = "https://github.com/SystemRDL/PeakRDL-busdecoder"
Tracker = "https://github.com/SystemRDL/PeakRDL-regblock/issues" Tracker = "https://github.com/SystemRDL/PeakRDL-busdecoder/issues"
Changelog = "https://github.com/SystemRDL/PeakRDL-regblock/releases" Changelog = "https://github.com/SystemRDL/PeakRDL-busdecoder/releases"
Documentation = "https://peakrdl-regblock.readthedocs.io/" Documentation = "https://peakrdl-busdecoder.readthedocs.io/"
[tool.setuptools.dynamic] [tool.setuptools.dynamic]
version = {attr = "peakrdl_regblock.__about__.__version__"} version = { attr = "peakrdl_busdecoder.__about__.__version__" }
[project.entry-points."peakrdl.exporters"] [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 .__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.config import schema
from peakrdl.plugins.entry_points import get_entry_points 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 .cpuif import CpuifBase, apb3, apb4, axi4lite, passthrough, avalon
from .udps import ALL_UDPS from .udps import ALL_UDPS
@@ -14,6 +14,7 @@ if TYPE_CHECKING:
import argparse import argparse
from systemrdl.node import AddrmapNode from systemrdl.node import AddrmapNode
class Exporter(ExporterSubcommandPlugin): class Exporter(ExporterSubcommandPlugin):
short_desc = "Generate a SystemVerilog control/status register (CSR) block" short_desc = "Generate a SystemVerilog control/status register (CSR) block"
@@ -26,7 +27,6 @@ class Exporter(ExporterSubcommandPlugin):
@functools.lru_cache() @functools.lru_cache()
def get_cpuifs(self) -> Dict[str, Type[CpuifBase]]: def get_cpuifs(self) -> Dict[str, Type[CpuifBase]]:
# All built-in CPUIFs # All built-in CPUIFs
cpuifs = { cpuifs = {
"passthrough": passthrough.PassthroughCpuif, "passthrough": passthrough.PassthroughCpuif,
@@ -41,67 +41,74 @@ class Exporter(ExporterSubcommandPlugin):
} }
# Load any cpuifs specified via entry points # 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 name = ep.name
cpuif = ep.load() cpuif = ep.load()
if name in cpuifs: 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): 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 cpuifs[name] = cpuif
# Load any CPUIFs via config import # 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: 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): 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 cpuifs[name] = cpuif
return cpuifs 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() cpuifs = self.get_cpuifs()
arg_group.add_argument( arg_group.add_argument(
"--cpuif", "--cpuif",
choices=cpuifs.keys(), choices=cpuifs.keys(),
default="apb3", default="apb3",
help="Select the CPU interface protocol to use [apb3]" help="Select the CPU interface protocol to use [apb3]",
) )
arg_group.add_argument( arg_group.add_argument(
"--module-name", "--module-name",
metavar="NAME", metavar="NAME",
default=None, default=None,
help="Override the SystemVerilog module name" help="Override the SystemVerilog module name",
) )
arg_group.add_argument( arg_group.add_argument(
"--package-name", "--package-name",
metavar="NAME", metavar="NAME",
default=None, default=None,
help="Override the SystemVerilog package name" help="Override the SystemVerilog package name",
) )
arg_group.add_argument( arg_group.add_argument(
"--type-style", "--type-style",
dest="type_style", dest="type_style",
choices=['lexical', 'hier'], choices=["lexical", "hier"],
default="lexical", default="lexical",
help="""Choose how HWIF struct type names are generated. help="""Choose how HWIF struct type names are generated.
The 'lexical' style will use RDL lexical scope & type names where The 'lexical' style will use RDL lexical scope & type names where
possible and attempt to re-use equivalent type definitions. possible and attempt to re-use equivalent type definitions.
The 'hier' style uses component's hierarchy as the struct type name. [lexical] The 'hier' style uses component's hierarchy as the struct type name. [lexical]
""" """,
) )
arg_group.add_argument( arg_group.add_argument(
"--hwif-report", "--hwif-report",
action="store_true", action="store_true",
default=False, default=False,
help="Generate a HWIF report file" help="Generate a HWIF report file",
) )
arg_group.add_argument( arg_group.add_argument(
@@ -109,25 +116,25 @@ class Exporter(ExporterSubcommandPlugin):
type=int, type=int,
default=None, default=None,
help="""Override the CPU interface's address width. By default, 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( arg_group.add_argument(
"--rt-read-fanin", "--rt-read-fanin",
action="store_true", action="store_true",
default=False, 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( arg_group.add_argument(
"--rt-read-response", "--rt-read-response",
action="store_true", action="store_true",
default=False, 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( arg_group.add_argument(
"--rt-external", "--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( arg_group.add_argument(
@@ -136,11 +143,10 @@ class Exporter(ExporterSubcommandPlugin):
default=None, default=None,
help="""Choose the default style of reset signal if not explicitly help="""Choose the default style of reset signal if not explicitly
specified by the SystemRDL design. If unspecified, the default reset 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() cpuifs = self.get_cpuifs()
retime_external_reg = False retime_external_reg = False
@@ -164,10 +170,13 @@ class Exporter(ExporterSubcommandPlugin):
retime_external_mem = True retime_external_mem = True
retime_external_addrmap = True retime_external_addrmap = True
else: 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' # 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": if default_rst == "rst":
default_reset_activelow = False default_reset_activelow = False
default_reset_async = False default_reset_async = False
@@ -183,8 +192,7 @@ class Exporter(ExporterSubcommandPlugin):
else: else:
raise RuntimeError raise RuntimeError
x = BusDecoderExporter()
x = RegblockExporter()
x.export( x.export(
top_node, top_node,
options.output, options.output,

View File

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

View File

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

View File

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

View File

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

View File

@@ -26,7 +26,8 @@ if TYPE_CHECKING:
from systemrdl.node import SignalNode from systemrdl.node import SignalNode
from systemrdl.rdltypes import UserEnum from systemrdl.rdltypes import UserEnum
class RegblockExporter:
class BusDecoderExporter:
hwif: Hwif hwif: Hwif
cpuif: CpuifBase cpuif: CpuifBase
address_decode: AddressDecode address_decode: AddressDecode
@@ -35,28 +36,35 @@ class RegblockExporter:
write_buffering: WriteBuffering write_buffering: WriteBuffering
read_buffering: ReadBuffering read_buffering: ReadBuffering
dereferencer: Dereferencer dereferencer: Dereferencer
ds: 'DesignState' ds: "DesignState"
def __init__(self, **kwargs: Any) -> None: def __init__(self, **kwargs: Any) -> None:
# Check for stray kwargs # Check for stray kwargs
if 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.FileSystemLoader(os.path.dirname(__file__)),
jj.PrefixLoader({ jj.PrefixLoader(
'base': jj.FileSystemLoader(os.path.dirname(__file__)), {
}, delimiter=":") "base": jj.FileSystemLoader(os.path.dirname(__file__)),
]) },
delimiter=":",
),
]
)
self.jj_env = jj.Environment( self.jj_env = jj.Environment(
loader=loader, loader=loader,
undefined=jj.StrictUndefined, undefined=jj.StrictUndefined,
) )
def export(
def export(self, node: Union[RootNode, AddrmapNode], output_dir:str, **kwargs: Any) -> None: self, node: Union[RootNode, AddrmapNode], output_dir: str, **kwargs: Any
) -> None:
""" """
Parameters Parameters
---------- ----------
@@ -65,7 +73,7 @@ class RegblockExporter:
output_dir: str output_dir: str
Path to the output directory where generated SystemVerilog will be written. Path to the output directory where generated SystemVerilog will be written.
Output includes two files: a module definition and package definition. 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. Specify the class type that implements the CPU interface of your choice.
Defaults to AMBA APB4. Defaults to AMBA APB4.
module_name: str module_name: str
@@ -115,7 +123,7 @@ class RegblockExporter:
the contents of the ``hwif_in`` and ``hwif_out`` structures. the contents of the ``hwif_in`` and ``hwif_out`` structures.
address_width: int address_width: int
Override the CPU interface's address width. By default, address width 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 default_reset_activelow: bool
If overriden to True, default reset is active-low instead of active-high. If overriden to True, default reset is active-low instead of active-high.
default_reset_async: bool default_reset_async: bool
@@ -129,16 +137,18 @@ class RegblockExporter:
self.ds = DesignState(top_node, kwargs) self.ds = DesignState(top_node, kwargs)
cpuif_cls = kwargs.pop("cpuif_cls", None) or APB4_Cpuif # type: Type[CpuifBase] cpuif_cls = kwargs.pop("cpuif_cls", None) or APB4_Cpuif # type: Type[CpuifBase]
generate_hwif_report = kwargs.pop("generate_hwif_report", False) # type: bool generate_hwif_report = kwargs.pop("generate_hwif_report", False) # type: bool
# Check for stray kwargs # Check for stray kwargs
if 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: if generate_hwif_report:
path = os.path.join(output_dir, f"{self.ds.module_name}_hwif.rpt") 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: else:
hwif_report_file = None hwif_report_file = None
@@ -181,7 +191,7 @@ class RegblockExporter:
"get_always_ff_event": self.dereferencer.get_always_ff_event, "get_always_ff_event": self.dereferencer.get_always_ff_event,
"ds": self.ds, "ds": self.ds,
"kwf": kwf, "kwf": kwf,
"SVInt" : SVInt, "SVInt": SVInt,
} }
# Write out design # Write out design
@@ -210,34 +220,38 @@ class DesignState:
self.top_node = top_node self.top_node = top_node
msg = top_node.env.msg msg = top_node.env.msg
#------------------------ # ------------------------
# Extract compiler args # Extract compiler args
#------------------------ # ------------------------
self.reuse_hwif_typedefs = kwargs.pop("reuse_hwif_typedefs", True) # type: bool 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.module_name = kwargs.pop("module_name", None) or kwf(
self.package_name = kwargs.pop("package_name", None) or (self.module_name + "_pkg") # type: str self.top_node.inst_name
user_addr_width = kwargs.pop("address_width", None) # type: Optional[int] ) # 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 # Pipelining options
self.retime_read_fanin = kwargs.pop("retime_read_fanin", False) # type: bool self.retime_read_fanin = kwargs.pop("retime_read_fanin", False) # type: bool
self.retime_read_response = kwargs.pop("retime_read_response", False) # type: bool self.retime_read_response = kwargs.pop("retime_read_response", False) # type: bool
self.retime_external_reg = kwargs.pop("retime_external_reg", False) # type: bool self.retime_external_reg = kwargs.pop("retime_external_reg", False) # type: bool
self.retime_external_regfile = kwargs.pop("retime_external_regfile", False) # type: bool self.retime_external_regfile = kwargs.pop("retime_external_regfile", False) # type: bool
self.retime_external_mem = kwargs.pop("retime_external_mem", False) # type: bool self.retime_external_mem = kwargs.pop("retime_external_mem", False) # type: bool
self.retime_external_addrmap = kwargs.pop("retime_external_addrmap", False) # type: bool self.retime_external_addrmap = kwargs.pop("retime_external_addrmap", False) # type: bool
# Default reset type # Default reset type
self.default_reset_activelow = kwargs.pop("default_reset_activelow", False) # type: bool self.default_reset_activelow = kwargs.pop("default_reset_activelow", False) # type: bool
self.default_reset_async = kwargs.pop("default_reset_async", False) # type: bool self.default_reset_async = kwargs.pop("default_reset_async", False) # type: bool
#------------------------ # ------------------------
# Info about the design # Info about the design
#------------------------ # ------------------------
self.cpuif_data_width = 0 self.cpuif_data_width = 0
# Collections of signals that were actually referenced by the design # Collections of signals that were actually referenced by the design
self.in_hier_signal_paths = set() # type: Set[str] self.in_hier_signal_paths = set() # type: Set[str]
self.out_of_hier_signals = OrderedDict() # type: OrderedDict[str, SignalNode] self.out_of_hier_signals = OrderedDict() # type: OrderedDict[str, SignalNode]
self.has_writable_msb0_fields = False self.has_writable_msb0_fields = False
self.has_buffered_write_regs = False self.has_buffered_write_regs = False
@@ -249,7 +263,7 @@ class DesignState:
self.has_paritycheck = False self.has_paritycheck = False
# Track any referenced enums # Track any referenced enums
self.user_enums = [] # type: List[Type[UserEnum]] self.user_enums = [] # type: List[Type[UserEnum]]
# Scan the design to fill in above variables # Scan the design to fill in above variables
DesignScanner(self).do_scan() DesignScanner(self).do_scan()
@@ -260,17 +274,21 @@ class DesignState:
# Assume 32-bits # Assume 32-bits
msg.warning( msg.warning(
"Addrmap being exported only contains external components. Unable to infer the CPUIF bus width. Assuming 32-bits.", "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 self.cpuif_data_width = 32
#------------------------ # ------------------------
# Min address width encloses the total size AND at least 1 useful address bit # 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 is not None:
if user_addr_width < self.addr_width: 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 self.addr_width = user_addr_width
@property @property

View File

@@ -6,12 +6,12 @@ from systemrdl.node import RegNode
from .forloop_generator import RDLForLoopGenerator from .forloop_generator import RDLForLoopGenerator
if TYPE_CHECKING: if TYPE_CHECKING:
from .exporter import RegblockExporter from .exporter import BusDecoderExporter
from systemrdl.node import AddressableNode from systemrdl.node import AddressableNode
class ExternalWriteAckGenerator(RDLForLoopGenerator): class ExternalWriteAckGenerator(RDLForLoopGenerator):
def __init__(self, exp: 'RegblockExporter') -> None: def __init__(self, exp: "BusDecoderExporter") -> None:
super().__init__() super().__init__()
self.exp = exp self.exp = exp
@@ -21,19 +21,21 @@ class ExternalWriteAckGenerator(RDLForLoopGenerator):
return "" return ""
return content return content
def enter_AddressableComponent(self, node: 'AddressableNode') -> WalkerAction: def enter_AddressableComponent(self, node: "AddressableNode") -> WalkerAction:
super().enter_AddressableComponent(node) super().enter_AddressableComponent(node)
if node.external: if node.external:
if not isinstance(node, RegNode) or node.has_sw_writable: 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.SkipDescendants
return WalkerAction.Continue return WalkerAction.Continue
class ExternalReadAckGenerator(RDLForLoopGenerator): class ExternalReadAckGenerator(RDLForLoopGenerator):
def __init__(self, exp: 'RegblockExporter') -> None: def __init__(self, exp: "BusDecoderExporter") -> None:
super().__init__() super().__init__()
self.exp = exp self.exp = exp
@@ -43,12 +45,14 @@ class ExternalReadAckGenerator(RDLForLoopGenerator):
return "" return ""
return content return content
def enter_AddressableComponent(self, node: 'AddressableNode') -> WalkerAction: def enter_AddressableComponent(self, node: "AddressableNode") -> WalkerAction:
super().enter_AddressableComponent(node) super().enter_AddressableComponent(node)
if node.external: if node.external:
if not isinstance(node, RegNode) or node.has_sw_readable: 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.SkipDescendants
return WalkerAction.Continue return WalkerAction.Continue

View File

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

View File

@@ -6,7 +6,8 @@ from ..utils import get_indexed_path
if TYPE_CHECKING: if TYPE_CHECKING:
from systemrdl.node import FieldNode from systemrdl.node import FieldNode
from ..exporter import RegblockExporter from ..exporter import BusDecoderExporter
class AssignmentPrecedence(enum.IntEnum): class AssignmentPrecedence(enum.IntEnum):
""" """
@@ -29,16 +30,15 @@ class AssignmentPrecedence(enum.IntEnum):
# Hardware access assignment groups # Hardware access assignment groups
HW_WRITE = 3000 HW_WRITE = 3000
HWSET = 2000 HWSET = 2000
HWCLR = 1000 HWCLR = 1000
COUNTER_INCR_DECR = 0 COUNTER_INCR_DECR = 0
class SVLogic: class SVLogic:
""" """
Represents a SystemVerilog logic signal Represents a SystemVerilog logic signal
""" """
def __init__(self, name: str, width: int, default_assignment: str) -> None: def __init__(self, name: str, width: int, default_assignment: str) -> None:
self.name = name self.name = name
self.width = width self.width = width
@@ -67,10 +67,10 @@ class NextStateConditional:
# Optional comment to emit next to the conditional # Optional comment to emit next to the conditional
comment = "" comment = ""
def __init__(self, exp:'RegblockExporter'): def __init__(self, exp: "BusDecoderExporter"):
self.exp = exp 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, Returns True if this conditional is relevant to the field. If so,
it instructs the FieldBuilder that Verilog for this conditional shall it instructs the FieldBuilder that Verilog for this conditional shall
@@ -78,17 +78,16 @@ class NextStateConditional:
""" """
raise NotImplementedError 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) 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 Returns the rendered conditional text
""" """
raise NotImplementedError 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 Returns a list of rendered assignment strings
This will basically always be two: This will basically always be two:
@@ -97,13 +96,14 @@ class NextStateConditional:
""" """
raise NotImplementedError 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 Return any additional combinational signals that this conditional
will assign if present. will assign if present.
""" """
return [] return []
class NextStateUnconditional(NextStateConditional): class NextStateUnconditional(NextStateConditional):
""" """
Use this class if predicate can never evaluate to false. 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 from .generators import EnumGenerator
if TYPE_CHECKING: if TYPE_CHECKING:
from ..exporter import RegblockExporter, DesignState from ..exporter import BusDecoderExporter, DesignState
class Hwif: class Hwif:
""" """
@@ -22,10 +23,7 @@ class Hwif:
- Signal inputs (except those that are promoted to the top) - Signal inputs (except those that are promoted to the top)
""" """
def __init__( def __init__(self, exp: "BusDecoderExporter", hwif_report_file: Optional[TextIO]):
self, exp: 'RegblockExporter',
hwif_report_file: Optional[TextIO]
):
self.exp = exp self.exp = exp
self.has_input_struct = False self.has_input_struct = False
@@ -41,31 +39,25 @@ class Hwif:
self._gen_out_cls = OutputStructGenerator_TypeScope self._gen_out_cls = OutputStructGenerator_TypeScope
@property @property
def ds(self) -> 'DesignState': def ds(self) -> "DesignState":
return self.exp.ds return self.exp.ds
@property @property
def top_node(self) -> AddrmapNode: def top_node(self) -> AddrmapNode:
return self.exp.ds.top_node return self.exp.ds.top_node
def get_extra_package_params(self) -> str: def get_extra_package_params(self) -> str:
lines = [""] lines = [""]
for param in self.top_node.inst.parameters: for param in self.top_node.inst.parameters:
value = param.get_value() value = param.get_value()
if isinstance(value, int): if isinstance(value, int):
lines.append( lines.append(f"localparam {param.name} = {SVInt(value)};")
f"localparam {param.name} = {SVInt(value)};"
)
elif isinstance(value, str): elif isinstance(value, str):
lines.append( lines.append(f"localparam {param.name} = {value};")
f"localparam {param.name} = {value};"
)
return "\n".join(lines) return "\n".join(lines)
def get_package_contents(self) -> str: def get_package_contents(self) -> str:
""" """
If this hwif requires a package, generate the string If this hwif requires a package, generate the string
@@ -74,8 +66,7 @@ class Hwif:
gen_in = self._gen_in_cls(self) gen_in = self._gen_in_cls(self)
structs_in = gen_in.get_struct( structs_in = gen_in.get_struct(
self.top_node, self.top_node, f"{self.top_node.inst_name}__in_t"
f"{self.top_node.inst_name}__in_t"
) )
if structs_in is not None: if structs_in is not None:
self.has_input_struct = True self.has_input_struct = True
@@ -85,8 +76,7 @@ class Hwif:
gen_out = self._gen_out_cls(self) gen_out = self._gen_out_cls(self)
structs_out = gen_out.get_struct( structs_out = gen_out.get_struct(
self.top_node, self.top_node, f"{self.top_node.inst_name}__out_t"
f"{self.top_node.inst_name}__out_t"
) )
if structs_out is not None: if structs_out is not None:
self.has_output_struct = True self.has_output_struct = True
@@ -101,7 +91,6 @@ class Hwif:
return "\n\n".join(lines) return "\n\n".join(lines)
@property @property
def port_declaration(self) -> str: def port_declaration(self) -> str:
""" """
@@ -122,9 +111,9 @@ class Hwif:
return ",\n".join(lines) return ",\n".join(lines)
#--------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# hwif utility functions # hwif utility functions
#--------------------------------------------------------------------------- # ---------------------------------------------------------------------------
def has_value_input(self, obj: Union[FieldNode, SignalNode]) -> bool: def has_value_input(self, obj: Union[FieldNode, SignalNode]) -> bool:
""" """
Returns True if the object infers an input wire in the hwif Returns True if the object infers an input wire in the hwif
@@ -137,14 +126,12 @@ class Hwif:
else: else:
raise RuntimeError raise RuntimeError
def has_value_output(self, obj: FieldNode) -> bool: def has_value_output(self, obj: FieldNode) -> bool:
""" """
Returns True if the object infers an output wire in the hwif Returns True if the object infers an output wire in the hwif
""" """
return obj.is_hw_readable return obj.is_hw_readable
def get_input_identifier( def get_input_identifier(
self, self,
obj: Union[FieldNode, SignalNode, PropertyReference], obj: Union[FieldNode, SignalNode, PropertyReference],
@@ -162,7 +149,7 @@ class Hwif:
raises an exception if obj is invalid raises an exception if obj is invalid
""" """
if isinstance(obj, FieldNode): if isinstance(obj, FieldNode):
next_value = obj.get_property('next') next_value = obj.get_property("next")
if next_value is not None: if next_value is not None:
# 'next' property replaces the inferred input signal # 'next' property replaces the inferred input signal
return self.exp.dereferencer.get_value(next_value, width) 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: def get_implied_prop_input_identifier(self, field: FieldNode, prop: str) -> str:
assert prop in { assert prop in {
'hwclr', 'hwset', 'swwe', 'swwel', 'we', 'wel', "hwclr",
'incr', 'decr', 'incrvalue', 'decrvalue' "hwset",
"swwe",
"swwel",
"we",
"wel",
"incr",
"decr",
"incrvalue",
"decrvalue",
} }
path = get_indexed_path(self.top_node, field) path = get_indexed_path(self.top_node, field)
return "hwif_in." + path + "." + prop return "hwif_in." + path + "." + prop
def get_output_identifier(self, obj: Union[FieldNode, PropertyReference]) -> str: def get_output_identifier(self, obj: Union[FieldNode, PropertyReference]) -> str:
""" """
Returns the identifier string that best represents the output object. Returns the identifier string that best represents the output object.
@@ -233,17 +227,27 @@ class Hwif:
raise RuntimeError(f"Unhandled reference to: {obj}") raise RuntimeError(f"Unhandled reference to: {obj}")
def get_implied_prop_output_identifier(
def get_implied_prop_output_identifier(self, node: Union[FieldNode, RegNode], prop: str) -> str: self, node: Union[FieldNode, RegNode], prop: str
) -> str:
if isinstance(node, FieldNode): if isinstance(node, FieldNode):
assert prop in { assert prop in {
"anded", "ored", "xored", "swmod", "swacc", "anded",
"incrthreshold", "decrthreshold", "overflow", "underflow", "ored",
"rd_swacc", "wr_swacc", "xored",
"swmod",
"swacc",
"incrthreshold",
"decrthreshold",
"overflow",
"underflow",
"rd_swacc",
"wr_swacc",
} }
elif isinstance(node, RegNode): elif isinstance(node, RegNode):
assert prop in { assert prop in {
"intr", "halt", "intr",
"halt",
} }
path = get_indexed_path(self.top_node, node) path = get_indexed_path(self.top_node, node)
return "hwif_out." + path + "." + prop return "hwif_out." + path + "." + prop

View File

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

View File

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

View File

@@ -6,12 +6,12 @@ from systemrdl.walker import WalkerAction
from .forloop_generator import RDLForLoopGenerator from .forloop_generator import RDLForLoopGenerator
if TYPE_CHECKING: if TYPE_CHECKING:
from .exporter import RegblockExporter from .exporter import BusDecoderExporter
from systemrdl.node import FieldNode, AddressableNode from systemrdl.node import FieldNode, AddressableNode
class ParityErrorReduceGenerator(RDLForLoopGenerator): class ParityErrorReduceGenerator(RDLForLoopGenerator):
def __init__(self, exp: 'RegblockExporter') -> None: def __init__(self, exp: "BusDecoderExporter") -> None:
super().__init__() super().__init__()
self.exp = exp self.exp = exp
@@ -21,14 +21,14 @@ class ParityErrorReduceGenerator(RDLForLoopGenerator):
return "" return ""
return content return content
def enter_AddressableComponent(self, node: 'AddressableNode') -> WalkerAction: def enter_AddressableComponent(self, node: "AddressableNode") -> WalkerAction:
super().enter_AddressableComponent(node) super().enter_AddressableComponent(node)
if node.external: if node.external:
return WalkerAction.SkipDescendants return WalkerAction.SkipDescendants
return WalkerAction.Continue return WalkerAction.Continue
def enter_Field(self, node: 'FieldNode') -> None: def enter_Field(self, node: "FieldNode") -> None:
if node.get_property('paritycheck') and node.implements_storage: if node.get_property("paritycheck") and node.implements_storage:
self.add_content( self.add_content(
f"err |= {self.exp.field_logic.get_parity_error_identifier(node)};" 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 from ..sv_int import SVInt
if TYPE_CHECKING: if TYPE_CHECKING:
from ..exporter import RegblockExporter from ..exporter import BusDecoderExporter
class ReadBuffering: class ReadBuffering:
def __init__(self, exp:'RegblockExporter'): def __init__(self, exp: "BusDecoderExporter"):
self.exp = exp self.exp = exp
@property @property
def top_node(self) -> 'AddrmapNode': def top_node(self) -> "AddrmapNode":
return self.exp.ds.top_node return self.exp.ds.top_node
def get_storage_struct(self) -> str: def get_storage_struct(self) -> str:
@@ -32,14 +32,16 @@ class ReadBuffering:
return s return s
def get_trigger(self, node: RegNode) -> str: def get_trigger(self, node: RegNode) -> str:
trigger = node.get_property('rbuffer_trigger') trigger = node.get_property("rbuffer_trigger")
if isinstance(trigger, RegNode): if isinstance(trigger, RegNode):
# Trigger is a register. # Trigger is a register.
# trigger when lowermost address of the register is written # trigger when lowermost address of the register is written
regwidth = trigger.get_property('regwidth') regwidth = trigger.get_property("regwidth")
accesswidth = trigger.get_property('accesswidth') accesswidth = trigger.get_property("accesswidth")
strb_prefix = self.exp.dereferencer.get_access_strobe(trigger, reduce_substrobes=False) strb_prefix = self.exp.dereferencer.get_access_strobe(
trigger, reduce_substrobes=False
)
if accesswidth < regwidth: if accesswidth < regwidth:
return f"{strb_prefix}[0] && !decoded_req_is_wr" return f"{strb_prefix}[0] && !decoded_req_is_wr"
@@ -47,7 +49,7 @@ class ReadBuffering:
return f"{strb_prefix} && !decoded_req_is_wr" return f"{strb_prefix} && !decoded_req_is_wr"
elif isinstance(trigger, SignalNode): elif isinstance(trigger, SignalNode):
s = self.exp.dereferencer.get_value(trigger) s = self.exp.dereferencer.get_value(trigger)
if trigger.get_property('activehigh'): if trigger.get_property("activehigh"):
return str(s) return str(s)
else: else:
return f"~{s}" return f"~{s}"

View File

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

View File

@@ -8,7 +8,8 @@ from ..forloop_generator import RDLForLoopGenerator, LoopBody
from ..utils import do_bitswap, do_slice from ..utils import do_bitswap, do_slice
if TYPE_CHECKING: if TYPE_CHECKING:
from ..exporter import RegblockExporter from ..exporter import BusDecoderExporter
class ReadbackLoopBody(LoopBody): class ReadbackLoopBody(LoopBody):
def __init__(self, dim: int, iterator: str, i_type: str) -> None: 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)) s = s.replace(token, str(self.n_regs))
return s return s
class ReadbackAssignmentGenerator(RDLForLoopGenerator): class ReadbackAssignmentGenerator(RDLForLoopGenerator):
i_type = "genvar" i_type = "genvar"
loop_body_cls = ReadbackLoopBody loop_body_cls = ReadbackLoopBody
def __init__(self, exp:'RegblockExporter') -> None: def __init__(self, exp: "BusDecoderExporter") -> None:
super().__init__() super().__init__()
self.exp = exp self.exp = exp
@@ -34,8 +36,8 @@ class ReadbackAssignmentGenerator(RDLForLoopGenerator):
# array. The array width is equal to the CPUIF bus width. Each entry in # array. The array width is equal to the CPUIF bus width. Each entry in
# the array represents an aligned read access. # the array represents an aligned read access.
self.current_offset = 0 self.current_offset = 0
self.start_offset_stack = [] # type: List[int] self.start_offset_stack = [] # type: List[int]
self.dim_stack = [] # type: List[int] self.dim_stack = [] # type: List[int]
@property @property
def current_offset_str(self) -> str: def current_offset_str(self) -> str:
@@ -72,22 +74,23 @@ class ReadbackAssignmentGenerator(RDLForLoopGenerator):
# Number of registers enclosed in this loop # Number of registers enclosed in this loop
n_regs = self.current_offset - start_offset n_regs = self.current_offset - start_offset
self.current_loop.n_regs = n_regs # type: ignore self.current_loop.n_regs = n_regs # type: ignore
super().pop_loop() super().pop_loop()
# Advance current scope's offset to account for loop's contents # Advance current scope's offset to account for loop's contents
self.current_offset = start_offset + n_regs * dim 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) super().enter_AddressableComponent(node)
if node.external and not isinstance(node, RegNode): if node.external and not isinstance(node, RegNode):
# External block # External block
strb = self.exp.hwif.get_external_rd_ack(node) strb = self.exp.hwif.get_external_rd_ack(node)
data = self.exp.hwif.get_external_rd_data(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 self.current_offset += 1
return WalkerAction.SkipDescendants return WalkerAction.SkipDescendants
@@ -101,12 +104,12 @@ class ReadbackAssignmentGenerator(RDLForLoopGenerator):
self.process_external_reg(node) self.process_external_reg(node)
return WalkerAction.SkipDescendants return WalkerAction.SkipDescendants
accesswidth = node.get_property('accesswidth') accesswidth = node.get_property("accesswidth")
regwidth = node.get_property('regwidth') regwidth = node.get_property("regwidth")
rbuf = node.get_property('buffer_reads') rbuf = node.get_property("buffer_reads")
if rbuf: if rbuf:
trigger = node.get_property('rbuffer_trigger') trigger = node.get_property("rbuffer_trigger")
is_own_trigger = (isinstance(trigger, RegNode) and trigger == node) is_own_trigger = isinstance(trigger, RegNode) and trigger == node
if is_own_trigger: if is_own_trigger:
if accesswidth < regwidth: if accesswidth < regwidth:
self.process_buffered_reg_with_bypass(node, regwidth, accesswidth) self.process_buffered_reg_with_bypass(node, regwidth, accesswidth)
@@ -125,18 +128,26 @@ class ReadbackAssignmentGenerator(RDLForLoopGenerator):
def process_external_reg(self, node: RegNode) -> None: def process_external_reg(self, node: RegNode) -> None:
strb = self.exp.hwif.get_external_rd_ack(node) strb = self.exp.hwif.get_external_rd_ack(node)
data = self.exp.hwif.get_external_rd_data(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: 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(
self.add_content(f"assign readback_array[{self.current_offset_str}][{regwidth-1}:0] = {strb} ? {data} : '0;") 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: 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 self.current_offset += 1
def process_reg(self, node: RegNode) -> None: def process_reg(self, node: RegNode) -> None:
current_bit = 0 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 # Fields are sorted by ascending low bit
for field in node.fields(): for field in node.fields():
if not field.is_sw_readable: if not field.is_sw_readable:
@@ -144,51 +155,67 @@ class ReadbackAssignmentGenerator(RDLForLoopGenerator):
# insert reserved assignment before this field if needed # insert reserved assignment before this field if needed
if field.low != current_bit: 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) value = self.exp.dereferencer.get_value(field)
if field.msb < field.lsb: if field.msb < field.lsb:
# Field gets bitswapped since it is in [low:high] orientation # Field gets bitswapped since it is in [low:high] orientation
value = do_bitswap(value) 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 current_bit = field.high + 1
# Insert final reserved assignment if needed # Insert final reserved assignment if needed
bus_width = self.exp.cpuif.data_width bus_width = self.exp.cpuif.data_width
if current_bit < bus_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 self.current_offset += 1
def process_buffered_reg(
def process_buffered_reg(self, node: RegNode, regwidth: int, accesswidth: int) -> None: self, node: RegNode, regwidth: int, accesswidth: int
) -> None:
rbuf = self.exp.read_buffering.get_rbuf_data(node) rbuf = self.exp.read_buffering.get_rbuf_data(node)
if accesswidth < regwidth: if accesswidth < regwidth:
# Is wide reg # Is wide reg
n_subwords = regwidth // accesswidth 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): for i in range(n_subwords):
rd_strb = f"({astrb}[{i}] && !decoded_req_is_wr)" rd_strb = f"({astrb}[{i}] && !decoded_req_is_wr)"
bslice = f"[{(i + 1) * accesswidth - 1}:{i*accesswidth}]" 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.add_content(
f"assign readback_array[{self.current_offset_str}] = {rd_strb} ? {rbuf}{bslice} : '0;"
)
self.current_offset += 1 self.current_offset += 1
else: else:
# Is regular reg # Is regular reg
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)"
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 bus_width = self.exp.cpuif.data_width
if regwidth < bus_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 self.current_offset += 1
def process_buffered_reg_with_bypass(
def process_buffered_reg_with_bypass(self, node: RegNode, regwidth: int, accesswidth: int) -> None: self, node: RegNode, regwidth: int, accesswidth: int
) -> None:
""" """
Special case for a buffered register when the register is its own trigger. Special case for a buffered register when the register is its own trigger.
First sub-word shall bypass the read buffer and assign directly. First sub-word shall bypass the read buffer and assign directly.
@@ -210,7 +237,9 @@ class ReadbackAssignmentGenerator(RDLForLoopGenerator):
if bidx < field.low: if bidx < field.low:
# insert padding before # 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: if field.high >= accesswidth:
# field gets truncated # field gets truncated
@@ -225,11 +254,17 @@ class ReadbackAssignmentGenerator(RDLForLoopGenerator):
f_low = field.width - 1 - f_low f_low = field.width - 1 - f_low
f_high = field.width - 1 - f_high f_high = field.width - 1 - f_high
f_low, f_high = f_high, f_low 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: 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 bidx = accesswidth
else: else:
# field fits in subword # field fits in subword
@@ -237,12 +272,16 @@ class ReadbackAssignmentGenerator(RDLForLoopGenerator):
if field.msb < field.lsb: if field.msb < field.lsb:
# Field gets bitswapped since it is in [low:high] orientation # Field gets bitswapped since it is in [low:high] orientation
value = do_bitswap(value) 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 bidx = field.high + 1
# pad up remainder of subword # pad up remainder of subword
if bidx < accesswidth: 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 self.current_offset += 1
# Assign remainder of subwords from read buffer # Assign remainder of subwords from read buffer
@@ -250,29 +289,35 @@ class ReadbackAssignmentGenerator(RDLForLoopGenerator):
rbuf = self.exp.read_buffering.get_rbuf_data(node) rbuf = self.exp.read_buffering.get_rbuf_data(node)
for i in range(1, n_subwords): for i in range(1, n_subwords):
rd_strb = f"({astrb}[{i}] && !decoded_req_is_wr)" rd_strb = f"({astrb}[{i}] && !decoded_req_is_wr)"
bslice = f"[{(i + 1) * accesswidth - 1}:{i*accesswidth}]" 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.add_content(
f"assign readback_array[{self.current_offset_str}] = {rd_strb} ? {rbuf}{bslice} : '0;"
)
self.current_offset += 1 self.current_offset += 1
def process_wide_reg(self, node: RegNode, accesswidth: int) -> None: def process_wide_reg(self, node: RegNode, accesswidth: int) -> None:
bus_width = self.exp.cpuif.data_width bus_width = self.exp.cpuif.data_width
subword_idx = 0 subword_idx = 0
current_bit = 0 # Bit-offset within the wide register 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 # Fields are sorted by ascending low bit
for field in node.fields(): for field in node.fields():
if not field.is_sw_readable: if not field.is_sw_readable:
continue continue
# insert zero assignment before this field if needed # 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 # field does not start in this subword
if current_bit > accesswidth * subword_idx: if current_bit > accesswidth * subword_idx:
# current subword had content. Assign remainder # current subword had content. Assign remainder
low = current_bit % accesswidth low = current_bit % accesswidth
high = bus_width - 1 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 self.current_offset += 1
# Advance to subword that contains the start of the field # 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 # assign zero up to start of this field
low = current_bit % accesswidth low = current_bit % accesswidth
high = (field.low % accesswidth) - 1 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 current_bit = field.low
# Assign field # Assign field
# loop until the entire field's assignments have been generated # loop until the entire field's assignments have been generated
field_pos = field.low field_pos = field.low
while current_bit <= field.high: while current_bit <= field.high:
# Assign the field # Assign the field
rd_strb = f"({access_strb}[{subword_idx}] && !decoded_req_is_wr)" 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 # entire field fits into this subword
low = field.low - accesswidth * subword_idx low = field.low - accesswidth * subword_idx
high = field.high - 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 # Field gets bitswapped since it is in [low:high] orientation
value = do_bitswap(value) 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 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 # Field ends at the subword boundary
subword_idx += 1 subword_idx += 1
self.current_offset += 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 # only a subset of the field can fit into this subword
# high end gets truncated # high end gets truncated
@@ -330,11 +380,19 @@ class ReadbackAssignmentGenerator(RDLForLoopGenerator):
f_high = field.width - 1 - f_high f_high = field.width - 1 - f_high
f_low, f_high = f_high, f_low 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: 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 # advance to the next subword
subword_idx += 1 subword_idx += 1
@@ -360,14 +418,22 @@ class ReadbackAssignmentGenerator(RDLForLoopGenerator):
f_high = field.width - 1 - f_high f_high = field.width - 1 - f_high
f_low, f_high = f_high, f_low 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: 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 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 # Field ends at the subword boundary
subword_idx += 1 subword_idx += 1
self.current_offset += 1 self.current_offset += 1
@@ -377,5 +443,7 @@ class ReadbackAssignmentGenerator(RDLForLoopGenerator):
# current subword had content. Assign remainder # current subword had content. Assign remainder
low = current_bit % accesswidth low = current_bit % accesswidth
high = bus_width - 1 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 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. 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.ds = ds
self.msg = self.top_node.env.msg self.msg = self.top_node.env.msg
@property @property
def top_node(self) -> 'AddrmapNode': def top_node(self) -> "AddrmapNode":
return self.ds.top_node return self.ds.top_node
def _get_out_of_hier_field_reset(self) -> None: def _get_out_of_hier_field_reset(self) -> None:
@@ -28,7 +29,7 @@ class DesignScanner(RDLListener):
current_node = self.top_node.parent current_node = self.top_node.parent
while current_node is not None: while current_node is not None:
for signal in current_node.signals(): for signal in current_node.signals():
if signal.get_property('field_reset'): if signal.get_property("field_reset"):
path = signal.get_path() path = signal.get_path()
self.ds.out_of_hier_signals[path] = signal self.ds.out_of_hier_signals[path] = signal
return return
@@ -50,19 +51,19 @@ class DesignScanner(RDLListener):
# Ensure addrmap is not a bridge. This concept does not make sense for # Ensure addrmap is not a bridge. This concept does not make sense for
# terminal components. # terminal components.
if self.top_node.get_property('bridge'): if self.top_node.get_property("bridge"):
self.msg.error( self.msg.error(
"Regblock generator does not support exporting bridge address maps", "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) self.top_node.inst.property_src_ref.get(
"bridge", self.top_node.inst.inst_src_ref
),
) )
RDLWalker().walk(self.top_node, self) RDLWalker().walk(self.top_node, self)
if self.msg.had_error: if self.msg.had_error:
self.msg.fatal( self.msg.fatal("Unable to export due to previous errors")
"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): if node.external and (node != self.top_node):
# Do not inspect external components. None of my business # Do not inspect external components. None of my business
return WalkerAction.SkipDescendants return WalkerAction.SkipDescendants
@@ -84,36 +85,42 @@ class DesignScanner(RDLListener):
return WalkerAction.Continue 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: if node.external and node != self.top_node:
self.ds.has_external_addressable = True self.ds.has_external_addressable = True
if not isinstance(node, RegNode): if not isinstance(node, RegNode):
self.ds.has_external_block = True 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 # 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.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_write_regs = self.ds.has_buffered_write_regs or bool(
self.ds.has_buffered_read_regs = self.ds.has_buffered_read_regs or bool(node.get_property('buffer_reads')) 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: def enter_Signal(self, node: "SignalNode") -> None:
if node.get_property('field_reset'): if node.get_property("field_reset"):
path = node.get_path() path = node.get_path()
self.ds.in_hier_signal_paths.add(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): if node.is_sw_writable and (node.msb < node.lsb):
self.ds.has_writable_msb0_fields = True 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 self.ds.has_paritycheck = True
if node.get_property('reset') is None: if node.get_property("reset") is None:
self.msg.warning( self.msg.warning(
f"Field '{node.inst_name}' includes parity check logic, but " f"Field '{node.inst_name}' includes parity check logic, but "
"its reset value was not defined. Will result in an undefined " "its reset value was not defined. Will result in an undefined "
"value on the module's 'parity_error' output.", "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,33 +10,33 @@ from .utils import roundup_pow2, is_pow2
from .utils import ref_is_internal from .utils import ref_is_internal
if TYPE_CHECKING: if TYPE_CHECKING:
from .exporter import RegblockExporter from .exporter import BusDecoderExporter
class DesignValidator(RDLListener): class DesignValidator(RDLListener):
""" """
Performs additional rule-checks on the design that check for limitations Performs additional rule-checks on the design that check for limitations
imposed by this exporter. imposed by this exporter.
""" """
def __init__(self, exp:'RegblockExporter') -> None:
def __init__(self, exp: "BusDecoderExporter") -> None:
self.exp = exp self.exp = exp
self.ds = exp.ds self.ds = exp.ds
self.msg = self.top_node.env.msg self.msg = self.top_node.env.msg
self._contains_external_block_stack = [] # type: List[bool] self._contains_external_block_stack = [] # type: List[bool]
self.contains_external_block = False self.contains_external_block = False
@property @property
def top_node(self) -> 'AddrmapNode': def top_node(self) -> "AddrmapNode":
return self.exp.ds.top_node return self.exp.ds.top_node
def do_validate(self) -> None: def do_validate(self) -> None:
RDLWalker().walk(self.top_node, self) RDLWalker().walk(self.top_node, self)
if self.msg.had_error: if self.msg.had_error:
self.msg.fatal( self.msg.fatal("Unable to export due to previous errors")
"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): if node.external and (node != self.top_node):
# Do not inspect external components. None of my business # Do not inspect external components. None of my business
return WalkerAction.SkipDescendants return WalkerAction.SkipDescendants
@@ -49,39 +49,41 @@ class DesignValidator(RDLListener):
if isinstance(value, PropertyReference): if isinstance(value, PropertyReference):
src_ref = value.src_ref src_ref = value.src_ref
else: 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( self.msg.error(
"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.",
src_ref src_ref,
) )
return None 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, # If encountering a CPUIF reset that is nested within the register model,
# warn that it will be ignored. # warn that it will be ignored.
# Only cpuif resets in the top-level node or above will be honored # 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( self.msg.warning(
"Only cpuif_reset signals that are instantiated in the top-level " "Only cpuif_reset signals that are instantiated in the top-level "
"addrmap or above will be honored. Any cpuif_reset signals nested " "addrmap or above will be honored. Any cpuif_reset signals nested "
"within children of the addrmap being exported will be ignored.", "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 # All registers must be aligned to the internal data bus width
alignment = self.exp.cpuif.data_width_bytes alignment = self.exp.cpuif.data_width_bytes
if (node.raw_address_offset % alignment) != 0: if (node.raw_address_offset % alignment) != 0:
self.msg.error( self.msg.error(
"Unaligned registers are not supported. Address offset of " "Unaligned registers are not supported. Address offset of "
f"instance '{node.inst_name}' must be a multiple of {alignment}", 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 if node.is_array and (node.array_stride % alignment) != 0: # type: ignore # is_array implies stride is not none
self.msg.error( self.msg.error(
"Unaligned registers are not supported. Address stride of " "Unaligned registers are not supported. Address stride of "
f"instance array '{node.inst_name}' must be a multiple of {alignment}", 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): if not isinstance(node, RegNode):
@@ -99,49 +101,49 @@ class DesignValidator(RDLListener):
self._check_sharedextbus(node) self._check_sharedextbus(node)
def _check_sharedextbus(self, node: Union[RegfileNode, AddrmapNode]) -> None: def _check_sharedextbus(self, node: Union[RegfileNode, AddrmapNode]) -> None:
if node.get_property('sharedextbus'): if node.get_property("sharedextbus"):
self.msg.error( self.msg.error(
"This exporter does not support enabling the 'sharedextbus' property yet.", "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 of wide registers must be consistent within the register block
accesswidth = node.get_property('accesswidth') accesswidth = node.get_property("accesswidth")
regwidth = node.get_property('regwidth') regwidth = node.get_property("regwidth")
if accesswidth < regwidth: if accesswidth < regwidth:
# register is 'wide' # register is 'wide'
if accesswidth != self.exp.cpuif.data_width: if accesswidth != self.exp.cpuif.data_width:
self.msg.error( self.msg.error(
f"Multi-word registers that have an accesswidth ({accesswidth}) " 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.", 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:
def enter_Field(self, node: 'FieldNode') -> None: parent_accesswidth = node.parent.get_property("accesswidth")
parent_accesswidth = node.parent.get_property('accesswidth') parent_regwidth = node.parent.get_property("regwidth")
parent_regwidth = node.parent.get_property('regwidth') if (parent_accesswidth < parent_regwidth) and (
if ( node.lsb // parent_accesswidth
(parent_accesswidth < parent_regwidth) ) != (node.msb // parent_accesswidth):
and (node.lsb // parent_accesswidth) != (node.msb // parent_accesswidth)
):
# field spans multiple sub-words # 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 # ... and is writable without the protection of double-buffering
# Enforce 10.6.1-f # Enforce 10.6.1-f
self.msg.error( self.msg.error(
f"Software-writable field '{node.inst_name}' shall not span" f"Software-writable field '{node.inst_name}' shall not span"
" multiple software-accessible subwords. Consider enabling" " multiple software-accessible subwords. Consider enabling"
" write double-buffering.\n" " write double-buffering.\n"
"For more details, see: https://peakrdl-regblock.readthedocs.io/en/latest/udps/write_buffering.html", "For more details, see: https://peakrdl-busdecoder.readthedocs.io/en/latest/udps/write_buffering.html",
node.inst.inst_src_ref 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 # ... is modified by an onread action without the atomicity of read buffering
# Enforce 10.6.1-f # Enforce 10.6.1-f
self.msg.error( self.msg.error(
@@ -149,8 +151,8 @@ class DesignValidator(RDLListener):
" subwords and is modified on-read, making it impossible to" " subwords and is modified on-read, making it impossible to"
" access its value correctly. Consider enabling read" " access its value correctly. Consider enabling read"
" double-buffering. \n" " double-buffering. \n"
"For more details, see: https://peakrdl-regblock.readthedocs.io/en/latest/udps/read_buffering.html", "For more details, see: https://peakrdl-busdecoder.readthedocs.io/en/latest/udps/read_buffering.html",
node.inst.inst_src_ref node.inst.inst_src_ref,
) )
# Check for unsynthesizable reset # Check for unsynthesizable reset
@@ -166,10 +168,9 @@ class DesignValidator(RDLListener):
if is_async_reset: if is_async_reset:
self.msg.error( self.msg.error(
"A field that uses an asynchronous reset cannot use a dynamic reset value. This is not synthesizable.", "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: def exit_AddressableComponent(self, node: AddressableNode) -> None:
if not isinstance(node, RegNode): if not isinstance(node, RegNode):
# Exiting block-like node # Exiting block-like node
@@ -194,14 +195,14 @@ class DesignValidator(RDLListener):
if (node.raw_address_offset % req_align) != 0: if (node.raw_address_offset % req_align) != 0:
self.msg.error( 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"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}.", f"This is required by the busdecoder exporter if a component {err_suffix}.",
node.inst.inst_src_ref node.inst.inst_src_ref,
) )
if node.is_array: if node.is_array:
assert node.array_stride is not None assert node.array_stride is not None
if not is_pow2(node.array_stride): if not is_pow2(node.array_stride):
self.msg.error( self.msg.error(
f"Address stride of instance array '{node.inst_name}' is not a power of 2" 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}.", f"This is required by the busdecoder exporter if a component {err_suffix}.",
node.inst.inst_src_ref node.inst.inst_src_ref,
) )

View File

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

View File

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

View File

@@ -104,8 +104,8 @@ Each testcase group has its own folder and contains the following:
`test_*/__init__.py` `test_*/__init__.py`
: Empty file required for test discovery. : Empty file required for test discovery.
`test_*/regblock.rdl` `test_*/busdecoder.rdl`
: Testcase RDL file. Testcase infrastructure will automatically compile this and generate the regblock output SystemVerilog. : Testcase RDL file. Testcase infrastructure will automatically compile this and generate the busdecoder output SystemVerilog.
`test_*/tb_template.sv` `test_*/tb_template.sv`
: Jinja template that defines the testcase-specific sequence. : Jinja template that defines the testcase-specific sequence.
@@ -116,4 +116,4 @@ Each testcase group has its own folder and contains the following:
## Parameterization ## 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 stub: run the testcase using a no-op simulator stub
skip: skip all the simulation tests skip: skip all the simulation tests
auto: choose the best simulator based on what is installed auto: choose the best simulator based on what is installed
""" """,
) )
parser.addoption( parser.addoption(
@@ -20,19 +20,18 @@ def pytest_addoption(parser):
Launch sim tool in GUI mode Launch sim tool in GUI mode
Only use this option when running a single test Only use this option when running a single test
""" """,
) )
parser.addoption( parser.addoption(
"--rerun", "--rerun",
default=False, default=False,
action="store_true", action="store_true",
help=""", 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. Useful if hand-editing a testcase interactively.
""" """,
) )
parser.addoption( parser.addoption(
@@ -44,5 +43,5 @@ def pytest_addoption(parser):
skip: skip all the simulation tests skip: skip all the simulation tests
auto: choose the best tool based on what is installed auto: choose the best tool based on what is installed
""" """,
) )

View File

@@ -9,8 +9,8 @@ import pathlib
import pytest import pytest
from systemrdl import RDLCompiler from systemrdl import RDLCompiler
from peakrdl_regblock import RegblockExporter from peakrdl_busdecoder import BusDecoderExporter
from peakrdl_regblock.udps import ALL_UDPS from peakrdl_busdecoder.udps import ALL_UDPS
from .cpuifs.base import CpuifTestMode from .cpuifs.base import CpuifTestMode
from .cpuifs.apb4 import APB4 from .cpuifs.apb4 import APB4
@@ -20,17 +20,17 @@ class BaseTestCase(unittest.TestCase):
#: Path to the testcase's RDL file. #: Path to the testcase's RDL file.
#: Relative to the testcase's dir. If unset, the first RDL file found in the #: Relative to the testcase's dir. If unset, the first RDL file found in the
#: testcase dir will be used #: testcase dir will be used
rdl_file = None # type: Optional[str] rdl_file = None # type: Optional[str]
#: RDL type name to elaborate. If unset, compiler will automatically choose #: RDL type name to elaborate. If unset, compiler will automatically choose
#: the top. #: the top.
rdl_elab_target = None # type: Optional[str] rdl_elab_target = None # type: Optional[str]
#: Parameters to pass into RDL elaboration #: Parameters to pass into RDL elaboration
rdl_elab_params = {} rdl_elab_params = {}
#: Define what CPUIF to use for this testcase #: Define what CPUIF to use for this testcase
cpuif = APB4() # type: CpuifTestMode cpuif = APB4() # type: CpuifTestMode
# Other exporter args: # Other exporter args:
retime_read_fanin = False retime_read_fanin = False
@@ -41,9 +41,9 @@ class BaseTestCase(unittest.TestCase):
default_reset_async = False default_reset_async = False
#: this gets auto-loaded via the _load_request autouse fixture #: this gets auto-loaded via the _load_request autouse fixture
request = None # type: pytest.FixtureRequest request = None # type: pytest.FixtureRequest
exporter = RegblockExporter() exporter = BusDecoderExporter()
@pytest.fixture(autouse=True) @pytest.fixture(autouse=True)
def _load_request(self, request): def _load_request(self, request):
@@ -72,16 +72,15 @@ class BaseTestCase(unittest.TestCase):
""" """
path = os.path.join(self.get_run_dir(), "params.txt") 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(): for k, v in self.__class__.__dict__.items():
if k.startswith("_") or callable(v): if k.startswith("_") or callable(v):
continue continue
f.write(f"{k}: {repr(v)}\n") f.write(f"{k}: {repr(v)}\n")
def export_busdecoder(self):
def export_regblock(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() this_dir = self.get_testcase_dir()
@@ -97,17 +96,17 @@ class BaseTestCase(unittest.TestCase):
for udp in ALL_UDPS: for udp in ALL_UDPS:
rdlc.register_udp(udp) rdlc.register_udp(udp)
# ... including the definition # ... 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(udp_file)
rdlc.compile_file(rdl_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( self.exporter.export(
root, root,
self.get_run_dir(), self.get_run_dir(),
module_name="regblock", module_name="busdecoder",
package_name="regblock_pkg", package_name="busdecoder_pkg",
cpuif_cls=self.cpuif.cpuif_cls, cpuif_cls=self.cpuif.cpuif_cls,
retime_read_fanin=self.retime_read_fanin, retime_read_fanin=self.retime_read_fanin,
retime_read_response=self.retime_read_response, retime_read_response=self.retime_read_response,
@@ -137,4 +136,4 @@ class BaseTestCase(unittest.TestCase):
self._write_params() self._write_params()
# Convert testcase RDL file --> SV # Convert testcase RDL file --> SV
self.export_regblock() self.export_busdecoder()

View File

@@ -1,6 +1,7 @@
from ..base import CpuifTestMode 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): class APB3(CpuifTestMode):
cpuif_cls = APB3_Cpuif cpuif_cls = APB3_Cpuif
@@ -13,6 +14,7 @@ class APB3(CpuifTestMode):
] ]
tb_template = "tb_inst.sv" tb_template = "tb_inst.sv"
class FlatAPB3(APB3): class FlatAPB3(APB3):
cpuif_cls = APB3_Cpuif_flattened cpuif_cls = APB3_Cpuif_flattened
rtl_files = [] rtl_files = []

View File

@@ -1,6 +1,7 @@
from ..base import CpuifTestMode 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): class APB4(CpuifTestMode):
cpuif_cls = APB4_Cpuif cpuif_cls = APB4_Cpuif
@@ -13,6 +14,7 @@ class APB4(CpuifTestMode):
] ]
tb_template = "tb_inst.sv" tb_template = "tb_inst.sv"
class FlatAPB4(APB4): class FlatAPB4(APB4):
cpuif_cls = APB4_Cpuif_flattened cpuif_cls = APB4_Cpuif_flattened
rtl_files = [] rtl_files = []

View File

@@ -1,6 +1,7 @@
from ..base import CpuifTestMode 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): class Avalon(CpuifTestMode):
cpuif_cls = Avalon_Cpuif cpuif_cls = Avalon_Cpuif
@@ -13,6 +14,7 @@ class Avalon(CpuifTestMode):
] ]
tb_template = "tb_inst.sv" tb_template = "tb_inst.sv"
class FlatAvalon(Avalon): class FlatAvalon(Avalon):
cpuif_cls = Avalon_Cpuif_flattened cpuif_cls = Avalon_Cpuif_flattened
rtl_files = [] rtl_files = []

View File

@@ -1,6 +1,7 @@
from ..base import CpuifTestMode 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): class AXI4Lite(CpuifTestMode):
cpuif_cls = AXI4Lite_Cpuif cpuif_cls = AXI4Lite_Cpuif
@@ -13,6 +14,7 @@ class AXI4Lite(CpuifTestMode):
] ]
tb_template = "tb_inst.sv" tb_template = "tb_inst.sv"
class FlatAXI4Lite(AXI4Lite): class FlatAXI4Lite(AXI4Lite):
cpuif_cls = AXI4Lite_Cpuif_flattened cpuif_cls = AXI4Lite_Cpuif_flattened
rtl_files = [] rtl_files = []

View File

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

View File

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

View File

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

View File

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

View File

@@ -25,7 +25,7 @@ set_msg_config -severity {CRITICAL WARNING} -new_severity "ERROR"
set_part [lindex [get_parts] 0] set_part [lindex [get_parts] 0]
read_verilog -sv $files read_verilog -sv $files
read_xdc $this_dir/constr.xdc 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 #write_checkpoint -force synth.dcp

View File

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

View File

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

View File

@@ -7,16 +7,16 @@
##1; ##1;
// check enum values // check enum values
assert(regblock_pkg::top__my_enum__val_1 == 'd3); assert(busdecoder_pkg::top__my_enum__val_1 == 'd3);
assert(regblock_pkg::top__my_enum__val_2 == 'd4); assert(busdecoder_pkg::top__my_enum__val_2 == 'd4);
// check initial conditions // 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 // 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 %} {% endblock %}

View File

@@ -296,8 +296,8 @@
// Check register struct bit-order // Check register struct bit-order
repeat(32) begin repeat(32) begin
regblock_pkg::top__my_reg_alt__external__fields__in_t fields_in; busdecoder_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__out_t fields_out;
fields_in = $urandom(); fields_in = $urandom();
fields_out = $urandom(); fields_out = $urandom();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -5,6 +5,7 @@ from systemrdl.messages import RDLCompileError
from ..lib.base_testcase import BaseTestCase from ..lib.base_testcase import BaseTestCase
class TestValidationErrors(BaseTestCase): class TestValidationErrors(BaseTestCase):
def setUp(self) -> None: def setUp(self) -> None:
# Stub usual pre-test setup # Stub usual pre-test setup
@@ -19,11 +20,10 @@ class TestValidationErrors(BaseTestCase):
f = io.StringIO() f = io.StringIO()
with contextlib.redirect_stderr(f): with contextlib.redirect_stderr(f):
with self.assertRaises(RDLCompileError): with self.assertRaises(RDLCompileError):
self.export_regblock() self.export_busdecoder()
stderr = f.getvalue() stderr = f.getvalue()
self.assertRegex(stderr, err_regex) self.assertRegex(stderr, err_regex)
def test_unaligned_reg(self) -> None: def test_unaligned_reg(self) -> None:
self.assert_validate_error( self.assert_validate_error(
"unaligned_reg.rdl", "unaligned_reg.rdl",
@@ -39,7 +39,7 @@ class TestValidationErrors(BaseTestCase):
def test_bad_external_ref(self) -> None: def test_bad_external_ref(self) -> None:
self.assert_validate_error( self.assert_validate_error(
"external_ref.rdl", "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: def test_sharedextbus_not_supported(self) -> None:
@@ -51,7 +51,7 @@ class TestValidationErrors(BaseTestCase):
def test_inconsistent_accesswidth(self) -> None: def test_inconsistent_accesswidth(self) -> None:
self.assert_validate_error( self.assert_validate_error(
"inconsistent_accesswidth.rdl", "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: def test_unbuffered_wide_w_fields(self) -> None:
@@ -117,5 +117,5 @@ class TestValidationErrors(BaseTestCase):
def test_signed_enum(self) -> None: def test_signed_enum(self) -> None:
self.assert_validate_error( self.assert_validate_error(
"signed_enum.rdl", "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.",
) )