Implement read buffering. (#22)
This commit is contained in:
@@ -121,4 +121,5 @@ Links
|
||||
:caption: Extended Properties
|
||||
|
||||
udps/intro
|
||||
udps/read_buffering
|
||||
udps/write_buffering
|
||||
|
||||
@@ -8,6 +8,8 @@ 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
|
||||
@@ -17,6 +19,20 @@ section.
|
||||
- Type
|
||||
- Description
|
||||
|
||||
* - buffer_reads
|
||||
- reg
|
||||
- boolean
|
||||
- If set, reads from the register are double-buffered.
|
||||
|
||||
See details here: :ref:`read_buffering`.
|
||||
|
||||
* - rbuffer_trigger
|
||||
- reg
|
||||
- reference
|
||||
- Defines the buffered read load trigger.
|
||||
|
||||
See details here: :ref:`read_buffering`.
|
||||
|
||||
* - buffer_writes
|
||||
- reg
|
||||
- boolean
|
||||
|
||||
158
docs/udps/read_buffering.rst
Normal file
158
docs/udps/read_buffering.rst
Normal file
@@ -0,0 +1,158 @@
|
||||
.. _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
|
||||
|
||||
``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 it's 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.
|
||||
|
||||
``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
|
||||
reagister 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.
|
||||
@@ -3,20 +3,18 @@
|
||||
Write-buffered Registers
|
||||
========================
|
||||
|
||||
In some situations, your hardware design may requre that fields be
|
||||
updated atomically (on the same clock cycle), but your cpuif is not wide enough
|
||||
to do so natively. Using some UDP extensions, this regblock generator implements
|
||||
a way for you to add write-buffering to specific registers, which allows the
|
||||
regblock to delay the effect of a software write operation until a defined
|
||||
trigger event.
|
||||
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.
|
||||
* 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
|
||||
@@ -32,43 +30,50 @@ Properties
|
||||
The behavior of write-buffered registers is defined using the following two
|
||||
properties:
|
||||
|
||||
.. literalinclude:: ../../hdl-src/regblock_udps.rdl
|
||||
:lines: 20-28
|
||||
|
||||
``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.
|
||||
* 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.
|
||||
|
||||
``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
|
||||
* 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.
|
||||
* 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.
|
||||
|
||||
|
||||
|
||||
@@ -105,8 +110,8 @@ 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.
|
||||
atomically. Using the ``wbuffer_trigger`` property, you can define which
|
||||
register write operation triggers the group to be updated.
|
||||
|
||||
|
||||
.. code-block:: systemrdl
|
||||
@@ -136,17 +141,18 @@ write operation triggers the group to be updated.
|
||||
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
|
||||
@@ -169,5 +175,5 @@ trigger is asserted.
|
||||
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.
|
||||
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.
|
||||
|
||||
Reference in New Issue
Block a user