CIRCT

Circuit IR Compilers and Tools

Passes

This document describes the available CIRCT passes and their contracts.

Conversion Passes 

-convert-affine-to-staticlogic: Convert Affine dialect to StaticLogic pipelines 

This pass analyzes Affine loops and control flow, creates a Scheduling problem using the Calyx operator library, solves the problem, and lowers the loops to a StaticLogic pipeline.

-convert-hw-to-llhd: Convert HW to LLHD 

This pass translates a HW design into an equivalent structural LLHD description.

-convert-llhd-to-llvm: Convert LLHD to LLVM 

This pass translates LLHD to LLVM.”

-convert-moore-to-core: Convert Moore to Core 

This pass translates Moore to the core dialects (Comb/HW/LLHD).

-create-pipeline: Create StaticLogic pipeline operations 

-export-split-verilog: Emit the IR to a (System)Verilog directory of files 

This pass generates (System)Verilog for the current design, mutating it where necessary to be valid Verilog.

Options 

-dir-name : Directory to emit into

-export-verilog: Emit the IR to a (System)Verilog file 

This pass creates empty module bodies for external modules. This is useful for linting to eliminate missing file errors.

-handshake-remove-block-structure: Remove block structure in Handshake IR 

-lower-calyx-to-hw: Lower Calyx to HW 

This pass lowers Calyx to HW.

-lower-firrtl-to-hw: Lower FIRRTL to HW 

Lower a module of FIRRTL dialect to the HW dialect family.

Options 

-warn-on-unprocessed-annotations : Emit warnings on unprocessed annotations during lower-to-hw pass

-lower-handshake-to-firrtl: Lower Handshake to FIRRTL 

Lower Handshake to FIRRTL.

Options 

-flatten : Flattens the generated FIRRTL component by inlining all dataflow component instantiations into the top module.

-lower-handshake-to-hw: Lower Handshake to ESI/HW/Comb/Seq 

Lower Handshake to ESI/HW/Comb/Seq.

-lower-scf-to-calyx: Lower SCF/Standard to Calyx 

This pass lowers SCF / standard to Calyx.

Options 

-top-level-function             : Identifier of top-level function to be the entry-point component of the Calyx program.
-cider-source-location-metadata : Whether to track source location for the Cider debugger.

-lower-static-logic-to-calyx: Lower StaticLogic to Calyx 

This pass lowers StaticLogic to Calyx.

Options 

-top-level-function             : Identifier of top-level function to be the entry-point component of the Calyx program.
-cider-source-location-metadata : Whether to track source location for the Cider debugger.

-lower-std-to-handshake: Lower Standard MLIR into Handshake IR 

Options 

-source-constants        : If true, will connect constants to source operations instead of to the control network. May reduce the size of the final circuit.
-disable-task-pipelining : If true, will disable support for task pipelining. This relaxes the restrictions put on the structure of the input CDFG. Disabling task pipelining may severely reduce kernel II.

Calyx Dialect Passes 

-calyx-clk-insertion: Inserts assignments from component clock to sub-component clock. 

-calyx-compile-control: Generates latency-insensitive finite state machines to realize control. 

This pass performs a bottom-up traversal of the control program and does the following:

  1. For each control statement such as “calyx.seq”, create a new GroupOp to contain all the structure to realize the schedule.
  2. Implement the schedule by setting the constituent groups’ GoOp and DoneOp.
  3. Replace the control statement in the control program with the corresponding compilation group.

-calyx-gicm: Lift group-invariant operations to wire-scope. 

This pass performs GICM (group-invariant code motion) of operations which are deemed to be invariant of the group in which they are placed. In practice, this amounts to anything which is not a calyx.group_done/assign/group_go operation. GICM’d operations are lifted to wire-scope.

After GICM, a Calyx component has the following properties:

  • No values are being defined within groups (excluding calyx.group_go). As such, groups will only contain group-level assignments (calyx.assign/group_done).
  • Any value referenced by operations within the group may safely be referenced by other groups, or operations in wire scope.
  • A group does not define anything structural; it exclusively describes wiring between existing structures.

