CIRCT

Circuit IR Compilers and Tools

Intrinsics

Intrinsics provide an implementation-specific way to extend the FIRRTL language with new operations.

Intrinsics are currently implemented as intmodule’s.

Motivation 

Intrinsics provide a way to add functionality to FIRRTL without having to extend the FIRRTL language. This allows a fast path for prototyping new operations to rapidly respond to output requirements. Intrinsics maintain strict definitions and type checking.

Supported Intrinsics 

circt_sizeof 

Returns the size of a type. The input port is not read from and may be any type, including uninferred types.

ParameterTypeDescription
PortDirectionTypeDescription
iinputAnyvalue whose type is to be returned
sizeoutputUInt<32>Size of type of i

circt_isX 

Tests if the value is a literal x. FIRRTL doesn’t have a notion of ‘x per-se, but x can come in to the system from external modules and from SV constructs.
Verification constructs need to explicitly test for ‘x.

ParameterTypeDescription
PortDirectionTypeDescription
iinputAnyvalue test
foundoutputUInt<1>i is x

circt_plusargs_value 

Tests and extracts a value from simulator command line options with SystemVerilog $value$plusargs. This is described in SystemVerilog 2012 section 21.6.

We do not currently check that the format string substitution flag matches the type of the result.

ParameterTypeDescription
FORMATstringFormat string per SV 21.6
PortDirectionTypeDescription
foundoutputUInt<1>found in args
resultoutputAnyTypefound in args

circt_plusargs_test 

Tests simulator command line options with SystemVerilog $test$plusargs. This is described in SystemVerilog 2012 section 21.6.

ParameterTypeDescription
FORMATstringFormat string per SV 21.6
PortDirectionTypeDescription
foundoutputUInt<1>found in args

circt_clock_gate 

Enables and disables a clock safely, without glitches, based on a boolean enable value. If the enable input is 1, the output clock produced by the clock gate is identical to the input clock. If the enable input is 0, the output clock is a constant zero.

The enable input is sampled at the rising edge of the input clock; any changes on the enable before or after that edge are ignored and do not affect the output clock.

ParameterTypeDescription
PortDirectionTypeDescription
ininputClockinput clock
eninputUInt<1>enable for the output clock
outoutputClockgated output clock

circt_chisel_assert 

Generate a clocked SV assert statement, with optional formatted error message.

ParameterTypeDescription
formatstringFormat string per SV 20.10, 21.2.1. Optional.
labelstringLabel for assert/assume. Optional.
guardsstringSemicolon-delimited list of pre-processor tokens to use as ifdef guards. Optional.
PortDirectionTypeDescription
clockinputClockinput clock
predicateinputUInt<1>predicate to assert/assume
enableinputUInt<1>enable signal
inputSignalsarguments to format string

Example output:

wire _GEN = ~enable | cond;
assert__label: assert property (@(posedge clock) _GEN) else $error("message");

circt_chisel_ifelsefatal 

Generate a particular Verilog sequence that’s similar to an assertion.

Has legacy special behavior and should not be used by new code.

ParameterTypeDescription
formatstringFormat string per SV 20.10, 21.2.1. Optional.

This intrinsic also accepts the label and guard parameters which are recorded but not used in the normal emission.

PortDirectionTypeDescription
clockinputClockinput clock
predicateinputUInt<1>predicate to check
enableinputUInt<1>enable signal
inputSignalsarguments to format string

Example SV output:

