CIRCT

Circuit IR Compilers and Tools

'handshake' Dialect

Types and operations for the handshake dialect This dialect defined the handshake dialect, modeling dataflow circuits. Handshake/dataflow IR is describes independent, unsynchronized processes communicating data through First-in First-out (FIFO) communication channels.

Operation definition

handshake.br (::circt::handshake::BranchOp)

branch operation

The branch operation represents an unconditional branch. The single data input is propagated to the single successor. The input must be triggered by some predecessor to avoid continous triggering of a successor block.

Example:

%1 = br %0 : i32

Traits: HasParent handshake::FuncOp

Interfaces: ControlInterface, ExecutableOpInterface, GeneralOpInterface, NamedIOInterface, NoSideEffect (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{}

Attributes:

AttributeMLIR TypeDescription
dataType::mlir::TypeAttrany type attribute

Operands:

OperandDescription
dataOperandany type

Results:

ResultDescription
dataResultany type

handshake.buffer (::circt::handshake::BufferOp)

buffer operation

The buffer operation represents a buffer operation. $slots must be an unsigned integer larger than 0. $bufferType=BufferTypeEnum::seq indicates a nontransparent buffer, while $bufferType=BufferTypeEnum::fifo indicates a transparent buffer.

An ‘initValues’ attribute containing a list of integer values may be provided. The list must be of the same length as the number of slots. This will initialize the buffer with the given values upon reset. For now, only sequential buffers are allowed to have initial values. @todo: How to support different init types? these have to be stored (and retrieved) as attributes, hence they must be of a known type.

Traits: HasClock, HasParent handshake::FuncOp

Interfaces: ControlInterface, ExecutableOpInterface, GeneralOpInterface, NamedIOInterface, NoSideEffect (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{}

Attributes:

AttributeMLIR TypeDescription
dataType::mlir::TypeAttrany type attribute
size::mlir::IntegerAttr32-bit signless integer attribute whose minimum value is 1
bufferType::circt::handshake::BufferTypeEnumAttrBufferOp seq or fifo
initValues::mlir::ArrayAttr64-bit integer array attribute

Operands:

OperandDescription
operandany type

Results:

ResultDescription
resultany type

handshake.cond_br (::circt::handshake::ConditionalBranchOp)

conditional branch operation

The cbranch operation represents a conditional branch. The data input is propagated to one of the two outputs based on the condition input.

Example:

%true, %false = conditional_branch %cond, %data : i32

Traits: HasParent handshake::FuncOp

Interfaces: ControlInterface, ExecutableOpInterface, NamedIOInterface, NoSideEffect (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{}

Operands:

OperandDescription
conditionOperand1-bit signless integer
dataOperandany type

Results:

ResultDescription
trueResultany type
falseResultany type

handshake.constant (::circt::handshake::ConstantOp)

constant operation

Syntax:

operation ::= `handshake.constant` $ctrl attr-dict `:` qualified(type($result))

The const has a constant value. When triggered by its single ctrl input, it sends the constant value to its single successor.

Example:

%0 = constant %ctrl {value = 42 : i32} : i32

Traits: HasParent handshake::FuncOp

Interfaces: ControlInterface, ExecutableOpInterface, GeneralOpInterface, NamedIOInterface, NoSideEffect (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{}

Attributes:

AttributeMLIR TypeDescription
value::mlir::TypedAttrTypedAttr instance

Operands:

OperandDescription
ctrlnone type

Results:

ResultDescription
resultany type

handshake.control_merge (::circt::handshake::ControlMergeOp)

control merge operation

The control_merge operation represents a (nondeterministic) control merge. Any input is propagated to the first output and the index of the propagated input is sent to the second output. The number of inputs corresponds to the number of predecessor blocks. ControlMerge is a control-only component(i.e., has no data but only bidirectional handshake).

Example:

%0, %idx = control_merge %a, %b, %c : i32

Traits: HasClock, HasParent handshake::FuncOp

Interfaces: ControlInterface, ExecutableOpInterface, MergeLikeOpInterface, NamedIOInterface, NoSideEffect (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{}

Attributes:

AttributeMLIR TypeDescription
dataType::mlir::TypeAttrany type attribute
size::mlir::IntegerAttr32-bit signless integer attribute whose minimum value is 1

Operands:

OperandDescription
dataOperandsany type

Results:

ResultDescription
resultany type
indexindex

handshake.end (::circt::handshake::EndOp)

end operation

Syntax:

operation ::= `handshake.end` operands attr-dict `:` qualified(type($control)) `,` qualified(type($operands))

The end propagates the result of the appropriate return operation from one of its inputs to its single output after all memory accesses have completed. Currently not used(data returned through ReturnOp).

Traits: HasParent handshake::FuncOp

Interfaces: ControlInterface, ExecutableOpInterface, NamedIOInterface

Operands:

OperandDescription
controlany type
operandsany type

handshake.extmemory (::circt::handshake::ExternalMemoryOp)

external memory

Syntax:

operation ::= `handshake.extmemory` `[` `ld` `=` $ldCount `,` `st` `=`  $stCount `]` `(` $memref `:` qualified(type($memref)) `)` `(` $inputs `)` attr-dict `:` functional-type($inputs, $outputs)

An ExternalMemoryOp represents a wrapper around a memref input to a handshake function. The semantics of the load/store operands are identical to what is decribed for MemoryOp. The only difference is that the first operand to this operand is a memref value. Upon lowering to FIRRTL, a handshake interface will be created in the top-level component for each load- and store which connected to this memory.

Example:

handshake.func @main(%i: index, %v: i32, %mem : memref<10xi32>, %ctrl: none) -> none {
  %stCtrl = extmemory[ld = 0, st = 1](%mem : memref<10xi32>)(%vout, %addr) {id = 0 : i32} : (i32, index) -> (none)
  %vout, %addr = store(%v, %i, %ctrl) : (i32, index, none) -> (i32, index)
  ...
}

Traits: HasParent handshake::FuncOp

Interfaces: ControlInterface, ExecutableOpInterface, NamedIOInterface

Attributes:

AttributeMLIR TypeDescription
ldCount::mlir::IntegerAttr32-bit signless integer attribute
stCount::mlir::IntegerAttr32-bit signless integer attribute
id::mlir::IntegerAttr32-bit signless integer attribute

Operands:

OperandDescription
memrefmemref of any type values
inputsany type

Results:

ResultDescription
outputsany type

handshake.fork (::circt::handshake::ForkOp)

fork operation

The fork operation represents a fork operation. A single input is replicated to N outputs and distributed to each output as soon as the corresponding successor is available.

Example:

%1:2 = fork [2] %0 : i32

Traits: HasClock, HasParent handshake::FuncOp

Interfaces: ControlInterface, ExecutableOpInterface, GeneralOpInterface, NamedIOInterface, NoSideEffect (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{}

Attributes:

AttributeMLIR TypeDescription
dataType::mlir::TypeAttrany type attribute
size::mlir::IntegerAttr32-bit signless integer attribute whose minimum value is 1

Operands:

OperandDescription
operandany type

Results:

ResultDescription
resultany type

handshake.func (::circt::handshake::FuncOp)

Handshake dialect function.

The func operation represents a handshaked function. This is almost exactly like a standard FuncOp, except that it has some extra verification conditions. In particular, each Value must only have a single use.

Traits: HasClock, IsolatedFromAbove

Interfaces: FunctionOpInterface, OpAsmOpInterface, RegionKindInterface, Symbol

handshake.instance (::circt::handshake::InstanceOp)

module instantiate operation

Syntax:

operation ::= `handshake.instance` $module `(` $operands `)` attr-dict `:` functional-type($operands, results)

The instance operation represents the instantiation of a module. This is similar to a function call, except that different instances of the same module are guaranteed to have their own distinct state. The instantiated module is encoded as a symbol reference attribute named “module”. An instance operation takes a control input as its last argument and returns a control output as its last result.

Example:

%2:2 = handshake.instance @my_add(%0, %1, %ctrl) : (f32, f32, none) -> (f32, none)

Traits: HasParent handshake::FuncOp

Interfaces: CallOpInterface, ControlInterface, NamedIOInterface, SymbolUserOpInterface

Attributes:

AttributeMLIR TypeDescription
module::mlir::FlatSymbolRefAttrflat symbol reference attribute

Operands:

OperandDescription
operandsany type

Results:

ResultDescription
«unnamed»any type

handshake.join (::circt::handshake::JoinOp)

join operation

A control-only synchronizer. Produces a valid output when all inputs become available.

Example:

%0 = join %a, %b, %c : i32

Traits: HasParent handshake::FuncOp

Interfaces: ControlInterface, ExecutableOpInterface, GeneralOpInterface, NamedIOInterface

Operands:

OperandDescription
datanone type

Results:

ResultDescription
resultnone type

handshake.lazy_fork (::circt::handshake::LazyForkOp)

lazy fork operation

The lazy_fork operation represents a lazy fork operation. A single input is replicated to N outputs and distributed to each output when all successors are available.

Example:

%1:2 = lazy_fork [2] %0 : i32

Traits: HasParent handshake::FuncOp

Interfaces: ControlInterface, NamedIOInterface, NoSideEffect (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{}

Attributes:

AttributeMLIR TypeDescription
dataType::mlir::TypeAttrany type attribute
size::mlir::IntegerAttr32-bit signless integer attribute whose minimum value is 1

Operands:

OperandDescription
operandany type

Results:

ResultDescription
resultany type

handshake.load (::circt::handshake::LoadOp)

load operation

Load memory port, sends load requests to MemoryOp. From dataflow predecessor, receives address indices and a control-only value which signals completion of all previous memory accesses which target the same memory. When all inputs are received, the load sends the address indices to MemoryOp. When the MemoryOp returns a piece of data, the load sends it to its dataflow successor.

Operands: address indices (from predecessor), data (from MemoryOp), control-only input. Results: data (to successor), address indices (to MemoryOp).

Example:

%dataToSucc, %addr1ToMem, %addr2ToMem = load [%addr1, %addr2] %dataFromMem, %ctrl : i8, i16, index

Traits: HasParent handshake::FuncOp

Interfaces: ControlInterface, ExecutableOpInterface, NamedIOInterface

Operands:

OperandDescription
addressesany type
dataany type
ctrlnone type

Results:

ResultDescription
dataResultany type
addressResultsany type

handshake.memory (::circt::handshake::MemoryOp)

memory

Syntax:

operation ::= `handshake.memory` `[` `ld` `=` $ldCount `,` `st` `=`  $stCount `]` `(` $inputs `)` attr-dict `:` $memRefType `,` functional-type($inputs, $outputs)

Each MemoryOp represents an independent memory or memory region (BRAM or external memory). It receives memory access requests from load and store operations. For every request, it returns data (for load) and a data-less token indicating completion. The memory op represents a flat, unidimensional memory. Operands: all stores (stdata1, staddr1, stdata2, staddr2, …), then all loads (ldaddr1, ldaddr2,…) Outputs: all load outputs, ordered the same as load addresses (lddata1, lddata2, …), followed by all none outputs, ordered as operands (stnone1, stnone2,…ldnone1, ldnone2,…)

Traits: HasClock, HasParent handshake::FuncOp

Interfaces: ControlInterface, ExecutableOpInterface, MemoryOpInterface, NamedIOInterface

Attributes:

AttributeMLIR TypeDescription
ldCount::mlir::IntegerAttr32-bit signless integer attribute
stCount::mlir::IntegerAttr32-bit signless integer attribute
id::mlir::IntegerAttr32-bit signless integer attribute
memRefType::mlir::TypeAttrmemref type attribute
lsq::mlir::IntegerAttr1-bit signless integer attribute

Operands:

OperandDescription
inputsany type

Results:

ResultDescription
outputsany type

handshake.merge (::circt::handshake::MergeOp)

merge operation

The merge operation represents a (nondeterministic) merge operation. Any input is propagated to the single output. The number of inputs corresponds to the number of predecessor blocks.

Example:

%0 = merge %a, %b, %c : i32

Traits: HasParent handshake::FuncOp

Interfaces: ControlInterface, ExecutableOpInterface, MergeLikeOpInterface, NamedIOInterface, NoSideEffect (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{}

Attributes:

AttributeMLIR TypeDescription
dataType::mlir::TypeAttrany type attribute
size::mlir::IntegerAttr32-bit signless integer attribute whose minimum value is 1

Operands:

OperandDescription
dataOperandsany type

Results:

ResultDescription
resultany type

handshake.mux (::circt::handshake::MuxOp)

mux operation

The mux operation represents a(deterministic) merge operation. Operands: select, data0, data1, data2, …

The ‘select’ operand is received from ControlMerge of the same block and it represents the index of the data operand that the mux should propagate to its single output. The number of data inputs corresponds to the number of predecessor blocks.

The mux operation is intended solely for control+dataflow selection. For purely dataflow selection, use the ‘select’ operation instead.

Example:

%0 = mux %select [%data0, %data1, %data2] {attributes}: index, i32

Traits: HasParent handshake::FuncOp

Interfaces: ControlInterface, ExecutableOpInterface, MergeLikeOpInterface, NamedIOInterface, NoSideEffect (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{}

Attributes:

AttributeMLIR TypeDescription
dataType::mlir::TypeAttrany type attribute
size::mlir::IntegerAttr32-bit signless integer attribute whose minimum value is 1

Operands:

OperandDescription
selectOperandany type
dataOperandsany type

Results:

ResultDescription
resultany type

handshake.never (::circt::handshake::NeverOp)

never operation

Syntax:

operation ::= `handshake.never` attr-dict `:` qualified(type($result))

The never operation represents disconnected data source. The source never sets any ‘valid’ signal which will never trigger the successor at any point in time.

Traits: HasParent handshake::FuncOp

Interfaces: ControlInterface, NamedIOInterface, NoSideEffect (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{}

Results:

ResultDescription
resultany type

handshake.pack (::circt::handshake::PackOp)

packs a tuple

The pack operation constructs a tuple from separate values. The number of operands corresponds to the number of tuple elements. Similar to join, the output is ready when all inputs are ready.

Example:

%tuple = handshake.pack %a, %b {attributes} : tuple<i32, i64>

Traits: HasParent handshake::FuncOp

Interfaces: ControlInterface, ExecutableOpInterface, GeneralOpInterface, NamedIOInterface

Operands:

OperandDescription
inputsany type

Results:

ResultDescription
resultFixed-sized collection of other types

handshake.return (::circt::handshake::ReturnOp)

Handshake dialect return.

Syntax:

operation ::= `handshake.return` operands attr-dict `:` qualified(type(operands))

The return operation represents a handshaked function. This is almost exactly like a standard ReturnOp, except that it exists in a handshake.func. It has the same operands as standard ReturnOp which it replaces and an additional control - only operand(exit point of control - only network).

Traits: HasParent handshake::FuncOp, Terminator

Interfaces: ControlInterface, NamedIOInterface

Operands:

OperandDescription
operandsany type
controlnone type

handshake.select (::circt::handshake::SelectOp)

Select operation

The select operation will select between two inputs based on an input conditional. The select operation differs from a mux in that

  1. All operands must be valid before the operation can transact
  2. All operands will be transacted at simultaneously

The ‘select’ operation is intended to handle ‘std.select’ and other ternary-like operators, which considers strictly dataflow. The ‘mux’ operator considers control+dataflow between blocks.

Example:

%res = select %cond, %true, %false : i32

Traits: HasParent handshake::FuncOp

Interfaces: ControlInterface, NamedIOInterface, NoSideEffect (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{}

Operands:

OperandDescription
condOperand1-bit signless integer
trueOperandany type
falseOperandany type

Results:

ResultDescription
resultany type

handshake.sink (::circt::handshake::SinkOp)

sink operation

The sink operation discards any data that arrives at its input.The sink has no successors and it can continuously consume data.

Example:

sink %data : i32

Traits: HasParent handshake::FuncOp

Interfaces: ControlInterface, ExecutableOpInterface, NamedIOInterface

Operands:

OperandDescription
operandany type

handshake.source (::circt::handshake::SourceOp)

source operation

The source operation represents continuous token source. The source continously sets a ‘valid’ signal which the successor can consume at any point in time.

Traits: HasParent handshake::FuncOp

Interfaces: ControlInterface, NamedIOInterface, NoSideEffect (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{}

Results:

ResultDescription
resultany type

handshake.start (::circt::handshake::StartOp)

start operation

Syntax:

operation ::= `handshake.start` attr-dict `:` qualified(type($result))

Triggers execution of the control - only network. Placed in entry block. Currently not used( trigger given as function argument)

Traits: HasParent handshake::FuncOp

Interfaces: ControlInterface, ExecutableOpInterface, NamedIOInterface, NoSideEffect (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{}

Attributes:

AttributeMLIR TypeDescription
control::mlir::BoolAttrbool attribute

Results:

ResultDescription
resultnone type

handshake.store (::circt::handshake::StoreOp)

store operation

Store memory port, sends store requests to MemoryOp. From dataflow predecessors, receives address indices, data, and a control-only value which signals completion of all previous memory accesses which target the same memory. When all inputs are received, the store sends the address and data to MemoryOp.

Operands: address indices, data, control-only input. Results: data and address indices (sent to MemoryOp). Types: data type followed by address type.

Example:

%dataToMem, %addrToMem = store [%addr1, %addr2] %dataFromPred , %ctrl : i8, i16, index

Traits: HasParent handshake::FuncOp

Interfaces: ControlInterface, ExecutableOpInterface, GeneralOpInterface, NamedIOInterface

Operands:

OperandDescription
addressesany type
dataany type
ctrlnone type

Results:

ResultDescription
dataResultany type
addressResultany type

handshake.terminator (::circt::handshake::TerminatorOp)

handshake terminator operation

This op is used as a terminator in every block of the dataflow netlist (as a replacement for StandardOp branches). It has no functionality and can be removed in some subsequent pass, when the block structure is removed.

Traits: HasParent handshake::FuncOp, Terminator

Interfaces: ControlInterface, NamedIOInterface

Successors:

SuccessorDescription
destsany successor

handshake.unpack (::circt::handshake::UnpackOp)

unpacks a tuple

The unpack operation assigns each value of a tuple to a separate value for further processing. The number of results corresponds to the number of tuple elements. Similar to fork, each output is distributed as soon as the corresponding successor is ready.

Example:

%a, %b = handshake.unpack %tuple {attributes} : tuple<i32, i64>

Traits: HasClock, HasParent handshake::FuncOp

Interfaces: ControlInterface, ExecutableOpInterface, GeneralOpInterface, NamedIOInterface

Operands:

OperandDescription
inputFixed-sized collection of other types

Results:

ResultDescription
resultsany type

Attribute definition

BufferTypeEnumAttr

BufferOp seq or fifo

Syntax:

!handshake.buffer_type_enum<
  ::BufferTypeEnum   # value
>

Parameters:

ParameterC++ typeDescription
value::BufferTypeEnuman enum of type BufferTypeEnum

'handshake' Dialect Docs