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

9.5.1. 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:

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

9.5.2. AXI4Buffer

Similar to the 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:

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

9.5.3. 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:

nouser.node := AXI4UserYanker(Some(1)) := hasuser.node

9.5.4. 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:

interleaved.node := AXI4Deinterleaver() := consecutive.node

9.5.5. 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:

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

9.5.6. AXI4Fragmenter

The AXI4Fragmenter is similar to the 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:

axi4lite.node := AXI4Fragmenter() := axi4full.node

9.5.7. 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:

// client.node may have >16 source IDs
// manager.node will only see 16
manager.node := TLSourceShrinker(16) := client.node

9.5.8. AXI4IdIndexer

The AXI4 equivalent of 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:

// 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 AXI4UserYanker.

9.5.9. 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:

// Assume the manager node sets beatBytes to 8
// With WidthWidget, client sees beatBytes of 4
manager.node := TLWidthWidget(4) := client.node

9.5.10. 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 Manager Node for explanation of region types).

9.5.11. 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:

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

9.5.12. 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:

axi4slave.node :=
    AXI4UserYanker() :=
    AXI4Deinterleaver(64) :=
    TLToAXI4() :=
    tlclient.node

tlmanager.node :=
    AXI4ToTL() :=
    AXI4UserYanker() :=
    AXI4Fragmenter() :=
    axi4master.node

You will need to add an 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 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 AXI4Fragmenter and AXI4UserYanker because the converter cannot deal with multi-beat transactions or user fields.

9.5.13. 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:

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.

9.5.14. 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:

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.