Skip to content

Simulation and Execution

XLS provides multiple simulation and execution mechanisms (including interpreters and JIT compilation) to validate designs across different abstraction levels, from input DSLX down to the netlist level.

Execution / Simulation Flow Overview

XLS Intermediate Representation (IR) serves as the central abstraction for modeling computations in the XLS compiler. It represents programs as a directed graph of operations, where each node corresponds to a computation such as arithmetic, bitwise logic, or data movement.

Once DSLX code is lowered into IR, it can be executed or transformed through multiple pathways depending on the use case.

Execution Modes

XLS supports multiple execution and validation mechanisms across different abstraction levels:

  • DSLX Interpreter
    • Executes high-level DSLX code directly
    • Useful for early-stage validation and unit testing
  • IR Interpreter
    • Executes XLS IR without compilation
    • Useful for debugging and correctness checking
  • IR-level Simulation
    • Supports LLVM-based JIT compilation as well as Ahead-of-Time (AOT) compilation
    • Enables cycle-accurate simulation using XLS Block IR after code generation
    • JIT/AOT options compile to native machine code for high-performance simulation.
  • Code Generation (Hardware Path)

    • Converts XLS IR into Verilog/SystemVerilog
    • Enables generation of synthesizable hardware designs
    • Supports both combinational and pipelined circuit generation
  • Simulation and Validation

    • Generated Verilog can be simulated using external simulators (e.g., Verilator)
    • Supports validation at both RTL and post-synthesis (netlist) levels

Why this matters ?

Understanding these execution paths enables developers to:

  • Validate correctness at multiple abstraction levels
  • Compare performance between execution strategies
  • Bridge the gap between software-style execution and hardware realization

This is particularly important for contributors working on compiler optimizations and hardware-aware transformations.

DSLX

The DSLX interpreter (//xls/dslx:interpreter_main) operates on DSLX .x files that contain both the design and unit tests to execute (present as #[test] annotated functions).

The adler32 example demonstrates this: the design is encapsulated in the main, adler32_seq, and mod functions, and the samples are present in the test adler32_one_char (note that unit-style tests/interpretations of adler32_seq and mod could also be present).

Interpreter targets are automatically generated for dslx_test() targets, so no special declarations are necessary to wrap DSLX code.

To invoke these samples, execute the following:

bazel build -c opt //xls/examples/adler32:adler32_dslx_test
./bazel-bin/xls/examples/adler32/adler32_dslx_test

To execute directly via the interpreter, you can instead run:

$ bazel build -c opt //xls/dslx/interpreter_main
$ ./bazel-bin/xls/dslx/interpreter_main \
    ./xls/examples/adler32/adler32.x

These two methods are equivalent.

Execution comparison

The DSL interpreter provides a flag, --compare, to implicitly compare its run results to those of the IR-converted DSL functions. This helps "spot check" consistency between IR and DSL execution (in addition to other methods used in more generally in XLS, like the fuzzer).

The user may compare DSL execution to IR interpreter execution, IR JIT execution, or not perform IR comparison at all.

$ ./bazel-bin/xls/dslx/interpreter_main \
    ./xls/examples/adler32/adler32.x --compare=jit
$ ./bazel-bin/xls/dslx/interpreter_main \
    ./xls/examples/adler32/adler32.x --compare=interpreter
$ ./bazel-bin/xls/dslx/interpreter_main \
    ./xls/examples/adler32/adler32.x --compare=none

IR

XLS provides two means of evaluating IR - interpretation and native host compilation (the JIT). Both are invoked in nearly the same way, via the eval_ir_main tool.

eval_ir_main supports a wide number of use cases, but the most common end-user case will be to run a sample through a design. To evaluate a sample (1.0 + 2.5) on the add function in floating-point adder, one would run the following:

bazel build -c opt //xls/tools:eval_ir_main
./bazel-bin/xls/tools/eval_ir_main    \
--input '(bits[1]: 0x0, bits[8]:0x7F, bits[23]:0x0); (bits[1]: 0x0, bits[8]:0x80, bits[23]:0x200000)'   \
./bazel-bin/xls/dslx/stdlib/float32_add.opt.ir

By default, this runs via the JIT. To use the interpreter, add the --use_llvm_jit=false flag to the invocation.

eval_ir_main supports a broad set of options and modes of execution. Refer to its [very thorough] --help documentation for full details.

Netlists

Finally, compiled netlists can also be interpreted against input samples via the aptly-named netlist_interpreter_main tool. This tool currently only supports single sample evaluation (as illustrated in the IR section above):

bazel build -c opt //xls/tools:netlist_interpreter_main
./bazel-bin/xls/netlist/netlist_interpreter_main \
  --netlist <path to netlist>
  --module  <module to evaluate>
  --cell_library[_proto] <path to the module's cell library [proto]>
  --inputs  <input sample, as above>

As XLS does not currently provide an sample/example netlist (TODO(rspringer)), concrete values can't [yet] be provided here. The --cell_library flag merits extra discussion, though.

During netlist compilation, a cell library is provided to indicate the individual logic cells available for the design, and these cells are referenced in the output netlist. The interpreter needs a description of these cells' behaviors/functions, so the cell library must be provided here, as well. Many cell libraries are very large (> 1GB), and can thus incur significant processing overhead at startup, so we also accept pre-processed cell libraries, as CellLibraryProto messages, that contain much-abridged cell descriptions. The function_extractor_main tool can automatically perform this extraction for Liberty-formatted cell library descriptions.