CIRCT

Circuit IR Compilers and Tools

LLHD Dialect

This dialect provides operations and types to interact with an event queue in an event-based simulation. It describes how signals change over time in reaction to changes in other signals and physical time advancing. Established hardware description languages such as SystemVerilog and VHDL use an event queue as their programming model to describe combinational and sequential logic, as well as test harnesses and test benches.

Rationale 

Register Reset Values 

Resets are problematic since Verilog forces designers to describe them as edge-sensitive triggers. This does not match the async resets found on almost all standard cell flip-flops, which are level-sensitive. Therefore a pass lowering from Verilog-style processes to a structural register such as seq.compreg would have to verify that the reset value is a constant in order to make the mapping from edge-sensitive Verilog description to level-sensitive standard cell valid.

In practice, designers commonly implement registers encapsulated in a Verilog module, with the reset value being provided as a module input port. This makes determining whether the input is a constant much more difficult. Most commercial tools relax this constraint and simply map the edge-sensitive reset to a level-sensitive one. This does not preserve the semantics of the input, which is bad. Most synthesis tools will then go ahead and fail during synthesis if a register’s reset value does not end up being a constant value.

Therefore the Deseq pass does not verify that a register’s reset value is a constant. Instead, it applies the same transform from edge-sensitive to level-sensitive reset as most other tools.

Types 

PtrType 

Pointer type

Syntax:

!llhd.ptr<
  ::mlir::Type   # elementType
>

Represents a pointer to a memory location holding a value of its element type. May be used to load and store data in distinct memory slots.

Parameters: 

ParameterC++ typeDescription
elementType::mlir::Type

TimeType 

Time type

Syntax: !llhd.time

Represents a simulation time value as a combination of a real time value in seconds (or any smaller SI time unit), a delta value representing infinitesimal time steps, and an epsilon value representing an absolute time slot within a delta step (used to model SystemVerilog scheduling regions).

Attributes 

InnerSymAttr 

Inner symbol definition

Defines the properties of an inner_sym attribute. It specifies the symbol name and symbol visibility for each field ID. For any ground types, there are no subfields and the field ID is 0. For aggregate types, a unique field ID is assigned to each field by visiting them in a depth-first pre-order. The custom assembly format ensures that for ground types, only @<sym_name> is printed.

Parameters: 

ParameterC++ typeDescription
props::llvm::ArrayRef<InnerSymPropertiesAttr>

InnerSymPropertiesAttr 

Parameters: 

ParameterC++ typeDescription
name::mlir::StringAttr
fieldIDuint64_t
sym_visibility::mlir::StringAttr

TimeAttr 

Time attribute

Represents a value of the LLHD time type.

Example: #llhd.time<0ns, 1d, 0e>

Parameters: 

ParameterC++ typeDescription
typellhd::TimeType
timeunsigned
timeUnit::llvm::StringRefSI time unit
deltaunsigned
epsilonunsigned

Operations 

llhd.combinational (::circt::llhd::CombinationalOp) 

A process that runs when any of its operand values change

Syntax:

operation ::= `llhd.combinational` (`->` type($results)^)?
              attr-dict-with-keyword $body

An llhd.combinational op encapsulates a region of IR that executes once at the beginning of the simulation, and subsequently whenever any of the values used in its body change. Control flow must eventually end in an llhd.yield terminator. The process may have results, in which case the llhd.yield terminators must provide a list of values to yield for the process results. Whenever any of the values used in the body change, the process reexecutes in order to compute updated results.

This op is commonly used to embed a control-flow description of some combinational logic inside the surrounding module’s graph region.

Example:

hw.module @Foo() {
  %0, %1 = llhd.combinational -> i42, i9001 {
    cf.cond_br %2, ^bb1(%3, %4 : i42, i9001), ^bb1(%5, %6 : i42, i9001)
  ^bb1(%7: i42, %8: i9001):
    llhd.yield %7, %8 : i42, i9001
  }
}

Traits: HasParent<hw::HWModuleOp>, NoRegionArguments, RecursiveMemoryEffects

Results: 

ResultDescription
resultsvariadic of any type

llhd.constant_time (::circt::llhd::ConstantTimeOp) 

Introduce a new time constant.

Syntax:

operation ::= `llhd.constant_time` $value attr-dict

