CIRCT

Circuit IR Compilers and Tools

Probe Dialect

The Probe dialect provides SSA handles for observation of hardware values. A probe handle can be passed through module outputs and used where access to the observed value is needed, without representing that relationship as ordinary hardware dataflow or committing to a particular hierarchical path representation.

Rationale 

Hardware IR often needs to preserve an observation relationship across module boundaries. Routing the observed value through ordinary ports changes the module interface and may introduce unnecessary data dependencies. The Probe dialect represents this relationship explicitly: !probe.ref<T> is a read-only handle to a value of type T, created by probe.send and observed by probe.read. A read does not require the compiler to materialize ordinary hardware dataflow from the probe origin to the read site.

A probe reference provides read-only access to an observed hardware value. Its payload type must be accepted by the HW probe type classification. The current implementation supports integer and non-inout HW value types, !seq.clock, and HW aggregates recursively containing supported types. Dialects may extend the set of supported payload types through hw::ProbeTypeDialectInterface. Writable or bidirectional hardware references are not supported.

Probe references may be exposed through HW module output ports. They must not appear, directly or nested in an aggregate, on input or inout ports. Frontends must legalize such cases to ordinary ports, XMRs, or another suitable representation before creating Probe dialect IR. This keeps the producer of an observation explicit in the module hierarchy. This restriction simplifies compilation for a wide range of backends.

Supported Placement and Propagation 

The initial Probe dialect supports the following narrow path: probe.send and probe.read appear directly in an hw.module body, a probe reference is returned through an output port, and the corresponding hw.instance result is consumed by probe.read in the enclosing module body. This is the only cross-module propagation path for which the dialect currently defines semantics.

The Probe operations themselves cannot be placed in procedural regions such as hw.triggered, or in other nested regions. Probe handles must not be created or read inside those regions. Procedural logic should consume an ordinary hardware value obtained by reading a probe in the enclosing module body. The dialect does not define semantics for passing a probe reference across a region boundary; such uses should be legalized before creating Probe dialect IR.

Probe references propagated through instance-choice operations are currently unsupported. The dialect does not define how a probe reference is associated with the selected module, so FIRRTL lowering must diagnose or reject such uses until the required semantics are specified.

Probe references are not supported as HW inner-symbol targets. This applies both to a !probe.ref<T> value and to any field containing one. Other propagation paths through generic operations are similarly outside the current Probe dialect contract, even if those operations accept the type through a generic type constraint such as AnyType.

Example 

The producer creates a probe handle for %in and returns it through an output port. The consumer receives the handle from an instance and reads the observed value through it.

hw.module @Producer(in %in: i8, out p: !probe.ref<i8>) {
  %p = probe.send %in : i8
  hw.output %p : !probe.ref<i8>
}

hw.module @Consumer(in %in: i8, out out: i8) {
  %p = hw.instance "producer" @Producer(in: %in: i8) -> (p: !probe.ref<i8>)
  %value = probe.read %p : <i8>
  hw.output %value : i8
}

probe.send accepts any SSA value whose type is valid as the payload of a probe reference, including the result of an expression. It produces only the probe reference; ordinary dataflow consumers continue to use the original SSA value:

%value = comb.xor %a, %b : i8
%ref = probe.send %value : i8
%next = comb.xor %value, %c : i8

A probe observes the value passed to probe.send, not a particular SSA definition or expression representation. Optimizations may rewrite the producer, retarget the probe, or remove an unused probe handle as long as probe.read observes the same value. The observed value does not need to have an explicit name in the IR. A backend may generate a name when required, but automatically generated names are implementation details and are not guaranteed to remain stable across compiler runs or IR transformations.

External-module probe ABIs are presently outside the scope of this dialect definition.

Types 

RefType 

Read-only hardware probe handle

Syntax:

!probe.ref<
  ::mlir::Type   # elementType
>

A probe.ref type is an SSA handle for read-only observation of a hardware value. The handle does not encode or require a particular hierarchical path. Its payload type must support probe observation.

Parameters: 

ParameterC++ typeDescription
elementType::mlir::Typetype supported for probe observation

Operations 

probe.read (circt::probe::ReadOp) 

Read the value observed by a probe handle

Syntax:

operation ::= `probe.read` $input attr-dict `:` type($input)

probe.read returns the value observed through its input probe handle. It does not require the compiler to materialize ordinary hardware dataflow between the origin of the probe and the read site.

The result is the hardware value observed through the read-only probe handle.

Traits: AlwaysSpeculatableImplTrait, HasParent<hw::HWModuleOp>

Interfaces: ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{}

Operands: 

OperandDescription
inputprobe ref type

Results: 

ResultDescription
resulttype supported for probe observation

probe.send (circt::probe::SendOp) 

Create a read-only probe handle for a hardware value

Syntax:

operation ::= `probe.send` $input attr-dict `:` type($input)

probe.send creates a read-only probe handle for an arbitrary SSA hardware value. The ref result represents an observation of that value and does not represent ordinary hardware dataflow. Users that also consume the input value should use the original SSA value directly.

The observed value does not need to have an explicit name in the IR. A backend may generate a name when required. Automatically generated names are implementation details and are not guaranteed to remain stable across compiler runs or IR transformations.

Traits: AlwaysSpeculatableImplTrait, HasParent<hw::HWModuleOp>

Interfaces: ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{}

Operands: 

OperandDescription
inputtype supported for probe observation

Results: 

ResultDescription
refprobe ref type