Back to features

Fast Simulation

RTL and gate-level simulation powered by a Rust backend. WASM support enables in-browser testing and debugging.

Rust WASM RTL Gate-Level

Simulation Architecture

RHDL's simulation engine is built in Rust for maximum performance. Ruby designs are compiled into an optimized intermediate representation that the Rust backend evaluates cycle-by-cycle. This architecture delivers 10-100x speedups over interpreted simulation while maintaining cycle-accurate behavior.

Ruby Design

Components, ports, behavior

Compilation

Lower to simulation IR

Rust Engine

Event-driven evaluation

VCD waveform output
Ruby test assertions
WASM browser target

Run simulations directly from Ruby with full control over clock generation, stimulus, and waveform capture.

simulation.rb
require 'rhdl'

counter = Counter.new(width: 8)

# Create a simulation context
sim = RHDL::Sim::Simulator.new(counter)

# Configure waveform dumping
sim.vcd("counter.vcd") do |vcd|
  vcd.trace(counter.count, counter.clk)
end

# Apply stimulus and run
sim.run do
  counter.rst <= 1
  tick
  counter.rst <= 0
  counter.en  <= 1

  256.times do |i|
    tick
    assert_eq counter.count, i & 0xFF
  end
end

puts "Simulation passed! #{sim.cycle_count} cycles"

RTL vs Gate-Level

RHDL supports simulation at multiple abstraction levels. RTL simulation runs your design at the register-transfer level for fast functional verification. Gate-level simulation runs the synthesized netlist for timing-accurate validation.

FeatureRTL SimulationGate-Level Simulation
AbstractionBehavioral / register-transferPrimitive gates and flip-flops
SpeedFast — millions of cycles/secSlower — evaluates every gate
AccuracyCycle-accurate, no timingGate delays, setup/hold checks
Use caseFunctional verification, debuggingPost-synthesis validation
WaveformsHigh-level signalsGate-level nets, glitches visible
DebugMaps to Ruby source linesMaps to netlist nodes

RTL simulation evaluates behavior blocks directly. Fast iteration for design exploration.

rtl_sim.rb
# RTL-level simulation (default)
sim = RHDL::Sim::Simulator.new(
  design,
  level: :rtl
)

sim.run do
  # Apply test vectors
  design.opcode <= 0xA9  # LDA imm
  design.data   <= 0x42
  tick 3

  assert_eq design.acc, 0x42
  assert design.zero == 0
end

puts "RTL sim: #{sim.cycles_per_sec} Hz"
# => RTL sim: 2_450_000 Hz

Gate-level simulation evaluates the synthesized netlist. Catches issues that RTL simulation misses.

gate_sim.rb
# Gate-level simulation
netlist = design.synthesize
sim = RHDL::Sim::Simulator.new(
  netlist,
  level: :gate
)

sim.run do
  # Same test vectors
  netlist.opcode <= 0xA9
  netlist.data   <= 0x42
  tick 3

  assert_eq netlist.acc, 0x42
  assert netlist.zero == 0
end

puts "Gate sim: #{sim.cycles_per_sec} Hz"
# => Gate sim: 185_000 Hz

Browser Simulation

The Rust simulation engine compiles to WebAssembly (WASM), enabling simulations to run entirely in the browser. This opens up possibilities for interactive tutorials, shareable demos, and collaborative debugging without any local toolchain installation.

Rust Sim Engine

Native simulation core

wasm-pack

Compile to WASM + JS bindings

Browser

Run in any modern browser

Compile your design for browser execution. The WASM output includes JavaScript bindings for controlling the simulation from web applications.

wasm_export.rb
# Compile design to WASM for browser simulation
design = Counter.new(width: 8)

design.to_wasm("web/sim.wasm",
  expose: [:clk, :rst, :en, :count],
  optimize: :size   # optimize for download size
)

# Generated JS usage:
# import init, { Simulation } from './sim.js';
# const sim = new Simulation();
# sim.set_input('rst', 1);
# sim.tick();
# sim.set_input('rst', 0);
# sim.set_input('en', 1);
# sim.tick();
# console.log(sim.get_output('count')); // 1