Initial Commit - Forked from PeakRDL-regblock @ a440cc19769069be831d267505da4f3789a26695
20
docs/Makefile
Normal file
@@ -0,0 +1,20 @@
|
||||
# Minimal makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line, and also
|
||||
# from the environment for the first two.
|
||||
SPHINXOPTS ?=
|
||||
SPHINXBUILD ?= sphinx-build
|
||||
SOURCEDIR = .
|
||||
BUILDDIR = _build
|
||||
|
||||
# Put it first so that "make" without argument is like "make help".
|
||||
help:
|
||||
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
|
||||
.PHONY: help Makefile
|
||||
|
||||
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||
%: Makefile
|
||||
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
50
docs/api.rst
Normal file
@@ -0,0 +1,50 @@
|
||||
Exporter API
|
||||
============
|
||||
|
||||
If you are not using the `PeakRDL command-line tool <https://peakrdl.readthedocs.io>`_,
|
||||
you can still generate regblocks programmatically using the exporter API:
|
||||
|
||||
.. autoclass:: peakrdl_regblock.RegblockExporter
|
||||
:members:
|
||||
|
||||
Example
|
||||
-------
|
||||
Below is a simple example that demonstrates how to generate a SystemVerilog
|
||||
implementation from SystemRDL source.
|
||||
|
||||
.. code-block:: python
|
||||
:emphasize-lines: 2-4, 29-33
|
||||
|
||||
from systemrdl import RDLCompiler, RDLCompileError
|
||||
from peakrdl_regblock import RegblockExporter
|
||||
from peakrdl_regblock.cpuif.axi4lite import AXI4Lite_Cpuif
|
||||
from peakrdl_regblock.udps import ALL_UDPS
|
||||
|
||||
input_files = [
|
||||
"PATH/TO/my_register_block.rdl"
|
||||
]
|
||||
|
||||
# Create an instance of the compiler
|
||||
rdlc = RDLCompiler()
|
||||
|
||||
# Register all UDPs that 'regblock' requires
|
||||
for udp in ALL_UDPS:
|
||||
rdlc.register_udp(udp)
|
||||
|
||||
try:
|
||||
# Compile your RDL files
|
||||
for input_file in input_files:
|
||||
rdlc.compile_file(input_file)
|
||||
|
||||
# Elaborate the design
|
||||
root = rdlc.elaborate()
|
||||
except RDLCompileError:
|
||||
# A compilation error occurred. Exit with error code
|
||||
sys.exit(1)
|
||||
|
||||
# Export a SystemVerilog implementation
|
||||
exporter = RegblockExporter()
|
||||
exporter.export(
|
||||
root, "path/to/output_dir",
|
||||
cpuif_cls=AXI4Lite_Cpuif
|
||||
)
|
||||
59
docs/architecture.rst
Normal file
@@ -0,0 +1,59 @@
|
||||
Register Block Architecture
|
||||
===========================
|
||||
|
||||
The generated register block RTL is organized into several sections.
|
||||
Each section is automatically generated based on the source register model and
|
||||
is rendered into the output register block SystemVerilog RTL.
|
||||
|
||||
.. figure:: diagrams/arch.png
|
||||
|
||||
Although it is not completely necessary to know the inner workings of the
|
||||
generated RTL, it can be helpful to understand the implications of various
|
||||
exporter configuration options.
|
||||
|
||||
|
||||
CPU Interface
|
||||
-------------
|
||||
The CPU interface logic layer provides an abstraction between the
|
||||
application-specific bus protocol and the internal register file logic.
|
||||
This logic layer normalizes external CPU read & write transactions into a common
|
||||
:ref:`cpuif_protocol` that is used to interact with the register file.
|
||||
|
||||
|
||||
Address Decode
|
||||
--------------
|
||||
A common address decode operation is generated which computes individual access
|
||||
strobes for each software-accessible register in the design.
|
||||
This operation is performed completely combinationally.
|
||||
|
||||
|
||||
Field Logic
|
||||
-----------
|
||||
This layer of the register block implements the storage elements and state-change
|
||||
logic for every field in the design. Field state is updated based on address
|
||||
decode strobes from software read/write actions, as well as events from the
|
||||
hardware interface input struct.
|
||||
This section also assigns any hardware interface outputs.
|
||||
|
||||
|
||||
Readback
|
||||
--------
|
||||
The readback layer aggregates and reduces all readable registers into a single
|
||||
read response. During a read operation, the same address decode strobes are used
|
||||
to select the active register that is being accessed.
|
||||
This allows for a simple OR-reduction operation to be used to compute the read
|
||||
data response.
|
||||
|
||||
For designs with a large number of software-readable registers, an optional
|
||||
fanin re-timing stage can be enabled. This stage is automatically inserted at a
|
||||
balanced point in the read-data reduction so that fanin and logic-levels are
|
||||
optimally reduced.
|
||||
|
||||
.. figure:: diagrams/readback.png
|
||||
:width: 65%
|
||||
:align: center
|
||||
|
||||
A second optional read response retiming register can be enabled in-line with the
|
||||
path back to the CPU interface layer. This can be useful if the CPU interface protocol
|
||||
used has a fully combinational response path, and the design's complexity requires
|
||||
this path to be retimed further.
|
||||
89
docs/conf.py
Normal file
@@ -0,0 +1,89 @@
|
||||
# Configuration file for the Sphinx documentation builder.
|
||||
#
|
||||
# This file only contains a selection of the most common options. For a full
|
||||
# list see the documentation:
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html
|
||||
|
||||
# -- Path setup --------------------------------------------------------------
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#
|
||||
import os
|
||||
import sys
|
||||
sys.path.insert(0, os.path.abspath('../src/'))
|
||||
|
||||
import datetime
|
||||
|
||||
# -- Project information -----------------------------------------------------
|
||||
|
||||
project = 'PeakRDL-regblock'
|
||||
copyright = '%d, Alex Mykyta' % datetime.datetime.now().year
|
||||
author = 'Alex Mykyta'
|
||||
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = [
|
||||
'sphinx.ext.autodoc',
|
||||
'sphinx.ext.napoleon',
|
||||
"sphinxcontrib.wavedrom",
|
||||
]
|
||||
render_using_wavedrompy = True
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
# This pattern also affects html_static_path and html_extra_path.
|
||||
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
||||
|
||||
|
||||
# -- Options for HTML output -------------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
#
|
||||
html_theme = "sphinx_book_theme"
|
||||
|
||||
html_theme_options = {
|
||||
"repository_url": "https://github.com/SystemRDL/PeakRDL-regblock",
|
||||
"path_to_docs": "docs",
|
||||
"use_download_button": False,
|
||||
"use_source_button": True,
|
||||
"use_repository_button": True,
|
||||
"use_issues_button": True,
|
||||
}
|
||||
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = []
|
||||
|
||||
|
||||
rst_epilog = """
|
||||
.. |iERR| image:: /img/err.svg
|
||||
:width: 18px
|
||||
:class: no-scaled-link
|
||||
|
||||
.. |iWARN| image:: /img/warn.svg
|
||||
:width: 18px
|
||||
:class: no-scaled-link
|
||||
|
||||
.. |iOK| image:: /img/ok.svg
|
||||
:width: 18px
|
||||
:class: no-scaled-link
|
||||
|
||||
.. |NO| replace:: |iERR| Not Supported
|
||||
|
||||
.. |EX| replace:: |iWARN| Experimental
|
||||
|
||||
.. |OK| replace:: |iOK| Supported
|
||||
|
||||
"""
|
||||
45
docs/configuring.rst
Normal file
@@ -0,0 +1,45 @@
|
||||
.. _peakrdl_cfg:
|
||||
|
||||
Configuring PeakRDL-regblock
|
||||
============================
|
||||
|
||||
If using the `PeakRDL command line tool <https://peakrdl.readthedocs.io/>`_,
|
||||
some aspects of the ``regblock`` command have additional configuration options
|
||||
available via the PeakRDL TOML file.
|
||||
|
||||
All regblock-specific options are defined under the ``[regblock]`` TOML heading.
|
||||
|
||||
.. data:: cpuifs
|
||||
|
||||
Mapping of additional CPU Interface implementation classes to load.
|
||||
The mapping's key indicates the cpuif's name.
|
||||
The value is a string that describes the import path and cpuif class to
|
||||
load.
|
||||
|
||||
For example:
|
||||
|
||||
.. code-block:: toml
|
||||
|
||||
[regblock]
|
||||
cpuifs.my-cpuif-name = "my_cpuif_module:MyCPUInterfaceClass"
|
||||
|
||||
|
||||
.. data:: default_reset
|
||||
|
||||
Choose the default style of reset signal if not explicitly
|
||||
specified by the SystemRDL design. If unspecified, the default reset
|
||||
is active-high and synchronous.
|
||||
|
||||
Choice of:
|
||||
|
||||
* ``rst`` (default)
|
||||
* ``rst_n``
|
||||
* ``arst``
|
||||
* ``arst_n``
|
||||
|
||||
For example:
|
||||
|
||||
.. code-block:: toml
|
||||
|
||||
[regblock]
|
||||
default_reset = "arst"
|
||||
59
docs/cpuif/apb.rst
Normal file
@@ -0,0 +1,59 @@
|
||||
AMBA APB
|
||||
========
|
||||
|
||||
Both APB3 and APB4 standards are supported.
|
||||
|
||||
.. warning::
|
||||
Some IP vendors will incorrectly implement the address signalling
|
||||
assuming word-addresses. (that each increment of ``PADDR`` is the next word)
|
||||
|
||||
For this exporter, values on the interface's ``PADDR`` input are interpreted
|
||||
as byte-addresses. (an APB interface with 32-bit wide data increments
|
||||
``PADDR`` in steps of 4 for every word). Even though APB protocol does not
|
||||
allow for unaligned transfers, this is in accordance to the official AMBA
|
||||
specification.
|
||||
|
||||
Be sure to double-check the interpretation of your interconnect IP. A simple
|
||||
bit-shift operation can be used to correct this if necessary.
|
||||
|
||||
|
||||
APB3
|
||||
----
|
||||
|
||||
Implements the register block using an
|
||||
`AMBA 3 APB <https://developer.arm.com/documentation/ihi0024/b/Introduction/About-the-AMBA-3-APB>`_
|
||||
CPU interface.
|
||||
|
||||
The APB3 CPU interface comes in two i/o port flavors:
|
||||
|
||||
SystemVerilog Interface
|
||||
* Command line: ``--cpuif apb3``
|
||||
* Interface Definition: :download:`apb3_intf.sv <../../hdl-src/apb3_intf.sv>`
|
||||
* Class: :class:`peakrdl_regblock.cpuif.apb3.APB3_Cpuif`
|
||||
|
||||
Flattened inputs/outputs
|
||||
Flattens the interface into discrete input and output ports.
|
||||
|
||||
* Command line: ``--cpuif apb3-flat``
|
||||
* Class: :class:`peakrdl_regblock.cpuif.apb3.APB3_Cpuif_flattened`
|
||||
|
||||
|
||||
APB4
|
||||
----
|
||||
|
||||
Implements the register block using an
|
||||
`AMBA 4 APB <https://developer.arm.com/documentation/ihi0024/d/?lang=en>`_
|
||||
CPU interface.
|
||||
|
||||
The APB4 CPU interface comes in two i/o port flavors:
|
||||
|
||||
SystemVerilog Interface
|
||||
* Command line: ``--cpuif apb4``
|
||||
* Interface Definition: :download:`apb4_intf.sv <../../hdl-src/apb4_intf.sv>`
|
||||
* Class: :class:`peakrdl_regblock.cpuif.apb4.APB4_Cpuif`
|
||||
|
||||
Flattened inputs/outputs
|
||||
Flattens the interface into discrete input and output ports.
|
||||
|
||||
* Command line: ``--cpuif apb4-flat``
|
||||
* Class: :class:`peakrdl_regblock.cpuif.apb4.APB4_Cpuif_flattened`
|
||||
33
docs/cpuif/avalon.rst
Normal file
@@ -0,0 +1,33 @@
|
||||
Intel Avalon
|
||||
============
|
||||
|
||||
Implements the register block using an
|
||||
`Intel Avalon MM <https://www.intel.com/content/www/us/en/docs/programmable/683091/22-3/memory-mapped-interfaces.html>`_
|
||||
CPU interface.
|
||||
|
||||
The Avalon interface comes in two i/o port flavors:
|
||||
|
||||
SystemVerilog Interface
|
||||
* Command line: ``--cpuif avalon-mm``
|
||||
* Interface Definition: :download:`avalon_mm_intf.sv <../../hdl-src/avalon_mm_intf.sv>`
|
||||
* Class: :class:`peakrdl_regblock.cpuif.avalon.Avalon_Cpuif`
|
||||
|
||||
Flattened inputs/outputs
|
||||
Flattens the interface into discrete input and output ports.
|
||||
|
||||
* Command line: ``--cpuif avalon-mm-flat``
|
||||
* Class: :class:`peakrdl_regblock.cpuif.avalon.Avalon_Cpuif_flattened`
|
||||
|
||||
|
||||
Implementation Details
|
||||
----------------------
|
||||
This implementation of the Avalon protocol has the following features:
|
||||
|
||||
* Interface uses word addressing.
|
||||
* Supports `pipelined transfers <https://www.intel.com/content/www/us/en/docs/programmable/683091/22-3/pipelined-transfers.html>`_
|
||||
* Responses may have variable latency
|
||||
|
||||
In most cases, latency is fixed and is determined by how many retiming
|
||||
stages are enabled in your design.
|
||||
However if your design contains external components, access latency is
|
||||
not guaranteed to be uniform.
|
||||
32
docs/cpuif/axi4lite.rst
Normal file
@@ -0,0 +1,32 @@
|
||||
.. _cpuif_axi4lite:
|
||||
|
||||
AMBA AXI4-Lite
|
||||
==============
|
||||
|
||||
Implements the register block using an
|
||||
`AMBA AXI4-Lite <https://developer.arm.com/documentation/ihi0022/e/AMBA-AXI4-Lite-Interface-Specification>`_
|
||||
CPU interface.
|
||||
|
||||
The AXI4-Lite CPU interface comes in two i/o port flavors:
|
||||
|
||||
SystemVerilog Interface
|
||||
* Command line: ``--cpuif axi4-lite``
|
||||
* Interface Definition: :download:`axi4lite_intf.sv <../../hdl-src/axi4lite_intf.sv>`
|
||||
* Class: :class:`peakrdl_regblock.cpuif.axi4lite.AXI4Lite_Cpuif`
|
||||
|
||||
Flattened inputs/outputs
|
||||
Flattens the interface into discrete input and output ports.
|
||||
|
||||
* Command line: ``--cpuif axi4-lite-flat``
|
||||
* Class: :class:`peakrdl_regblock.cpuif.axi4lite.AXI4Lite_Cpuif_flattened`
|
||||
|
||||
|
||||
Pipelined Performance
|
||||
---------------------
|
||||
This implementation of the AXI4-Lite interface supports transaction pipelining
|
||||
which can significantly improve performance of back-to-back transfers.
|
||||
|
||||
In order to support transaction pipelining, the CPU interface will accept multiple
|
||||
concurrent transactions. The number of outstanding transactions allowed is automatically
|
||||
determined based on the register file pipeline depth (affected by retiming options),
|
||||
and influences the depth of the internal transaction response skid buffer.
|
||||
114
docs/cpuif/customizing.rst
Normal file
@@ -0,0 +1,114 @@
|
||||
Customizing the CPU interface
|
||||
=============================
|
||||
|
||||
Use your own existing SystemVerilog interface definition
|
||||
--------------------------------------------------------
|
||||
|
||||
This exporter comes pre-bundled with its own SystemVerilog interface declarations.
|
||||
What if you already have your own SystemVerilog interface declaration that you prefer?
|
||||
|
||||
Not a problem! As long as your interface definition is similar enough, it is easy
|
||||
to customize and existing CPUIF definition.
|
||||
|
||||
|
||||
As an example, let's use the SystemVerilog interface definition for
|
||||
:ref:`cpuif_axi4lite` that is bundled with this project. This interface uses
|
||||
the following style and naming conventions:
|
||||
|
||||
* SystemVerilog interface type name is ``axi4lite_intf``
|
||||
* Defines modports named ``master`` and ``slave``
|
||||
* Interface signals are all upper-case: ``AWREADY``, ``AWVALID``, etc...
|
||||
|
||||
Lets assume your preferred SV interface definition uses a slightly different naming convention:
|
||||
|
||||
* SystemVerilog interface type name is ``axi4_lite_interface``
|
||||
* Modports are capitalized and use suffixes ``Master_mp`` and ``Slave_mp``
|
||||
* Interface signals are all lower-case: ``awready``, ``awvalid``, etc...
|
||||
|
||||
Rather than rewriting a new CPU interface definition, you can extend and adjust the existing one:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from peakrdl_regblock.cpuif.axi4lite import AXI4Lite_Cpuif
|
||||
|
||||
class My_AXI4Lite(AXI4Lite_Cpuif):
|
||||
@property
|
||||
def port_declaration(self) -> str:
|
||||
# Override the port declaration text to use the alternate interface name and modport style
|
||||
return "axi4_lite_interface.Slave_mp s_axil"
|
||||
|
||||
def signal(self, name:str) -> str:
|
||||
# Override the signal names to be lowercase instead
|
||||
return "s_axil." + name.lower()
|
||||
|
||||
Then use your custom CPUIF during export:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
exporter = RegblockExporter()
|
||||
exporter.export(
|
||||
root, "path/to/output_dir",
|
||||
cpuif_cls=My_AXI4Lite
|
||||
)
|
||||
|
||||
|
||||
|
||||
Custom CPU Interface Protocol
|
||||
-----------------------------
|
||||
|
||||
If you require a CPU interface protocol that is not included in this project,
|
||||
you can define your own.
|
||||
|
||||
1. Create a SystemVerilog CPUIF implementation template file.
|
||||
|
||||
This contains the SystemVerilog implementation of the bus protocol. The logic
|
||||
in this shall implement a translation between your custom protocol and the
|
||||
:ref:`cpuif_protocol`.
|
||||
|
||||
Reminder that this template will be preprocessed using
|
||||
`Jinja <https://jinja.palletsprojects.com>`_, so you can use
|
||||
some templating tags to dynamically render content. See the implementations of
|
||||
existing CPU interfaces as an example.
|
||||
|
||||
2. Create a Python class that defines your CPUIF
|
||||
|
||||
Extend your class from :class:`peakrdl_regblock.cpuif.CpuifBase`.
|
||||
Define the port declaration string, and provide a reference to your template file.
|
||||
|
||||
3. Use your new CPUIF definition when exporting.
|
||||
4. If you think the CPUIF protocol is something others might find useful, let me
|
||||
know and I can add it to PeakRDL!
|
||||
|
||||
|
||||
Loading into the PeakRDL command line tool
|
||||
------------------------------------------
|
||||
There are two ways to make your custom CPUIF class visible to the
|
||||
`PeakRDL command-line tool <https://peakrdl.readthedocs.io>`_.
|
||||
|
||||
Via the PeakRDL TOML
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
The easiest way to add your cpuif is via the TOML config file. See the
|
||||
:ref:`peakrdl_cfg` section for more details.
|
||||
|
||||
Via a package's entry point definition
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
If you are publishing a collection of PeakRDL plugins as an installable Python
|
||||
package, you can advertise them to PeakRDL using an entry point.
|
||||
This advertises your custom CPUIF class to the PeakRDL-regblock tool as a plugin
|
||||
that should be loaded, and made available as a command-line option in PeakRDL.
|
||||
|
||||
.. code-block:: toml
|
||||
|
||||
[project.entry-points."peakrdl_regblock.cpuif"]
|
||||
my-cpuif = "my_package.my_module:MyCPUIF"
|
||||
|
||||
|
||||
* ``my_package``: The name of your installable Python module
|
||||
* ``peakrdl-regblock.cpuif``: This is the namespace that PeakRDL-regblock will
|
||||
search. Any cpuif plugins you create must be enclosed in this namespace in
|
||||
order to be discovered.
|
||||
* ``my_package.my_module:MyCPUIF``: This is the import path that
|
||||
points to your CPUIF class definition.
|
||||
* ``my-cpuif``: The lefthand side of the assignment is your cpuif's name. This
|
||||
text is what the end-user uses in the command line interface to select your
|
||||
CPUIF implementation.
|
||||
232
docs/cpuif/internal_protocol.rst
Normal file
@@ -0,0 +1,232 @@
|
||||
.. _cpuif_protocol:
|
||||
|
||||
Internal CPUIF Protocol
|
||||
=======================
|
||||
|
||||
Internally, the regblock generator uses a common CPU interface handshake
|
||||
protocol. This strobe-based protocol is designed to add minimal overhead to the
|
||||
regblock implementation, while also being flexible enough to support advanced
|
||||
features of a variety of bus interface standards.
|
||||
|
||||
|
||||
Signal Descriptions
|
||||
-------------------
|
||||
|
||||
Request
|
||||
^^^^^^^
|
||||
cpuif_req
|
||||
When asserted, a read or write transfer will be initiated.
|
||||
Denotes that the following signals are valid: ``cpuif_addr``,
|
||||
``cpuif_req_is_wr``, and ``cpuif_wr_data``.
|
||||
|
||||
A transfer will only initiate if the relevant stall signal is not asserted.
|
||||
If stalled, the request shall be held until accepted. A request's parameters
|
||||
(type, address, etc) shall remain static throughout the stall.
|
||||
|
||||
cpuif_addr
|
||||
Byte-address of the transfer.
|
||||
|
||||
cpuif_req_is_wr
|
||||
If ``1``, denotes that the current transfer is a write. Otherwise transfer is
|
||||
a read.
|
||||
|
||||
cpuif_wr_data
|
||||
Data to be written for the write transfer. This signal is ignored for read
|
||||
transfers.
|
||||
|
||||
cpuif_wr_biten
|
||||
Active-high bit-level write-enable strobes.
|
||||
Only asserted bit positions will change the register value during a write
|
||||
transfer.
|
||||
|
||||
cpuif_req_stall_rd
|
||||
If asserted, and the next pending request is a read operation, then the
|
||||
transfer will not be accepted until this signal is deasserted.
|
||||
|
||||
cpuif_req_stall_wr
|
||||
If asserted, and the next pending request is a write operation, then the
|
||||
transfer will not be accepted until this signal is deasserted.
|
||||
|
||||
|
||||
Read Response
|
||||
^^^^^^^^^^^^^
|
||||
cpuif_rd_ack
|
||||
Single-cycle strobe indicating a read transfer has completed.
|
||||
Qualifies that the following signals are valid: ``cpuif_rd_err`` and
|
||||
``cpuif_rd_data``
|
||||
|
||||
cpuif_rd_err
|
||||
If set, indicates that the read transaction failed and the CPUIF logic
|
||||
should return an error response if possible.
|
||||
|
||||
cpuif_rd_data
|
||||
Read data. Is sampled on the same cycle that ``cpuif_rd_ack`` is asserted.
|
||||
|
||||
Write Response
|
||||
^^^^^^^^^^^^^^
|
||||
cpuif_wr_ack
|
||||
Single-cycle strobe indicating a write transfer has completed.
|
||||
Qualifies that the ``cpuif_wr_err`` signal is valid.
|
||||
|
||||
cpuif_wr_err
|
||||
If set, indicates that the write transaction failed and the CPUIF logic
|
||||
should return an error response if possible.
|
||||
|
||||
|
||||
Transfers
|
||||
---------
|
||||
|
||||
Transfers have the following characteristics:
|
||||
|
||||
* Only one transfer can be initiated per clock-cycle. This is implicit as there
|
||||
is only one set of request signals.
|
||||
* The register block implementation shall guarantee that only one response can be
|
||||
asserted in a given clock cycle. Only one ``cpuif_*_ack`` signal can be
|
||||
asserted at a time.
|
||||
* Responses shall arrive in the same order as their corresponding request was
|
||||
dispatched.
|
||||
|
||||
|
||||
Basic Transfer
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
Depending on the configuration of the exported register block, transfers can be
|
||||
fully combinational or they may require one or more clock cycles to complete.
|
||||
Both are valid and CPU interface logic shall be designed to anticipate either.
|
||||
|
||||
.. wavedrom::
|
||||
|
||||
{
|
||||
"signal": [
|
||||
{"name": "clk", "wave": "p...."},
|
||||
{"name": "cpuif_req", "wave": "010.."},
|
||||
{"name": "cpuif_req_is_wr", "wave": "x2x.."},
|
||||
{"name": "cpuif_addr", "wave": "x2x..", "data": ["A"]},
|
||||
{},
|
||||
{"name": "cpuif_*_ack", "wave": "010.."},
|
||||
{"name": "cpuif_*_err", "wave": "x2x.."}
|
||||
],
|
||||
"foot": {
|
||||
"text": "Zero-latency transfer"
|
||||
}
|
||||
}
|
||||
|
||||
.. wavedrom::
|
||||
|
||||
{
|
||||
"signal": [
|
||||
{"name": "clk", "wave": "p..|..."},
|
||||
{"name": "cpuif_req", "wave": "010|..."},
|
||||
{"name": "cpuif_req_is_wr", "wave": "x2x|..."},
|
||||
{"name": "cpuif_addr", "wave": "x2x|...", "data": ["A"]},
|
||||
{},
|
||||
{"name": "cpuif_*_ack", "wave": "0..|10."},
|
||||
{"name": "cpuif_*_err", "wave": "x..|2x."}
|
||||
],
|
||||
"foot": {
|
||||
"text": "Transfer with non-zero latency"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Read & Write Transactions
|
||||
-------------------------
|
||||
|
||||
Waveforms below show the timing relationship of simple read/write transactions.
|
||||
For brevity, only showing non-zero latency transfers.
|
||||
|
||||
.. wavedrom::
|
||||
|
||||
{
|
||||
"signal": [
|
||||
{"name": "clk", "wave": "p..|..."},
|
||||
{"name": "cpuif_req", "wave": "010|..."},
|
||||
{"name": "cpuif_req_is_wr", "wave": "x0x|..."},
|
||||
{"name": "cpuif_addr", "wave": "x3x|...", "data": ["A"]},
|
||||
{},
|
||||
{"name": "cpuif_rd_ack", "wave": "0..|10."},
|
||||
{"name": "cpuif_rd_err", "wave": "x..|0x."},
|
||||
{"name": "cpuif_rd_data", "wave": "x..|5x.", "data": ["D"]}
|
||||
],
|
||||
"foot": {
|
||||
"text": "Read Transaction"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.. wavedrom::
|
||||
|
||||
{
|
||||
"signal": [
|
||||
{"name": "clk", "wave": "p..|..."},
|
||||
{"name": "cpuif_req", "wave": "010|..."},
|
||||
{"name": "cpuif_req_is_wr", "wave": "x1x|..."},
|
||||
{"name": "cpuif_addr", "wave": "x3x|...", "data": ["A"]},
|
||||
{"name": "cpuif_wr_data", "wave": "x5x|...", "data": ["D"]},
|
||||
{},
|
||||
{"name": "cpuif_wr_ack", "wave": "0..|10."},
|
||||
{"name": "cpuif_wr_err", "wave": "x..|0x."}
|
||||
],
|
||||
"foot": {
|
||||
"text": "Write Transaction"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Transaction Pipelining & Stalls
|
||||
-------------------------------
|
||||
If the CPU interface supports it, read and write operations can be pipelined.
|
||||
|
||||
.. wavedrom::
|
||||
|
||||
{
|
||||
"signal": [
|
||||
{"name": "clk", "wave": "p......"},
|
||||
{"name": "cpuif_req", "wave": "01..0.."},
|
||||
{"name": "cpuif_req_is_wr", "wave": "x0..x.."},
|
||||
{"name": "cpuif_addr", "wave": "x333x..", "data": ["A1", "A2", "A3"]},
|
||||
{},
|
||||
{"name": "cpuif_rd_ack", "wave": "0.1..0."},
|
||||
{"name": "cpuif_rd_err", "wave": "x.0..x."},
|
||||
{"name": "cpuif_rd_data", "wave": "x.555x.", "data": ["D1", "D2", "D3"]}
|
||||
]
|
||||
}
|
||||
|
||||
It is very likely that the transfer latency of a read transaction will not
|
||||
be the same as a write for a given register block configuration. Typically read
|
||||
operations will be more deeply pipelined. This latency asymmetry would create a
|
||||
hazard for response collisions.
|
||||
|
||||
In order to eliminate this hazard, additional stall signals (``cpuif_req_stall_rd``
|
||||
and ``cpuif_req_stall_wr``) are provided to delay the next incoming transfer
|
||||
request if necessary. When asserted, the CPU interface shall hold the next pending
|
||||
request until the stall is cleared.
|
||||
|
||||
For non-pipelined CPU interfaces that only allow one outstanding transaction at a time,
|
||||
these stall signals can be safely ignored.
|
||||
|
||||
In the following example, the regblock is configured such that:
|
||||
|
||||
* A read transaction takes 1 clock cycle to complete
|
||||
* A write transaction takes 0 clock cycles to complete
|
||||
|
||||
.. wavedrom::
|
||||
|
||||
{
|
||||
"signal": [
|
||||
{"name": "clk", "wave": "p......."},
|
||||
{"name": "cpuif_req", "wave": "01.....0"},
|
||||
{"name": "cpuif_req_is_wr", "wave": "x1.0.1.x"},
|
||||
{"name": "cpuif_addr", "wave": "x33443.x", "data": ["W1", "W2", "R1", "R2", "W3"]},
|
||||
{"name": "cpuif_req_stall_wr", "wave": "0...1.0."},
|
||||
{},
|
||||
{"name": "cpuif_rd_ack", "wave": "0...220.", "data": ["R1", "R2"]},
|
||||
{"name": "cpuif_wr_ack", "wave": "0220..20", "data": ["W1", "W2", "W3"]}
|
||||
]
|
||||
}
|
||||
|
||||
In the above waveform, observe that:
|
||||
|
||||
* The ``R2`` read request is not affected by the assertion of the write stall,
|
||||
since the write stall only applies to write requests.
|
||||
* The ``W3`` write request is stalled for one cycle, and is accepted once the stall is cleared.
|
||||
36
docs/cpuif/introduction.rst
Normal file
@@ -0,0 +1,36 @@
|
||||
Introduction
|
||||
============
|
||||
|
||||
The CPU interface logic layer provides an abstraction between the
|
||||
application-specific bus protocol and the internal register file logic.
|
||||
When exporting a design, you can select from a variety of popular CPU interface
|
||||
protocols. These are described in more detail in the pages that follow.
|
||||
|
||||
|
||||
Bus Width
|
||||
^^^^^^^^^
|
||||
The CPU interface bus width is automatically determined from the contents of the
|
||||
design being exported. The bus width is equal to the widest ``accesswidth``
|
||||
encountered in the design.
|
||||
|
||||
|
||||
Addressing
|
||||
^^^^^^^^^^
|
||||
|
||||
The regblock exporter will always generate its address decoding logic using local
|
||||
address offsets. The absolute address offset of your device shall be
|
||||
handled by your system interconnect, and present addresses to the regblock that
|
||||
only include the local offset.
|
||||
|
||||
For example, consider a fictional AXI4-Lite device that:
|
||||
|
||||
- Consumes 4 kB of address space (``0x000``-``0xFFF``).
|
||||
- The device is instantiated in your system at global address range ``0x30_0000 - 0x50_0FFF``.
|
||||
- After decoding transactions destined to the device, the system interconnect shall
|
||||
ensure that AxADDR values are presented to the device as relative addresses - within
|
||||
the range of ``0x000``-``0xFFF``.
|
||||
- If care is taken to align the global address offset to the size of the device,
|
||||
creating a relative address is as simple as pruning down address bits.
|
||||
|
||||
By default, the bit-width of the address bus will be the minimum size to span the contents
|
||||
of the register block. If needed, the address width can be overridden to a larger range.
|
||||
10
docs/cpuif/passthrough.rst
Normal file
@@ -0,0 +1,10 @@
|
||||
CPUIF Passthrough
|
||||
=================
|
||||
|
||||
This CPUIF mode bypasses the protocol converter stage and directly exposes the
|
||||
internal CPUIF handshake signals to the user.
|
||||
|
||||
* Command line: ``--cpuif passthrough``
|
||||
* Class: :class:`peakrdl_regblock.cpuif.passthrough.PassthroughCpuif`
|
||||
|
||||
For more details on the protocol itself, see: :ref:`cpuif_protocol`.
|
||||
10
docs/dev_notes/Alpha-Beta Versioning
Normal file
@@ -0,0 +1,10 @@
|
||||
Holy smokes this is complicated
|
||||
|
||||
Keep this exporter in Alpha/Beta for a while
|
||||
Add some text in the readme or somewhere:
|
||||
- No guarantees of correctness! This is always true with open source software,
|
||||
but even more here!
|
||||
Be sure to do your own validation before using this in production.
|
||||
- Alpha means the implementation may change drastically!
|
||||
Unlike official sem-ver, I am not making any guarantees on compatibility
|
||||
- I need your help! Validating, finding edge cases, etc...
|
||||
67
docs/dev_notes/Hierarchy-and-Indexing
Normal file
@@ -0,0 +1,67 @@
|
||||
--------------------------------------------------------------------------------
|
||||
Preserve Hierarchy
|
||||
--------------------------------------------------------------------------------
|
||||
I *reaaaally* want to be able to make deferred RDL parameters a reality in the
|
||||
future. (https://github.com/SystemRDL/systemrdl-compiler/issues/58)
|
||||
|
||||
Proactively design templates to retain "real" hierarchy. This means:
|
||||
- Do not flatten/unroll signals. Use SV structs & arrays
|
||||
- Do not flatten/unroll logic. Use nested for loops
|
||||
|
||||
Sticking to the above should make adding parameter support somewhat less painful.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
Indexing & references
|
||||
--------------------------------------------------------------------------------
|
||||
Need to define a consistent scheme for referencing hierarchical elements.
|
||||
|
||||
When inside a nesting of for loops, and array indexes are intended to increment,
|
||||
always use an incrementing indexing scheme when generating iterators:
|
||||
i0, i1, i2, i3, ... i9, i10, i11, etc...
|
||||
For example:
|
||||
access_strb.2d_array[i0][i1].array[i3]
|
||||
|
||||
Sometimes, an RDL input may create the need to reference an element with
|
||||
partially constant indexes.
|
||||
For example, given this RDL:
|
||||
|
||||
addrmap top {
|
||||
regfile {
|
||||
reg {
|
||||
field {} f1;
|
||||
} x[8];
|
||||
|
||||
reg {
|
||||
field {} f2;
|
||||
} y;
|
||||
|
||||
y.f2->next = x[3].f1;
|
||||
|
||||
} rf_loop[16];
|
||||
};
|
||||
|
||||
The 'next' assignment will have a reference that has the following hierarchical
|
||||
path:
|
||||
top.rf_loop[].x[3].f1
|
||||
| |
|
||||
| +--- known index
|
||||
+--- unknown index
|
||||
|
||||
It is provable that any RDL references will always follow these truths:
|
||||
- a reference may have a mix of known/unknown indexes in its path
|
||||
- unknown indexes (if any) will always precede known indexes
|
||||
- unknown indexes are not actually part of the relative reference path, and
|
||||
represent replication of the reference.
|
||||
It is impossible for the reference itself to introduce unknown indexes.
|
||||
|
||||
When generating SystemVerilog, be sure to generate code such that "unknown" indexes
|
||||
are always implicitly known due to the reference being used from within a for loop.
|
||||
For example:
|
||||
|
||||
for(int i0=0; i0<16; i0++) begin : rf_loop_array
|
||||
top.rf_loop[i0].y.f2 = top.rf_loop[i0].x[3].f1
|
||||
end
|
||||
|
||||
This should be a reasonable thing to accomplish, since unknown indexes should
|
||||
only show up in situations where the consumer of the reference is being
|
||||
replicated as well, and is therefore implicitly going to be inside a for loop.
|
||||
23
docs/dev_notes/Program Flow
Normal file
@@ -0,0 +1,23 @@
|
||||
|
||||
1. Scan design. Collect information
|
||||
- Check for unsupported constructs. Throw errors as appropriate
|
||||
- Uniform regwidth, accesswidth, etc.
|
||||
|
||||
- Collect reset signals
|
||||
cpuif_reset, field_reset
|
||||
explicitly assigned to field->resetsignal
|
||||
|
||||
- Collect any other misc user signals that are referenced in the design
|
||||
|
||||
- Top-level interrupts
|
||||
Collect X & Y:
|
||||
X = set of all registers that have an interrupt field
|
||||
Y = set of all interrupt registers that are referenced by a field
|
||||
Top level interrupt registers are the set in X, but not in Y
|
||||
(and probably other caveats. See notes)
|
||||
|
||||
2. Create intermediate template objects
|
||||
|
||||
3. Render top-level IO struct package (if applicable)
|
||||
|
||||
4. Render top-level module template
|
||||
11
docs/dev_notes/Resets
Normal file
@@ -0,0 +1,11 @@
|
||||
================================================================================
|
||||
Resets
|
||||
================================================================================
|
||||
use whatever is defined in RDL based on cpuif_reset and field_reset signals
|
||||
Otherwise, provide configuration that defines what the default is:
|
||||
a single reset that is active high/low, or sync/async
|
||||
|
||||
If cpuif_reset is specified, what do fields use?
|
||||
I assume they still use the default reset separately?
|
||||
YES. Agnisys appears to be wrong.
|
||||
cpuif_reset has no influence on the fields' reset according to the spec
|
||||
22
docs/dev_notes/Signal Dereferencer
Normal file
@@ -0,0 +1,22 @@
|
||||
I need some sort of signal "dereferencer" that can be easily used to translate references
|
||||
to stuff via a normalized interface.
|
||||
|
||||
For example, if RDL defines:
|
||||
my_field->next = my_other_field
|
||||
Then in Python (or a template) I could do:
|
||||
x = my_field.get_property('next')
|
||||
y = dereferencer.get(x)
|
||||
and trust that I'll get a value/identifier/whatever that accurately represents
|
||||
the value being referenced
|
||||
|
||||
Values:
|
||||
If X is a field reference:
|
||||
... that implements storage, return its DFF value reference
|
||||
... no storage, but has a hw input, grab from the hwif input
|
||||
... no storage, and no hw input, return its constant reset value?
|
||||
If X is a property reference... do whats right...
|
||||
my_field->anded === (&path.to.my_field)
|
||||
if X is a static value, return the literal
|
||||
|
||||
|
||||
See `Hierarchy and Indexing` on details on how to build path references to stuff
|
||||
183
docs/dev_notes/Validation Needed
Normal file
@@ -0,0 +1,183 @@
|
||||
|
||||
================================================================================
|
||||
Things that need validation by the compiler
|
||||
================================================================================
|
||||
Many of these are probably already covered, but being paranoid.
|
||||
Make a list of things as I think of them.
|
||||
Keep them here in case I forget and re-think of them.
|
||||
|
||||
Mark these as follows:
|
||||
X = Yes, confirmed that the compiler covers this
|
||||
! = No! Confirmed that the compiler does not check this, and should.
|
||||
? = TBD
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
X resetsignal width
|
||||
reset signals shall have width of 1
|
||||
|
||||
X Field has no knowable value
|
||||
- does not implement storage
|
||||
- hw is not writable
|
||||
- sw is readable
|
||||
- No reset value specified
|
||||
|
||||
--> emit a warning?
|
||||
|
||||
X References to a component or component property must use unambiguous array indexing
|
||||
For example, if "array_o_regs" is an array...
|
||||
The following is illegal:
|
||||
my_reg.my_field->next = array_o_regs.thing
|
||||
my_reg.my_field->next = array_o_regs.thing->anded
|
||||
This is ok:
|
||||
my_reg.my_field->next = array_o_regs[2].thing
|
||||
my_reg.my_field->next = array_o_regs[2].thing->anded
|
||||
|
||||
NEVERMIND - compiler does not allow indefinite array references at all!
|
||||
References are guaranteed to be unambiguous:
|
||||
"Incompatible number of index dimensions after 'CTRL'. Expected 1, found 0."
|
||||
|
||||
X Clause 10.6.1-f (wide registers cannot have access side-effects)
|
||||
|
||||
X multiple field_reset in the same hierarchy
|
||||
there can only be one signal declared with field_reset
|
||||
in a given hierarchy
|
||||
|
||||
X multiple cpuif_reset in the same hierarchy
|
||||
there can only be one signal declared with cpuif_reset
|
||||
in a given hierarchy
|
||||
|
||||
X Mutually-exclusive property checking
|
||||
--> Yes. compiler now auto-clears mutex partners on assign, so it is
|
||||
implicitly handled
|
||||
|
||||
X incrwidth/incrvalue & decrvalue/decrwidth
|
||||
these pairs are mutually exclusive.
|
||||
Make sure they are not both set after elaboration
|
||||
Compiler checks for mutex within the same scope, but
|
||||
i dont think I check for mutexes post-elaborate
|
||||
|
||||
... or, make these properties clear each-other on assignment
|
||||
|
||||
X Illegal property references:
|
||||
- reference any of the counter property references to something that isn't a counter
|
||||
decrsaturate / incrsaturate / saturate
|
||||
overflow / underflow
|
||||
- reference hwclr or hwset, but the owner node has them set to False
|
||||
means that the actual inferred signal doesnt exist!
|
||||
- reference swwe/swwel or we/wel, but the owner node has them, AND their complement set to False
|
||||
means that the actual inferred signal doesnt exist!
|
||||
- only valid to reference if owner has this prop set
|
||||
enable/mask
|
||||
haltenable/haltmask
|
||||
hwenable
|
||||
hwmask
|
||||
decr/incr, decr../incrthreshold/..value
|
||||
- others references that may not always make sense:
|
||||
intr/halt - target must contain interrupt/halt fields
|
||||
next
|
||||
is this ever illegal?
|
||||
|
||||
X If a node ispresent=true, and any of its properties are a reference,
|
||||
then those references' ispresent shall also be true
|
||||
This is an explicit clause in the spec: 5.3.1-i
|
||||
|
||||
X Flag illegal sw actions if not readable/writable
|
||||
The following combinations dont get flagged currently:
|
||||
sw=w; rclr;
|
||||
sw=w; rset;
|
||||
sw=r; woset;
|
||||
sw=r; woclr;
|
||||
their counterparts do get flagged. such as:
|
||||
sw=w; onread=rclr;
|
||||
|
||||
X Signals marked as field_reset or cpuif_reset need to have activehigh/activelow
|
||||
specified. (8.2.1-d states that activehigh/low does not have an implied default state if unset!)
|
||||
Also applies to signals referenced by resetsignal
|
||||
|
||||
X incrvalue/decrvalue needs to be the same or narrower than counter itself
|
||||
|
||||
X field shall be hw writable if "next" is assigned.
|
||||
|
||||
X sticky=true + "(posedge|negedge|bothedge) intr"
|
||||
Edge-sensitivty doesnt make sense for full-field stickiness
|
||||
|
||||
X we/wel + implied or explicit "sticky"/"stickybit"
|
||||
we/wel modifier doesn't make sense here.
|
||||
|
||||
X sticky/stickybit shall be hw writable
|
||||
|
||||
X Illegal to use enable/mask/haltenable/haltmask on non-intr fields
|
||||
|
||||
X incrwidth/decrwidth must be between 1 and the width of the counter
|
||||
|
||||
X counter field that saturates should not set overflow
|
||||
counter; incrsaturate; overflow;
|
||||
counter; decrsaturate; underflow;
|
||||
|
||||
Flag this as an error on the overflow/underflow property.
|
||||
overflow/underflow property is meaningless since it can never happen.
|
||||
|
||||
Same goes to prop references to overflow/underflow
|
||||
|
||||
! hwclr/hwset/we/wel probably shouldn't be able to reference itself
|
||||
y->hwclr = y;
|
||||
y->we = y;
|
||||
... it works, but should it be allowed? Seems like user-error
|
||||
|
||||
================================================================================
|
||||
Things that need validation by this exporter
|
||||
================================================================================
|
||||
List of stuff in case I forget.
|
||||
X = Yes! I already implemented this.
|
||||
! = No! exporter does not enforce this yet
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
X Contents of target are all internal. No external regs
|
||||
|
||||
X Does not contain any mem components
|
||||
|
||||
X Warn/error on any signal with cpuif_reset set, that is not in the top-level
|
||||
addrmap. At the very least, warn that it will be ignored
|
||||
|
||||
|
||||
X "bridge" addrmap not supported
|
||||
export shall refuse to process an addrmap marked as a "bridge"
|
||||
Only need to check top-level. Compiler will enforce that child nodes arent bridges
|
||||
|
||||
X regwidth/accesswidth is sane
|
||||
X accesswidth == regwidth
|
||||
Enforce this for now. Dont feel like supporting fancy modes yet
|
||||
X regwidth < accesswidth
|
||||
This is illegal and is enforced by the compiler.
|
||||
X regwidth > accesswidth
|
||||
Need to extend address decode strobes to have multiple bits
|
||||
this is where looking at endianness matters to determine field placement
|
||||
Dont feel like supporting this yet
|
||||
X constant regwidth?
|
||||
For now, probably limit to only allow the same regwidth everywhere?
|
||||
|
||||
|
||||
X Do not allow unaligned addresses
|
||||
All offsets & strides shall be a multiple of the regwidth used
|
||||
|
||||
X each reg needs to be aligned to its width
|
||||
X each regfile/addrmap/stride shall be aligned to the largest regwidth it encloses
|
||||
|
||||
X Error if a property is a reference to something that is external, or enclosed
|
||||
in an external component.
|
||||
Limit this check to child nodes inside the export hierarchy
|
||||
|
||||
! async data signals
|
||||
Only supporting async signals if they are exclusively used in resets.
|
||||
Anything else declared as "async" shall emit a warning that it is ignored
|
||||
I have zero interest in implementing resynchronizers
|
||||
|
||||
! Error if a property references a non-signal component, or property reference from
|
||||
outside the export hierarchy
|
||||
|
||||
! Add warning for sticky race condition
|
||||
stickybit and other similar situations generally should use hw precedence.
|
||||
Emit a warning as appropriate
|
||||
Or should this be a compiler warning??
|
||||
51
docs/dev_notes/template-layers/1-port-declaration
Normal file
@@ -0,0 +1,51 @@
|
||||
--------------------------------------------------------------------------------
|
||||
Port Declaration
|
||||
--------------------------------------------------------------------------------
|
||||
Generates the port declaration of the module:
|
||||
- Parameters
|
||||
- rd/wr error response/data behavior
|
||||
Do missed accesses cause a SLVERR?
|
||||
Do reads respond with a magic value?
|
||||
- Pipeline enables
|
||||
Enable reg stages in various places
|
||||
|
||||
- RDL-derived Parameters:
|
||||
Someday in the future if i ever get around to this: https://github.com/SystemRDL/systemrdl-compiler/issues/58
|
||||
|
||||
- Clock/Reset
|
||||
Single clk
|
||||
One or more resets
|
||||
|
||||
- CPU Bus Interface
|
||||
Given the bus interface object, emits the IO
|
||||
This can be flattened ports, or a SV Interface
|
||||
Regardless, it shall be malleable so that the user can use their favorite
|
||||
declaration style
|
||||
|
||||
- Hardware interface
|
||||
Two options:
|
||||
- 2-port struct interface
|
||||
Everything is rolled into two unpacked structs - inputs and outputs
|
||||
- Flattened --> NOT DOING
|
||||
Flatten/Unroll everything
|
||||
No. not doing. I hate this and dont want to waste time implementing this.
|
||||
This will NEVER be able to support parameterized regmaps, and just
|
||||
creates a ton of corner cases i dont care to deal with.
|
||||
|
||||
Other IO Signals I need to be aware of:
|
||||
any signals declared, and used in any references:
|
||||
field.resetsignal
|
||||
field.next
|
||||
... etc ...
|
||||
any signals declared and marked as cpuif_reset, or field_reset
|
||||
These override the default rst
|
||||
If both are defined, be sure to not emit the default
|
||||
Pretty straightforward (see 17.1)
|
||||
Also have some notes on this in my general Logbook
|
||||
Will have to make a call on how these propagate if multiple defined
|
||||
in different hierarchies
|
||||
interrupt/halt outputs
|
||||
See "Interrupts" logbook for explanation
|
||||
addrmap.errextbus, regfile.errextbus, reg.errextbus
|
||||
???
|
||||
Apparently these are inputs
|
||||
103
docs/dev_notes/template-layers/1.1.hardware-interface
Normal file
@@ -0,0 +1,103 @@
|
||||
================================================================================
|
||||
Summary
|
||||
================================================================================
|
||||
|
||||
RTL interface that provides access to per-field context signals
|
||||
|
||||
Regarding signals:
|
||||
RDL-declared signals are part of the hwif input structure.
|
||||
Only include them if they are referenced by the design (need to scan the
|
||||
full design anyways, so may as well filter out unreferenced ones)
|
||||
|
||||
It is possible to use signals declared in a parent scope.
|
||||
This means that not all signals will be discovered by a hierarchical listener alone
|
||||
Need to scan ALL assigned properties for signal references too.
|
||||
- get signal associated with top node's cpuif_reset helper property, if any
|
||||
- collect all field_resets
|
||||
X check all signal instances in the hier tree
|
||||
- search parents of top node for the first field_reset signal, if any
|
||||
This is WAY less expensive than querying EACH field's resetsignal property
|
||||
X Check all explicitly assigned properties
|
||||
only need to do this for fields
|
||||
Collect all of these into the following:
|
||||
- If inside the hier, add to a list of paths
|
||||
- if outside the hier, add to a dict of path:SignalNode
|
||||
These are all the signals in-use by the design
|
||||
|
||||
Pass list into the hwif generator
|
||||
If the hwif generator encounters a signal during traversal:
|
||||
check if it exists in the signal path list
|
||||
|
||||
out-of-hier signals are inserted outside of the hwif_in as standalone signals.
|
||||
For now, just use their plain inst names. If I need to uniquify them i can add that later.
|
||||
I should at least check against a list of known "dirty words". Seems very likely someone will choose
|
||||
a signal called "rst".
|
||||
Prefix with usersig_ if needed
|
||||
|
||||
|
||||
|
||||
|
||||
================================================================================
|
||||
Naming Scheme
|
||||
================================================================================
|
||||
|
||||
hwif_out
|
||||
.my_regblock
|
||||
.my_reg[X][Y]
|
||||
.my_field
|
||||
.value
|
||||
.anded
|
||||
|
||||
hwif_in
|
||||
.my_regblock
|
||||
.my_reg[X][Y]
|
||||
.my_field
|
||||
.value
|
||||
.we
|
||||
.my_signal
|
||||
.my_fieldreset_signal
|
||||
|
||||
================================================================================
|
||||
Flattened mode? --> NO
|
||||
================================================================================
|
||||
If user wants a flattened list of ports,
|
||||
still use the same hwif_in/out struct internally.
|
||||
Rather than declaring hwif_in and hwif_out in the port list, declare it internally
|
||||
|
||||
Add a mapping layer in the body of the module that performs a ton of assign statements
|
||||
to map flat signals <-> struct
|
||||
|
||||
Alternatively, don't do this at all.
|
||||
If I want to add a flattened mode, generate a wrapper module instead.
|
||||
|
||||
Marking this as YAGNI for now.
|
||||
|
||||
|
||||
================================================================================
|
||||
IO Signals
|
||||
================================================================================
|
||||
|
||||
Outputs:
|
||||
field value
|
||||
If hw readable
|
||||
bitwise reductions
|
||||
if anded, ored, xored == True, output a signal
|
||||
swmod/swacc
|
||||
event strobes
|
||||
|
||||
Inputs:
|
||||
field value
|
||||
If hw writable
|
||||
we/wel
|
||||
if either is boolean, and true
|
||||
not part of external hwif if reference
|
||||
mutually exclusive
|
||||
hwclr/hwset
|
||||
if either is boolean, and true
|
||||
not part of external hwif if reference
|
||||
incr/decr
|
||||
if counter=true, generate BOTH
|
||||
incrvalue/decrvalue
|
||||
if either incrwidth/decrwidth are set
|
||||
signals!
|
||||
any signal instances instantiated in the scope
|
||||
72
docs/dev_notes/template-layers/2-CPUIF
Normal file
@@ -0,0 +1,72 @@
|
||||
--------------------------------------------------------------------------------
|
||||
CPU Bus interface layer
|
||||
--------------------------------------------------------------------------------
|
||||
Provides an abstraction layer between the outside SoC's bus interface, and the
|
||||
internal register block's implementation.
|
||||
Converts a user-selectable bus protocol to generic register file signals.
|
||||
|
||||
Upstream Signals:
|
||||
Signal names are defined in the bus interface class and shall be malleable
|
||||
to the user.
|
||||
User can choose a flat signal interface, or a SV interface.
|
||||
SV interface shall be easy to tweak since various orgs will use different
|
||||
naming conventions in their library of interface definitions
|
||||
|
||||
Downstream Signals:
|
||||
- cpuif_req
|
||||
- Single-cycle pulse
|
||||
- Qualifies the following child signals:
|
||||
- cpuif_req_is_wr
|
||||
1 denotes this is a write transfer
|
||||
- cpuif_addr
|
||||
Byte address
|
||||
- cpuif_wr_data
|
||||
- cpuif_wr_biten
|
||||
per-bit strobes
|
||||
some protocols may opt to tie this to all 1's
|
||||
- cpuif_rd_ack
|
||||
- Single-cycle pulse
|
||||
- Qualifies the following child signals:
|
||||
- cpuif_rd_data
|
||||
- cpuif_rd_err
|
||||
|
||||
- cpuif_wr_ack
|
||||
- Single-cycle pulse
|
||||
- Qualifies the following child signals:
|
||||
- cpuif_wr_err
|
||||
|
||||
|
||||
Misc thoughts
|
||||
- Internal cpuif_* signals use a strobe-based protocol:
|
||||
- Unknown, but fixed latency
|
||||
- Makes for easy pipelining if needed
|
||||
- Decided to keep cpuif_req signals common for read write:
|
||||
This will allow address decode logic to be shared for read/write
|
||||
Downside is split protocols like axi-lite can't have totally separate rd/wr
|
||||
access lanes, but who cares?
|
||||
- separate response strobes
|
||||
Not necessary to use, but this lets me independently pipeline read/write paths.
|
||||
read path will need more time if readback mux is large
|
||||
- On multiple outstanding transactions
|
||||
Currently, cpuif doesnt really support this. Goal was to make it easily pipelineable
|
||||
without having to backfeed stall logic.
|
||||
Could still be possible to do a "fly-by" pipeline with a more intelligent cpuif layer
|
||||
Not worrying about this now.
|
||||
|
||||
|
||||
Implementation:
|
||||
Implement this mainly as a Jinja template.
|
||||
Upstream bus intf signals are fetched via busif class properties. Ex:
|
||||
{{busif.signal('pready')}} <= '1;
|
||||
This allows the actual SV or flattened signal to be emitted
|
||||
|
||||
What protocols do I care about?
|
||||
- AXI4 Lite
|
||||
- Ignore AxPROT?
|
||||
- APB3
|
||||
- APB4
|
||||
- Ignore pprot?
|
||||
- AHB?
|
||||
- Wishbone
|
||||
- Generic
|
||||
breakout the above signals as-is (reassign with a prefix or something)
|
||||
51
docs/dev_notes/template-layers/3-address-decode
Normal file
@@ -0,0 +1,51 @@
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
Address Decode layer
|
||||
--------------------------------------------------------------------------------
|
||||
A bunch of combinational address decodes that generate individual register
|
||||
req strobes
|
||||
|
||||
Possible decode logic styles:
|
||||
- Big case statement
|
||||
+ Probably more sim-efficient
|
||||
- Hard to do loop parameterization
|
||||
- More annoying to do multiple regs per address
|
||||
- Big always_comb + One if/else chain
|
||||
+ Easy to nest loops & parameterize if needed
|
||||
- sim has a lot to evaluate each time
|
||||
- More annoying to do multiple regs per address
|
||||
- implies precedence? Synth tools should be smart enough?
|
||||
- Big always_comb + inline conditionals <---- DO THIS
|
||||
+ Easy to nest loops & parameterize if needed
|
||||
- sim has a lot to evaluate each time
|
||||
+ Multiple regs per address possible
|
||||
+ implies address decode parallelism.
|
||||
?? Should I try using generate loops + assigns?
|
||||
This would be more explicit parallelism, however some tools may
|
||||
get upset at multiple assignments to a common struct
|
||||
|
||||
Implementation:
|
||||
Jinja is inappropriate here
|
||||
Very logic-heavy. Jinja may end up being annoying
|
||||
Also, not much need for customization here
|
||||
This may even make sense as a visitor that dumps lines
|
||||
- visit each reg
|
||||
- upon entering an array, create for loops
|
||||
- upon exiting an array, emit 'end'
|
||||
Make the strobe struct declared locally
|
||||
No need for it to leave the block
|
||||
Error handling
|
||||
If no strobe generated, respond w error?
|
||||
This is actually pretty expensive to do for writes.
|
||||
Hold off on this for now.
|
||||
Reads get this effectively for free in the readback mux.
|
||||
Implement write response strobes back upstream to cpuif
|
||||
Eventually allow for optional register stage for strobe struct
|
||||
Will need to also pipeline the other cpuif signals
|
||||
ok to discard the cpuif_addr. no longer needed
|
||||
|
||||
|
||||
Downstream Signals:
|
||||
- access strobes
|
||||
Encase these into a struct datatype
|
||||
- is_write + wr_data/wr_bitstrobe
|
||||
163
docs/dev_notes/template-layers/4-fields
Normal file
@@ -0,0 +1,163 @@
|
||||
--------------------------------------------------------------------------------
|
||||
Field storage / next value layer
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
Where all the magic happens!!
|
||||
|
||||
Any field that implements storage is defined here.
|
||||
Bigass struct that only contains storage elements
|
||||
|
||||
Each field consists of:
|
||||
- Entries in the storage element struct
|
||||
- if implements storage - field value
|
||||
- user extensible values?
|
||||
- Entries in the combo struct
|
||||
- if implements storage:
|
||||
- Field's "next" value
|
||||
- load-enable strobe
|
||||
- If counter
|
||||
various event strobes (overflow/overflow).
|
||||
These are convenient to generate alongside the field next state logic
|
||||
- user extensible values?
|
||||
- an always_comb block:
|
||||
- generates the "next value" combinational signal
|
||||
- May generate other intermediate strobes?
|
||||
incr/decr?
|
||||
- series of if/else statements that assign the next value in the storage element
|
||||
Think of this as a flat list of "next state" conditons, ranked by their precedence as follows:
|
||||
- reset
|
||||
Actually, handle this in the always_ff
|
||||
- sw access (if sw precedence)
|
||||
- onread/onwrite
|
||||
- hw access
|
||||
- Counter
|
||||
beware of clear events and incr/decr events happening simultaneously
|
||||
- next
|
||||
- etc
|
||||
- sw access (if hw precedence)
|
||||
- onread/onwrite
|
||||
- always_comb block to also generate write-enable strobes for the actual
|
||||
storage element
|
||||
This is better for low-power design
|
||||
- an always_ff block
|
||||
Implements the actual storage element
|
||||
Also a tidy place to abstract the specifics of activehigh/activelow field reset
|
||||
selection.
|
||||
|
||||
TODO:
|
||||
Scour the RDL spec.
|
||||
Does this "next state" precedence model hold true in all situations?
|
||||
|
||||
TODO:
|
||||
Think about user-extensibility
|
||||
Provide a mechanism for users to extend/override field behavior
|
||||
|
||||
TODO:
|
||||
Does the endianness the user sets matter anywhere?
|
||||
|
||||
Implementation
|
||||
Makes sense to use a listener class
|
||||
|
||||
Be sure to skip alias registers
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
NextStateConditional Class
|
||||
Describes a single conditional action that determines the next state of a field
|
||||
Provides information to generate the following content:
|
||||
if(<conditional>) begin
|
||||
<assignments>
|
||||
end
|
||||
|
||||
- is_match(self, field: FieldNode) -> bool:
|
||||
Returns True if this conditional is relevant to the field. If so,
|
||||
it instructs the FieldBuider that code for this conditional shall be emitted
|
||||
TODO: better name than "is_match"? More like "is this relevant"
|
||||
|
||||
- get_predicate(self, field: FieldNode) -> str:
|
||||
Returns the rendered conditional text
|
||||
|
||||
- get_assignments(self, field: FieldNode) -> List[str]:
|
||||
Returns a list of rendered assignment strings
|
||||
This will basically always be two:
|
||||
<field>.next = <next value>
|
||||
<field>.load_next = '1;
|
||||
|
||||
- get_extra_combo_signals(self, field: FieldNode) -> List[TBD]:
|
||||
Some conditionals will need to set some extra signals (eg. counter underflow/overflow strobes)
|
||||
Compiler needs to know to:
|
||||
- declare these inthe combo struct
|
||||
- initialize them in the beginning of always_comb
|
||||
|
||||
Return something that denotes the following information: (namedtuple?)
|
||||
- signal name: str
|
||||
- width: int
|
||||
- default value assignment: str
|
||||
|
||||
Multiple NextStateConditional can declare the same extra combo signal
|
||||
as long as their definitions agree
|
||||
--> Assert this
|
||||
|
||||
|
||||
FieldBuilder Class
|
||||
Describes how to build fields
|
||||
|
||||
Contains NextStateConditional definitions
|
||||
Nested inside the class namespace, define all the NextStateConditional classes
|
||||
that apply
|
||||
User can override definitions or add own to extend behavior
|
||||
|
||||
NextStateConditional objects are stored in a dictionary as follows:
|
||||
_conditionals {
|
||||
assignment_precedence: [
|
||||
conditional_option_1,
|
||||
conditional_option_2,
|
||||
conditional_option_3,
|
||||
]
|
||||
}
|
||||
|
||||
add_conditional(self, conditional, assignment_precedence):
|
||||
Inserts the NextStateConditional into the given assignment precedence bin
|
||||
The first one added to a precedence bin is first in that bin's search order
|
||||
|
||||
init_conditionals(self) -> None:
|
||||
Called from __init__.
|
||||
loads all possible conditionals into self.conditionals list
|
||||
This function is to provide a hook for the user to add their own.
|
||||
|
||||
Do not do fancy class introspection. Load them explicitly by name like so:
|
||||
self.add_conditional(MyNextState(), AssignmentPrecedence.SW_ACCESS)
|
||||
|
||||
If user wants to extend this class, they can pile onto the bins of conditionals freely!
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
Misc
|
||||
--------------------------------------------------------------------------------
|
||||
What about complex behaviors like a read-clear counter?
|
||||
if({{software read}})
|
||||
next = 0
|
||||
elif({{increment}})
|
||||
next = prev + 1
|
||||
|
||||
--> Implement this by stacking multiple NextStateConditional in the same assignment precedence.
|
||||
In this case, there would be a special action on software read that would be specific to read-clear counters
|
||||
this would get inserted ahead of the search order.
|
||||
|
||||
|
||||
Precedence & Search order
|
||||
There are two layers of priority I need to keep track of:
|
||||
- Assignment Precedence
|
||||
RTL precedence of the assignment conditional
|
||||
- Search order (sp?)
|
||||
Within an assignment precedence, order in which the NextStateConditional classes are
|
||||
searched for a match
|
||||
|
||||
For assignment precedence, it makes sense to use an integer enumeration for this
|
||||
since there really aren't too many precedence levels that apply here.
|
||||
Space out the integer enumerations so that user can reliably insert their own actions, ie:
|
||||
my_precedence = AssignmentPrecedence.SW_ACCESS + 1
|
||||
|
||||
For search order, provide a user API to load a NextStateConditional into
|
||||
a precedence 'bin'. Pushing into a bin always inserts into the front of the search order
|
||||
This makes sense since user overrides will always want to be highest priority - and
|
||||
rule themselves out before falling back to builtin behavior
|
||||
49
docs/dev_notes/template-layers/5-readback-mux
Normal file
@@ -0,0 +1,49 @@
|
||||
--------------------------------------------------------------------------------
|
||||
Readback mux layer
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
Implementation:
|
||||
- Big always_comb block
|
||||
- Initialize default rd_data value
|
||||
- Lotsa if statements that operate on reg strb to assign rd_data
|
||||
- Merges all fields together into reg
|
||||
- pulls value from storage element struct, or input struct
|
||||
- Provision for optional flop stage?
|
||||
|
||||
Mux Strategy:
|
||||
Flat case statement:
|
||||
-- Cant parameterize
|
||||
+ better performance?
|
||||
|
||||
Flat 1-hot array then OR reduce:
|
||||
- Create a bus-wide flat array
|
||||
eg: 32-bits x N readable registers
|
||||
- Assign each element:
|
||||
the readback value of each register
|
||||
... masked by the register's access strobe
|
||||
- I could also stuff an extra bit into the array that denotes the read is valid
|
||||
A missed read will OR reduce down to a 0
|
||||
- Finally, OR reduce all the elements in the array down to a flat 32-bit bus
|
||||
- Retiming the large OR fanin can be done by chopping up the array into stages
|
||||
for 2 stages, sqrt(N) gives each stage's fanin size. Round to favor
|
||||
more fanin on 2nd stage
|
||||
3 stages uses cube-root. etc...
|
||||
- This has the benefit of re-using the address decode logic.
|
||||
synth can choose to replicate logic if fanout is bad
|
||||
|
||||
|
||||
WARNING:
|
||||
Beware of read/write flop stage asymmetry & race conditions.
|
||||
Eg. If a field is rclr, dont want to sample it after it gets read:
|
||||
addr --> strb --> clear
|
||||
addr --> loooong...retime --> sample rd value
|
||||
Should guarantee that read-sampling happens at the same cycle as any read-modify
|
||||
|
||||
|
||||
Forwards response strobe back up to cpu interface layer
|
||||
|
||||
TODO:
|
||||
Dont forget about alias registers here
|
||||
|
||||
TODO:
|
||||
Does the endinness the user sets matter anywhere?
|
||||
9
docs/dev_notes/template-layers/6-output-port-mapping
Normal file
@@ -0,0 +1,9 @@
|
||||
--------------------------------------------------------------------------------
|
||||
Output Port mapping layer
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
Assign to output struct port
|
||||
|
||||
Still TBD if this will actually be a distinct layer.
|
||||
Cosmetically, this might be nicer to interleave with the field section above
|
||||
Assign storage element & other derived values as requested by properties
|
||||
BIN
docs/diagrams/arch.png
Normal file
|
After Width: | Height: | Size: 148 KiB |
BIN
docs/diagrams/diagrams.odg
Normal file
BIN
docs/diagrams/rbuf.png
Normal file
|
After Width: | Height: | Size: 122 KiB |
BIN
docs/diagrams/readback.png
Normal file
|
After Width: | Height: | Size: 89 KiB |
BIN
docs/diagrams/wbuf.png
Normal file
|
After Width: | Height: | Size: 123 KiB |
131
docs/faq.rst
Normal file
@@ -0,0 +1,131 @@
|
||||
Frequently Asked Questions
|
||||
==========================
|
||||
|
||||
Why isn't there an option for a flat non-struct hardware interface?
|
||||
-------------------------------------------------------------------
|
||||
SystemRDL is inherently a very hierarchical language.
|
||||
For small register blocks, flattening the hardware interface may be acceptable,
|
||||
but this ends up scaling very poorly as the design becomes larger and has more
|
||||
complex hierarchy.
|
||||
Using struct compositions for the hardware interface has the benefit of
|
||||
preserving conceptual hierarchy and arrays exactly as defined in the original
|
||||
SystemRDL.
|
||||
|
||||
How do I know I connected everything? Structs are harder to review
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Initially this can be daunting, but fortunately the tool has an option to generate a
|
||||
flattened hardware interface report upon export. Try using the ``--hwif-report``
|
||||
command line option when exporting. This is the easiest way to quickly
|
||||
understand the structure of the hardware interface.
|
||||
|
||||
|
||||
|
||||
Why does the tool generate un-packed structs? I prefer packed structs.
|
||||
----------------------------------------------------------------------
|
||||
Packed structs are great when describing vectors that have bit-level structure.
|
||||
In this tool, the use of un-packed structs is intentional since the hardware
|
||||
interface is not something that is meant to be bit-accessible. In the case of
|
||||
the hardware interface structs, using a packed struct is semantically inappropriate.
|
||||
|
||||
... Then how can I initialize the struct?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
We get this request most often because designers want to initialize the ``hwif_in``
|
||||
struct with a simple assignment:
|
||||
|
||||
.. code:: systemverilog
|
||||
|
||||
always_comb begin
|
||||
hwif_in = '0;
|
||||
end
|
||||
|
||||
Of course since the struct actually is **unpacked**, this will result in a
|
||||
compile error which usually leads to the inappropriate assumption that it ought
|
||||
to be packed. (See this amusing blog post about `X/Y problems <https://xyproblem.info>`_)
|
||||
|
||||
If your goal is to initialize the packed struct, fortunately SystemVerilog already
|
||||
has syntax to do this:
|
||||
|
||||
.. code:: systemverilog
|
||||
|
||||
always_comb begin
|
||||
hwif_in = '{default: '0};
|
||||
end
|
||||
|
||||
This is lesser-known syntax, but still very well supported by synthesis
|
||||
tools, and is the recommended way to handle this.
|
||||
|
||||
... What if I want to assign it to a bit-vector?
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Assigning the hwif struct to a bit-vector is strongly discouraged. This tool makes
|
||||
no guarantees regarding the field ordering of the hwif structure, so doing so
|
||||
should be considered functionally dangerous.
|
||||
|
||||
That said, if you still need to do this, it is still trivially possible to
|
||||
without requiring packed structs. Instead, use the SystemVerilog streaming operator:
|
||||
|
||||
.. code:: systemverilog
|
||||
|
||||
my_packed_vector = {<<{hwif_out}};
|
||||
|
||||
|
||||
... Why are unpacked structs preferred?
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
In the case of the hardware interface ports, unpacked structs help prevent
|
||||
mistakes that are very easy to make.
|
||||
Consider the following situation - a designer has a field that sets the following
|
||||
properties: ``sw=rw; hw=rw; we;``, and wants to assign the hardware input value,
|
||||
so they erroneously do the following assignment in Verilog:
|
||||
|
||||
.. code:: systemverilog
|
||||
|
||||
assign hwif_in.my_register.my_field = <some value>;
|
||||
|
||||
This is actually a bug since the ``my_field`` member is actually a struct that
|
||||
has two members: ``we`` and ``next``. If this were a packed struct, this would
|
||||
silently compile and you would potentially have a bug that may not be noticed
|
||||
(depending on how thorough the test campaign is).
|
||||
With an unpacked struct, this gets flagged immediately as a compile error since
|
||||
the assignment is invalid.
|
||||
|
||||
The designer may have simply forgotten that the field is an aggregate of multiple
|
||||
members and intended to do the following:
|
||||
|
||||
.. code:: systemverilog
|
||||
|
||||
assign hwif.my_register.my_field.next = <some value>;
|
||||
assign hwif.my_register.my_field.we = <some control signal>;
|
||||
|
||||
|
||||
The generated output does not match our organization's coding style
|
||||
-------------------------------------------------------------------
|
||||
SystemVerilog coding styles vary wildly, and unfortunately there is little
|
||||
consensus on this topic within the digital design community.
|
||||
|
||||
The output generated by PeakRDL-regblock strives to be as human-readable as possible,
|
||||
and follow consistent indentation and styling. We do our best to use the most
|
||||
widely accepted coding style, but since this is a very opinionated space, it is
|
||||
impossible to satisfy everyone.
|
||||
|
||||
In general, we strive to follow the
|
||||
`SystemVerilog style guide by lowRISC <https://github.com/lowRISC/style-guides/blob/master/VerilogCodingStyle.md>`_,
|
||||
but may deviate in some areas if not practical or would impose excessive complexity on the code generator.
|
||||
|
||||
|
||||
The lint tool I am using is flagging violations in generated code
|
||||
-----------------------------------------------------------------
|
||||
Code linting tools are a great way to check for user-error, flag inconsistencies,
|
||||
and enforce best-practices within an organization. In many cases, linter tools
|
||||
may be configured to also enforce stylistic preferences.
|
||||
Unfortunately just like coding styles, lint rules can often be more
|
||||
opinionated than practical.
|
||||
|
||||
In general, we will not address lint violations unless they flag actual
|
||||
structural issues or semantically dangerous code.
|
||||
Stylistic violations that pose no actual danger to the correctness of the design
|
||||
will rarely be addressed, especially if the change would add unreasonable
|
||||
complexity to the tool.
|
||||
|
||||
If you encounter a lint violation, please carefully review and consider waiving
|
||||
it if it does not pose an actual danger. If you still believe it is a problem,
|
||||
please let us know by `submitting an issue <https://github.com/SystemRDL/PeakRDL-regblock/issues>`_
|
||||
that describes the problem.
|
||||
61
docs/hwif.rst
Normal file
@@ -0,0 +1,61 @@
|
||||
Hardware Interface
|
||||
------------------
|
||||
|
||||
The generated register block will present the entire hardware interface to the user
|
||||
using two struct ports:
|
||||
|
||||
* ``hwif_in``
|
||||
* ``hwif_out``
|
||||
|
||||
All field inputs and outputs as well as signals are consolidated into these
|
||||
struct ports. The presence of each depends on the specific contents of the design
|
||||
being exported.
|
||||
|
||||
|
||||
Using structs for the hardware interface has the following benefits:
|
||||
|
||||
* Preserves register map component grouping, arrays, and hierarchy.
|
||||
* Avoids naming collisions and cumbersome signal name flattening.
|
||||
* Allows for more natural mapping and distribution of register block signals to a design's hardware components.
|
||||
* Use of unpacked arrays/structs prevents common assignment mistakes as they are enforced by the compiler.
|
||||
|
||||
|
||||
Structs are organized as follows: ``hwif_out.<heir_path>.<feature>``
|
||||
|
||||
For example, a simple design such as:
|
||||
|
||||
.. code-block:: systemrdl
|
||||
|
||||
addrmap my_design {
|
||||
reg {
|
||||
field {
|
||||
sw = rw;
|
||||
hw = rw;
|
||||
we;
|
||||
} my_field;
|
||||
} my_reg[2];
|
||||
};
|
||||
|
||||
... results in the following struct members:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
hwif_out.my_reg[0].my_field.value
|
||||
hwif_in.my_reg[0].my_field.next
|
||||
hwif_in.my_reg[0].my_field.we
|
||||
hwif_out.my_reg[1].my_field.value
|
||||
hwif_in.my_reg[1].my_field.next
|
||||
hwif_in.my_reg[1].my_field.we
|
||||
|
||||
For brevity in this documentation, hwif features will be described using shorthand
|
||||
notation that omits the hierarchical path: ``hwif_out..<feature>``
|
||||
|
||||
|
||||
.. important::
|
||||
|
||||
The PeakRDL tool makes no guarantees on the field order of the hwif structs.
|
||||
For this reason, it is strongly recommended to always access struct members
|
||||
directly, by name.
|
||||
|
||||
If using the SystemVerilog streaming operator to assign the hwif struct to a
|
||||
packed vector, be extremely careful to avoid assumptions on the resulting bit-position of a field.
|
||||
53
docs/img/err.svg
Normal file
@@ -0,0 +1,53 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
viewBox="0 0 512 512"
|
||||
version="1.1"
|
||||
id="svg4"
|
||||
sodipodi:docname="times-circle.svg"
|
||||
inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)">
|
||||
<metadata
|
||||
id="metadata10">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs8" />
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="983"
|
||||
id="namedview6"
|
||||
showgrid="false"
|
||||
inkscape:zoom="0.4609375"
|
||||
inkscape:cx="18.440678"
|
||||
inkscape:cy="245.15254"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="27"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg4" />
|
||||
<!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) -->
|
||||
<path
|
||||
d="M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm121.6 313.1c4.7 4.7 4.7 12.3 0 17L338 377.6c-4.7 4.7-12.3 4.7-17 0L256 312l-65.1 65.6c-4.7 4.7-12.3 4.7-17 0L134.4 338c-4.7-4.7-4.7-12.3 0-17l65.6-65-65.6-65.1c-4.7-4.7-4.7-12.3 0-17l39.6-39.6c4.7-4.7 12.3-4.7 17 0l65 65.7 65.1-65.6c4.7-4.7 12.3-4.7 17 0l39.6 39.6c4.7 4.7 4.7 12.3 0 17L312 256l65.6 65.1z"
|
||||
id="path2"
|
||||
style="fill:#b40000;fill-opacity:1" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
53
docs/img/ok.svg
Normal file
@@ -0,0 +1,53 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
viewBox="0 0 512 512"
|
||||
version="1.1"
|
||||
id="svg4"
|
||||
sodipodi:docname="check-circle.svg"
|
||||
inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)">
|
||||
<metadata
|
||||
id="metadata10">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs8" />
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="983"
|
||||
id="namedview6"
|
||||
showgrid="false"
|
||||
inkscape:zoom="0.4609375"
|
||||
inkscape:cx="-402.44068"
|
||||
inkscape:cy="247.32203"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="27"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg4" />
|
||||
<!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) -->
|
||||
<path
|
||||
d="M504 256c0 136.967-111.033 248-248 248S8 392.967 8 256 119.033 8 256 8s248 111.033 248 248zM227.314 387.314l184-184c6.248-6.248 6.248-16.379 0-22.627l-22.627-22.627c-6.248-6.249-16.379-6.249-22.628 0L216 308.118l-70.059-70.059c-6.248-6.248-16.379-6.248-22.628 0l-22.627 22.627c-6.248 6.248-6.248 16.379 0 22.627l104 104c6.249 6.249 16.379 6.249 22.628.001z"
|
||||
id="path2"
|
||||
style="fill:#00b405;fill-opacity:1" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
53
docs/img/warn.svg
Normal file
@@ -0,0 +1,53 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
viewBox="0 0 576 512"
|
||||
version="1.1"
|
||||
id="svg4"
|
||||
sodipodi:docname="exclamation-triangle.svg"
|
||||
inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)">
|
||||
<metadata
|
||||
id="metadata10">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs8" />
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="983"
|
||||
id="namedview6"
|
||||
showgrid="false"
|
||||
inkscape:zoom="0.4609375"
|
||||
inkscape:cx="46.101695"
|
||||
inkscape:cy="256"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="27"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg4" />
|
||||
<!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) -->
|
||||
<path
|
||||
d="M569.517 440.013C587.975 472.007 564.806 512 527.94 512H48.054c-36.937 0-59.999-40.055-41.577-71.987L246.423 23.985c18.467-32.009 64.72-31.951 83.154 0l239.94 416.028zM288 354c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z"
|
||||
id="path2"
|
||||
style="fill:#ffa705;fill-opacity:1" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
94
docs/index.rst
Normal file
@@ -0,0 +1,94 @@
|
||||
Introduction
|
||||
============
|
||||
|
||||
PeakRDL-regblock is a free and open-source control & status register (CSR) compiler.
|
||||
This code generator translates your SystemRDL register description into
|
||||
a synthesizable SystemVerilog RTL module that can be easily instantiated into
|
||||
your hardware design.
|
||||
|
||||
* Generates fully synthesizable SystemVerilog RTL (IEEE 1800-2012)
|
||||
* Options for many popular CPU interface protocols (AMBA APB, AXI4-Lite, and more)
|
||||
* Configurable pipelining options for designs with fast clock rates.
|
||||
* Broad support for SystemRDL 2.0 features
|
||||
|
||||
|
||||
Quick Start
|
||||
-----------
|
||||
The easiest way to use PeakRDL-regblock is via the `PeakRDL command line tool <https://peakrdl.readthedocs.io/>`_:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# Install PeakRDL-regblock along with the command-line tool
|
||||
python3 -m pip install peakrdl-regblock[cli]
|
||||
|
||||
# Export!
|
||||
peakrdl regblock atxmega_spi.rdl -o regblock/ --cpuif axi4-lite
|
||||
|
||||
|
||||
Looking for VHDL?
|
||||
-----------------
|
||||
This project generates SystemVerilog RTL. If you prefer using VHDL, check out
|
||||
the sister project which aims to be a feature-equivalent fork of
|
||||
PeakRDL-regblock: `PeakRDL-regblock-VHDL <https://peakrdl-regblock-vhdl.readthedocs.io>`_
|
||||
|
||||
|
||||
Links
|
||||
-----
|
||||
|
||||
- `Source repository <https://github.com/SystemRDL/PeakRDL-regblock>`_
|
||||
- `Release Notes <https://github.com/SystemRDL/PeakRDL-regblock/releases>`_
|
||||
- `Issue tracker <https://github.com/SystemRDL/PeakRDL-regblock/issues>`_
|
||||
- `PyPi <https://pypi.org/project/peakrdl-regblock>`_
|
||||
- `SystemRDL Specification <http://accellera.org/downloads/standards/systemrdl>`_
|
||||
|
||||
|
||||
.. toctree::
|
||||
:hidden:
|
||||
|
||||
self
|
||||
architecture
|
||||
hwif
|
||||
configuring
|
||||
limitations
|
||||
faq
|
||||
licensing
|
||||
api
|
||||
|
||||
.. toctree::
|
||||
:hidden:
|
||||
:caption: CPU Interfaces
|
||||
|
||||
cpuif/introduction
|
||||
cpuif/apb
|
||||
cpuif/axi4lite
|
||||
cpuif/avalon
|
||||
cpuif/passthrough
|
||||
cpuif/internal_protocol
|
||||
cpuif/customizing
|
||||
|
||||
.. toctree::
|
||||
:hidden:
|
||||
:caption: SystemRDL Properties
|
||||
|
||||
props/field
|
||||
props/reg
|
||||
props/addrmap
|
||||
props/signal
|
||||
props/rhs_props
|
||||
|
||||
.. toctree::
|
||||
:hidden:
|
||||
:caption: Other SystemRDL Features
|
||||
|
||||
rdl_features/external
|
||||
|
||||
.. toctree::
|
||||
:hidden:
|
||||
:caption: Extended Properties
|
||||
|
||||
udps/intro
|
||||
udps/read_buffering
|
||||
udps/write_buffering
|
||||
udps/extended_swacc
|
||||
udps/signed
|
||||
udps/fixedpoint
|
||||
50
docs/licensing.rst
Normal file
@@ -0,0 +1,50 @@
|
||||
Licensing
|
||||
=========
|
||||
|
||||
Re-distribution of the PeakRDL-regblock code generator tool shall adhere to the
|
||||
terms outlined by the GNU LGPL v3 license. For a copy of the license, see:
|
||||
https://github.com/SystemRDL/PeakRDL-regblock/blob/main/LICENSE
|
||||
|
||||
|
||||
Why LGPLv3?
|
||||
-----------
|
||||
LGPLv3 was chosen because my intent is to promote a thriving ecosystem of free and
|
||||
open source register automation tools. The license terms discourage this tool from
|
||||
being bundled into some commercially sold closed-source software, as that would
|
||||
be contrary to this project's philosophy.
|
||||
|
||||
|
||||
What is covered by the LGPL v3 license?
|
||||
--------------------------------------
|
||||
The LGPL license is intended for the code generator itself. This includes all
|
||||
Python sources, Jinja template files, as well as testcase infrastructure not
|
||||
explicitly mentioned in the exemptions below.
|
||||
|
||||
|
||||
What is exempt from the LGPLv3 license?
|
||||
---------------------------------------
|
||||
Don't worry. Not everything that the PeakRDL-regblock project touches is
|
||||
considered LGPLv3 code.
|
||||
|
||||
The following are exempt and are free to use with no restrictions:
|
||||
|
||||
* Any code that is generated using PeakRDL-regblock is 100% yours. Since it
|
||||
was derived from your regblock definition, it remains yours. You can
|
||||
distribute it freely, use it in a proprietary ASIC, sell it as part of an
|
||||
IP, whatever.
|
||||
* Any code snippets in this documentation can be freely copy/pasted. These are
|
||||
examples that are intended for this purpose.
|
||||
* All reference files that are downloadable from this documentation, which are
|
||||
also available in the `hdl-src folder in the repository <https://github.com/SystemRDL/PeakRDL-regblock/tree/main/hdl-src>`_
|
||||
|
||||
|
||||
Can I use this as part of my company's internally developed tools?
|
||||
------------------------------------------------------------------
|
||||
Absolutely!
|
||||
|
||||
Sometimes it may be necessary to integrate this into a larger toolchain at your
|
||||
workplace. This is totally OK, as long as you don't start distributing it
|
||||
outside your workplace in ways that violate the LGPLv3 license.
|
||||
|
||||
That said, I'd encourage you to check out the `PeakRDL command line tool <https://peakrdl.readthedocs.io/>`_.
|
||||
It may already do everything you need.
|
||||
53
docs/limitations.rst
Normal file
@@ -0,0 +1,53 @@
|
||||
Known Limitations
|
||||
=================
|
||||
|
||||
Not all SystemRDL features are supported by this exporter. For a listing of
|
||||
supported properties, see the appropriate property listing page in the sections
|
||||
that follow.
|
||||
|
||||
|
||||
Alias Registers
|
||||
---------------
|
||||
Registers instantiated using the ``alias`` keyword are not supported yet.
|
||||
|
||||
|
||||
Unaligned Registers
|
||||
-------------------
|
||||
All address offsets & strides shall be a multiple of the cpuif bus width used. Specifically:
|
||||
|
||||
* Bus width is inferred by the maximum accesswidth used in the regblock.
|
||||
* Each component's address and array stride shall be aligned to the bus width.
|
||||
|
||||
|
||||
Uniform accesswidth
|
||||
-------------------
|
||||
All registers within a register block shall use the same accesswidth.
|
||||
|
||||
One exception is that registers with regwidth that is narrower than the cpuif
|
||||
bus width are permitted, provided that their regwidth is equal to their accesswidth.
|
||||
|
||||
For example:
|
||||
|
||||
.. code-block:: systemrdl
|
||||
|
||||
// (Largest accesswidth used is 32, therefore the CPUIF bus width is 32)
|
||||
|
||||
reg {
|
||||
regwidth = 32;
|
||||
accesswidth = 32;
|
||||
} reg_a @ 0x00; // OK. Regular 32-bit register
|
||||
|
||||
reg {
|
||||
regwidth = 64;
|
||||
accesswidth = 32;
|
||||
} reg_b @ 0x08; // OK. "Wide" register of 64-bits, but is accessed using 32-bit subwords
|
||||
|
||||
reg {
|
||||
regwidth = 8;
|
||||
accesswidth = 8;
|
||||
} reg_c @ 0x10; // OK. Is aligned to the cpuif bus width
|
||||
|
||||
reg {
|
||||
regwidth = 32;
|
||||
accesswidth = 8;
|
||||
} bad_reg @ 0x14; // NOT OK. accesswidth conflicts with cpuif width
|
||||
28
docs/props/addrmap.rst
Normal file
@@ -0,0 +1,28 @@
|
||||
Addrmap/Regfile Properties
|
||||
==========================
|
||||
|
||||
.. note:: Any properties not explicitly listed here are either implicitly
|
||||
supported, or are not relevant to the regblock exporter and are ignored.
|
||||
|
||||
|
||||
errextbus
|
||||
---------
|
||||
|NO|
|
||||
|
||||
sharedextbus
|
||||
------------
|
||||
|NO|
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
Addrmap Properties
|
||||
==================
|
||||
|
||||
bigendian/littleendian
|
||||
----------------------
|
||||
|NO|
|
||||
|
||||
rsvdset
|
||||
-------
|
||||
|NO|
|
||||
491
docs/props/field.rst
Normal file
@@ -0,0 +1,491 @@
|
||||
Field Properties
|
||||
================
|
||||
|
||||
.. note:: Any properties not explicitly listed here are either implicitly
|
||||
supported, or are not relevant to the regblock exporter and are ignored.
|
||||
|
||||
Software Access Properties
|
||||
--------------------------
|
||||
|
||||
onread/onwrite
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
All onread/onwrite actions are supported (except for ruser/wuser)
|
||||
|
||||
rclr/rset
|
||||
^^^^^^^^^
|
||||
|
||||
See ``onread``. These are effectively aliases of the onread property.
|
||||
|
||||
singlepulse
|
||||
^^^^^^^^^^^
|
||||
|
||||
If set, field will get cleared back to zero after being written.
|
||||
|
||||
.. wavedrom::
|
||||
|
||||
{"signal": [
|
||||
{"name": "clk", "wave": "p....."},
|
||||
{"name": "<swmod>", "wave": "0.10.."},
|
||||
{"name": "hwif_out..value", "wave": "0..10."}
|
||||
]}
|
||||
|
||||
sw
|
||||
^^^
|
||||
All sw access modes are supported except for ``w1`` and ``rw1``.
|
||||
|
||||
swacc
|
||||
^^^^^
|
||||
|
||||
If true, infers an output signal ``hwif_out..swacc`` that is asserted when
|
||||
accessed by software. Specifically, on the same clock cycle that the field is
|
||||
being sampled during a software read operation, or as it is being written.
|
||||
|
||||
.. wavedrom::
|
||||
|
||||
{"signal": [
|
||||
{"name": "clk", "wave": "p...."},
|
||||
{"name": "hwif_in..next", "wave": "x.=x.", "data": ["D"]},
|
||||
{"name": "hwif_out..swacc", "wave": "0.10."}
|
||||
]}
|
||||
|
||||
|
||||
swmod
|
||||
^^^^^
|
||||
|
||||
If true, infers an output signal ``hwif_out..swmod`` that is asserted as the
|
||||
field is being modified by software. This can be due to a software write
|
||||
operation, or a software read operation that has clear/set side-effects.
|
||||
|
||||
|
||||
.. wavedrom::
|
||||
|
||||
{"signal": [
|
||||
{"name": "clk", "wave": "p....."},
|
||||
{"name": "hwif_out..value", "wave": "=..=..", "data": ["old", "new"]},
|
||||
{"name": "hwif_out..swmod", "wave": "0.10.."}
|
||||
]}
|
||||
|
||||
|
||||
swwe/swwel
|
||||
^^^^^^^^^^
|
||||
|
||||
Provides a mechanism that allows hardware to override whether the field is
|
||||
writable by software.
|
||||
|
||||
boolean
|
||||
If True, infers an input signal ``hwif_in..swwe`` or ``hwif_in..swwel``.
|
||||
|
||||
reference
|
||||
Single-bit reference controls field's behavior.
|
||||
|
||||
|
||||
woclr/woset
|
||||
^^^^^^^^^^^
|
||||
See ``onwrite``. These are effectively aliases of the onwrite property.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
Hardware Access Properties
|
||||
--------------------------
|
||||
|
||||
anded/ored/xored
|
||||
^^^^^^^^^^^^^^^^
|
||||
If true, infers the existence of output signal: ``hwif_out..anded``,
|
||||
``hwif_out..ored``, ``hwif_out..xored``
|
||||
|
||||
|
||||
hw
|
||||
^^^
|
||||
Controls hardware access to the field.
|
||||
|
||||
If readable, enables output signal ``hwif_out..value``. If writable, enables
|
||||
input ``hwif_in..next``.
|
||||
|
||||
Hardware-writable fields can optionally define the ``next`` property which replaces
|
||||
the inferred ``hwif_in..next`` input with an alternate reference.
|
||||
|
||||
|
||||
hwclr/hwset
|
||||
^^^^^^^^^^^
|
||||
If both ``hwclr`` and ``hwset`` properties are used, and both are asserted at
|
||||
the same clock cycle, then ``hwset`` will take precedence.
|
||||
|
||||
boolean
|
||||
If true, infers the existence of input signal: ``hwif_in..hwclr``, ``hwif_in..hwset``
|
||||
|
||||
reference
|
||||
Reference to any single-bit internal object to drive this control.
|
||||
|
||||
|
||||
hwenable/hwmask
|
||||
^^^^^^^^^^^^^^^
|
||||
Reference to a component that provides bit-level control of hardware writeability.
|
||||
|
||||
|
||||
we/wel
|
||||
^^^^^^
|
||||
Write-enable control from hardware interface.
|
||||
|
||||
If true, infers the existence of input signal: ``hwif_in..we``, ``hwif_in..wel``
|
||||
|
||||
.. wavedrom::
|
||||
|
||||
{"signal": [
|
||||
{"name": "clk", "wave": "p...."},
|
||||
{"name": "hwif_in..next", "wave": "x.=x.", "data": ["D"]},
|
||||
{"name": "hwif_in..we", "wave": "0.10."},
|
||||
{"name": "hwif_in..wel", "wave": "1.01."},
|
||||
{"name": "<field value>", "wave": "x..=.", "data": ["D"]}
|
||||
]}
|
||||
|
||||
boolean
|
||||
If true, infers the existence of input signal ``hwif_in..we`` or ``hwif_in..wel``
|
||||
|
||||
reference
|
||||
Reference to any single-bit internal object to drive this control.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
Counter Properties
|
||||
------------------
|
||||
|
||||
counter
|
||||
^^^^^^^
|
||||
If true, marks this field as a counter. The counter direction is inferred based
|
||||
based on which properties are assigned. By default, an up-counter is implemented.
|
||||
If any of the properties associated with an up-counter are used, then up-counting
|
||||
capabilities will be implemented. The same is true for down-counters and up/down
|
||||
counters.
|
||||
|
||||
Unless alternate control signals are specified, the existence of input signals
|
||||
``hwif_in..incr`` and ``hwif_in..decr`` will be inferred depending on the type
|
||||
of counter described.
|
||||
|
||||
|
||||
incr
|
||||
^^^^
|
||||
Assign a reference to an alternate control signal to increment the counter.
|
||||
If assigned, the inferred ``hwif_in..incr`` input will not be generated.
|
||||
|
||||
incrsaturate/saturate
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
If assigned, indicates that the counter will saturate instead of wrapping.
|
||||
If an alternate saturation point is specified, the counter value will be
|
||||
adjusted so that it does not exceed that limit, even after non-increment actions.
|
||||
|
||||
boolean
|
||||
If true, saturation point is at the counter's maximum count value. (2^width - 1)
|
||||
|
||||
integer
|
||||
Specify a static saturation value.
|
||||
|
||||
reference
|
||||
Specify a dynamic saturation value.
|
||||
|
||||
|
||||
incrthreshold/threshold
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
If assigned, infers a ``hwif_out..incrthreshold`` output signal. This signal is
|
||||
asserted if the counter value is greater or equal to the threshold.
|
||||
|
||||
.. wavedrom::
|
||||
|
||||
{
|
||||
"signal": [
|
||||
{"name": "clk", "wave": "p......"},
|
||||
{"name": "hwif_in..incr", "wave": "01...0."},
|
||||
{"name": "<counter>", "wave": "=.=3==..", "data": [4,5,6,7,8,9]},
|
||||
{"name": "hwif_out..incrthreshold", "wave": "0..1...."}
|
||||
],
|
||||
"foot": {
|
||||
"text": "Example where incrthreshold = 6"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
boolean
|
||||
If true, threshold is the counter's maximum count value. (2^width - 1)
|
||||
|
||||
integer
|
||||
Specify a static threshold value.
|
||||
|
||||
reference
|
||||
Specify a dynamic threshold value.
|
||||
|
||||
|
||||
incrvalue
|
||||
^^^^^^^^^
|
||||
Override the counter's increment step size.
|
||||
|
||||
integer
|
||||
Specify a static increment step size.
|
||||
|
||||
reference
|
||||
Reference a component that controls the step size.
|
||||
|
||||
incrwidth
|
||||
^^^^^^^^^
|
||||
If assigned, infers an input signal ``hwif_in..incrvalue``. The value of this
|
||||
property defines the signal's width.
|
||||
|
||||
|
||||
overflow
|
||||
^^^^^^^^
|
||||
If true, infers an output signal ``hwif_out..overflow`` that is asserted when
|
||||
the counter is about to wrap.
|
||||
|
||||
.. wavedrom::
|
||||
|
||||
{
|
||||
"signal": [
|
||||
{"name": "clk", "wave": "p......."},
|
||||
{"name": "hwif_in..incr", "wave": "0101010."},
|
||||
{"name": "<counter>", "wave": "=.=.=.=.", "data": [14,15,0,1]},
|
||||
{"name": "hwif_out..overflow", "wave": "0..10..."}
|
||||
],
|
||||
"foot": {
|
||||
"text": "A 4-bit counter overflowing"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
decr
|
||||
^^^^
|
||||
Assign a reference to an alternate control signal to decrement the counter.
|
||||
If assigned, the inferred ``hwif_in..decr`` input will not be generated.
|
||||
|
||||
|
||||
decrsaturate
|
||||
^^^^^^^^^^^^
|
||||
If assigned, indicates that the counter will saturate instead of wrapping.
|
||||
If an alternate saturation point is specified, the counter value will be
|
||||
adjusted so that it does not exceed that limit, even after non-decrement actions.
|
||||
|
||||
boolean
|
||||
If true, saturation point is when the counter reaches 0.
|
||||
|
||||
integer
|
||||
Specify a static saturation value.
|
||||
|
||||
reference
|
||||
Specify a dynamic saturation value.
|
||||
|
||||
|
||||
decrthreshold
|
||||
^^^^^^^^^^^^^
|
||||
If assigned, infers a ``hwif_out..decrthreshold`` output signal. This signal is
|
||||
asserted if the counter value is less than or equal to the threshold.
|
||||
|
||||
.. wavedrom::
|
||||
|
||||
{
|
||||
"signal": [
|
||||
{"name": "clk", "wave": "p......"},
|
||||
{"name": "hwif_in..decr", "wave": "01...0."},
|
||||
{"name": "<counter>", "wave": "=.=3==.", "data": [9,8,7,6,5,4]},
|
||||
{"name": "hwif_out..decrthreshold", "wave": "0..1..."}
|
||||
],
|
||||
"foot": {
|
||||
"text": "Example where incrthreshold = 7"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
boolean
|
||||
If true, threshold is 0.
|
||||
|
||||
integer
|
||||
Specify a static threshold value.
|
||||
|
||||
reference
|
||||
Specify a dynamic threshold value.
|
||||
|
||||
|
||||
decrvalue
|
||||
^^^^^^^^^
|
||||
Override the counter's decrement step size.
|
||||
|
||||
integer
|
||||
Specify a static step size.
|
||||
|
||||
reference
|
||||
Reference to a component that controls the step size.
|
||||
|
||||
|
||||
decrwidth
|
||||
^^^^^^^^^
|
||||
If assigned, infers an input signal ``hwif_in..decrvalue``. The value of this
|
||||
property defines the signal's width.
|
||||
|
||||
|
||||
underflow
|
||||
^^^^^^^^^
|
||||
If true, infers an output signal ``hwif_out..underflow`` that is asserted when
|
||||
the counter is about to wrap.
|
||||
|
||||
.. wavedrom::
|
||||
|
||||
{
|
||||
"signal": [
|
||||
{"name": "clk", "wave": "p......."},
|
||||
{"name": "hwif_in..decr", "wave": "0101010."},
|
||||
{"name": "<counter>", "wave": "=.=.=.=.", "data": [1,0,15,14]},
|
||||
{"name": "hwif_out..underflow", "wave": "0..10..."}
|
||||
],
|
||||
"foot": {
|
||||
"text": "A 4-bit counter underflowing"
|
||||
}
|
||||
}
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
Interrupt Properties
|
||||
--------------------
|
||||
|
||||
intr
|
||||
^^^^
|
||||
|
||||
If set, this field becomes an interrupt field.
|
||||
The enclosing register infers an output signal ``hwif_out..intr`` which denotes
|
||||
that an interrupt is active. This is an or-reduction of all interrupt fields
|
||||
after applying the appropriate ``enable`` or ``mask`` to the field value.
|
||||
|
||||
level (default)
|
||||
Interrupt is level-sensitive. If a bit on the field's ``hwif_in..next`` input
|
||||
is '1', it will trigger an interrupt event.
|
||||
|
||||
posedge
|
||||
If a bit on the field's ``hwif_in..next`` input transitions from '0' to '1',
|
||||
it will trigger an interrupt event. This transition shall still be synchronous
|
||||
to the register block's clock.
|
||||
|
||||
negedge
|
||||
If a bit on the field's ``hwif_in..next`` input transitions from '1' to '0',
|
||||
it will trigger an interrupt event. This transition shall still be synchronous
|
||||
to the register block's clock.
|
||||
|
||||
bothedge
|
||||
If a bit on the field's ``hwif_in..next`` input transitions from '0' to '1' or '1' to '0',
|
||||
it will trigger an interrupt event. This transition shall still be synchronous
|
||||
to the register block's clock.
|
||||
|
||||
nonsticky
|
||||
Interrupt event is not sticky.
|
||||
|
||||
|
||||
enable
|
||||
^^^^^^
|
||||
Reference to a field or signal that, if set to 1, define which bits in the field
|
||||
are used to assert an interrupt.
|
||||
|
||||
|
||||
mask
|
||||
^^^^
|
||||
Reference to a field or signal that, if set to 1, define which bits in the field
|
||||
are *not* used to assert an interrupt.
|
||||
|
||||
|
||||
haltenable
|
||||
^^^^^^^^^^
|
||||
Reference to a field or signal that, if set to 1, define which bits in the field
|
||||
are used to assert the halt output.
|
||||
|
||||
If this property is set, the enclosing register will infer a ``hwif_out..halt`` output.
|
||||
|
||||
|
||||
haltmask
|
||||
^^^^^^^^
|
||||
Reference to a field or signal that, if set to 1, define which bits in the field
|
||||
are *not* used to assert the halt output.
|
||||
|
||||
If this property is set, the enclosing register will infer a ``hwif_out..halt`` output.
|
||||
|
||||
|
||||
stickybit
|
||||
^^^^^^^^^
|
||||
When an interrupt trigger occurs, a stickybit field will set the corresponding
|
||||
bit to '1' and hold it until it is cleared by a software access.
|
||||
|
||||
The interrupt trigger depends on the interrupt type. By default, interrupts are
|
||||
level-sensitive, but the interrupt modifiers allow for edge-sensitive triggers as
|
||||
well.
|
||||
|
||||
The waveform below demonstrates a level-sensitive interrupt:
|
||||
|
||||
.. wavedrom::
|
||||
|
||||
{
|
||||
"signal": [
|
||||
{"name": "clk", "wave": "p....."},
|
||||
{"name": "hwif_in..next", "wave": "010..."},
|
||||
{"name": "<field value>", "wave": "0.1..."}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
sticky
|
||||
^^^^^^
|
||||
Unlike ``stickybit`` fields, a sticky field will latch an entire value. The
|
||||
value is latched as soon as ``hwif_in..next`` is nonzero, and is held until the
|
||||
field contents are cleared back to 0 by a software access.
|
||||
|
||||
.. wavedrom::
|
||||
|
||||
{
|
||||
"signal": [
|
||||
{"name": "clk", "wave": "p....."},
|
||||
{"name": "hwif_in..next", "wave": "23.22.", "data": [0,10,20,30]},
|
||||
{"name": "<field value>", "wave": "2.3...", "data": [0, 10]}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
Misc
|
||||
----
|
||||
|
||||
encode
|
||||
^^^^^^
|
||||
If assigned a user-defined enumeration, the resulting package file will include
|
||||
its definition. Due to limitations from type-strictness rules in SystemVerilog,
|
||||
the field will remain as a ``logic`` datatype.
|
||||
|
||||
|
||||
next
|
||||
^^^^
|
||||
If assigned, replaces the inferred ``hwif_in..next`` input with an explicit reference.
|
||||
|
||||
|
||||
paritycheck
|
||||
^^^^^^^^^^^
|
||||
If set, enables parity checking for this field.
|
||||
|
||||
Adds a ``parity_error`` output signal to the module.
|
||||
|
||||
.. note::
|
||||
|
||||
If this field does not implement storage, the ``partycheck`` property is ignored.
|
||||
|
||||
|
||||
|
||||
precedence
|
||||
^^^^^^^^^^
|
||||
Control whether hardware or software has precedence when field value update
|
||||
contention occurs. Software has precedence by default.
|
||||
|
||||
reset
|
||||
^^^^^
|
||||
Control the reset value of the field's storage element.
|
||||
If not specified, the field will not be reset.
|
||||
|
||||
integer
|
||||
Static reset value
|
||||
|
||||
reference
|
||||
Reference to a dynamic reset value.
|
||||
|
||||
resetsignal
|
||||
^^^^^^^^^^^
|
||||
Provide an alternate reset trigger for this field.
|
||||
14
docs/props/reg.rst
Normal file
@@ -0,0 +1,14 @@
|
||||
Register Properties
|
||||
===================
|
||||
|
||||
.. note:: Any properties not explicitly listed here are either implicitly
|
||||
supported, or are not relevant to the regblock exporter and are ignored.
|
||||
|
||||
accesswidth
|
||||
-----------
|
||||
Control the software access width. The register block's CPUIF bus width is
|
||||
determined by the maximum accesswidth encountered.
|
||||
|
||||
regwidth
|
||||
--------
|
||||
Control the bit-width of the register.
|
||||
182
docs/props/rhs_props.rst
Normal file
@@ -0,0 +1,182 @@
|
||||
RHS Property References
|
||||
=======================
|
||||
|
||||
SystemRDL allows some properties to be referenced in the righthand-side of
|
||||
property assignment expressions:
|
||||
|
||||
.. code-block:: systemrdl
|
||||
|
||||
some_property = my_reg.my_field -> some_property;
|
||||
|
||||
The official SystemRDL spec refers to these as "Ref targets" in Table G1, but
|
||||
unfortunately does not describe their semantics in much detail.
|
||||
|
||||
The text below describes the interpretations used for this exporter.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
Field
|
||||
-----
|
||||
|
||||
field -> swacc
|
||||
^^^^^^^^^^^^^^
|
||||
Single-cycle strobe that indicates the field is being accessed by software
|
||||
(read or write).
|
||||
|
||||
|
||||
field -> swmod
|
||||
^^^^^^^^^^^^^^^
|
||||
Single-cycle strobe that indicates the field is being modified during a software
|
||||
access operation.
|
||||
|
||||
|
||||
field -> swwe/swwel
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
Represents the signal that controls the field's swwe/swwel behavior.
|
||||
|
||||
|
||||
field -> anded/ored/xored
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Represents the current and/or/xor reduction of the field's value.
|
||||
|
||||
|
||||
field -> hwclr/hwset
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|EX|
|
||||
|
||||
Represents the signal that controls the field's hwclr/hwset behavior.
|
||||
|
||||
|
||||
field -> hwenable/hwmask
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Represents the signal that controls the field's hwenable/hwmask behavior.
|
||||
|
||||
field -> we/wel
|
||||
^^^^^^^^^^^^^^^
|
||||
Represents the signal that controls the field's we/wel behavior.
|
||||
|
||||
field -> next
|
||||
^^^^^^^^^^^^^
|
||||
|EX|
|
||||
|
||||
field -> reset
|
||||
^^^^^^^^^^^^^^
|
||||
Represents the value that was assigned to this property.
|
||||
|
||||
field -> resetsignal
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
Represents the value that was assigned to this property.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
Field Counter Properties
|
||||
------------------------
|
||||
|
||||
field -> incr
|
||||
^^^^^^^^^^^^^
|
||||
Represents the signal that controls the field's counter increment control.
|
||||
|
||||
|
||||
field -> incrsaturate/saturate
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Represents the internal 1-bit event signal that indicates whether the counter is saturated
|
||||
at its saturation value.
|
||||
|
||||
.. wavedrom::
|
||||
|
||||
{
|
||||
"signal": [
|
||||
{"name": "clk", "wave": "p......"},
|
||||
{"name": "hwif_in..decr", "wave": "0101010"},
|
||||
{"name": "<counter>", "wave": "=.=....", "data": [1,0]},
|
||||
{"name": "<decrsaturate>", "wave": "0.1...."}
|
||||
],
|
||||
"foot": {
|
||||
"text": "A 4-bit counter saturating"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
field -> incrthreshold/threshold
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Represents the 1-bit event signal that indicates whether the counter has met or
|
||||
exceeded its incrthreshold.
|
||||
|
||||
field -> incrvalue
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
Represents the value that was assigned to this property.
|
||||
|
||||
field -> overflow
|
||||
^^^^^^^^^^^^^^^^^
|
||||
Represents the event signal that is asserted when the counter is about to wrap.
|
||||
|
||||
field -> decr
|
||||
^^^^^^^^^^^^^
|
||||
Represents the signal that controls the field's counter decrement control.
|
||||
|
||||
field -> decrsaturate
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
Represents the internal 1-bit event signal that indicates whether the counter is saturated
|
||||
at its saturation value.
|
||||
|
||||
.. wavedrom::
|
||||
|
||||
{
|
||||
"signal": [
|
||||
{"name": "clk", "wave": "p......"},
|
||||
{"name": "hwif_in..incr", "wave": "0101010"},
|
||||
{"name": "<counter>", "wave": "=.=....", "data": [14,15]},
|
||||
{"name": "<incrsaturate>", "wave": "0.1...."}
|
||||
],
|
||||
"foot": {
|
||||
"text": "A 4-bit counter saturating"
|
||||
}
|
||||
}
|
||||
|
||||
field -> decrthreshold
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
Represents the 1-bit event signal that indicates whether the counter has met or
|
||||
exceeded its incrthreshold.
|
||||
|
||||
field -> decrvalue
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
Represents the value that was assigned to this property.
|
||||
|
||||
field -> underflow
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
Represents the event signal that is asserted when the counter is about to wrap.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
Field Interrupt Properties
|
||||
--------------------------
|
||||
|
||||
field -> enable
|
||||
^^^^^^^^^^^^^^^
|
||||
Represents the value that was assigned to this property.
|
||||
|
||||
field -> mask
|
||||
^^^^^^^^^^^^^
|
||||
Represents the value that was assigned to this property.
|
||||
|
||||
field -> haltenable
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
Represents the value that was assigned to this property.
|
||||
|
||||
field -> haltmask
|
||||
^^^^^^^^^^^^^^^^^
|
||||
Represents the value that was assigned to this property.
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
Register
|
||||
--------
|
||||
|
||||
reg -> intr
|
||||
^^^^^^^^^^^
|
||||
References the register's ``hwif_out..intr`` signal.
|
||||
|
||||
reg -> halt
|
||||
^^^^^^^^^^^
|
||||
References the register's ``hwif_out..halt`` signal.
|
||||
28
docs/props/signal.rst
Normal file
@@ -0,0 +1,28 @@
|
||||
Signal Properties
|
||||
=================
|
||||
|
||||
.. note:: Any properties not explicitly listed here are either implicitly
|
||||
supported, or are not relevant to the regblock exporter and are ignored.
|
||||
|
||||
|
||||
activehigh/activelow
|
||||
--------------------
|
||||
Only relevant for signals used as resets. Defines the reset signal's polarity.
|
||||
|
||||
|
||||
sync/async
|
||||
----------
|
||||
Only supported for signals used as resets to infer edge-sensitive reset.
|
||||
Ignored in all other contexts.
|
||||
|
||||
|
||||
cpuif_reset
|
||||
-----------
|
||||
Specify that this signal shall be used as alternate reset signal for the CPU
|
||||
interface for this regblock.
|
||||
|
||||
|
||||
field_reset
|
||||
-----------
|
||||
Specify that this signal is used as an alternate reset signal for all fields
|
||||
instantiated in sub-hierarchies relative to this signal.
|
||||
155
docs/rdl_features/external.rst
Normal file
@@ -0,0 +1,155 @@
|
||||
External Components
|
||||
===================
|
||||
SystemRDL allows some component instances to be defined as "external" elements
|
||||
of an address space definition. In the context of this regblock generator,
|
||||
the implementation of an external component is left up to the designer. When
|
||||
generating the RTL for a regblock, the implementations of external components
|
||||
are omitted and instead a user-interface is presented on the
|
||||
``hwif_in``/``hwif_out`` i/o structs.
|
||||
|
||||
External component signals on the hardware interface closely follow the semantics
|
||||
of the :ref:`cpuif_protocol`.
|
||||
|
||||
|
||||
Things you should know
|
||||
----------------------
|
||||
|
||||
* By default external ``hwif_out`` signals are driven combinationally. An
|
||||
optional output retiming stage can be enabled if needed.
|
||||
* Due to the uncertain access latency of external components, the regblock will
|
||||
only issue one outstanding transaction to an external component at a time.
|
||||
This is enforced even if the CPUIF is capable of pipelined accesses such as
|
||||
AXI4-Lite.
|
||||
|
||||
|
||||
External Registers
|
||||
------------------
|
||||
External registers can be useful if it is necessary to implement a register that
|
||||
cannot easily be expressed using SystemRDL semantics. This could be a unique
|
||||
access policy, or FIFO-like push/pop registers.
|
||||
|
||||
External registers are annotated as such by using the ``external`` keyword:
|
||||
|
||||
.. code-block:: systemrdl
|
||||
|
||||
// An internal register
|
||||
my_reg int_reg;
|
||||
|
||||
// An external register
|
||||
external my_reg ext_reg;
|
||||
|
||||
Request
|
||||
^^^^^^^
|
||||
hwif_out..req
|
||||
When asserted, a read or write transfer will be initiated.
|
||||
Qualifies all other request signals.
|
||||
|
||||
If the register is wide (``regwidth`` > ``accesswidth``), then the
|
||||
``hwif_out..req`` will consist of multiple bits, representing the access
|
||||
strobe for each sub-word of the register.
|
||||
|
||||
If the register does not contain any readable fields, this strobe will be
|
||||
suppressed for read operations.
|
||||
|
||||
If the register does not contain any writable readable fields, this strobe
|
||||
will be suppressed for write operations.
|
||||
|
||||
hwif_out..req_is_wr
|
||||
If ``1``, denotes that the current transfer is a write. Otherwise transfer is
|
||||
a read.
|
||||
|
||||
hwif_out..wr_data
|
||||
Data to be written for the write transfer. This signal is ignored for read
|
||||
transfers.
|
||||
|
||||
The bit-width of this signal always matches the CPUIF's bus width,
|
||||
regardless of the regwidth.
|
||||
|
||||
If the register does not contain any writable fields, this signal is omitted.
|
||||
|
||||
hwif_out..wr_biten
|
||||
Active-high bit-level write-enable strobes.
|
||||
Only asserted bit positions will change the register value during a write
|
||||
transfer.
|
||||
|
||||
If the register does not contain any writable fields, this signal is omitted.
|
||||
|
||||
|
||||
Read Response
|
||||
^^^^^^^^^^^^^
|
||||
hwif_in..rd_ack
|
||||
Single-cycle strobe indicating a read transfer has completed.
|
||||
Qualifies all other read response signals.
|
||||
|
||||
If the transfer is always completed in the same cycle, it is acceptable to
|
||||
tie this signal to ``hwif_out..req && !hwif_out..req_is_wr``.
|
||||
|
||||
If the register does not contain any readable fields, this signal is omitted.
|
||||
|
||||
hwif_in..rd_data
|
||||
Read response data.
|
||||
|
||||
If the register does not contain any readable fields, this signal is omitted.
|
||||
|
||||
Write Response
|
||||
^^^^^^^^^^^^^^
|
||||
hwif_in..wr_ack
|
||||
Single-cycle strobe indicating a write transfer has completed.
|
||||
|
||||
If the transfer is always completed in the same cycle, it is acceptable to
|
||||
tie this signal to ``hwif_out..req && hwif_out..req_is_wr``.
|
||||
|
||||
If the register does not contain any writable fields, this signal is omitted.
|
||||
|
||||
|
||||
|
||||
External Blocks
|
||||
---------------
|
||||
Broader external address regions can be represented by external block-like
|
||||
components such as ``addrmap``, ``regfile`` or ``mem`` elements.
|
||||
|
||||
To ensure address decoding for external blocks is simple (only requires simple bit-pruning),
|
||||
blocks that are external to an exported regblock shall be aligned to their size.
|
||||
|
||||
Request
|
||||
^^^^^^^
|
||||
hwif_out..req
|
||||
When asserted, a read or write transfer will be initiated.
|
||||
Qualifies all other request signals.
|
||||
|
||||
hwif_out..addr
|
||||
Byte-address of the transfer.
|
||||
|
||||
Address is always relative to the block's local addressing. i.e: The first
|
||||
byte within an external block is represented as ``hwif_out..addr`` == 0,
|
||||
regardless of the absolute address of the block.
|
||||
|
||||
hwif_out..req_is_wr
|
||||
If ``1``, denotes that the current transfer is a write. Otherwise transfer is
|
||||
a read.
|
||||
|
||||
hwif_out..wr_data
|
||||
Data to be written for the write transfer. This signal is ignored for read
|
||||
transfers.
|
||||
|
||||
The bit-width of this signal always matches the CPUIF's bus width,
|
||||
regardless of the contents of the block.
|
||||
|
||||
hwif_out..wr_biten
|
||||
Active-high bit-level write-enable strobes.
|
||||
Only asserted bit positions will change the register value during a write
|
||||
transfer.
|
||||
|
||||
Read Response
|
||||
^^^^^^^^^^^^^
|
||||
hwif_in..rd_ack
|
||||
Single-cycle strobe indicating a read transfer has completed.
|
||||
Qualifies all other read response signals.
|
||||
|
||||
hwif_in..rd_data
|
||||
Read response data.
|
||||
|
||||
Write Response
|
||||
^^^^^^^^^^^^^^
|
||||
hwif_in..wr_ack
|
||||
Single-cycle strobe indicating a write transfer has completed.
|
||||
3
docs/requirements.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
pygments-systemrdl
|
||||
sphinxcontrib-wavedrom
|
||||
sphinx-book-theme
|
||||
49
docs/udps/extended_swacc.rst
Normal file
@@ -0,0 +1,49 @@
|
||||
.. _extended_swacc:
|
||||
|
||||
Read/Write-specific swacc
|
||||
=========================
|
||||
|
||||
SystemRDL defines the ``swacc`` property, but it does not distinguish between
|
||||
read and write operations - it is asserted on *all* software accesses.
|
||||
Similarly, the spec defines ``swmod`` which gets asserted on software writes,
|
||||
but can also get asserted if the field has on-read side-effects.
|
||||
|
||||
What if you just wanted a plain and simple strobe that is asserted when software
|
||||
reads or writes a field? The ``rd_swacc`` and ``wr_swacc`` UDPs provide this
|
||||
functionality.
|
||||
|
||||
|
||||
Properties
|
||||
----------
|
||||
These UDP definitions, along with others supported by PeakRDL-regblock can be
|
||||
enabled by compiling the following file along with your design:
|
||||
:download:`regblock_udps.rdl <../../hdl-src/regblock_udps.rdl>`.
|
||||
|
||||
.. describe:: rd_swacc
|
||||
|
||||
If true, infers an output signal ``hwif_out..rd_swacc`` that is asserted
|
||||
when accessed by a software read operation. The output signal is asserted
|
||||
on the same clock cycle that the field is being sampled during the software
|
||||
read operation.
|
||||
|
||||
.. wavedrom::
|
||||
|
||||
{"signal": [
|
||||
{"name": "clk", "wave": "p...."},
|
||||
{"name": "hwif_in..next", "wave": "x.=x.", "data": ["D"]},
|
||||
{"name": "hwif_out..rd_swacc", "wave": "0.10."}
|
||||
]}
|
||||
|
||||
|
||||
.. describe:: wr_swacc
|
||||
|
||||
If true, infers an output signal ``hwif_out..wr_swacc`` that is asserted
|
||||
as the field is being modified by a software write operation.
|
||||
|
||||
.. wavedrom::
|
||||
|
||||
{"signal": [
|
||||
{"name": "clk", "wave": "p....."},
|
||||
{"name": "hwif_out..value", "wave": "=..=..", "data": ["old", "new"]},
|
||||
{"name": "hwif_out..wr_swacc", "wave": "0.10.."}
|
||||
]}
|
||||
103
docs/udps/fixedpoint.rst
Normal file
@@ -0,0 +1,103 @@
|
||||
.. _fixedpoint:
|
||||
|
||||
Fixed-Point Fields
|
||||
==================
|
||||
|
||||
`Fixed-point <https://en.wikipedia.org/wiki/Fixed-point_arithmetic>`_ numbers
|
||||
can be used to efficiently represent real numbers using integers. Fixed-point
|
||||
numbers consist of some combination of integer bits and fractional bits. The
|
||||
number of integer/fractional bits is usually implicitly tracked (not stored)
|
||||
for each number, unlike for floating-point numbers.
|
||||
|
||||
For this SystemVerilog exporter, these properties only affect the signal type in
|
||||
the the ``hwif`` structs. There is no special handling in the internals of
|
||||
the regblock.
|
||||
|
||||
Properties
|
||||
----------
|
||||
Fields can be declared as fixed-point numbers using the following two properties:
|
||||
|
||||
.. literalinclude:: ../../hdl-src/regblock_udps.rdl
|
||||
:lines: 46-54
|
||||
|
||||
The :ref:`is_signed<signed>` property can be used in conjunction with these
|
||||
properties to declare signed fixed-point fields.
|
||||
|
||||
These UDP definitions, along with others supported by PeakRDL-regblock, can be
|
||||
enabled by compiling the following file along with your design:
|
||||
:download:`regblock_udps.rdl <../../hdl-src/regblock_udps.rdl>`.
|
||||
|
||||
.. describe:: intwidth
|
||||
|
||||
* The ``intwidth`` property defines the number of integer bits in the
|
||||
fixed-point representation (including the sign bit, if present).
|
||||
|
||||
.. describe:: fracwidth
|
||||
|
||||
* The ``fracwidth`` property defines the number of fractional bits in the
|
||||
fixed-point representation.
|
||||
|
||||
Representable Numbers
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The range of representable real numbers is summarized in the table below.
|
||||
|
||||
.. list-table:: Representable Numbers
|
||||
:header-rows: 1
|
||||
|
||||
* - Signedness
|
||||
- Minimum Value
|
||||
- Maximum Value
|
||||
- Step Size
|
||||
|
||||
* - Unsigned
|
||||
- :math:`0`
|
||||
- :math:`2^{\mathrm{intwidth}} - 2^{-\mathrm{fracwidth}}`
|
||||
- :math:`2^{-\mathrm{fracwidth}}`
|
||||
|
||||
* - Signed
|
||||
- :math:`-2^{\mathrm{intwidth}-1}`
|
||||
- :math:`2^{\mathrm{intwidth}-1} - 2^{-\mathrm{fracwidth}}`
|
||||
- :math:`2^{-\mathrm{fracwidth}}`
|
||||
|
||||
SystemVerilog Types
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
When either ``intwidth`` or ``fracwidth`` are defined for a field, that field's
|
||||
type in the generated SystemVerilog ``hwif`` struct is
|
||||
``logic (signed) [intwidth-1:-fracwidth]``. The bit at index :math:`i` contributes
|
||||
a weight of :math:`2^i` to the real number represented.
|
||||
|
||||
Other Rules
|
||||
^^^^^^^^^^^
|
||||
* Only one of ``intwidth`` or ``fracwidth`` need be defined. The other is
|
||||
inferred from the field bit width.
|
||||
* The bit width of the field shall be equal to ``intwidth`` + ``fracwidth``.
|
||||
* If both ``intwidth`` and ``fracwidth`` are defined for a field, it is an
|
||||
error if their sum does not equal the bit width of the field.
|
||||
* Either ``fracwidth`` or ``intwidth`` can be a negative integer. Because
|
||||
SystemRDL does not have a signed integer type, the only way to achieve
|
||||
this is to define one of the widths as larger than the bit width of the
|
||||
component so that the other width is inferred as a negative number.
|
||||
* The properties defined above are mutually exclusive with the ``counter``
|
||||
property.
|
||||
* The properties defined above are mutually exclusive with the ``encode``
|
||||
property.
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
A 12-bit signed fixed-point field with 4 integer bits and 8 fractional bits
|
||||
can be declared with
|
||||
|
||||
.. code-block:: systemrdl
|
||||
:emphasize-lines: 3, 4
|
||||
|
||||
field {
|
||||
sw=rw; hw=r;
|
||||
intwidth = 4;
|
||||
is_signed;
|
||||
} fixedpoint_num[11:0] = 0;
|
||||
|
||||
This field can represent values from -8.0 to 7.99609375
|
||||
in steps of 0.00390625.
|
||||
85
docs/udps/intro.rst
Normal file
@@ -0,0 +1,85 @@
|
||||
Introduction
|
||||
============
|
||||
|
||||
Although the official SystemRDL spec defines numerous properties that allow you
|
||||
to define complex register map structures, sometimes they are not enough to
|
||||
accurately describe a necessary feature. Fortunately the SystemRDL spec allows
|
||||
the language to be extended using "User Defined Properties" (UDPs). The
|
||||
PeakRDL-regblock tool understands several UDPs that are described in this
|
||||
section.
|
||||
|
||||
To enable these UDPs, compile this RDL file prior to the rest of your design:
|
||||
:download:`regblock_udps.rdl <../../hdl-src/regblock_udps.rdl>`.
|
||||
|
||||
.. list-table:: Summary of UDPs
|
||||
:header-rows: 1
|
||||
|
||||
* - Name
|
||||
- Component
|
||||
- Type
|
||||
- Description
|
||||
|
||||
* - buffer_reads
|
||||
- reg
|
||||
- boolean
|
||||
- If set, reads from the register are double-buffered.
|
||||
|
||||
See: :ref:`read_buffering`.
|
||||
|
||||
* - rbuffer_trigger
|
||||
- reg
|
||||
- reference
|
||||
- Defines the buffered read load trigger.
|
||||
|
||||
See: :ref:`read_buffering`.
|
||||
|
||||
* - buffer_writes
|
||||
- reg
|
||||
- boolean
|
||||
- If set, writes to the register are double-buffered.
|
||||
|
||||
See: :ref:`write_buffering`.
|
||||
|
||||
* - wbuffer_trigger
|
||||
- reg
|
||||
- reference
|
||||
- Defines the buffered write commit trigger.
|
||||
|
||||
See: :ref:`write_buffering`.
|
||||
|
||||
* - rd_swacc
|
||||
- field
|
||||
- boolean
|
||||
- Enables an output strobe that is asserted on sw reads.
|
||||
|
||||
See: :ref:`extended_swacc`.
|
||||
|
||||
* - wr_swacc
|
||||
- field
|
||||
- boolean
|
||||
- Enables an output strobe that is asserted on sw writes.
|
||||
|
||||
See: :ref:`extended_swacc`.
|
||||
|
||||
* - is_signed
|
||||
- field
|
||||
- boolean
|
||||
- Defines the signedness of a field.
|
||||
|
||||
See: :ref:`signed`.
|
||||
|
||||
* - intwidth
|
||||
- field
|
||||
- unsigned integer
|
||||
- Defines the number of integer bits in the fixed-point representation
|
||||
of a field.
|
||||
|
||||
See: :ref:`fixedpoint`.
|
||||
|
||||
* - fracwidth
|
||||
- field
|
||||
- unsigned integer
|
||||
- Defines the number of fractional bits in the fixed-point representation
|
||||
of a field.
|
||||
|
||||
See: :ref:`fixedpoint`.
|
||||
164
docs/udps/read_buffering.rst
Normal file
@@ -0,0 +1,164 @@
|
||||
.. _read_buffering:
|
||||
|
||||
Read-buffered Registers
|
||||
=======================
|
||||
|
||||
Read buffering is a mechanism that allows for software accesses to read a
|
||||
snapshot of one or more registers atomically. When enabled on a register, a
|
||||
read buffer will latch the state of its fields when triggered such that software
|
||||
can read a coherent snapshot of one or more registers' value.
|
||||
|
||||
Some examples of when this is useful:
|
||||
* A wide 64-bit status register needs to be read atomically, but the CPU
|
||||
interface is only 32-bits.
|
||||
* Software needs to be able to read the state of multiple registers
|
||||
atomically.
|
||||
* A hardware event latches the software-visible state of one or more
|
||||
registers.
|
||||
|
||||
.. figure:: ../diagrams/rbuf.png
|
||||
|
||||
|
||||
Properties
|
||||
----------
|
||||
The behavior of read-buffered registers is defined using the following two
|
||||
properties:
|
||||
|
||||
.. literalinclude:: ../../hdl-src/regblock_udps.rdl
|
||||
:lines: 10-18
|
||||
|
||||
These UDP definitions, along with others supported by PeakRDL-regblock can be
|
||||
enabled by compiling the following file along with your design:
|
||||
:download:`regblock_udps.rdl <../../hdl-src/regblock_udps.rdl>`.
|
||||
|
||||
.. describe:: buffer_reads
|
||||
|
||||
* Assigned value is a boolean.
|
||||
* If true, enables double-buffering of software reads of this register.
|
||||
* The read buffer will load the register's field values when its trigger
|
||||
event is asserted.
|
||||
* Unless specified otherwise, the buffer trigger occurs when the lowest
|
||||
address of the buffered register is read.
|
||||
* When read by software the data returned is from the buffer contents, not
|
||||
directly from the register's fields.
|
||||
|
||||
.. describe:: rbuffer_trigger
|
||||
|
||||
* Assigned value is a reference to a register, single-bit field, signal, or
|
||||
single-bit property.
|
||||
* Controls when the double-buffer loads the register's field vaues into the
|
||||
buffer storage element.
|
||||
* If reference is a single-bit value (signal, field, property reference),
|
||||
then the assertion of that value triggers the buffer to be evicted.
|
||||
* Signal references shall have either activehigh/activelow property set to
|
||||
define the polarity.
|
||||
* If the reference is a reg, then buffer is loaded when the register's
|
||||
lowest address is read.
|
||||
|
||||
Other Rules
|
||||
^^^^^^^^^^^
|
||||
* It is an error to set ``buffer_reads`` if the register does not contain any
|
||||
readable fields
|
||||
* If ``buffer_reads`` is false, then anything assigned to ``rbuffer_trigger``
|
||||
is ignored.
|
||||
* The buffered register and the trigger reference shall both be within the same
|
||||
internal device. ie: one cannot be in an external scope with respect to the
|
||||
other.
|
||||
* Unless it is a register, the reference assigned to ``rbuffer_trigger`` shall
|
||||
represent a single bit.
|
||||
* The software read operation considered to take place when the buffer is loaded.
|
||||
This influences the behavior of properties like ``swmod`` and ``swacc`` -
|
||||
they are not asserted until the register's fields are actually sampled by the
|
||||
buffer.
|
||||
* If a read-buffered register is wide (accesswidth < regwidth) and is its own
|
||||
trigger, the first sub-word's buffer is bypassed to ensure the first read
|
||||
operation is atomically coherent with the rest of the sampled register.
|
||||
|
||||
|
||||
Examples
|
||||
--------
|
||||
Below are several examples of what you can do with registers that are
|
||||
read-buffered.
|
||||
|
||||
Wide Atomic Register
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
In this example, a wide 64-bit read-clear counter is implemented.
|
||||
Without read-buffering, it is impossible to coherently read the state of the
|
||||
counter using a 32-bit CPU interface without risking a discontinuity. With
|
||||
read-buffering enabled, the read of the lower half of the register will trigger
|
||||
the upper half's value to be latched. A subsequent software access can then
|
||||
coherently read the rest of the register's buffered value.
|
||||
|
||||
.. code-block:: systemrdl
|
||||
:emphasize-lines: 4
|
||||
|
||||
reg {
|
||||
regwidth = 64;
|
||||
accesswidth = 32;
|
||||
buffer_reads = true;
|
||||
field {
|
||||
sw=r; hw=na;
|
||||
counter;
|
||||
incr;
|
||||
} my_counter[63:0] = 0;
|
||||
};
|
||||
|
||||
|
||||
Atomic Group of Registers
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Perhaps you have a group of registers that monitor some rapidly-changing state
|
||||
within your design. Using the ``rbuffer_trigger`` property, you can define which
|
||||
register read operation triggers the buffered registers' values to be latched.
|
||||
|
||||
.. code-block:: systemrdl
|
||||
:emphasize-lines: 11-14
|
||||
|
||||
reg my_status_reg {
|
||||
field {
|
||||
sw=r; hw=w;
|
||||
} value[31:0];
|
||||
};
|
||||
|
||||
my_status_reg status1;
|
||||
my_status_reg status2;
|
||||
my_status_reg status3;
|
||||
|
||||
status2->buffer_reads = true;
|
||||
status2->rbuffer_trigger = status1;
|
||||
status3->buffer_reads = true;
|
||||
status3->rbuffer_trigger = status1;
|
||||
|
||||
In this example, when software reads status1, this triggers status2-status3
|
||||
registers to latch their values into their respective read buffers. Subsequent
|
||||
reads to status2 and status3 return the value that these registers contained at
|
||||
the moment that status1 was read. This makes it possible for software to read
|
||||
the state of multiple registers atomically.
|
||||
|
||||
|
||||
Externally Triggered Register Sampling
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
If needed, an external trigger can be used to load a read buffer.
|
||||
This can be useful if precise timing of software's view of the register state is
|
||||
required.
|
||||
|
||||
.. code-block:: systemrdl
|
||||
:emphasize-lines: 14-15
|
||||
|
||||
reg my_status_reg {
|
||||
buffer_reads = true;
|
||||
field {
|
||||
sw=r; hw=w;
|
||||
} value[31:0];
|
||||
};
|
||||
|
||||
my_status_reg status1;
|
||||
my_status_reg status2;
|
||||
|
||||
signal {
|
||||
activehigh;
|
||||
} trigger_signal;
|
||||
status1->rbuffer_trigger = trigger_signal;
|
||||
status2->rbuffer_trigger = trigger_signal;
|
||||
|
||||
When ``hwif_in..trigger_signal`` is asserted, the state of registers ``status1``
|
||||
and ``status2`` is buffered.
|
||||
74
docs/udps/signed.rst
Normal file
@@ -0,0 +1,74 @@
|
||||
.. _signed:
|
||||
|
||||
Signed Fields
|
||||
=============
|
||||
|
||||
SystemRDL does not natively provide a way to mark fields as signed or unsigned.
|
||||
The ``is_signed`` user-defined property fills this need.
|
||||
|
||||
For this SystemVerilog exporter, marking a field as signed only affects the
|
||||
signal type in the ``hwif`` structs. There is no special handling in the internals
|
||||
of the regblock.
|
||||
|
||||
Properties
|
||||
----------
|
||||
A field can be marked as signed using the following user-defined property:
|
||||
|
||||
.. literalinclude:: ../../hdl-src/regblock_udps.rdl
|
||||
:lines: 40-44
|
||||
|
||||
This UDP definition, along with others supported by PeakRDL-regblock, can be
|
||||
enabled by compiling the following file along with your design:
|
||||
:download:`regblock_udps.rdl <../../hdl-src/regblock_udps.rdl>`.
|
||||
|
||||
.. describe:: is_signed
|
||||
|
||||
* Assigned value is a boolean.
|
||||
* If true, the hardware interface field will have the type
|
||||
``logic signed [width-1:0]``.
|
||||
* If false or not defined for a field, the hardware interface field will
|
||||
have the type ``logic [width-1:0]``, which is unsigned by definition.
|
||||
|
||||
Other Rules
|
||||
^^^^^^^^^^^
|
||||
|
||||
* ``is_signed=true`` is mutually exclusive with the ``counter`` property.
|
||||
* ``is_signed=true`` is mutually exclusive with the ``encode`` property.
|
||||
|
||||
Examples
|
||||
--------
|
||||
Below are some examples of fields with different signedness.
|
||||
|
||||
Signed Fields
|
||||
^^^^^^^^^^^^^
|
||||
.. code-block:: systemrdl
|
||||
:emphasize-lines: 3, 8
|
||||
|
||||
field {
|
||||
sw=rw; hw=r;
|
||||
is_signed;
|
||||
} signed_num[63:0] = 0;
|
||||
|
||||
field {
|
||||
sw=r; hw=w;
|
||||
is_signed = true;
|
||||
} another_signed_num[19:0] = 20'hFFFFF; // -1
|
||||
|
||||
SystemRDL's own integer type is always unsigned. In order to specify a negative
|
||||
reset value, the two's complement value must be used as shown in the second
|
||||
example above.
|
||||
|
||||
Unsigned Fields
|
||||
^^^^^^^^^^^^^^^
|
||||
.. code-block:: systemrdl
|
||||
:emphasize-lines: 3, 8
|
||||
|
||||
field {
|
||||
sw=rw; hw=r;
|
||||
// fields are unsigned by default
|
||||
} unsigned_num[63:0] = 0;
|
||||
|
||||
field {
|
||||
sw=r; hw=w;
|
||||
is_signed = false;
|
||||
} another_unsigned_num[19:0] = 0;
|
||||
183
docs/udps/write_buffering.rst
Normal file
@@ -0,0 +1,183 @@
|
||||
.. _write_buffering:
|
||||
|
||||
Write-buffered Registers
|
||||
========================
|
||||
|
||||
In order to support larger software write accesses that are atomic, the
|
||||
regblock generator understands several UDPs that implement write-buffering to
|
||||
specific registers. This causes the regblock to delay the effect of a software
|
||||
write operation until a defined trigger event.
|
||||
|
||||
Some examples of when this is useful:
|
||||
* You need to have software update a wide 64-bit register atomically, but
|
||||
the CPU interface is only 32-bits.
|
||||
* Software needs to be able to write multiple registers such that the
|
||||
hardware is updated atomically.
|
||||
* Software can pre-load one or more registers with their next value, and
|
||||
trigger the update via an external hardware signal.
|
||||
|
||||
If a register is write-buffered, a holding buffer stage is inserted between the
|
||||
decode logic and the field logic. This effectively defers any software write
|
||||
operations to that register until a trigger event occurs that releases it.
|
||||
Write buffering storage is unique to each register that enables it.
|
||||
If a register is not write buffered, this buffer stage is bypassed.
|
||||
|
||||
.. figure:: ../diagrams/wbuf.png
|
||||
|
||||
|
||||
Properties
|
||||
----------
|
||||
The behavior of write-buffered registers is defined using the following two
|
||||
properties:
|
||||
|
||||
.. literalinclude:: ../../hdl-src/regblock_udps.rdl
|
||||
:lines: 20-28
|
||||
|
||||
These UDP definitions, along with others supported by PeakRDL-regblock can be
|
||||
enabled by compiling the following file along with your design:
|
||||
:download:`regblock_udps.rdl <../../hdl-src/regblock_udps.rdl>`.
|
||||
|
||||
.. describe:: buffer_writes
|
||||
|
||||
* Assigned value is a boolean.
|
||||
* If true, enables double-buffering of writes to this register.
|
||||
* Any software write operation to a buffered register is held back in a
|
||||
storage element unique to the register.
|
||||
* The software write operation is committed to the register once triggered
|
||||
to do so.
|
||||
* Unless specified otherwise, the buffer trigger occurs when the highest
|
||||
address of the buffered register is written.
|
||||
|
||||
.. describe:: wbuffer_trigger
|
||||
|
||||
* Assigned value is a reference to a register, single-bit field, signal,
|
||||
or single-bit property.
|
||||
* Controls when the double-buffer commits the software write operation to
|
||||
the register's fields.
|
||||
* If reference is a single-bit value (signal, field, property reference),
|
||||
then the assertion of that value triggers the buffer to be evicted.
|
||||
* Signal references shall have either activehigh/activelow property set to
|
||||
define the polarity.
|
||||
* If the reference is a reg, then buffer is evicted when the register's
|
||||
highest address is written.
|
||||
|
||||
|
||||
Other Rules
|
||||
^^^^^^^^^^^
|
||||
* It is an error to set ``buffer_writes`` if the register does not contain any
|
||||
writable fields
|
||||
* If ``buffer_writes`` is false, then anything assigned to ``wbuffer_trigger``
|
||||
is ignored.
|
||||
* The buffered register and the trigger reference shall both be within the
|
||||
same internal device. ie: one cannot be in an external scope with respect to
|
||||
the other.
|
||||
* Unless it is a register, the reference assigned to ``wbuffer_trigger`` shall
|
||||
represent a single bit.
|
||||
* If a buffered register was not written, any trigger events are ignored.
|
||||
* It is valid for a buffered register to be partially written (either via
|
||||
write strobes, or partial addressing).
|
||||
* The software write operation is not considered to take place until the
|
||||
buffer is evicted by the trigger. This influences the behavior of properties
|
||||
like ``swmod`` and ``swacc`` - they are not asserted until the register's
|
||||
fields are actually written by the buffer.
|
||||
|
||||
|
||||
|
||||
Examples
|
||||
--------
|
||||
Below are several examples of what you can do with registers that are
|
||||
write-buffered.
|
||||
|
||||
Wide Atomic Register
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
Without write-buffering, it is impossible to update the state of a 64-bit
|
||||
register using a 32-bit CPU interface in a single clock-cycle.
|
||||
In this example, it still requires two write-cycles to update the register, but
|
||||
the register's storage element is not updated until both sub-words are written.
|
||||
Upon writing the 2nd sub-word (the higher byte address), the write data for both
|
||||
write cycles are committed to the register's storage element together on the
|
||||
same clock cycle. The register is updated atomically.
|
||||
|
||||
.. code-block:: systemrdl
|
||||
:emphasize-lines: 4
|
||||
|
||||
reg {
|
||||
regwidth = 64;
|
||||
accesswidth = 32;
|
||||
buffer_writes = true;
|
||||
field {
|
||||
sw=rw; hw=r;
|
||||
} my_field[63:0] = 0;
|
||||
};
|
||||
|
||||
|
||||
Atomic Group of Registers
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Perhaps you have a group of registers that need their state to be updated
|
||||
atomically. Using the ``wbuffer_trigger`` property, you can define which
|
||||
register write operation triggers the group to be updated.
|
||||
|
||||
|
||||
.. code-block:: systemrdl
|
||||
:emphasize-lines: 2, 18-20
|
||||
|
||||
reg my_buffered_reg {
|
||||
buffer_writes = true;
|
||||
field {
|
||||
sw=rw; hw=r;
|
||||
} my_field[31:0] = 0;
|
||||
};
|
||||
|
||||
my_buffered_reg reg1;
|
||||
my_buffered_reg reg2;
|
||||
my_buffered_reg reg3;
|
||||
|
||||
reg {
|
||||
field {
|
||||
sw=rw; hw=r;
|
||||
} my_field[31:0] = 0;
|
||||
} reg4;
|
||||
|
||||
reg1->wbuffer_trigger = reg4;
|
||||
reg2->wbuffer_trigger = reg4;
|
||||
reg3->wbuffer_trigger = reg4;
|
||||
|
||||
|
||||
In this example software may pre-write information into reg1-reg3, but the
|
||||
register write operations do not take effect until software also writes to reg4.
|
||||
The write operation to reg4 triggers the buffered data to be committed to
|
||||
reg1-reg3. This is guaranteed to occur on the same clock-cycle.
|
||||
|
||||
|
||||
Externally Triggered Register Update
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Some applications may require precise timing for when a register (or group of
|
||||
registers) update their value. Often software cannot offer such timing
|
||||
precision.
|
||||
|
||||
In this example, the trigger event is bound to an external signal. When
|
||||
asserted, any pending write operation the buffered register will be committed.
|
||||
The hwif_out value presents the new register state on the clock cycle after the
|
||||
trigger is asserted.
|
||||
|
||||
.. code-block:: systemrdl
|
||||
:emphasize-lines: 2, 11-13
|
||||
|
||||
reg my_buffered_reg {
|
||||
buffer_writes = true;
|
||||
field {
|
||||
sw=rw; hw=r;
|
||||
} my_field[31:0] = 0;
|
||||
};
|
||||
|
||||
my_buffered_reg reg1;
|
||||
my_buffered_reg reg2;
|
||||
|
||||
signal {
|
||||
activehigh;
|
||||
} trigger_signal;
|
||||
reg1->wbuffer_trigger = trigger_signal;
|
||||
reg2->wbuffer_trigger = trigger_signal;
|
||||
|
||||
After software writes to ``reg1`` & ``reg2``, the written data is held back in
|
||||
the write buffer until ``hwif_in..trigger_signal`` is asserted by the hardware.
|
||||