Initial Commit - Forked from PeakRDL-regblock @ a440cc19769069be831d267505da4f3789a26695

This commit is contained in:
Arnav Sacheti
2025-10-10 22:28:36 -07:00
commit 9bf5cd1e68
308 changed files with 19414 additions and 0 deletions

20
docs/Makefile Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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.

View 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.

View 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.

View 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`.

View 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...

View 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.

View 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
View 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

View 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

View 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??

View 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

View 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

View 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)

View 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

View 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

View 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?

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

BIN
docs/diagrams/diagrams.odg Normal file

Binary file not shown.

BIN
docs/diagrams/rbuf.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

BIN
docs/diagrams/readback.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

BIN
docs/diagrams/wbuf.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

131
docs/faq.rst Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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.

View 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
View File

@@ -0,0 +1,3 @@
pygments-systemrdl
sphinxcontrib-wavedrom
sphinx-book-theme

View 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
View 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
View 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`.

View 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
View 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;

View 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.