The llhd.constant_time instruction introduces a new constant time value as an SSA-operator.

Example:

%1 = llhd.constant_time #llhd.time<1ns, 2d, 3d>

Traits: AlwaysSpeculatableImplTrait, ConstantLike

Interfaces: ConditionallySpeculatable, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{}

Attributes: 

AttributeMLIR TypeDescription
valuellhd::TimeAttrtime attribute

Results: 

ResultDescription
resulttime type

llhd.delay (::circt::llhd::DelayOp) 

Specifies value propagation delay

Syntax:

operation ::= `llhd.delay` $input `by` $delay attr-dict `:` type($result)

This operation propagates all value changes of the input to the output after the specified time delay. Reference values are not supported (e.g., pointers, inout, etc.) since the store-like operation used for those types should encode a delayed store.

Traits: AlwaysSpeculatableImplTrait, SameOperandsAndResultType

Interfaces: ConditionallySpeculatable, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{}

Attributes: 

AttributeMLIR TypeDescription
delayllhd::TimeAttrtime attribute

Operands: 

OperandDescription
inputa type without inout

Results: 

ResultDescription
resulta type without inout

llhd.drv (::circt::llhd::DrvOp) 

Drive a value into a signal.

Syntax:

operation ::= `llhd.drv` $signal `,` $value `after` $time ( `if` $enable^ )? attr-dict `:`
              qualified(type($signal))

The llhd.drv operation drives a new value onto a signal. A time operand also has to be passed, which specifies the frequency at which the drive will be performed. An optional enable value can be passed as last argument. In this case the drive will only be performed if the value is 1. In case no enable signal is passed the drive will always be performed. This operation does not define any new SSA operands.

Example:

%true = hw.constant true
%false = hw.constant false
%time = llhd.constant_time <1ns, 0d, 0e>
%sig = llhd.sig %true : i1

llhd.drv %sig, %false after %time : !hw.inout<i1>
llhd.drv %sig, %false after %time if %true : !hw.inout<i1>

Interfaces: DestructurableAccessorOpInterface, SafeMemorySlotAccessOpInterface

Operands: 

OperandDescription
signalInOutType
valuea known primitive element
timetime type
enable1-bit signless integer

llhd.final (::circt::llhd::FinalOp) 

A process that runs at the end of simulation

Syntax:

operation ::= `llhd.final` attr-dict-with-keyword $body

An llhd.final op encapsulates a region of IR that is to be executed after the last time step of a simulation has completed. This can be used to implement various forms of state cleanup and tear-down. Some verifications ops may also want to check that certain final conditions hold at the end of a simulation run.

The llhd.wait terminator is not allowed in llhd.final processes since there is no later time slot for the execution to resume. Control flow must eventually end in an llhd.halt terminator.

Execution order between multiple llhd.final ops is undefined.

Example:

hw.module @Foo() {
  llhd.final {
    func.call @printSimulationStatistics() : () -> ()
    llhd.halt
  }
}

Traits: HasParent<hw::HWModuleOp>, NoRegionArguments, RecursiveMemoryEffects

llhd.halt (::circt::llhd::HaltOp) 

Terminate execution of a process

Syntax:

operation ::= `llhd.halt` ($yieldOperands^ `:` type($yieldOperands))?
              attr-dict

The llhd.halt terminator suspends execution of the parent process forever, effectively terminating it. The yieldOperands are yielded as the result values of the parent process.

Example:

llhd.halt
llhd.halt %0, %1 : i42, i9001

Traits: HasParent<ProcessOp, FinalOp>, Terminator

Operands: 

OperandDescription
yieldOperandsvariadic of any type

llhd.load (::circt::llhd::LoadOp) 

Load a value.

Syntax:

operation ::= `llhd.load` $pointer attr-dict `:` qualified(type($pointer))

The llhd.load operation loads a value from a memory region given by pointer.

Example:

%int = hw.constant 0 : i32
%iPtr = llhd.var %int : i32

%iLd = llhd.load %iPtr : !llhd.ptr<i32>

Interfaces: InferTypeOpInterface

Operands: 

OperandDescription
pointerpointer type

Results: 

ResultDescription
resulta known primitive element

llhd.output (::circt::llhd::OutputOp) 

Introduce a new signal and drive a value onto it.

Syntax:

