CIRCT

Circuit IR Compilers and Tools

'esi' Dialect

The Elastic Silicon Interconnect dialect aims to aid in accelerator system construction.

Application channels

The main component of ESI are point-to-point, typed channels that allow designers to connect modules to each other and software, then communicate by sending messages. Channels largely abstract away the details of message communication from the designer, though the designer can declaratively specify how to implement the channel.

Messages have types: ints, structs, arrays, unions, and variable-length lists. The width of a channel is not necessarily the same width as the message. ESI “windows” can be used to break up a message into a series of “frames”. IP blocks can emit / absorb “windowed” messages or full-sized messages, which can be automatically broken up to save wire area at the cost of bandwidth.

Any channel which is exposed to the host will have a platform-agnostic software API constructed for it based on the type of the channel. The software application merely has to connect to the accelerator then invoke a method to send or receive messages from the accelerator system.

ChannelBundleType

a group of related channels

Syntax:

!esi.bundle<
  ::llvm::ArrayRef<BundledChannel>,   # channels
  ::mlir::UnitAttr   # resettable
>

A channel bundle (sometimes referred to as just “bundle”) is a set of channels of associated signals, along with per-channel names and directions. The prototypical example for a bundle is a request-response channel pair.

The direction terminology is a bit confusing. Let us designate the module which is outputting the bundle as the “sender” module and a module which has a bundle as an input as the “receiver”. The directions “from” and “to” are from the senders perspective. So, the “to” direction means that channel is transmitting messages from the sender to the receiver. Then, “from” means that the sender is getting messages from the receiver (typically responses).

When requesting a bundle from a service, the client is always considered the sender. So the “to” direction is for the client to send messages to the service.

Parameters:

ParameterC++ typeDescription
channels::llvm::ArrayRef<BundledChannel>
resettable::mlir::UnitAttrboolean flag

ChannelType

An ESI-compatible channel port

Syntax:

!esi.channel<
  Type,   # inner
  ::circt::esi::ChannelSignaling   # signaling
>

An ESI port kind which models a latency-insensitive, unidirectional, point-to-point data stream. Channels are typed (like all of ESI). Said type can be any MLIR type, but must be lowered to something a backend knows how to output (i.e. something emitVerilog knows about).

Example:

hw.module.extern @Sender() -> (%x: !esi.channel<i1>)
hw.module @Reciever(%a: !esi.channel<hw.array<5xi16>>) { }

Parameters:

ParameterC++ typeDescription
innerType
signaling::circt::esi::ChannelSignaling

ClockType

A type for clock-carrying wires

Syntax: !seq.clock

The !seq.clock type represents signals which can be used to drive the clock input of sequential operations.

ListType

a runtime-variably sized list

Syntax:

!esi.list<
  Type   # elementType
>

In software, a chunk of memory with runtime-specified length. In hardware, a stream of runtime-specified amount of data transmitted over many cycles in compile-time specified specified windows (chunks).

Parameters:

ParameterC++ typeDescription
elementTypeType

WindowType

a data window

Syntax:

!esi.window<
  StringAttr,   # name
  mlir::Type,   # into
  ::llvm::ArrayRef<WindowFrameType>   # frames
>

