Update documentation to use correct repository name PeakRDL-BusDecoder and clarify project purpose (#7)

* Initial plan

* Update documentation to use correct repository name PeakRDL-BusDecoder

Co-authored-by: arnavsacheti <36746504+arnavsacheti@users.noreply.github.com>

* Update CONTRIBUTING.md and GitHub templates with correct repository name

Co-authored-by: arnavsacheti <36746504+arnavsacheti@users.noreply.github.com>

* Update author to arnavsacheti and clarify bus decoder purpose in documentation

Co-authored-by: arnavsacheti <36746504+arnavsacheti@users.noreply.github.com>

* Update author to 'Arnav Sacheti' and revise UDP documentation to reflect no current UDP support

Co-authored-by: arnavsacheti <36746504+arnavsacheti@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: arnavsacheti <36746504+arnavsacheti@users.noreply.github.com>
This commit is contained in:
Copilot
2025-10-23 23:23:10 -07:00
committed by GitHub
parent 8cfd2a86c1
commit 6489f49873
21 changed files with 116 additions and 739 deletions

View File

@@ -1,49 +0,0 @@
.. _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-busdecoder can be
enabled by compiling the following file along with your design:
:download:`busdecoder_udps.rdl <../../hdl-src/busdecoder_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.."}
]}

View File

@@ -1,103 +0,0 @@
.. _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 busdecoder.
Properties
----------
Fields can be declared as fixed-point numbers using the following two properties:
.. literalinclude:: ../../hdl-src/busdecoder_udps.rdl
:lines: 46-54
The :ref:`is_signed<signed>` property can be used in conjunction with these
properties to declare signed fixed-point fields.
These UDP definitions, along with others supported by PeakRDL-busdecoder, can be
enabled by compiling the following file along with your design:
:download:`busdecoder_udps.rdl <../../hdl-src/busdecoder_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.

View File

@@ -4,82 +4,76 @@ 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-busdecoder tool understands several UDPs that are described in this
section.
the language to be extended using "User Defined Properties" (UDPs).
To enable these UDPs, compile this RDL file prior to the rest of your design:
:download:`busdecoder_udps.rdl <../../hdl-src/busdecoder_udps.rdl>`.
Current UDP Support
-------------------
.. list-table:: Summary of UDPs
:header-rows: 1
**Note:** PeakRDL-BusDecoder currently does not implement any User Defined Properties.
The focus of this tool is on bus decoding and address space routing rather than
field-level or register-level behavioral extensions.
* - Name
- Component
- Type
- Description
If you need UDPs for field-level behaviors (such as buffering, signedness, or
fixed-point representations), consider using `PeakRDL-regblock <https://github.com/SystemRDL/PeakRDL-regblock>`_,
which is designed for comprehensive register block generation with extensive UDP support.
* - buffer_reads
- reg
- boolean
- If set, reads from the register are double-buffered.
Extending with Custom UDPs
---------------------------
See: :ref:`read_buffering`.
If your bus decoder design requires custom User Defined Properties, you can extend
PeakRDL-BusDecoder by:
* - rbuffer_trigger
- reg
- reference
- Defines the buffered read load trigger.
1. **Define your UDP in SystemRDL**
See: :ref:`read_buffering`.
Create a ``.rdl`` file that defines your custom properties:
* - buffer_writes
- reg
- boolean
- If set, writes to the register are double-buffered.
.. code-block:: systemrdl
See: :ref:`write_buffering`.
property my_custom_prop {
component = addrmap;
type = boolean;
};
* - wbuffer_trigger
- reg
- reference
- Defines the buffered write commit trigger.
2. **Implement the UDP in Python**
See: :ref:`write_buffering`.
Create a Python UDP definition class in your project:
* - rd_swacc
- field
- boolean
- Enables an output strobe that is asserted on sw reads.
.. code-block:: python
See: :ref:`extended_swacc`.
from systemrdl.udp import UDPDefinition
* - wr_swacc
- field
- boolean
- Enables an output strobe that is asserted on sw writes.
class MyCustomUDP(UDPDefinition):
name = "my_custom_prop"
valid_components = {"addrmap"}
valid_type = bool
default = False
See: :ref:`extended_swacc`.
3. **Register the UDP with the compiler**
* - is_signed
- field
- boolean
- Defines the signedness of a field.
When using PeakRDL-BusDecoder programmatically, register your UDP:
See: :ref:`signed`.
.. code-block:: python
* - intwidth
- field
- unsigned integer
- Defines the number of integer bits in the fixed-point representation
of a field.
from systemrdl import RDLCompiler
from peakrdl_busdecoder import BusDecoderExporter
See: :ref:`fixedpoint`.
rdlc = RDLCompiler()
rdlc.register_udp(MyCustomUDP)
# Compile your RDL files
rdlc.compile_file("my_udp_defs.rdl")
rdlc.compile_file("my_design.rdl")
root = rdlc.elaborate()
# Export
exporter = BusDecoderExporter()
exporter.export(root, "output/")
* - fracwidth
- field
- unsigned integer
- Defines the number of fractional bits in the fixed-point representation
of a field.
4. **Access UDP values in your design**
See: :ref:`fixedpoint`.
UDP values can be accessed from nodes in the SystemRDL tree and used to
customize the generated bus decoder logic as needed.
For more information on creating User Defined Properties, see the
`SystemRDL Compiler documentation <https://systemrdl-compiler.readthedocs.io/en/stable/model_structure.html#user-defined-properties>`_.

View File

@@ -1,164 +0,0 @@
.. _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/busdecoder_udps.rdl
:lines: 10-18
These UDP definitions, along with others supported by PeakRDL-busdecoder can be
enabled by compiling the following file along with your design:
:download:`busdecoder_udps.rdl <../../hdl-src/busdecoder_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.

View File

@@ -1,74 +0,0 @@
.. _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 busdecoder.
Properties
----------
A field can be marked as signed using the following user-defined property:
.. literalinclude:: ../../hdl-src/busdecoder_udps.rdl
:lines: 40-44
This UDP definition, along with others supported by PeakRDL-busdecoder, can be
enabled by compiling the following file along with your design:
:download:`busdecoder_udps.rdl <../../hdl-src/busdecoder_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

@@ -1,183 +0,0 @@
.. _write_buffering:
Write-buffered Registers
========================
In order to support larger software write accesses that are atomic, the
busdecoder generator understands several UDPs that implement write-buffering to
specific registers. This causes the busdecoder to delay the effect of a software
write operation until a defined trigger event.
Some examples of when this is useful:
* 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/busdecoder_udps.rdl
:lines: 20-28
These UDP definitions, along with others supported by PeakRDL-busdecoder can be
enabled by compiling the following file along with your design:
:download:`busdecoder_udps.rdl <../../hdl-src/busdecoder_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.