operation ::= `llhd.output` ( $name^ )? $value `after` $time attr-dict `:` qualified(type($value))

The llhd.output operation introduces a new signal and continuously drives a the given value onto it after a given time-delay. The same value is used to initialize the signal in the same way as the ‘init’ value in llhd.sig. An optional name can be given to the created signal. This shows up, e.g., in the simulation trace.

Example:

%value = hw.constant true
%time = llhd.constant_time <1ns, 0d, 0e>
%sig = llhd.output "sigName" %value after %time : i1

// is equivalent to

%value = hw.constant true
%time = llhd.constant_time <1ns, 0d, 0e>
%sig = llhd.sig "sigName" %value : i1
llhd.drv %sig, %value after %time : !hw.inout<i1>

Interfaces: InferTypeOpInterface

Attributes: 

AttributeMLIR TypeDescription
name::mlir::StringAttrstring attribute

Operands: 

OperandDescription
valuea known primitive element
timetime type

Results: 

ResultDescription
resultInOutType

llhd.prb (::circt::llhd::PrbOp) 

Probe a signal.

Syntax:

operation ::= `llhd.prb` $signal attr-dict `:` qualified(type($signal))

This operation probes a signal and returns the value it currently carries as a new SSA operand. The result type is always the type carried by the signal. In SSACFG regions, the operation has a read side effect on the signal operand. In graph regions, the operation is memory-effect free.

Example:

%true = hw.constant true
%sig_i1 = llhd.sig %true : i1
%prbd = llhd.prb %sig_i1 : !hw.inout<i1>

Interfaces: DestructurableAccessorOpInterface, InferTypeOpInterface, MemoryEffectOpInterface, SafeMemorySlotAccessOpInterface

Operands: 

OperandDescription
signalInOutType

Results: 

ResultDescription
resulta known primitive element

llhd.process (::circt::llhd::ProcessOp) 

A process that runs concurrently during simulation

Syntax:

operation ::= `llhd.process` (`->` type($results)^)?
              attr-dict-with-keyword $body

An llhd.process op encapsulates a region of IR that executes concurrently during simulation. Execution can be suspended using the llhd.wait terminator, which also includes a list of values that will cause the process execution to resume whenever they change. The llhd.halt terminator can be used to suspend execution forever. The process may have results, in which case any llhd.wait or llhd.halt terminators must provide a list of values to yield for the process results whenever execution is suspended. The process holds these result values until it is resumed and new result values are yielded.

Example:

hw.module @top() {
  %0, %1 = llhd.process -> i42, i9001 {
    llhd.wait yield (%2, %3 : i42, i9001), ^bb1
  ^bb1:
    llhd.halt %4, %5 : i42, i9001
  }
}

Traits: HasParent<hw::HWModuleOp>, NoRegionArguments, RecursiveMemoryEffects

Results: 

ResultDescription
resultsvariadic of any type

llhd.ptr.array_get (::circt::llhd::PtrArrayGetOp) 

Extract an element from a pointer to an array.

Syntax:

operation ::= `llhd.ptr.array_get` $input `[` $index `]` attr-dict `:` qualified(type($input))

The llhd.ptr.array_get operation allows to access the element of the $input operand at position $index. A new pointer aliasing the element will be returned.

Example:

// Returns a !llhd.ptr<i8>
%0 = llhd.ptr.array_get %arr[%index] : !llhd.ptr<!hw.array<4xi8>>

Traits: AlwaysSpeculatableImplTrait

Interfaces: ConditionallySpeculatable, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{}

Operands: 

OperandDescription
inputLLHD pointer type of an ArrayType values
indexa signless integer bitvector

Results: 

ResultDescription
resultLLHD pointer type of a type without inout values

llhd.ptr.array_slice (::circt::llhd::PtrArraySliceOp) 

Get a range of consecutive values from a pointer to an array

Syntax:

operation ::= `llhd.ptr.array_slice` $input `at` $lowIndex attr-dict `:` functional-type($input, $result)

The llhd.ptr.array_slice operation allows to access a sub-range of the $input operand, starting at the index given by the $lowIndex operand. The resulting slice length is defined by the result type. Returns a pointer aliasing the elements of the slice.

Width of ’lowIndex’ is defined to be the precise number of bits required to index the ‘input’ array. More precisely: for an input array of size M, the width of ’lowIndex’ is ceil(log2(M)). Lower and upper bound indexes which are larger than the size of the ‘input’ array results in undefined behavior.

