A compiler-based RTL simulator for Veryl.
Celox compiles an elaborated Veryl design into executable simulation kernels and exposes the design through a type-safe TypeScript API. It is both a practical way to test RTL with Vitest and an open testbed for exploring how RTL simulators should be structured.
Try the Playground · Read the guide · Use the starter template · Browse the API
Celox explores a simple question: what does a modern RTL simulator architecture look like when compilation, scheduling, state representation, code generation, and testbench integration are designed together?
The project makes those boundaries explicit:
- Veryl-specific analysis ends at a source-independent design representation.
- Combinational dependencies and clock domains are scheduled before execution.
- A backend-independent IR and state layout are shared by multiple code generators.
- Native and WebAssembly execution use the same runtime contract.
- The testbench sees the same typed design API regardless of the execution backend.
This makes Celox useful as an architecture laboratory without reducing it to a compiler demo: the result can run real RTL tests, in Node.js or in a browser.
- Import
.verylmodules directly into TypeScript with generated port and hierarchy types. - Write assertions, fixtures, and parameterized tests with Vitest.
- Choose explicit event-based stepping or scheduled, time-based simulation.
- Exercise multiple clock domains and combinational clock cascades.
- Enable four-state simulation and drive or inspect
XandZvalues. - Override top-level parameters and include test-only Veryl sources.
- Inspect child instances and emit VCD waveforms.
- Compile a Veryl design into a native executable and run cocotb tests through VPI.
- Build an external netlist frontend with the Rust SDK and ship its simulator as a native application binary or a frontend-specific N-API/WASI addon.
- Run through the custom native backend on x86-64 and AArch64, the Cranelift fallback on other native targets, or WebAssembly in the browser.
The fastest way to start is the
celox-template. For an existing
Veryl project, install Celox, its Vite plugin, and Vitest:
npm add -D @celox-sim/celox @celox-sim/vite-plugin vitestEnable the plugin in vitest.config.ts:
import { defineConfig } from "vitest/config";
import celox from "@celox-sim/vite-plugin";
export default defineConfig({
plugins: [celox()],
});Given a Veryl module such as src/Adder.veryl:
module Adder (
a: input logic<16>,
b: input logic<16>,
sum: output logic<17>,
) {
always_comb {
sum = a + b;
}
}
you can import the design and test it as a typed object:
import { describe, expect, test } from "vitest";
import { Simulator } from "@celox-sim/celox";
import { Adder } from "../src/Adder.veryl";
describe("Adder", () => {
test("adds two values", () => {
const sim = Simulator.create(Adder);
try {
sim.dut.a = 100n;
sim.dut.b = 200n;
expect(sim.dut.sum).toBe(300n);
} finally {
sim.dispose();
}
});
});The Vite plugin analyzes the project and generates TypeScript sidecars, so port
names, signal values, and visible hierarchy are checked by TypeScript. See the
Getting Started guide
for the required Veryl.toml and tsconfig.json setup.
Simulator gives a test direct control over events. It is a good fit for
combinational blocks and cycle-oriented unit tests:
const sim = Simulator.create(Counter);
sim.dut.enable = 1n;
sim.tick();
expect(sim.dut.count).toBe(1n);
sim.dispose();Simulation manages clocks and simulation time. It is intended for multi-clock
and time-oriented scenarios:
const sim = Simulation.create(Counter);
sim.addClock("clk", { period: 10 });
sim.reset("rst");
sim.runUntil(100);
expect(sim.time()).toBe(100);
sim.dispose();Veryl source
│
▼
frontend analysis and hierarchy elaboration
│
▼
symbolic logic and dependency scheduling
│
▼
Simulator IR (SIR) and backend-independent optimization
│
▼
shared physical state layout
│
├──► native x86-64
├──► native AArch64
├──► Cranelift JIT
└──► WebAssembly
│
▼
event-driven runtime
│
▼
Rust / Node.js / browser hosts
The shared pipeline is deliberate. Scheduling and RTL semantics do not have to be reimplemented for every target, while backend implementations can still own their instruction selection, machine IR, register allocation, and code emission. The runtime separates next-state evaluation from commit when clock domains trigger together, then propagates combinational changes until the step settles.
For details, see the architecture overview, compiler components, and SIR reference.
Celox is under active development. Its current focus is synchronous RTL written in Veryl and tested at the design level. It is not a general SystemVerilog simulator, a gate-level timing simulator, or an implementation of detailed delta-cycle event semantics.
That narrower scope is intentional: it keeps the simulator small enough to make architectural changes, compare execution strategies, and test new compiler and runtime boundaries in a complete working system. Expect unsupported constructs and API changes while the design is still evolving.
- Getting Started
- Writing Tests
- Celox CLI and cocotb
- External Frontends and Rust Binaries
- Four-State Simulation
- VCD Waveforms
- TypeScript API
- Simulator Internals
- Release Policy
Celox is a Rust and pnpm workspace. The main local checks are:
cargo test
pnpm install
pnpm run build:napi
pnpm run build
pnpm testArchitecture discussions, bug reports, and focused experiments are welcome in GitHub Issues.
The repository dev container includes cocotb 2.0.1 and Verilator for native VPI integration tests and SystemVerilog benchmarks.
Licensed under either Apache License 2.0 or MIT, at your option.