Files
chipyard/docs/TileLink-Diplomacy-Reference/Widgets.rst
2022-12-21 15:59:46 -08:00

468 lines
16 KiB
ReStructuredText

.. _diplomatic_widgets:
Diplomatic Widgets
==================
RocketChip provides a library of diplomatic TileLink and AXI4 widgets.
The most commonly used widgets are documented here. The TileLink widgets
are available from ``freechips.rocketchip.tilelink`` and the AXI4 widgets
from ``freechips.rocketchip.amba.axi4``.
TLBuffer
--------
A widget for buffering TileLink transactions. It simply instantiates queues
for each of the 2 (or 5 for TL-C) decoupled channels. To configure the queue
for each channel, you pass the constructor a
``freechips.rocketchip.diplomacy.BufferParams`` object. The arguments for
this case class are:
- ``depth: Int`` - The number of entries in the queue
- ``flow: Boolean`` - If true, combinationally couple the valid signals so
that an input can be consumed on the same cycle it is enqueued.
- ``pipe: Boolean`` - If true, combinationally couple the ready signals so
that single-entry queues can run at full rate.
There is an implicit conversion from ``Int`` available. If you pass an
integer instead of a BufferParams object, the queue will be the depth
given in the integer and ``flow`` and ``pipe`` will both be false.
You can also use one of the predefined BufferParams objects.
- ``BufferParams.default`` = ``BufferParams(2, false, false)``
- ``BufferParams.none`` = ``BufferParams(0, false, false)``
- ``BufferParams.flow`` = ``BufferParams(1, true, false)``
- ``BufferParams.pipe`` = ``BufferParams(1, false, true)``
**Arguments:**
There are four constructors available with zero, one, two, or five arguments.
The zero-argument constructor uses ``BufferParams.default`` for all of the
channels.
The single-argument constructor takes a ``BufferParams`` object to use for all
channels.
The arguments for the two-argument constructor are:
- ``ace: BufferParams`` - Parameters to use for the A, C, and E channels.
- ``bd: BufferParams`` - Parameters to use for the B and D channels
The arguments for the five-argument constructor are
- ``a: BufferParams`` - Buffer parameters for the A channel
- ``b: BufferParams`` - Buffer parameters for the B channel
- ``c: BufferParams`` - Buffer parameters for the C channel
- ``d: BufferParams`` - Buffer parameters for the D channel
- ``e: BufferParams`` - Buffer parameters for the E channel
**Example Usage:**
.. code-block:: scala
// Default settings
manager0.node := TLBuffer() := client0.node
// Using implicit conversion to make buffer with 8 queue entries per channel
manager1.node := TLBuffer(8) := client1.node
// Use default on A channel but pipe on D channel
manager2.node := TLBuffer(BufferParams.default, BufferParams.pipe) := client2.node
// Only add queues for the A and D channel
manager3.node := TLBuffer(
BufferParams.default,
BufferParams.none,
BufferParams.none,
BufferParams.default,
BufferParams.none) := client3.node
AXI4Buffer
----------
Similar to the :ref:`TileLink-Diplomacy-Reference/Widgets:TLBuffer`, but for AXI4. It also takes ``BufferParams`` objects
as arguments.
**Arguments:**
Like TLBuffer, AXI4Buffer has zero, one, two, and five-argument constructors.
The zero-argument constructor uses the default BufferParams for all channels.
The one-argument constructor uses the provided BufferParams for all channels.
The two-argument constructor has the following arguments.
- ``aw: BufferParams`` - Buffer parameters for the "ar", "aw", and "w" channels.
- ``br: BufferParams`` - Buffer parameters for the "b", and "r" channels.
The five-argument constructor has the following arguments
- ``aw: BufferParams`` - Buffer parameters for the "ar" channel
- ``w: BufferParams`` - Buffer parameters for the "w" channel
- ``b: BufferParams`` - Buffer parameters for the "b" channel
- ``ar: BufferParams`` - Buffer parameters for the "ar" channel
- ``r: BufferParams`` - Buffer parameters for the "r" channel
**Example Usage:**
.. code-block:: scala
// Default settings
slave0.node := AXI4Buffer() := master0.node
// Using implicit conversion to make buffer with 8 queue entries per channel
slave1.node := AXI4Buffer(8) := master1.node
// Use default on aw/w/ar channel but pipe on b/r channel
slave2.node := AXI4Buffer(BufferParams.default, BufferParams.pipe) := master2.node
// Single-entry queues for aw, b, and ar but two-entry queues for w and r
slave3.node := AXI4Buffer(1, 2, 1, 1, 2) := master3.node
AXI4UserYanker
--------------
This widget takes an AXI4 port that has a user field and turns it into
one without a user field. The values of the user field from input AR and AW
requests is kept in internal queues associated with the ARID/AWID, which is
then used to associate the correct user field to the responses.
**Arguments:**
- ``capMaxFlight: Option[Int]`` - (optional) An option which can hold the
number of requests that can be inflight for each ID. If ``None`` (the default),
the UserYanker will support the maximum number of inflight requests.
**Example Usage:**
.. code-block:: scala
nouser.node := AXI4UserYanker(Some(1)) := hasuser.node
AXI4Deinterleaver
-----------------
Multi-beat AXI4 read responses for different IDs can potentially be interleaved.
This widget reorders read responses from the slave so that all of the beats
for a single transaction are consecutive.
**Arguments:**
- ``maxReadBytes: Int`` - The maximum number of bytes that can be read
in a single transaction.
**Example Usage:**
.. code-block:: scala
interleaved.node := AXI4Deinterleaver() := consecutive.node
TLFragmenter
------------
The TLFragmenter widget shrinks the maximum logical transfer size of the
TileLink interface by breaking larger transactions into multiple smaller
transactions.
**Arguments:**
- ``minSize: Int`` - Minimum size of transfers supported by all outward managers.
- ``maxSize: Int`` - Maximum size of transfers supported after the Fragmenter is applied.
- ``alwaysMin: Boolean`` - (optional) Fragment all requests down to minSize (else fragment to maximum supported by manager). (default: false)
- ``earlyAck: EarlyAck.T`` - (optional) Should a multibeat Put be acknowledged on the first beat or last beat?
Possible values (default: ``EarlyAck.None``):
- ``EarlyAck.AllPuts`` - always acknowledge on first beat.
- ``EarlyAck.PutFulls`` - acknowledge on first beat if PutFull, otherwise acknowledge on last beat.
- ``EarlyAck.None`` - always acknowledge on last beat.
- ``holdFirstDeny: Boolean`` - (optional) Allow the Fragmenter to unsafely combine multibeat Gets by taking the first denied for the whole burst. (default: false)
**Example Usage:**
.. code-block:: scala
val beatBytes = 8
val blockBytes = 64
single.node := TLFragmenter(beatBytes, blockBytes) := multi.node
axi4lite.node := AXI4Fragmenter() := axi4full.node
**Additional Notes**
- TLFragmenter modifies: PutFull, PutPartial, LogicalData, Get, Hint
- TLFragmenter passes: ArithmeticData (truncated to minSize if alwaysMin)
- TLFragmenter cannot modify acquire (could livelock); thus it is unsafe to put caches on both sides
AXI4Fragmenter
--------------
The AXI4Fragmenter is similar to the :ref:`TileLink-Diplomacy-Reference/Widgets:TLFragmenter`.
The AXI4Fragmenter slices all AXI accesses into simple power-of-two sized and aligned transfers
of the largest size supported by the manager. This makes it suitable as a first stage transformation
to apply before an AXI4=>TL bridge. It also makes it suitable for placing after TL=>AXI4 bridge
driving an AXI-lite slave.
**Example Usage:**
.. code-block:: scala
axi4lite.node := AXI4Fragmenter() := axi4full.node
TLSourceShrinker
----------------
The number of source IDs that a manager sees is usually computed based on the
clients that connect to it. In some cases, you may wish to fix the
number of source IDs. For instance, you might do this if you wish to export
the TileLink port to a Verilog black box. This will pose a problem, however,
if the clients require a larger number of source IDs. In this situation,
you will want to use a TLSourceShrinker.
**Arguments:**
- ``maxInFlight: Int`` - The maximum number of source IDs that will be sent
from the TLSourceShrinker to the manager.
**Example Usage:**
.. code-block:: scala
// client.node may have >16 source IDs
// manager.node will only see 16
manager.node := TLSourceShrinker(16) := client.node
AXI4IdIndexer
-------------
The AXI4 equivalent of :ref:`TileLink-Diplomacy-Reference/Widgets:TLSourceShrinker`. This limits the number of
AWID/ARID bits in the slave AXI4 interface. Useful for connecting to external
or black box AXI4 ports.
**Arguments:**
- ``idBits: Int`` - The number of ID bits on the slave interface.
**Example Usage:**
.. code-block:: scala
// master.node may have >16 unique IDs
// slave.node will only see 4 ID bits
slave.node := AXI4IdIndexer(4) := master.node
**Notes:**
The AXI4IdIndexer will create a ``user`` field on the slave interface, as it
stores the ID of the master requests in this field. If connecting to an AXI4
interface that doesn't have a ``user`` field, you'll need to use the :ref:`TileLink-Diplomacy-Reference/Widgets:AXI4UserYanker`.
TLWidthWidget
-------------
This widget changes the physical width of the TileLink interface. The width
of a TileLink interface is configured by managers, but sometimes you want
the client to see a particular width.
**Arguments:**
- ``innerBeatBytes: Int`` - The physical width (in bytes) seen by the client
**Example Usage:**
.. code-block:: scala
// Assume the manager node sets beatBytes to 8
// With WidthWidget, client sees beatBytes of 4
manager.node := TLWidthWidget(4) := client.node
TLFIFOFixer
-----------
TileLink managers that declare a FIFO domain must ensure that all requests to
that domain from clients which have requested FIFO ordering see responses in
order. However, they can only control the ordering of their own responses, and
do not have control over how those responses interleave with responses from
other managers in the same FIFO domain. Responsibility for ensuring FIFO order
across managers goes to the TLFIFOFixer.
**Arguments:**
- ``policy: TLFIFOFixer.Policy`` - (optional) Which managers will the
TLFIFOFixer enforce ordering on? (default: ``TLFIFOFixer.all``)
The possible values of ``policy`` are:
- ``TLFIFOFixer.all`` - All managers (including those without a FIFO domain)
will have ordering guaranteed
- ``TLFIFOFixer.allFIFO`` - All managers that define a FIFO domain will have
ordering guaranteed
- ``TLFIFOFixer.allVolatile`` - All managers that have a RegionType of
``VOLATILE``, ``PUT_EFFECTS``, or ``GET_EFFECTS`` will have ordering
guaranteed (see :ref:`TileLink-Diplomacy-Reference/NodeTypes:Manager Node` for explanation of region types).
TLXbar and AXI4Xbar
-------------------
These are crossbar generators for TileLink and AXI4 which will route requests
from TL client / AXI4 master nodes to TL manager / AXI4 slave nodes based on
the addresses defined in the managers / slaves. Normally, these are constructed
without arguments. However, you can change the arbitration policy, which
determines which client ports get precedent in the arbiters. The default policy
is ``TLArbiter.roundRobin``, but you can change it to ``TLArbiter.lowestIndexFirst``
if you want a fixed arbitration precedence.
**Arguments:**
All arguments are optional.
- ``arbitrationPolicy: TLArbiter.Policy`` - The arbitration policy to use.
- ``maxFlightPerId: Int`` - (AXI4 only) The number of transactions with the
same ID that can be inflight at a time. (default: 7)
- ``awQueueDepth: Int`` - (AXI4 only) The depth of the write address queue.
(default: 2)
**Example Usage:**
.. code-block:: scala
// Instantiate the crossbar lazy module
val tlBus = LazyModule(new TLXbar)
// Connect a single input edge
tlBus.node := tlClient0.node
// Connect multiple input edges
tlBus.node :=* tlClient1.node
// Connect a single output edge
tlManager0.node := tlBus.node
// Connect multiple output edges
tlManager1.node :*= tlBus.node
// Instantiate a crossbar with lowestIndexFirst arbitration policy
// Yes, we still use the TLArbiter singleton even though this is AXI4
val axiBus = LazyModule(new AXI4Xbar(TLArbiter.lowestIndexFirst))
// The connections work the same as TL
axiBus.node := axiClient0.node
axiBus.node :=* axiClient1.node
axiManager0.node := axiBus.node
axiManager1.node :*= axiBus.node
TLToAXI4 and AXI4ToTL
---------------------
These are converters between the TileLink and AXI4 protocols. TLToAXI4
takes a TileLink client and connects to an AXI4 slave. AXI4ToTL takes an
AXI4 master and connects to a TileLink manager. Generally you don't want to
override the default arguments of the constructors for these widgets.
**Example Usage:**
.. code-block:: scala
axi4slave.node :=
AXI4UserYanker() :=
AXI4Deinterleaver(64) :=
TLToAXI4() :=
tlclient.node
tlmanager.node :=
AXI4ToTL() :=
AXI4UserYanker() :=
AXI4Fragmenter() :=
axi4master.node
You will need to add an :ref:`TileLink-Diplomacy-Reference/Widgets:AXI4Deinterleaver` after the TLToAXI4 converter
because it cannot deal with interleaved read responses. The TLToAXI4 converter
also uses the AXI4 user field to store some information, so you will need an
:ref:`TileLink-Diplomacy-Reference/Widgets:AXI4UserYanker` if you want to connect to an AXI4 port without user
fields.
Before you connect an AXI4 port to the AXI4ToTL widget, you will need to
add an :ref:`TileLink-Diplomacy-Reference/Widgets:AXI4Fragmenter` and :ref:`TileLink-Diplomacy-Reference/Widgets:AXI4UserYanker` because the converter cannot
deal with multi-beat transactions or user fields.
TLROM
------
The TLROM widget provides a read-only memory that can be accessed using
TileLink. Note: this widget is in the ``freechips.rocketchip.devices.tilelink``
package, not the ``freechips.rocketchip.tilelink`` package like the others.
**Arguments:**
- ``base: BigInt`` - The base address of the memory
- ``size: Int`` - The size of the memory in bytes
- ``contentsDelayed: => Seq[Byte]`` - A function which, when called generates
the byte contents of the ROM.
- ``executable: Boolean`` - (optional) Specify whether the CPU can fetch
instructions from the ROM (default: ``true``).
- ``beatBytes: Int`` - (optional) The width of the interface in bytes.
(default: 4).
- ``resources: Seq[Resource]`` - (optional) Sequence of resources to add to
the device tree.
**Example Usage:**
.. code-block:: scala
val rom = LazyModule(new TLROM(
base = 0x100A0000,
size = 64,
contentsDelayed = Seq.tabulate(64) { i => i.toByte },
beatBytes = 8))
rom.node := TLFragmenter(8, 64) := client.node
**Supported Operations:**
The TLROM only supports single-beat reads. If you want to perform multi-beat
reads, you should attach a TLFragmenter in front of the ROM.
TLRAM and AXI4RAM
-----------------
The TLRAM and AXI4RAM widgets provide read-write memories implemented as SRAMs.
**Arguments:**
- ``address: AddressSet`` - The address range that this RAM will cover.
- ``cacheable: Boolean`` - (optional) Can the contents of this RAM be cached.
(default: ``true``)
- ``executable: Boolean`` - (optional) Can the contents of this RAM be fetched
as instructions. (default: ``true``)
- ``beatBytes: Int`` - (optional) Width of the TL/AXI4 interface in bytes.
(default: 4)
- ``atomics: Boolean`` - (optional, TileLink only) Does the RAM support
atomic operations? (default: ``false``)
**Example Usage:**
.. code-block:: scala
val xbar = LazyModule(new TLXbar)
val tlram = LazyModule(new TLRAM(
address = AddressSet(0x1000, 0xfff)))
val axiram = LazyModule(new AXI4RAM(
address = AddressSet(0x2000, 0xfff)))
tlram.node := xbar.node
axiram := TLToAXI4() := xbar.node
**Supported Operations:**
TLRAM only supports single-beat TL-UL requests. If you set ``atomics`` to true,
it will also support Logical and Arithmetic operations. Use a ``TLFragmenter``
if you want multi-beat reads/writes.
AXI4RAM only supports AXI4-Lite operations, so multi-beat reads/writes and
reads/writes smaller than full-width are not supported. Use an ``AXI4Fragmenter``
if you want to use the full AXI4 protocol.