Example:

%3 = llhd.ptr.array_slice %input at %lowIndex :
  (!llhd.ptr<!hw.array<4xi8>>) -> !llhd.ptr<!hw.array<2xi8>>

Traits: AlwaysSpeculatableImplTrait

Interfaces: ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{}

Operands: 

OperandDescription
inputLLHD pointer type of an ArrayType values
lowIndexa signless integer bitvector

Results: 

ResultDescription
resultLLHD pointer type of an ArrayType values

llhd.ptr.extract (::circt::llhd::PtrExtractOp) 

Extract a range of bits from an integer pointer

Syntax:

operation ::= `llhd.ptr.extract` $input `from` $lowBit attr-dict `:` functional-type($input, $result)

The llhd.ptr.extract operation allows to access a range of bits of the $input operand, starting at the index given by the $lowBit operand. The result length is defined by the result type.

Traits: AlwaysSpeculatableImplTrait

Interfaces: ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{}

Operands: 

OperandDescription
inputLLHD pointer type of a signless integer bitvector values
lowBita signless integer bitvector

Results: 

ResultDescription
resultLLHD pointer type of a signless integer bitvector values

llhd.ptr.struct_extract (::circt::llhd::PtrStructExtractOp) 

Extract a field from a pointer to a struct.

Syntax:

operation ::= `llhd.ptr.struct_extract` $input `[` $field `]` attr-dict `:` qualified(type($input))

The llhd.ptr.struct_extract operation allows access to the field of the $input operand given by its name via the $field attribute. A new pointer aliasing the field will be returned.

Example:

// Returns a !llhd.ptr<i8>
%0 = llhd.ptr.struct_extract %struct["foo"]
  : !llhd.ptr<!hw.struct<foo: i8, bar: i16>>

Traits: AlwaysSpeculatableImplTrait

Interfaces: ConditionallySpeculatable, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{}

Attributes: 

AttributeMLIR TypeDescription
field::mlir::StringAttrstring attribute

Operands: 

OperandDescription
inputLLHD pointer type of a StructType values

Results: 

ResultDescription
resultLLHD pointer type of a type without inout values

llhd.sig.array_get (::circt::llhd::SigArrayGetOp) 

Extract an element from a signal of an array.

Syntax:

operation ::= `llhd.sig.array_get` $input `[` $index `]` attr-dict `:` qualified(type($input))

The llhd.sig.array_get operation allows to access the element of the $input operand at position $index. A new subsignal aliasing the element will be returned.

Example:

// Returns a !hw.inout<i8>
%0 = llhd.sig.array_get %arr[%index] : !hw.inout<array<4xi8>>

Traits: AlwaysSpeculatableImplTrait

Interfaces: ConditionallySpeculatable, DestructurableAccessorOpInterface, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface), SafeMemorySlotAccessOpInterface

Effects: MemoryEffects::Effect{}

Operands: 

OperandDescription
inputInOutType of an ArrayType values
indexa signless integer bitvector

Results: 

ResultDescription
resultInOutType of a type without inout values

llhd.sig.array_slice (::circt::llhd::SigArraySliceOp) 

Get a range of consecutive values from a signal of an array

Syntax:

operation ::= `llhd.sig.array_slice` $input `at` $lowIndex attr-dict `:` functional-type($input, $result)

The llhd.sig.array_slice operation allows to access a sub-range of the $input operand, starting at the index given by the $lowIndex operand. The resulting slice length is defined by the result type. Returns a signal aliasing the elements of the slice.

Width of ’lowIndex’ is defined to be the precise number of bits required to index the ‘input’ array. More precisely: for an input array of size M, the width of ’lowIndex’ is ceil(log2(M)). Lower and upper bound indexes which are larger than the size of the ‘input’ array results in undefined behavior.

Example:

%3 = llhd.sig.array_slice %input at %lowIndex :
  (!hw.inout<array<4xi8>>) -> !hw.inout<array<2xi8>>

Traits: AlwaysSpeculatableImplTrait

Interfaces: ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{}

Operands: 

OperandDescription
inputInOutType of an ArrayType values
lowIndexa signless integer bitvector

Results: 

ResultDescription
resultInOutType of an ArrayType values