-calyx-go-insertion: Insert go signals into the guards of a group’s non-hole assignments 

This pass inserts the operation “calyx.group_go” into the guards of all assignments housed in the group, with the exception of the “calyx.group_done” terminator. For example,

Before:

calyx.group @Group1 {
  calyx.assign %in = %out1, %guard ? : i8
  %done = calyx.group_done %out2 : i1
}

After:

// The `go` assignment takes on an undefined
// value until the Compile Control pass.
%undef = calyx.undef : i1
...
calyx.group @Group1 {
  %go = calyx.group_go %undef : i1

  %and = comb.and %guard, %go : i1
  calyx.assign %in = %out1, %and ? : i8

  %done = calyx.group_done %out2 : i1
}

-calyx-remove-groups: Inlines the groups in a Calyx component. 

This pass removes the Group interface from the Calyx program, and inlines all assignments. This is done in the following manner:

  1. Assign values to the ‘done’ signal of the component, corresponding with the top-level control group’s DoneOp. Add the ‘go’ signal of the component to all assignments.
  2. TODO(Calyx): If there are multiple writes to a signal, replace the reads with the disjunction.
  3. Remove all groups.

-calyx-reset-insertion: Connect component reset to sub-component reset for applicable components. 

ESI Dialect Passes 

-lower-esi-ports: Lower ESI input and/or output ports. 

-lower-esi-to-hw: Lower ESI to HW where possible and SV elsewhere. 

-lower-esi-to-physical: Lower ESI abstract Ops to ESI physical ops. 

FIRRTL Dialect Passes 

-firrtl-add-seqmem-ports: Add extra ports to memory modules 

This pass looks for AddSeqMemPortAnnotation annotations and adds extra ports to memories. It will emit metadata based if the AddSeqMemPortsFileAnnotation annotation is specified.

This pass requires that FIRRTL MemOps have been lowered to modules to add the extra ports.

-firrtl-blackbox-reader: Load source files for black boxes into the IR 

This pass handles reads the Verilog source files for black boxes and adds them as sv.verbatim.file operations into the IR. Later passes can then write these files back to disk to ensure that they can be accessed by other tools down the line in a well-known location. Supports inline and path annotations for black box source files.

The supported firrtl.circuit annotations are:

  • {class = "firrtl.transforms.BlackBoxTargetDirAnno", targetDir = "..."} Overrides the target directory into which black box source files are emitted.
  • {class = "firrtl.transforms.BlackBoxResourceFileNameAnno", resourceFileName = "xyz.f"} Specifies the output file name for the list of black box source files that is generated as a collateral of the pass.

The supported firrtl.extmodule annotations are:

  • {
      class = "firrtl.transforms.BlackBoxInlineAnno",
      name = "myfile.v",
      text = "..."
    }
    
    Specifies the black box source code (text) inline. Generates a file with the given name in the target directory.
  • {
      class = "firrtl.transforms.BlackBoxPathAnno",
      path = "myfile.v"
    }
    
    Specifies the file path as source code for the module. Copies the file to the target directory.

Options 

-input-prefix : Prefix for input paths in black box annotations. This should be the directory where the input file was located, to allow for annotations relative to the input file.

-firrtl-check-comb-cycles: Check combinational cycles and emit errors 

This pass checks combinational cycles in the IR and emit errors.

Options 

-print-simple-cycle : Print a simple cycle instead of printing all operations in SCC

-firrtl-dedup: Deduplicate modules which are structurally equivalent 

This pass detects modules which are structurally equivalent and removes the duplicate module by replacing all instances of one with the other. Structural equivalence ignores the naming of operations and fields in bundles, and any annotations. Deduplicating a module may cause the result type of instances to change if the field names of a bundle type change. To handle this, the pass will update any bulk-connections so that the correct fields are legally connected. Deduplicated modules will have their annotations merged, which tends to create many non-local annotations.

