CIRCT organizes its 30+ MLIR dialects into layers that correspond to different levels of hardware abstraction. This page catalogs the major dialects, their purpose, and how they relate to each other.
Core RTL Dialects
These three dialects form the backbone of CIRCT and represent hardware at the register-transfer level.
hw — Hardware Structure
The hw dialect1 provides structural abstractions analogous to Verilog’s module/instance hierarchy. It is designed as an open extension point — other dialects “mix in” with hw to add functionality.
Key operations:
hw.module— defines a hardware module with typed input/output portshw.instance— instantiates a modulehw.output— specifies the output values of a module
Key types:
hw.inout— bidirectional port typehw.struct— named aggregate typehw.array— fixed-size array type
hw.module @Adder(in %a: i32, in %b: i32, out sum: i32) {
%result = comb.add %a, %b : i32
hw.output %result : i32
}comb — Combinational Logic
Pure combinational operations with no state or side effects. This separation from sequential logic enables straightforward dataflow analysis and algebraic simplification.
Key operations:
- Arithmetic:
comb.add,comb.mul,comb.sub,comb.divs,comb.divu - Bitwise:
comb.and,comb.or,comb.xor,comb.shl,comb.shru,comb.shrs - Comparison:
comb.icmp(with predicates: eq, ne, slt, ult, sle, ule, sgt, ugt, sge, uge) - Selection:
comb.mux(2-input),comb.array_create,comb.array_get - Bit manipulation:
comb.concat,comb.extract,comb.replicate
All comb operations are marked as having no side effects, enabling aggressive CSE (common subexpression elimination) and dead code elimination.
seq — Sequential Logic
Stateful elements — registers, memories, and clock domain management.
Key operations:
seq.compreg— a register clocked on an edge, with optional resetseq.firreg— a FIRRTL-flavored register (used during FIRRTL lowering)seq.hlmem— high-level memory abstractionseq.clock_gate— clock gating primitive
%reg = seq.compreg %next_val, %clk reset %rst, %init_val : i32The separation of seq from comb is architecturally significant: it means combinational optimization passes never need to reason about state, and sequential optimizations (retiming, clock gating) never need to reason about arithmetic.
High-Level Input Dialects
firrtl — Chisel’s Intermediate Representation
FIRRTL (Flexible Intermediate Representation for RTL) is the IR emitted by the Chisel hardware construction language. CIRCT provides a complete FIRRTL dialect and the firtool compiler that replaces the legacy Scala FIRRTL Compiler (SFC).
Key features:
- Width inference (automatic bit-width propagation)
- Annotation-driven transformations (e.g., memory macro replacement, signal naming)
- Multiple FIRRTL abstraction levels: High, Mid, Low FIRRTL within the dialect
- Lowering from
chirrtl(Chisel-specific extensions) through tohw/comb/seq
moore — SystemVerilog Frontend
The Moore dialect provides a frontend for parsing SystemVerilog into MLIR. It reaches approximately 73% of the sv-tests compliance suite. This enables CIRCT to ingest existing SystemVerilog designs for analysis, transformation, or simulation.
llhd — Low-Level Hardware Description
The LLHD dialect provides a lower-level hardware description that captures time-based simulation semantics. It models signals, processes, and time-stepped execution, suitable for event-driven simulation.
HLS and Scheduling Dialects
calyx — Control and Data Separation
Calyx2 (from Cornell’s CAPRA group) is an intermediate language for HLS that separates hardware into:
- Structural components: registers, ALUs, memories, multiplexers — describing the datapath
- Control program: FSM-like constructs (
seq,par,if,while) — describing the schedule
This separation makes it natural to express statically scheduled hardware. Calyx serves as a target for multiple HLS frontends including Dahlia, Futil, and the PyTorch-to-Calyx flow.
calyx.component @main(%go: i1, %clk: i1, %reset: i1) -> (%done: i1) {
%r.in, %r.out = calyx.register @r : i32
%add.left, %add.right, %add.out = calyx.std_add @add : i32
calyx.wires { ... }
calyx.control {
calyx.seq {
calyx.enable @compute_group
}
}
}handshake — Dataflow Circuits
The Handshake dialect models dynamically scheduled dataflow circuits where operations communicate through ready/valid handshaking. Each operation fires when all its inputs are available — there is no global schedule.
Used by Dynamatic (EPFL) for dynamic HLS. Operations include:
handshake.func— a dataflow functionhandshake.fork— duplicates a token to multiple consumershandshake.join— waits for all inputs before producing outputhandshake.branch— conditional routinghandshake.mux— data selection based on a control tokenhandshake.buffer— pipeline buffer for throughput
pipeline — Static Pipeline Scheduling
Represents statically scheduled pipelines with explicit stage boundaries. Used for simple pipeline generation where the schedule is known at compile time.
ssp — Static Scheduling Problem
Models scheduling problems as a set of operations with precedence constraints and resource limitations. Used internally by HLS tools to formulate and solve scheduling.
Interconnect and Communication Dialects
esi — Elastic Silicon Interconnect
ESI3 addresses the SoC communication problem. Key concepts:
- Channels: Point-to-point, typed connections between modules with latency-insensitive semantics (FIFO-based)
- Message types: Rich type system including structs, arrays, unions, and variable-length lists
- Windows: Break large messages into frames to save wire area at the cost of bandwidth
- Services: Abstract communication substrates — ESI can select AXI, Avalon-MM, or custom protocols
ESI was originally developed at Microsoft4 and is being integrated into CIRCT. It provides typed, latency-insensitive interconnect that abstracts away signaling protocol details.
// A typed channel carrying 32-bit integers
!chan = !esi.channel<i32>
// A bundle of channels for bidirectional communication
!bundle = !esi.bundle<[!esi.channel<i32> from "request", !esi.channel<i64> to "response"]>msft — Microsoft-Specific Extensions
Physical placement and device-specific annotations. Provides operations for specifying physical locations on FPGA devices.
Verification Dialects
verif — Verification Constructs
High-level verification operations: assumptions, assertions, and cover points.
ltl — Linear Temporal Logic
Temporal logic properties for hardware verification. Expresses properties like “signal A is always eventually followed by signal B.”
smt — SMT Solver Interface
Interface to SMT solvers (Z3, Bitwuzla) for formal verification. Used by circt-bmc and circt-lec.
debug — Debug Information
Carries debug metadata through the compilation pipeline, enabling source-level debugging of generated hardware.
Simulation Dialects
arc — Simulation Abstractions
Models hardware as state-transition functions for cycle-accurate simulation. Connects to LLVM IR for compilation to native code (the Arcilator flow). Key insight: by modeling hardware as pure functions over state, the simulator can leverage LLVM’s optimization pipeline.
Utility Dialects
emit — Emission Control
Controls output formatting: file splitting, include guards, and SystemVerilog dialect selection.
sv — SystemVerilog Constructs
Provides direct access to SystemVerilog-specific constructs for the “last mile” of code generation:
sv.always_ff,sv.always_comb— behavioral blockssv.ifdef— conditional compilationsv.wire,sv.reg— declarationssv.verbatim— raw text injection (escape hatch)
om — Object Model
Metadata and configuration objects that travel alongside hardware descriptions but don’t map to logic.
Dialect Relationships and Lowering Paths
graph TD firrtl --> core["hw + comb + seq"] moore --> core calyx --> core handshake --> core pipeline --> core core --> sv["sv + emit"] core --> arc["arc → llvm"] core --> smt sv --> SV["SystemVerilog output"] arc --> Sim["Simulation binary"] smt --> FV["Formal verification"]
Multiple input representations all converge on the core hw/comb/seq representation, which then fans out to different backends: SystemVerilog emission, simulation compilation, or formal verification.
MLIR Affine and SCF Dialects for HLS
While not part of CIRCT itself, the standard MLIR affine and scf dialects play a critical role as input representations for HLS tools.
affine Dialect
The affine dialect5 represents loop nests with affine (linear) bounds and subscripts. This enables polyhedral analysis — a mathematically precise framework for reasoning about loop dependencies, enabling:
- Loop tiling: Breaking loops into blocks for data locality
- Loop interchange: Reordering loop dimensions
- Loop fusion/fission: Combining or splitting loop bodies
- Parallelization: Automatically identifying independent iterations via
--affine-parallelize - Memory access analysis: Proving independence of memory operations
Key operations:
affine.for— loop with affine boundsaffine.if— conditional with affine constraintsaffine.load/affine.store— memory access with affine indices
ScaleHLS and the POM framework both use the affine dialect as their primary loop-level IR, leveraging MLIR’s built-in polyhedral analysis libraries.
scf (Structured Control Flow) Dialect
The scf dialect6 represents general structured control flow — for, while, if — without the affine restriction. It serves as:
- A lowering target for
affine(after affine analysis is complete, loops lower toscf.for) - An input representation for HLS tools that don’t need polyhedral analysis
- A bridge between high-level algorithm descriptions and hardware scheduling dialects
The lowering path is: affine → scf → calyx/handshake (HLS scheduling) → hw/comb/seq (RTL).
AMD’s AIR dialect transforms scf.for loops into ping-pong buffering patterns for AIE architectures, constructing dependency edges between producer and consumer processes for concurrent communication and compute.
Footnotes
References
- HW Dialect Rationale
- HWArith Dialect Rationale
- Calyx Dialect in CIRCT
- ESI Dialect Documentation
- ESI Rationale
- MLIR Affine Dialect
- MLIR SCF Dialect