llhd.sig.extract (::circt::llhd::SigExtractOp) 

Extract a range of bits from an integer signal

Syntax:

operation ::= `llhd.sig.extract` $input `from` $lowBit attr-dict `:` functional-type($input, $result)

The llhd.sig.extract operation allows to access a range of bits of the $input operand, starting at the index given by the $lowBit operand. The result length is defined by the result type.

Traits: AlwaysSpeculatableImplTrait

Interfaces: ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface)

Effects: MemoryEffects::Effect{}

Operands: 

OperandDescription
inputInOutType of a signless integer bitvector values
lowBita signless integer bitvector

Results: 

ResultDescription
resultInOutType of a signless integer bitvector values

llhd.sig.struct_extract (::circt::llhd::SigStructExtractOp) 

Extract a field from a signal of a struct.

Syntax:

operation ::= `llhd.sig.struct_extract` $input `[` $field `]` attr-dict `:` qualified(type($input))

The llhd.sig.struct_extract operation allows access to the field of the $input operand given by its name via the $field attribute. A new subsignal aliasing the field will be returned.

Example:

// Returns a !hw.inout<i8>
%0 = llhd.sig.struct_extract %struct["foo"]
  : !hw.inout<struct<foo: i8, bar: i16>>

Traits: AlwaysSpeculatableImplTrait

Interfaces: ConditionallySpeculatable, DestructurableAccessorOpInterface, InferTypeOpInterface, NoMemoryEffect (MemoryEffectOpInterface), SafeMemorySlotAccessOpInterface

Effects: MemoryEffects::Effect{}

Attributes: 

AttributeMLIR TypeDescription
field::mlir::StringAttrstring attribute

Operands: 

OperandDescription
inputInOutType of a StructType values

Results: 

ResultDescription
resultInOutType of a type without inout values

llhd.sig (::circt::llhd::SignalOp) 

Create a signal.

Syntax:

operation ::= `llhd.sig` `` custom<ImplicitSSAName>($name) $init attr-dict
              `:` type($init)

The llhd.sig instruction introduces a new signal in the IR. The input operand determines the initial value carried by the signal, while the result type will always be a signal carrying the type of the init operand. A signal defines a unique name within the entity it resides in.

Example:

%c123_i64 = hw.constant 123 : i64
%foo = llhd.sig %c123_i64 : i64
%0 = llhd.sig name "foo" %c123_i64 : i64

This example creates a new signal named “foo”, carrying an i64 type with initial value of 123.

Interfaces: DestructurableAllocationOpInterface, InferTypeOpInterface, OpAsmOpInterface

Attributes: 

AttributeMLIR TypeDescription
name::mlir::StringAttrstring attribute

Operands: 

OperandDescription
inita known primitive element

Results: 

ResultDescription
resultInOutType

llhd.store (::circt::llhd::StoreOp) 

Store a value.

Syntax:

operation ::= `llhd.store` $pointer `,` $value attr-dict `:` qualified(type($pointer))

The llhd.store operation stores the value value to the memory region given by pointer.

Example:

%int = hw.constant 0 : i32
%iPtr = llhd.var %int : i32

llhd.store %iPtr, %int : !llhd.ptr<i32>

Operands: 

OperandDescription
pointerpointer type
valuea known primitive element

llhd.var (::circt::llhd::VarOp) 

Stack allocation.

Syntax:

operation ::= `llhd.var` $init attr-dict `:` qualified(type($init))

The llhd.var operation allocates a memory region on the stack with the initial value given by init, and returns a pointer to the allocated region.

Example:

%int = hw.constant 0 : i32
%iPtr = llhd.var %int : i32

Interfaces: InferTypeOpInterface

Operands: 

OperandDescription
inita known primitive element

Results: 

ResultDescription
resultpointer type

llhd.wait (::circt::llhd::WaitOp) 

Suspend execution of a process

Syntax:

operation ::= `llhd.wait` (`yield` ` ` `(` $yieldOperands^ `:` type($yieldOperands) `)` `,`)?
              (`delay` $delay^ `,`)?
              (`(`$observed^ `:` qualified(type($observed))`)` `,`)?
              $dest (`(` $destOperands^ `:` qualified(type($destOperands)) `)`)?
              attr-dict

