Background: The Chisel/FIRRTL Ecosystem

Chisel1 (Constructive Hardware in a Scala Embedded Language) is a hardware description language embedded in Scala, developed at UC Berkeley. Instead of writing Verilog directly, hardware engineers write Scala programs that generate hardware — enabling software-like abstractions (parameterized modules, type-safe interfaces, functional composition) for hardware design2.

FIRRTL3 (Flexible Intermediate Representation for RTL) is the intermediate representation emitted by Chisel. The original compilation flow was:

graph LR
    A["Chisel (Scala)"] --> B["FIRRTL IR (.fir file)"] --> C["Scala FIRRTL Compiler (SFC)"] --> D["Verilog"]

The Scala FIRRTL Compiler (SFC) was a compiler written in Scala that performed width inference, type checking, optimization passes, and Verilog emission. While functional, the SFC had significant limitations as designs grew larger.

The Problem: SFC Limitations

As RISC-V SoC designs (particularly SiFive’s commercial chips and the open-source Rocket Chip / BOOM processors) grew to millions of gates, the SFC became a bottleneck:

  • Performance: Scala/JVM implementation was orders of magnitude slower than C++ alternatives for large designs. Compilation of production SoCs could take tens of minutes to hours.
  • Memory usage: JVM garbage collection overhead was substantial for the large IR graphs involved.
  • Optimization limitations: The SFC’s pass infrastructure was ad-hoc compared to compiler frameworks like LLVM that had decades of optimization research.
  • Maintainability: Adding new features required duplicating infrastructure that already existed in mature compiler frameworks.

The Solution: CIRCT’s FIRRTL Dialect

CIRCT’s FIRRTL dialect4 provides a drop-in replacement for the SFC. The CIRCT implementation:

  • Is written in C++ using MLIR’s infrastructure, providing 10-100x faster compilation
  • Tracks the FIRRTL specification closely, with strategic deviations to leverage MLIR’s strengths
  • Supports the full FIRRTL spec including the CHIRRTL variant directly produced by Chisel
  • Handles FIRRTL annotations (the metadata system used by Chisel for tool-specific directives)

Key Design Decisions

FIRRTL-specific types rather than MLIR integers: The FIRRTL dialect uses !firrtl.uint<32> and !firrtl.sint<16> instead of MLIR’s standard i32/i16. This allows uniform handling of unknown-width integers (where width inference has not yet run), which would be impossible with fixed-width MLIR integer types.

Multiple-result instances: In the FIRRTL spec, an instance produces a bundle of all the module’s ports. CIRCT’s firrtl.instance instead produces multiple results (one per port), leveraging MLIR’s variadic result support. This avoids the need for bundle-manipulation operations just to access ports.

SSA dominance: The FIRRTL spec allows cyclic connections (module bodies are graph regions). CIRCT enforces SSA def-before-use dominance in module bodies, using firrtl.wire operations as forward declarations when cycles are needed.

Separated invalidation: The FIRRTL spec combines is invalid as a statement. CIRCT separates this into firrtl.invalidvalue (which produces an invalid value) and firrtl.connect (which performs the connection), simplifying analysis and transformation.

The Two-Compiler Era (Chipyard)

During the transition period, the Chipyard5 SoC framework (used for Rocket Chip, BOOM, and other Berkeley designs) ran both compilers:

  1. SFC compiled Chisel to CHIRRTL (an intermediate FIRRTL format)
  2. MFC (MLIR FIRRTL Compiler, i.e., firtool) compiled CHIRRTL to Verilog

The SFC was kept as a fallback for features not yet supported by firtool (e.g., fixed-point types). This arrangement has since been largely phased out as firtool reached feature completeness.

Current State: firtool as the Default

As of Chisel 5.x, firtool is the default and recommended FIRRTL compiler. The integration works as follows:

Compilation Flow

graph TD
    A["Chisel 5 (Scala)"] -->|"Emits FIRRTL (.fir) or CHIRRTL"| B["firtool (CIRCT)"]
    B --> C["Parse FIRRTL → firrtl.circuit MLIR"]
    C --> D["Width inference, reset inference"]
    D --> E["Module deduplication, inlining"]
    E --> F["Lower types (bundles/vectors → ground types)"]
    F --> G["Lower FIRRTL → HW + Comb + Seq"]
    G --> H["HW optimization passes"]
    H --> I["Lower to SV"]
    I --> J["Verilog emission"]
    J --> K["SystemVerilog output"]

Integration Mechanisms

sbt/mill plugin: Chisel’s build system plugins invoke firtool automatically. The firtool binary must be on the system PATH.

C API: Chisel can invoke firtool programmatically through its C API (circt-c/Firtool.h), avoiding the overhead of process spawning for iterative workflows.

chisel-circt (archived): SiFive previously maintained a separate chisel-circt6 library that provided a ChiselStage-like interface for MFC. This project is now archived because upstream Chisel natively supports CIRCT.

FIRRTL Annotations

Annotations are FIRRTL’s metadata system — JSON objects attached to circuit elements that drive tool-specific behavior. Examples include:

  • DontTouchAnnotation — prevents dead-code elimination of a signal
  • BlackBoxInlineAnnotation — embeds Verilog source for a black-box module
  • MemoryFileInlineAnnotation — specifies initial memory contents
  • Grand Central annotations — generate verification interfaces

firtool processes annotations through dedicated passes (firrtl-lower-annotations). Some annotations that were SFC-specific required new CIRCT implementations.

FIRRTL Specification Tracking

The FIRRTL dialect closely tracks updates to the FIRRTL specification. The CIRCT codebase uses feature-gating constants to manage specification versioning:

  • nextFIRVersion — features accepted into the spec but not yet released
  • missingSpecFIRVersion — behaviors that exist in the SFC but are not formally specified

This versioning system ensures compatibility across FIRRTL spec revisions while maintaining the ability to support legacy designs.

Performance Comparison: firtool vs SFC

The C++/MLIR implementation provides dramatic speedups over the Scala implementation:

  • Small designs (e.g., simple Rocket core): 5-10x faster
  • Medium designs (e.g., BOOM): 10-50x faster
  • Large SoC designs: 50-100x faster, with proportionally better memory usage

These improvements are due to:

  1. C++ vs JVM execution overhead
  2. MLIR’s efficient in-memory IR representation (no JVM object overhead)
  3. MLIR’s parallel pass infrastructure
  4. Better memory locality in C++ data structures

Beyond Chisel: Other FIRRTL Producers

While Chisel is the primary producer of FIRRTL, the FIRRTL format is designed as an open interchange format. Other tools that produce or consume FIRRTL include:

  • Spatial — a domain-specific language for accelerator design (Stanford)
  • PyRTL — a Python-based hardware description framework
  • ESSENT — a high-performance simulator that consumes FIRRTL directly
  • Custom generators — any tool can emit .fir files for processing by firtool

Footnotes

References

Footnotes

  1. Chisel GitHub Repository

  2. Chisel Documentation

  3. FIRRTL Specification

  4. FIRRTL Dialect Rationale

  5. Chipyard SoC Framework

  6. SiFive chisel-circt