regblock -> busdecoder
This commit is contained in:
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -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.
|
||||||
|
|||||||
2
.github/ISSUE_TEMPLATE/feature_request.md
vendored
2
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -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.
|
||||||
|
|||||||
2
.github/pull_request_template.md
vendored
2
.github/pull_request_template.md
vendored
@@ -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.
|
||||||
|
|||||||
6
.github/workflows/build.yml
vendored
6
.github/workflows/build.yml
vendored
@@ -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:
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
recursive-include src/peakrdl_regblock *.sv
|
recursive-include src/peakrdl_busdecoder *.sv
|
||||||
prune tests
|
prune tests
|
||||||
|
|||||||
12
README.md
12
README.md
@@ -1,12 +1,12 @@
|
|||||||
[](http://peakrdl-regblock.readthedocs.io)
|
[](http://peakrdl-busdecoder.readthedocs.io)
|
||||||
[](https://github.com/SystemRDL/PeakRDL-regblock/actions?query=workflow%3Abuild+branch%3Amain)
|
[](https://github.com/SystemRDL/PeakRDL-busdecoder/actions?query=workflow%3Abuild+branch%3Amain)
|
||||||
[](https://coveralls.io/github/SystemRDL/PeakRDL-regblock?branch=main)
|
[](https://coveralls.io/github/SystemRDL/PeakRDL-busdecoder?branch=main)
|
||||||
[](https://pypi.org/project/peakrdl-regblock)
|
[](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
|
||||||
|
|||||||
14
docs/api.rst
14
docs/api.rst
@@ -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
|
||||||
|
|||||||
19
docs/conf.py
19
docs/conf.py
@@ -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,
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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`
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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`.
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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>`_
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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?
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
--------------------------
|
--------------------------
|
||||||
|
|||||||
@@ -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
|
||||||
-----------
|
-----------
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
^^^^^^^
|
^^^^^^^
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
from .__about__ import __version__
|
from .__about__ import __version__
|
||||||
|
|
||||||
from .exporter import RegblockExporter
|
from .exporter import BusDecoderExporter
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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)"
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 %} #(
|
||||||
|
|||||||
@@ -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}};
|
||||||
|
|
||||||
|
|||||||
@@ -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)};"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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}"
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 =
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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
|
||||||
"""
|
""",
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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 = []
|
||||||
|
|||||||
@@ -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 = []
|
||||||
|
|||||||
@@ -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 = []
|
||||||
|
|||||||
@@ -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 = []
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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 %}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 %}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|
||||||
|
|||||||
@@ -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 %}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
addrmap regblock {
|
addrmap busdecoder {
|
||||||
default sw=rw;
|
default sw=rw;
|
||||||
default hw=r;
|
default hw=r;
|
||||||
|
|
||||||
|
|||||||
@@ -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 %}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
addrmap regblock {
|
addrmap busdecoder {
|
||||||
default sw=rw;
|
default sw=rw;
|
||||||
default hw=r;
|
default hw=r;
|
||||||
|
|
||||||
|
|||||||
@@ -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()
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -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.",
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user