Statistics 

num-erased-modules : Number of modules which were erased by deduplication

-firrtl-dft: Wires test enables to clock gates for DFT infrastructure 

This pass will take a 1-bit signal targeted by DFTTestModeEnableAnnotation and wires it to the test_en port of every module named EICG_wrapper. This will create ports in any intermediate module on the path from the signal to the EICG_wrapper modules. This pass is used to enable the “Design For Testing” style of design when the intermediate modules were not originally built with DFT in mind.

-firrtl-emit-metadata: Emit metadata of the FIRRTL modules 

This pass handles the emission of several different kinds of metadata.

Options 

-repl-seq-mem         : Lower the seq mem for macro replacement and emit relevant metadata
-repl-seq-mem-circuit : Circuit root for seq mem metadata
-repl-seq-mem-file    : File to which emit seq meme metadata

-firrtl-emit-omir: Emit OMIR annotations 

This pass gathers the OMIRAnnotations in the design, updates the contained targets with the trackers that were scattered throughout the design upon reading the OMIR, and serializes the resulting data into a JSON file.

Options 

-file : Output file for the JSON-serialized OMIR data

-firrtl-expand-whens: Remove all when conditional blocks. 

This pass will:

  1. Resolve last connect semantics.
  2. Remove all when operations.

When a wire has multiple connections, only the final connection is used, all previous connections are overwritten. When there is a conditional connect, the previous connect is only overwritten when the condition holds:

w <= a
when c :
  w <= b

; Equivalent to:
w <= mux(c, b, a)

This pass requires that all connects are expanded.

-firrtl-extract-instances: Move annotated instances upwards in the module hierarchy 

This pass takes instances in the design annotated with one out of a particular set of annotations and pulls them upwards to a location further up in the module hierarchy.

The annotations that control the behaviour of this pass are:

  • MarkDUTAnnotation
  • ExtractBlackBoxAnnotation
  • ExtractClockGatesFileAnnotation

-firrtl-flatten-memory: Flatten aggregate memory data to a UInt 

This pass flattens the aggregate data of memory into a UInt, and inserts appropriate bitcasts to access the data.

-firrtl-grand-central: Remove Grand Central Annotations 

Processes annotations associated with SiFive’s Grand Central utility.

-firrtl-grand-central-signal-mappings: Generate signal mappings that force/probe remote signals 

Options 

-file : Output file for the JSON-serialized OMIR data

-firrtl-grand-central-taps: Generate code for grand central data and memory taps 

-firrtl-imconstprop: Intermodule constant propagation and dead code elimination 

Use optimistic constant propagation to delete ports and unreachable IR.

Statistics 

num-folded-op : Number of operations folded
num-erased-op : Number of operations erased

-firrtl-infer-resets: Infer reset synchronicity and add implicit resets 

This pass infers whether resets are synchronous or asynchronous, and extends reset-less registers with an asynchronous reset based on the following annotations:

  • sifive.enterprise.firrtl.FullAsyncResetAnnotation
  • sifive.enterprise.firrtl.IgnoreFullAsyncResetAnnotation

-firrtl-infer-rw: Infer the read-write memory port 

This pass merges the read and write ports of a memory, based a simple module-scoped heuristic. The heuristic checks if the read and write enable conditions are mutually exclusive. The heuristic tries to break up the read enable and write enable logic into an AND expression tree. It then compares the read and write AND terms, looking for a situation where the read/write is the complement of the write/read.

-firrtl-infer-widths: Infer the width of types 

This pass infers the widths of all types throughout a FIRRTL module, and emits diagnostics for types that could not be inferred.

-firrtl-inject-dut-hier: Add a level of hierarchy outside the DUT 

This pass takes the DUT (as indicated by the presence of a MarkDUTAnnotation) and moves all the contents of it into a new module insided the DUT named by an InjectDUTHierarchyAnnotation. This pass is intended to be used in conjunction with passes that pull things out of the DUT, e.g., SRAM extraction, to give the extracted modules a new home that is still inside the original DUT.