`ifndef SYNTHESIS
  always @(posedge clock) begin
    if (enable & ~cond) begin
      if (`ASSERT_VERBOSE_COND_)
        $error("message");
      if (`STOP_COND_)
        $fatal;
    end
  end // always @(posedge)
`endif // not def SYNTHESIS

circt_chisel_assume 

Generate a clocked SV assume statement, with optional formatted error message.

ParameterTypeDescription
formatstringFormat string per SV 20.10, 21.2.1. Optional.
labelstringLabel for assume statement. Optional.
guardsstringSemicolon-delimited list of pre-processor tokens to use as ifdef guards. Optional.
PortDirectionTypeDescription
clockinputClockinput clock
predicateinputUInt<1>predicate to assume
enableinputUInt<1>enable signal
inputSignalsarguments to format string

Example SV output:

assume__label: assume property (@(posedge clock) ~enable | cond) else $error("message");	

circt_chisel_cover 

Generate a clocked SV cover statement.

ParameterTypeDescription
labelstringLabel for cover statement. Optional.
guardsstringSemicolon-delimited list of pre-processor tokens to use as ifdef guards. Optional.
PortDirectionTypeDescription
clockinputClockinput clock
predicateinputUInt<1>predicate to cover
enableinputUInt<1>enable signal

Example SV output:

cover__label: cover property (@(posedge clock) enable & cond);

circt_unclocked_assume 

Generate a SV assume statement whose predicate is used in a sensitivity list of the enclosing always block.

ParameterTypeDescription
formatstringFormat string per SV 20.10, 21.2.1. Optional.
labelstringLabel for assume statement. Optional.
guardsstringSemicolon-delimited list of pre-processor tokens to use as ifdef guards. Optional.
PortDirectionTypeDescription
predicateinputUInt<1>predicate to assume
enableinputUInt<1>enable signal
inputSignalsarguments to format string

Example SV output:

ifdef USE_FORMAL_ONLY_CONSTRAINTS
 `ifdef USE_UNR_ONLY_CONSTRAINTS
   wire _GEN = ~enable | pred;
   always @(edge _GEN)
     assume_label: assume(_GEN) else $error("Conditional compilation example for UNR-only and formal-only assert");
 `endif // USE_UNR_ONLY_CONSTRAINTS
endif // USE_FORMAL_ONLY_CONSTRAINTS

circt_dpi_call 

Call a DPI function. clock is optional and if clock is not provided, the callee is invoked when input values are changed. If provided, the dpi function is called at clock’s posedge. The result values behave like registers and the DPI function is used as a state transfer function of them.

enable operand is used to conditionally call the DPI since DPI call could be quite more expensive than native constructs. When enable is low, results of unclocked calls are undefined and evaluated into X. Users are expected to gate result values by another enable to model a default value of results.

For clocked calls, a low enable means that its register state transfer function is not called. Hence their values will not be modify in that clock.

ParameterTypeDescription
isClockedintSet 1 if the dpi call is clocked.
functionNamestringSpecify the function name.
inputNamesstringSemicolon-delimited list of input names. Optional.
outputNamestringOutput name. Optional.
PortDirectionTypeDescription
clock (optional)inputClockOptional clock operand
enableinputUInt<1>Enable signal
inputSignalsArguments to DPI function call
result (optional)outputSignalOptional result of the dpi call
DPI Intrinsic ABI 

Function Declaration:

  • Imported DPI function must be a void function that has input arguments which correspond to operand types, and an output argument which correspond to a result type.
  • Output argument must be a last argument.

Types:

  • Operand and result types must be passive.
  • A vector is lowered to an unpacked open array type, e.g. a: Vec<4, UInt<8>> to byte a [].
  • A bundle is lowered to a packed struct.
  • Integer types are lowered into into 2-state types.
  • Small integer types (< 64 bit) must be compatible to C-types and arguments are passed by values. Users are required to use specific integer types for small integers shown in the table below. Large integers are lowered to bit and passed by a reference.
WidthVerilog TypeArgument Passing Modes
1bitvalue
8bytevalue
16shortintvalue
32intvalue
64longintvalue
> 64bit [w-1:0]reference

Example SV output:

node result = intrinsic(circt_dpi_call<isClocked = 1, functionName="dpi_func"> : UInt<64>, clock, enable, uint_8_value, uint_32_value, uint_8_vector)
import "DPI-C" function void dpi_func(
  input  byte    in_0,
         int     in_1,
         byte    in_2[],
  output longint out_0
);

...

logic [63:0] _dpi_func_0;
reg   [63:0] _GEN;
always @(posedge clock) begin
  if (enable) begin
    dpi_func(in1, in2, _dpi_func_0);
    _GEN <= _dpi_func_0;
  end
end