The llhd.wait terminator suspends execution of the parent process until any of the observed values change or a fixed delay has passed. Execution resumes at the dest block with the destOperands arguments. The yieldOperands are yielded as the result values of the parent process.

Example:

llhd.wait ^bb1(%0, %1 : i42, i9001)
llhd.wait yield (%0, %1 : i42, i9001), ^bb1
llhd.wait delay %time, ^bb1
llhd.wait (%0, %1 : i42, i9001), ^bb1

Traits: AttrSizedOperandSegments, HasParent<ProcessOp>, Terminator

Operands: 

OperandDescription
yieldOperandsvariadic of any type
delaytime type
observedvariadic of a known primitive element
destOperandsvariadic of any type

Successors: 

SuccessorDescription
destany successor

llhd.yield (::circt::llhd::YieldOp) 

Yield results back from a combinational process

Syntax:

operation ::= `llhd.yield` ($yieldOperands^ `:` type($yieldOperands))?
              attr-dict

The llhd.yield terminator terminates control flow in the parent process and yields the yieldOperands as the result values of the process.

Example:

llhd.combinational {
  llhd.yield
}
%2:2 = llhd.combinational -> i42, i9001 {
  llhd.yield %0, %1 : i42, i9001
}

Traits: AlwaysSpeculatableImplTrait, HasParent<CombinationalOp>, ReturnLike, Terminator

Interfaces: ConditionallySpeculatable, NoMemoryEffect (MemoryEffectOpInterface), RegionBranchTerminatorOpInterface

Effects: MemoryEffects::Effect{}

Operands: 

OperandDescription
yieldOperandsvariadic of any type

Passes 

-llhd-combine-drives 

Combine scalar drives into aggregate drives

If individual drives cover all of an aggregate signal’s fields, merge them into a single drive of the whole aggregate value.

-llhd-deseq 

Convert sequential processes to registers

-llhd-desequentialize 

Convert sequential processes to registers

Analyzes the drive conditions of all drives in sequential processes and tries to convert them to registers. This is not always possible because a process might consist of more than two temporal regions, or the drive condition might depend on values sampled in the past that are not a clock, or the condition depends on the conjunction of two clocks which is not supported by regular registers, etc. If all drives in a process were successfully lowered to registers, the process is inlined into the surrounding module.

Options 

-max-primitives : The maximum number of primitives to analyze.

-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.process or hw.module.

-llhd-hoist-signals 

Hoist probes and promote drives to process results

-llhd-lower-processes 

Convert process ops to combinational ops where possible

-llhd-mem2reg 

Promotes memory and signal slots into values.

-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.process {
  %c5 = hw.constant 5 : i32
  %cond = llhd.prb %condsig : !hw.inout<i1>
  %ptr = llhd.var %c5 : i32
  cond_br %cond, ^bb1, ^bb2
^bb1:
  %c6 = hw.constant 6 : i32
  llhd.store %ptr, %c6 : !llhd.ptr<i32>
  br ^bb2
^bb2:
  %ld = llhd.load %ptr : !llhd.ptr<i32>
  %c-1_i32 = hw.constant -1 : i32
  %res = comb.xor %ld, %c-1_i32 : i32
  llhd.halt
}

is transformed to

llhd.process {
  %c5 = hw.constant 5 : i32
  %cond = llhd.prb %condsig : !hw.inout<i1>
  cond_br %cond, ^bb1, ^bb2(%c5 : i32)
^bb1:
  %c6 = hw.constant 6 : i32
  br ^bb2(%c6 : i32)
^bb2(%arg : i32):
  %c-1_i32 = hw.constant -1 : i32
  %res = comb.xor %arg, %c-1_i32 : i32
  llhd.halt
}

-llhd-process-lowering 

Lowers LLHD Processes to Entities.

TODO

-llhd-sig2reg 

Promote LLHD signals to SSA values

-llhd-temporal-code-motion 

Move drive operations to the exit basic block in processes

This pass uses the temporal region analysis to transform the IR such that every temporal region has a unique exit block and moves all ’llhd.drv’ operations in a temporal region into its exit block by adjusting the enable operand. Furthermore, it combines ’llhd.drv’ operations driving the same signal with the same delay by multiplexing the driven value according to their enable operands. This pass assumes that the early code motion pass has been run beforehand. Otherwise, dominance errors are to be expected.