-firrtl-inliner: Performs inlining, flattening, and dead module elimination 

This inliner pass will inline any instance of module marked as inline, and recursively inline all instances inside of a module marked with flatten. This pass performs renaming of every entity with a name that is inlined by prefixing it with the instance name. This pass also will remove any module which is not reachable from the top level module.

The inline and flatten annotation attributes are attached to module definitions, and they are:

  {class = "firrtl.passes.InlineAnnotation"}
  {class = "firrtl.transforms.FlattenAnnotation"}

-firrtl-lower-annotations: Lower FIRRTL annotations to usable entities 

Lower FIRRTL annotations to usable forms. FIRRTL annotations are a big bag of semi-structured, irregular json. This pass normalizes all supported annotations and annotation paths.

Options 

-disable-annotation-classless : Ignore classless annotations.
-disable-annotation-unknown   : Ignore unknown annotations.

-firrtl-lower-chirrtl: Infer the memory ports of SeqMem and CombMem 

This pass finds the CHIRRTL behavioral memories and their ports, and transforms them into standard FIRRTL memory operations. For each seqmem or combmem, a new memory is created. For every memoryport operation using a CHIRRTL memory, a memory port is defined on the new standard memory.

The direction or kind of the port is inferred from how each of the memory ports is used in the IR. If a memory port is only written to, it becomes a Write port. If a memory port is only read from, it become a Read port. If it is used both ways, it becomes a ReadWrite port.

Write, ReadWrite and combinational Read ports are disabled by default, but then enabled when the CHIRRTL memory port is declared. Sequential Read ports have more complicated enable inference:

  1. If a wire or register is used as the index of the memory port, then the memory is enabled whenever a non-invalid value is driven to the address.
  2. If a node is used as the index of the memory port, then the memory is enabled at the declaration of the node.
  3. In all other cases, the memory is never enabled.

In the first two cases, they can easily produce a situation where we try to enable the memory before it is even declared. This produces a compilation error.

-firrtl-lower-memory: Lower memories to generated modules 

This pass lowers FIRRTL memory operations to generated modules.

-firrtl-lower-types: Lower FIRRTL types to ground types 

Lower aggregate FIRRTL types to ground types. Memories, ports, wires, etc are split appart by elements of aggregate types. The only aggregate types which exist after this pass are memory ports, though memory data types are split.

Connect and partial connect expansion and canonicalization happen in this pass.

Options 

-flatten-mem           : Concat all elements of the aggregate data into a single element.
-preserve-aggregate    : Preserve passive aggregate types in the module.
-preserve-public-types : Force to lower ports of toplevel and external modules evenwhen aggregate preservation mode.

-firrtl-prefix-modules: Prefixes names of modules and mems in a hiearchy 

This pass looks for modules annotated with the NestedPrefixModulesAnnotation and prefixes the names of all modules instantiated underneath it. If inclusive is true, it includes the target module in the renaming. If inclusive is false, it will only rename modules instantiated underneath the target module. If a module is required to have two different prefixes, it will be cloned.

The supported annotation is:

  {
    class = "sifive.enterprise.firrtl.NestedPrefixModulesAnnotation",
    prefix = "MyPrefix_",
    inclusive = true
  }

-firrtl-print-instance-graph: Print a DOT graph of the module hierarchy. 

-firrtl-print-nla-table: Print the NLA Table. 

-firrtl-remove-unused-ports: Remove unused ports 

This pass removes unused ports without annotations or symbols. Implementation wise, this pass iterates over the instance graph in a topological order from leaves to the top so that we can remove unused ports optimally.

Statistics 

num-removed-ports : Number of ports erased

-firrtl-sfc-compat: Perform SFC Compatibility fixes 

-mem-to-regofvec: Convert the memory to a Register vector 