A ‘data window’ allows designers to break up large messages into multiple frames (aka phits) spread across multiple cycles. Windows are specified in terms of a mapping of struct fields to frames. The width of a window is the maximum frame size + the union tag (log2(#frames)).

A data window does NOT imply an ESI channel.

Current restrictions:

  • A field may only appear once.
  • Fields may only be re-ordered (wrt the original message) within a frame.
  • Array fields whose array length is not evenly divisible by ’numItems’ will have an implicit frame inserted directly after containing the leftover array items.
  • Array fields with an array length MUST be in their own frame.

Parameters:

ParameterC++ typeDescription
nameStringAttr
intomlir::Type
frames::llvm::ArrayRef<WindowFrameType>

FirMemType

A FIRRTL-flavored memory

Syntax:

!seq.firmem<
  uint64_t,   # depth
  uint32_t,   # width
  std::optional<uint32_t>   # maskWidth
>

The !seq.firmem type represents a FIRRTL-flavored memory declared by a seq.firmem operation. It captures the parameters of the memory that are relevant to the read, write, and read-write ports, such as width and depth.

Parameters:

ParameterC++ typeDescription
depthuint64_t
widthuint32_t
maskWidthstd::optional<uint32_t>

HLMemType

Multi-dimensional memory type

Syntax:

hlmem-type ::== `hlmem` `<` dim-list element-type `>`

The HLMemType represents the type of an addressable memory structure. The type is inherently multidimensional. Dimensions must be known integer values.

Note: unidimensional memories are represented as <1x{element type}> - <{element type}> is illegal.

Parameters:

ParameterC++ typeDescription
shape::llvm::ArrayRef<int64_t>
elementTypeType

WindowFieldType

a field-in-frame specifier

Syntax:

!esi.window.field<
  StringAttr,   # fieldName
  uint64_t   # numItems
>

Specify that a field should appear within the enclosing frame.

Parameters:

ParameterC++ typeDescription
fieldNameStringAttr
numItemsuint64_t# of items in arrays or lists

WindowFrameType

Declare a data window frame

Syntax:

!esi.window.frame<
  StringAttr,   # name
  ::llvm::ArrayRef<WindowFieldType>   # members
>

A named list of fields which should appear in a given frame.

Parameters:

ParameterC++ typeDescription
nameStringAttr
members::llvm::ArrayRef<WindowFieldType>

ListType

a runtime-variably sized list

Syntax:

!esi.list<
  Type   # elementType
>

In software, a chunk of memory with runtime-specified length. In hardware, a stream of runtime-specified amount of data transmitted over many cycles in compile-time specified specified windows (chunks).

Parameters:

ParameterC++ typeDescription
elementTypeType

esi.decode.capnp (::circt::esi::CapnpDecodeOp)

Translate bits in Cap’nProto messages to HW typed data

Syntax:

operation ::= `esi.decode.capnp` $clk $valid $capnpBits attr-dict `:` qualified(type($capnpBits)) `->`
              qualified(type($decodedData))

Traits: AlwaysSpeculatableImplTrait

Interfaces: ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{}

Operands:

OperandDescription
clkA type for clock-carrying wires
valid1-bit signless integer
capnpBitsan HW bit array

Results:

ResultDescription
decodedDataany type

esi.encode.capnp (::circt::esi::CapnpEncodeOp)

Translate HW typed data to Cap’nProto

Syntax:

operation ::= `esi.encode.capnp` $clk $valid $dataToEncode attr-dict `:` qualified(type($dataToEncode))
              `->` qualified(type($capnpBits))

Traits: AlwaysSpeculatableImplTrait

Interfaces: ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{}

Operands:

OperandDescription
clkA type for clock-carrying wires
valid1-bit signless integer
dataToEncodeany type

Results:

ResultDescription
capnpBitsan HW bit array

esi.buffer (::circt::esi::ChannelBufferOp)

Control options for an ESI channel.

A channel buffer (buffer) is essentially a set of options on a channel. It always adds at least one cycle of latency (pipeline stage) to the channel, but this is configurable.

This operation is inserted on an ESI dataflow edge. It must exist previous to SystemVerilog emission but can be added in a lowering pass.

A stages attribute may be provided to specify a specific number of cycles (pipeline stages) to use on this channel. Must be greater than 0.

A name attribute may be provided to assigned a name to a buffered connection.

Example:

%esiChan = hw.instance "sender" @Sender () : () -> (!esi.channel<i1>)
// Allow automatic selection of options.
%bufferedChan = esi.buffer %esiChan : i1
hw.instance "recv" @Reciever (%bufferedChan) : (!esi.channel<i1>) -> ()

// Alternatively, specify the number of stages.
%fourStageBufferedChan = esi.buffer %esiChan { stages = 4 } : i1

Traits: AlwaysSpeculatableImplTrait

Interfaces: ChannelOpInterface, ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{}

Attributes:

AttributeMLIR TypeDescription
stages::mlir::IntegerAttr64-bit signless integer attribute whose minimum value is 1
name::mlir::StringAttrstring attribute

Operands:

OperandDescription
clkA type for clock-carrying wires
rst1-bit signless integer
inputan ESI channel

Results:

ResultDescription
outputan ESI channel

esi.cosim (::circt::esi::CosimEndpointOp)

Co-simulation endpoint

Syntax:

operation ::= `esi.cosim` $clk `,` $rst `,` $send `,` $name attr-dict
              `:` qualified(type($send)) `->` qualified(type($recv))

A co-simulation endpoint is a connection from the simulation to some outside process, usually a software application responsible for driving the simulation (driver).

ESI uses a serialization protocol called Cap’n Proto (capnp for short). The advantage of capnp is the decoding overhead: for value types (ints, structs, etc.) there is none! This stands in contrast to Protocol Buffers and Bond as their messages contain metadata for each field which must be interpreted.

The advantage of using a well-supported serialization protocol is language support – driver applications can be written in any language supported by the specific protocol.

Attributes:

AttributeMLIR TypeDescription
name::mlir::StringAttrstring attribute

Operands:

OperandDescription
clkA type for clock-carrying wires
rst1-bit signless integer
sendan ESI channel

Results:

ResultDescription
recvan ESI channel

esi.null (::circt::esi::NullSourceOp)

An op which never produces messages.

Syntax:

operation ::= `esi.null` attr-dict `:` qualified(type($out))

Traits: AlwaysSpeculatableImplTrait

Interfaces: ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{}

Results:

ResultDescription
outan ESI channel

esi.bundle.pack (::circt::esi::PackBundleOp)

Pack channels into a bundle

Syntax:

operation ::= `esi.bundle.pack` $toChannels attr-dict `:` custom<UnPackBundleType>(
              type($toChannels), type($fromChannels), type($bundle))

Interfaces: OpAsmOpInterface

Operands:

OperandDescription
toChannelsan ESI channel

Results:

ResultDescription
bundlea group of related channels
fromChannelsan ESI channel

esi.stage (::circt::esi::PipelineStageOp)

An elastic buffer stage.

An individual elastic pipeline register. Generally lowered to from a ChannelBuffer (‘buffer’), though can be inserted anywhere to add an additional pipeline stage. Adding individually could be useful for late-pass latency balancing.

Traits: AlwaysSpeculatableImplTrait

Interfaces: ChannelOpInterface, ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{}

Operands:

OperandDescription
clkA type for clock-carrying wires
rst1-bit signless integer
inputan ESI channel

Results:

ResultDescription
outputan ESI channel

esi.bundle.unpack (::circt::esi::UnpackBundleOp)

Unpack channels from a bundle

Syntax:

operation ::= `esi.bundle.unpack` $fromChannels `from` $bundle attr-dict `:` custom<UnPackBundleType>(
              type($toChannels), type($fromChannels), type($bundle))

Interfaces: OpAsmOpInterface

Operands:

OperandDescription
bundlea group of related channels
fromChannelsan ESI channel

Results:

ResultDescription
toChannelsan ESI channel

esi.unwrap.fifo (::circt::esi::UnwrapFIFOOp)

Unwrap a value from an ESI port into a FIFO interface

Syntax:

operation ::= `esi.unwrap.fifo` $chanInput `,` $rden attr-dict `:` qualified(type($chanInput))

Interfaces: ChannelOpInterface, InferTypeOpInterface

Operands:

OperandDescription
chanInputan ESI channel
rden1-bit signless integer

Results:

ResultDescription
dataany type
empty1-bit signless integer

esi.unwrap.iface (::circt::esi::UnwrapSVInterfaceOp)

Unwrap an SV interface from an ESI port

Syntax:

operation ::= `esi.unwrap.iface` $chanInput `into` $interfaceSource attr-dict `:` `(` qualified(type($chanInput)) `,` qualified(type($interfaceSource)) `)`

Unwrap an ESI channel into a SystemVerilog interface containing valid, ready, and data signals.

Interfaces: ChannelOpInterface

Operands:

OperandDescription
chanInputan ESI channel
interfaceSourcesv.interface

esi.unwrap.vr (::circt::esi::UnwrapValidReadyOp)

Unwrap a value from an ESI port

Unwrapping a value allows operations on the contained value. Unwrap the channel along with a ready signal that you generate. Result is the data along with a valid signal.

Interfaces: ChannelOpInterface

Operands:

OperandDescription
chanInputan ESI channel
ready1-bit signless integer

Results:

ResultDescription
rawOutputany type
valid1-bit signless integer

esi.window.unwrap (::circt::esi::UnwrapWindow)

Unwrap a data window into a union

Syntax:

operation ::= `esi.window.unwrap` $window attr-dict `:` qualified(type($window))

Traits: AlwaysSpeculatableImplTrait

Interfaces: ConditionallySpeculatable, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{}

Operands:

OperandDescription
windowa data window

Results:

ResultDescription
framea UnionType

esi.wrap.fifo (::circt::esi::WrapFIFOOp)

Wrap a value into an ESI port with FIFO signaling

Syntax:

operation ::= `esi.wrap.fifo` $data `,` $empty attr-dict `:`
              custom<WrapFIFOType>(type($data), type($chanOutput))

Interfaces: ChannelOpInterface

Operands:

OperandDescription
dataany type
empty1-bit signless integer

Results:

ResultDescription
chanOutputan ESI channel
rden1-bit signless integer

esi.wrap.iface (::circt::esi::WrapSVInterfaceOp)

Wrap an SV interface into an ESI port

Syntax:

operation ::= `esi.wrap.iface` $interfaceSink attr-dict `:` qualified(type($interfaceSink)) `->` qualified(type($output))

Wrap a SystemVerilog interface into an ESI channel. Interface MUST look like an interface produced by ESI meaning it MUST contain valid, ready, and data signals. Any other signals will be discarded.

Interfaces: ChannelOpInterface

Operands:

OperandDescription
interfaceSinksv.interface

Results:

ResultDescription
outputan ESI channel

esi.wrap.vr (::circt::esi::WrapValidReadyOp)

Wrap a value into an ESI port

Wrapping a value into an ESI port type allows modules to send values down an ESI port. Wrap data with valid bit, result is the ESI channel and the ready signal from the other end of the channel.

Interfaces: ChannelOpInterface

Operands:

OperandDescription
rawInputany type
valid1-bit signless integer

Results:

ResultDescription
chanOutputan ESI channel
ready1-bit signless integer

esi.window.wrap (::circt::esi::WrapWindow)

Wrap a union into a data window

Syntax:

operation ::= `esi.window.wrap` $frame attr-dict `:` custom<InferWindowRet>(type($frame), type($window))

Traits: AlwaysSpeculatableImplTrait

Interfaces: ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{}

Operands:

OperandDescription
framea UnionType

Results:

ResultDescription
windowa data window

Services

ESI “services” provide device-wide connectivity and arbitration for shared resources, which can be requested from any IP block (service “client”). Standard services will include DRAM, clock/reset, statistical counter reporting, and debug.

esi.service.decl (::circt::esi::CustomServiceDeclOp)

An ESI service interface declaration

Syntax:

operation ::= `esi.service.decl` $sym_name $ports attr-dict

A declaration of an ESI service interface. Defines a contract between a service provider and its clients.

Example:

esi.service.decl @HostComms {
  esi.service.to_server send : !esi.channel<!esi.any>
  esi.service.to_client recieve : !esi.channel<i8>
}

Traits: HasParent<::mlir::ModuleOp>, NoTerminator, SingleBlock

Interfaces: ServiceDeclOpInterface, Symbol

Attributes:

AttributeMLIR TypeDescription
sym_name::mlir::StringAttrstring attribute

esi.service.req.inout (::circt::esi::RequestInOutChannelOp)

Request a bidirectional channel

Syntax:

operation ::= `esi.service.req.inout` $toServer `->` $servicePort `(` $clientNamePath `)` attr-dict `:`
              qualified(type($toServer)) `->` qualified(type($toClient))

Interfaces: ServiceReqOpInterface, SymbolUserOpInterface

Attributes:

AttributeMLIR TypeDescription
servicePort::circt::hw::InnerRefAttrRefer to a name inside a module
clientNamePath::mlir::ArrayAttrstring array attribute

Operands:

OperandDescription
toServeran ESI channel

Results:

ResultDescription
toClientan ESI channel

esi.service.req.to_client (::circt::esi::RequestToClientConnectionOp)

Request a connection to receive data

Syntax:

operation ::= `esi.service.req.to_client` $servicePort `(` $clientNamePath `)`
              attr-dict `:` qualified(type($toClient))

Interfaces: ServiceReqOpInterface, SymbolUserOpInterface

Attributes:

AttributeMLIR TypeDescription
servicePort::circt::hw::InnerRefAttrRefer to a name inside a module
clientNamePath::mlir::ArrayAttrstring array attribute

Results:

ResultDescription
toClientan ESI channel

esi.service.req.to_server (::circt::esi::RequestToServerConnectionOp)

Request a connection to send data

Syntax:

operation ::= `esi.service.req.to_server` $toServer `->` $servicePort `(` $clientNamePath `)`
              attr-dict `:` qualified(type($toServer))

Interfaces: ServiceReqOpInterface, SymbolUserOpInterface

Attributes:

AttributeMLIR TypeDescription
servicePort::circt::hw::InnerRefAttrRefer to a name inside a module
clientNamePath::mlir::ArrayAttrstring array attribute

Operands:

OperandDescription
toServeran ESI channel

esi.service.inout (::circt::esi::ServiceDeclInOutOp)

An ESI service port which has both directions

Syntax:

operation ::= `esi.service.inout` $inner_sym attr-dict
              `:` qualified($toServerType) `->` qualified($toClientType)

Traits: HasParent<::circt::esi::CustomServiceDeclOp>

Attributes:

AttributeMLIR TypeDescription
inner_sym::mlir::StringAttrstring attribute
toServerType::mlir::TypeAttrany type attribute
toClientType::mlir::TypeAttrany type attribute

esi.service.hierarchy.metadata (::circt::esi::ServiceHierarchyMetadataOp)

Metadata about a service in the service hierarchy

Syntax:

operation ::= `esi.service.hierarchy.metadata` `path` $serverNamePath (`implementing` $service_symbol^)?
              `impl` `as` $impl_type (`opts` $impl_details^)?
              `clients` $clients attr-dict

Interfaces: SymbolUserOpInterface

Attributes:

AttributeMLIR TypeDescription
service_symbol::mlir::FlatSymbolRefAttrflat symbol reference attribute
serverNamePath::mlir::ArrayAttrarray attribute
impl_type::mlir::StringAttrstring attribute
impl_details::mlir::DictionaryAttrdictionary of named attribute values
clients::mlir::ArrayAttrarray attribute

esi.service.impl_req (::circt::esi::ServiceImplementReqOp)

Request for a service to be implemented

Syntax:

operation ::= `esi.service.impl_req` (`svc` $service_symbol^)? `impl` `as` $impl_type (`opts` $impl_opts^)?
              `(` $inputs `)` attr-dict `:` functional-type($inputs, results)
              $portReqs

The connect services pass replaces service.instances with this op. The portReqs region is the set of connection requests which need to be implemented for this service instance. Channels to/from the requests have been added to the operands/results of this op and consumers/producers have been redirected.

Some other pass or frontend is expected to replace this op with an actual implementation.

Traits: NoTerminator

Attributes:

AttributeMLIR TypeDescription
service_symbol::mlir::FlatSymbolRefAttrflat symbol reference attribute
impl_type::mlir::StringAttrstring attribute
impl_opts::mlir::DictionaryAttrdictionary of named attribute values

Operands:

OperandDescription
inputsany type

Results:

ResultDescription
outputsany type

esi.service.instance (::circt::esi::ServiceInstanceOp)

Instantiate a server module

Syntax:

operation ::= `esi.service.instance` (`svc` $service_symbol^)? `impl` `as` $impl_type (`opts` $impl_opts^)?
              `(` $inputs `)` attr-dict `:` functional-type($inputs, results)

Instantiate a service adhering to a service declaration interface.

A pass collects all of the connection requests to the service this op implements from the containing modules’ descendants (in the instance hierarchy). It bubbles them all up to the module containing this op, creating the necessary ESI channel ports, groups them appropriately, then replaces this op with a service.impl_req.

If ‘service_symbol’ isn’t specified, this instance will be used to implement all of the service requests which get surfaced to here. This option is generally used at the top level to specify host connectivity.

Since implementing the server will usually need “normal” I/O, inputs and results act like normal hw.instance ports.

$identifier is used by frontends to specify or remember the type of implementation to use for this service.

Attributes:

AttributeMLIR TypeDescription
service_symbol::mlir::FlatSymbolRefAttrflat symbol reference attribute
impl_type::mlir::StringAttrstring attribute
impl_opts::mlir::DictionaryAttrdictionary of named attribute values

Operands:

OperandDescription
inputsany type

Results:

ResultDescription
«unnamed»any type

esi.service.to_client (::circt::esi::ToClientOp)

An ESI service port headed to a particular client

Syntax:

operation ::= `esi.service.to_client` $inner_sym  attr-dict `:` $toClientType

Traits: HasParent<::circt::esi::CustomServiceDeclOp>

Attributes:

AttributeMLIR TypeDescription
inner_sym::mlir::StringAttrstring attribute
toClientType::mlir::TypeAttrany type attribute

esi.service.to_server (::circt::esi::ToServerOp)

An ESI service port headed to the service

Syntax:

operation ::= `esi.service.to_server` $inner_sym attr-dict `:` $toServerType

Traits: HasParent<::circt::esi::CustomServiceDeclOp>

Attributes:

AttributeMLIR TypeDescription
inner_sym::mlir::StringAttrstring attribute
toServerType::mlir::TypeAttrany type attribute

esi.service.decl (::circt::esi::CustomServiceDeclOp)

An ESI service interface declaration

Syntax:

operation ::= `esi.service.decl` $sym_name $ports attr-dict

A declaration of an ESI service interface. Defines a contract between a service provider and its clients.

Example:

esi.service.decl @HostComms {
  esi.service.to_server send : !esi.channel<!esi.any>
  esi.service.to_client recieve : !esi.channel<i8>
}

Traits: HasParent<::mlir::ModuleOp>, NoTerminator, SingleBlock

Interfaces: ServiceDeclOpInterface, Symbol

Attributes:

AttributeMLIR TypeDescription
sym_name::mlir::StringAttrstring attribute

esi.mem.ram (::circt::esi::RandomAccessMemoryDeclOp)

Random access memory service

Syntax:

operation ::= `esi.mem.ram` $sym_name $innerType `x` $depth attr-dict

Declares a service which is backed by a memory of some sort. Allows random access of the inner elements.

Ports: read(address: clog2(depth)) -> data: innerType write({address: clog2(depth), data: innerType}) -> done: i0

Users can ensure R/W ordering by waiting for the write “done” message before issuing a potentially dependant read. Ordering of R/W messages in flight is undefined.

Traits: HasParent<::mlir::ModuleOp>, NoTerminator, SingleBlock

Interfaces: ServiceDeclOpInterface, Symbol

Attributes:

AttributeMLIR TypeDescription
sym_name::mlir::StringAttrstring attribute
innerType::mlir::TypeAttrany type attribute
depth::mlir::IntegerAttr64-bit signless integer attribute

esi.service.req.inout (::circt::esi::RequestInOutChannelOp)

Request a bidirectional channel

Syntax:

operation ::= `esi.service.req.inout` $toServer `->` $servicePort `(` $clientNamePath `)` attr-dict `:`
              qualified(type($toServer)) `->` qualified(type($toClient))

Interfaces: ServiceReqOpInterface, SymbolUserOpInterface

Attributes:

AttributeMLIR TypeDescription
servicePort::circt::hw::InnerRefAttrRefer to a name inside a module
clientNamePath::mlir::ArrayAttrstring array attribute

Operands:

OperandDescription
toServeran ESI channel

Results:

ResultDescription
toClientan ESI channel

esi.service.req.to_client (::circt::esi::RequestToClientConnectionOp)

Request a connection to receive data

Syntax:

operation ::= `esi.service.req.to_client` $servicePort `(` $clientNamePath `)`
              attr-dict `:` qualified(type($toClient))

Interfaces: ServiceReqOpInterface, SymbolUserOpInterface

Attributes:

AttributeMLIR TypeDescription
servicePort::circt::hw::InnerRefAttrRefer to a name inside a module
clientNamePath::mlir::ArrayAttrstring array attribute

Results:

ResultDescription
toClientan ESI channel

esi.service.req.to_server (::circt::esi::RequestToServerConnectionOp)

Request a connection to send data

Syntax:

operation ::= `esi.service.req.to_server` $toServer `->` $servicePort `(` $clientNamePath `)`
              attr-dict `:` qualified(type($toServer))

Interfaces: ServiceReqOpInterface, SymbolUserOpInterface

Attributes:

AttributeMLIR TypeDescription
servicePort::circt::hw::InnerRefAttrRefer to a name inside a module
clientNamePath::mlir::ArrayAttrstring array attribute

Operands:

OperandDescription
toServeran ESI channel

esi.service.inout (::circt::esi::ServiceDeclInOutOp)

An ESI service port which has both directions

Syntax:

operation ::= `esi.service.inout` $inner_sym attr-dict
              `:` qualified($toServerType) `->` qualified($toClientType)

Traits: HasParent<::circt::esi::CustomServiceDeclOp>

Attributes:

AttributeMLIR TypeDescription
inner_sym::mlir::StringAttrstring attribute
toServerType::mlir::TypeAttrany type attribute
toClientType::mlir::TypeAttrany type attribute

esi.service.hierarchy.metadata (::circt::esi::ServiceHierarchyMetadataOp)

Metadata about a service in the service hierarchy

Syntax:

operation ::= `esi.service.hierarchy.metadata` `path` $serverNamePath (`implementing` $service_symbol^)?
              `impl` `as` $impl_type (`opts` $impl_details^)?
              `clients` $clients attr-dict

Interfaces: SymbolUserOpInterface

Attributes:

AttributeMLIR TypeDescription
service_symbol::mlir::FlatSymbolRefAttrflat symbol reference attribute
serverNamePath::mlir::ArrayAttrarray attribute
impl_type::mlir::StringAttrstring attribute
impl_details::mlir::DictionaryAttrdictionary of named attribute values
clients::mlir::ArrayAttrarray attribute

esi.service.impl_req (::circt::esi::ServiceImplementReqOp)

Request for a service to be implemented

Syntax:

operation ::= `esi.service.impl_req` (`svc` $service_symbol^)? `impl` `as` $impl_type (`opts` $impl_opts^)?
              `(` $inputs `)` attr-dict `:` functional-type($inputs, results)
              $portReqs

The connect services pass replaces service.instances with this op. The portReqs region is the set of connection requests which need to be implemented for this service instance. Channels to/from the requests have been added to the operands/results of this op and consumers/producers have been redirected.

Some other pass or frontend is expected to replace this op with an actual implementation.

Traits: NoTerminator

Attributes:

AttributeMLIR TypeDescription
service_symbol::mlir::FlatSymbolRefAttrflat symbol reference attribute
impl_type::mlir::StringAttrstring attribute
impl_opts::mlir::DictionaryAttrdictionary of named attribute values

Operands:

OperandDescription
inputsany type

Results:

ResultDescription
outputsany type

esi.service.instance (::circt::esi::ServiceInstanceOp)

Instantiate a server module

Syntax:

operation ::= `esi.service.instance` (`svc` $service_symbol^)? `impl` `as` $impl_type (`opts` $impl_opts^)?
              `(` $inputs `)` attr-dict `:` functional-type($inputs, results)

Instantiate a service adhering to a service declaration interface.

A pass collects all of the connection requests to the service this op implements from the containing modules’ descendants (in the instance hierarchy). It bubbles them all up to the module containing this op, creating the necessary ESI channel ports, groups them appropriately, then replaces this op with a service.impl_req.

If ‘service_symbol’ isn’t specified, this instance will be used to implement all of the service requests which get surfaced to here. This option is generally used at the top level to specify host connectivity.

Since implementing the server will usually need “normal” I/O, inputs and results act like normal hw.instance ports.

$identifier is used by frontends to specify or remember the type of implementation to use for this service.

Attributes:

AttributeMLIR TypeDescription
service_symbol::mlir::FlatSymbolRefAttrflat symbol reference attribute
impl_type::mlir::StringAttrstring attribute
impl_opts::mlir::DictionaryAttrdictionary of named attribute values

Operands:

OperandDescription
inputsany type

Results:

ResultDescription
«unnamed»any type

esi.service.to_client (::circt::esi::ToClientOp)

An ESI service port headed to a particular client

Syntax:

operation ::= `esi.service.to_client` $inner_sym  attr-dict `:` $toClientType

Traits: HasParent<::circt::esi::CustomServiceDeclOp>

Attributes:

AttributeMLIR TypeDescription
inner_sym::mlir::StringAttrstring attribute
toClientType::mlir::TypeAttrany type attribute

esi.service.to_server (::circt::esi::ToServerOp)

An ESI service port headed to the service

Syntax:

operation ::= `esi.service.to_server` $inner_sym attr-dict `:` $toServerType

Traits: HasParent<::circt::esi::CustomServiceDeclOp>

Attributes:

AttributeMLIR TypeDescription
inner_sym::mlir::StringAttrstring attribute
toServerType::mlir::TypeAttrany type attribute

Structural

ESI has a special module which doesn’t expose ports. All external interactions are expected to be done through services.

esi.pure_module.input (::circt::esi::ESIPureModuleInputOp)

Inputs become input ports when the module is lowered

Syntax:

operation ::= `esi.pure_module.input` $name attr-dict `:` type($value)

To create input ports when lowering a pure module op into an HWModuleOp, use this op. This op is typically created by a service implementation generator.

If two ‘input’ ops exist in the same block, the names match, and the type matches they’ll become one port during lowering. Two or more may not exist with the same name and different types. Useful for ‘clk’ and ‘rst’.

Traits: HasParent

Attributes:

AttributeMLIR TypeDescription
name::mlir::StringAttrstring attribute

Results:

ResultDescription
valueany type

esi.pure_module (::circt::esi::ESIPureModuleOp)

ESI pure module

Syntax:

operation ::= `esi.pure_module` $sym_name attr-dict-with-keyword $body

A module containing only ESI channels and modules with only ESI ports. All non-local connectivity is done through ESI services. If this module is the top level in the design, then the design’s actual top level ports are defined by a BSP.

Useful on its own for simulation and BSPs which don’t define a top-level.

Traits: HasParent mlir::ModuleOp, NoRegionArguments, NoTerminator, SingleBlock

Interfaces: HWModuleLike, InstanceGraphModuleOpInterface, PortList, RegionKindInterface, Symbol, SymboledPortList

Attributes:

AttributeMLIR TypeDescription
sym_name::mlir::StringAttrstring attribute

esi.pure_module.output (::circt::esi::ESIPureModuleOutputOp)

Outputs become output ports when the module is lowered

Syntax:

operation ::= `esi.pure_module.output` $name `,` $value attr-dict `:` type($value)

To create output ports when lowering a pure module op into an HWModuleOp, use this op. This op is typically created by a service implementation generator.

Two ‘output’ ops with the same name cannot exist in the same block.

Traits: HasParent

Attributes:

AttributeMLIR TypeDescription
name::mlir::StringAttrstring attribute

Operands:

OperandDescription
valueany type

esi.pure_module.param (::circt::esi::ESIPureModuleParamOp)

Params become module parameters when the module is lowered

Syntax:

operation ::= `esi.pure_module.param` $name `:` $type attr-dict

Allows attaching parameters to modules which become HW module parameters when lowering. Currently, they are ignored. Some low-level BSPs instantiate modules with parameters. This allows the modules produced to accept parameters so those BSPs can instantiate them.

Traits: HasParent

Attributes:

AttributeMLIR TypeDescription
name::mlir::StringAttrstring attribute
type::mlir::TypeAttrany type attribute

Interfaces

Misc CIRCT interfaces.

ChannelOpInterface (ChannelOpInterface)

“An interface for operations which carries channel semantics.”

Methods:

channelType

circt::esi::ChannelType channelType();

“Returns the channel type of this operation.” NOTE: This method must be implemented by the user.

innerType

mlir::Type innerType();

“Returns the inner type of this channel. This will be the type of the data value of the channel, if the channel carries data semantics. Else, return NoneType.” NOTE: This method must be implemented by the user.

ServiceDeclOpInterface (ServiceDeclOpInterface)

Any op which represents a service declaration should implement this interface.

Methods:

getPortList

void getPortList(llvm::SmallVectorImpl<ServicePortInfo>&ports);

Returns the list of interface ports for this service interface. NOTE: This method must be implemented by the user.

validateRequest

mlir::LogicalResult validateRequest(mlir::Operation*req);

Validate this service request against this service decl. NOTE: This method must be implemented by the user.

ServiceReqOpInterface (ServiceReqOpInterface)

Any op which is requesting connection to a service’s port or has information pertaining to one of said requests.

Methods:

getServicePort

hw::InnerRefAttr getServicePort();

Returns the service port symbol. NOTE: This method must be implemented by the user.

getClientNamePath

ArrayAttr getClientNamePath();

Returns the client name path. NOTE: This method must be implemented by the user.

setClientNamePath

void setClientNamePath(ArrayAttr newClientNamePath);

Set the client name path. NOTE: This method must be implemented by the user.

getToClientType

Type getToClientType();

Returns the type headed to the client. Can be null.

getToClient

Value getToClient();

Returns the value headed to the client. Can be null. NOTE: This method must be implemented by the user.

getToServerType

Type getToServerType();

Returns the type headed to the server. Can be null.

getToServer

Value getToServer();

Returns the value headed to the server. Can be null. NOTE: This method must be implemented by the user.

setToServer

void setToServer(Value newValue);

Set the value headed to the server. Can assert(). NOTE: This method must be implemented by the user.

'esi' Dialect Docs