This pass generates the logic to implement a memory using Registers.

Options 

-repl-seq-mem           : Prepare seq mems for macro replacement
-ignore-read-enable-mem : ignore the read enable signal, instead of assigning X on read disable

-merge-connections: Merge field-level connections into full bundle connections 

Options 

-aggressive-merging : Merge connections even when source values won't be simplified.

FSM Dialect Passes 

-fsm-print-graph: Print a DOT graph of the module hierarchy. 

Handshake Dialect Passes 

-handshake-add-ids: Add an ID to each operation in a handshake function. 

This pass adds an ID to each operation in a handshake function. This id can be used in lowerings facilitate mapping lowered IR back to the handshake code which it originated from. An ID is unique with respect to other operations of the same type in the function. The tuple of the operation name and the operation ID denotes a unique identifier for the operation within the handshake.func operation.

-handshake-dematerialize-forks-sinks: Dematerialize fork and sink operations. 

This pass analyses a handshake.func operation and removes all fork and sink operations.

-handshake-insert-buffers: Insert buffers to break graph cycles 

Options 

-strategy    : Strategy to apply. Possible values are: cycles, allFIFO, all (default)
-buffer-size : Number of slots in each buffer

-handshake-materialize-forks-sinks: Materialize fork and sink operations. 

This pass analyses a handshake.func operation and inserts fork and sink operations ensuring that all values have exactly one use.

-handshake-op-count: Count the number of operations (resources) in a handshake function. 

This pass analyses a handshake.func operation and prints the number of operations (resources) used the function.

-handshake-print-dot: Print .dot graph of a handshake function. 

This pass analyses a handshake.func operation and prints a .dot graph of the structure. If multiple functions are present in the IR, the top level function will be printed, and called functions will be subgraphs within the main graph.

-handshake-remove-buffers: Remove buffers from handshake functions. 

This pass analyses a handshake.func operation and removes any buffers from the function.

HW Dialect Passes 

-hw-print-instance-graph: Print a DOT graph of the module hierarchy. 

-hw-specialize: Specializes instances of parametric hw.modules 

Any hw.instance operation instantiating a parametric hw.module will trigger a specialization procedure which resolves all parametric types and values within the module based on the set of provided parameters to the hw.instance operation. This specialized module is created as a new hw.module and the referring hw.instance operation is rewritten to instantiate the newly specialized module.

LLHD Dialect Passes 

-llhd-early-code-motion: Move side-effect-free instructions and llhd.prb up in the CFG 

Moves side-effect-free instructions as far up in the CFG as possible. That means to the earliest block where all operands are defined. Special care has to be given to the llhd.prb instruction (which is the only side-effect instruction moved by this pass) as it must stay in the same temporal region, because otherwise it might sample an older or newer state of the signal. This pass is designed as a preparatory pass for the Temporal Code Motion pass to be able to move the llhd.drv operations in a single TR exiting block without having to move operations defining the operands used by the llhd.drv. It also enables total control flow elimination as the llhd.prb instructions would not be moved by other canonicalization passes.

-llhd-function-elimination: Deletes all functions. 

Deletes all functions in the module. In case there is still a function call in an entity or process, it fails. This pass is intended as a post-inlining pass to check if all functions could be successfully inlined and remove the inlined functions. This is necessary because Structural LLHD does not allow functions. Fails in the case that there is still a function call left in a llhd.proc or llhd.entity.

-llhd-memory-to-block-argument: Promote memory to block arguments. 

Promotes memory locations allocated with llhd.var to block arguments. This enables other optimizations and is required to be able to lower behavioral LLHD to structural LLHD. This is because there are no memory model and control flow in structural LLHD. After executing this pass, the “-llhd-block-argument-to-mux” pass can be used to convert the block arguments to multiplexers to enable more control-flow elimination.

Example:

llhd.proc @check_simple(%condsig : !llhd.sig<i1>) -> () {
  %c5 = llhd.const 5 : i32
  %cond = llhd.prb %condsig : !llhd.sig<i1>
  %ptr = llhd.var %c5 : i32
  cond_br %cond, ^bb1, ^bb2
^bb1:
  %c6 = llhd.const 6 : i32
  llhd.store %ptr, %c6 : !llhd.ptr<i32>
  br ^bb2
^bb2:
  %ld = llhd.load %ptr : !llhd.ptr<i32>
  %res = llhd.not %ld : i32
  llhd.halt
}

is transformed to

llhd.proc @check_simple(%condsig : !llhd.sig<i1>) -> () {
  %c5 = llhd.const 5 : i32
  %cond = llhd.prb %condsig : !llhd.sig<i1>
  cond_br %cond, ^bb1, ^bb2(%c5 : i32)
^bb1:
  %c6 = llhd.const 6 : i32
  br ^bb2(%c6 : i32)
^bb2(%arg : i32):
  %res = llhd.not %arg : i32
  llhd.halt
}

-llhd-process-lowering: Lowers LLHD Processes to Entities. 

TODO

MSFT Dialect Passes 

-lower-msft-to-hw: Lower MSFT ops to hw ops 

Options 

-verilog-file : File to output Verilog into

-msft-export-tcl: Create tcl ops 

Options 

-tops     : List of top modules to export Tcl for
-tcl-file : File to output Tcl into

-msft-lower-constructs: Lower high-level constructs 

-msft-lower-instances: Lower dynamic instances 

-msft-partition: Move the entities targeted for a design partition 

-msft-wire-cleanup: Cleanup unnecessary ports and wires 

Seq Dialect Passes 

-lower-seq-to-sv: Lower sequential ops to SV. 

SV Dialect Passes 

-hw-cleanup: Cleanup transformations for operations in hw.module bodies 

This pass merges sv.alwaysff operations with the same condition, sv.ifdef nodes with the same condition, and perform other cleanups for the IR. This is a good thing to run early in the HW/SV pass pipeline to expose opportunities for other simpler passes (like canonicalize).

-hw-export-module-hierarchy: Export module and instance hierarchy information 

This pass exports the module and instance hierarchy tree for each module with the firrtl.moduleHierarchyFile attribute. These are lowered to sv.verbatim ops with the output_file attribute.

Options 

-dir-name : Directory to emit into

-hw-generator-callout: Lower Generator Schema to external module 

This pass calls an external program for all the hw.module.generated nodes, following the description in the hw.generator.schema node.

Options 

-schema-name                    : Name of the schema to process
-generator-executable           : Generator program executable with optional full path
-generator-executable-arguments : Generator program arguments separated by ;

-hw-legalize-modules: Eliminate features marked unsupported in LoweringOptions 

This pass lowers away features in the SV/Comb/HW dialects that are unsupported by some tools, e.g. multidimensional arrays. This pass is run relatively late in the pipeline in preparation for emission. Any passes run after this must be aware they cannot introduce new invalid constructs.

-hw-memory-sim: Implement FIRRTMMem memories nodes with simulation model 

This pass replaces generated module nodes of type FIRRTLMem with a model suitable for simulation.

Options 

-repl-seq-mem           : Prepare seq mems for macro replacement
-ignore-read-enable-mem : ignore the read enable signal, instead of assigning X on read disable

-hw-stub-external-modules: transform external hw modules to empty hw modules 

This pass creates empty module bodies for external modules. This is useful for linting to eliminate missing file errors.

-prettify-verilog: Transformations to improve quality of ExportVerilog output 

This pass contains elective transformations that improve the quality of SystemVerilog generated by the ExportVerilog library. This pass is not compulsory: things that are required for ExportVerilog to be correct should be included as part of the ExportVerilog pass itself to make sure it is self contained.

-sv-extract-test-code: Extract simulation only constructs to modules and bind 

This pass extracts cover, assume, assert operations to a module, along with any ops feeding them only, to modules which are instantiated with a bind statement.