Python bindings for the ruvsim simulator runner
Project description
ruvsim (Rust-VSim)
A Rust library with Python bindings for controlling ModelSim/Questa simulators programmatically. Provides compilation, simulation control, signal inspection, breakpoint management, and memory access capabilities.
Currently only supports Verilog/SystemVerilog at the moment.
Rust API
The Rust API provides direct access to ModelSim/Questa functionality through a type-safe interface.
Features
Compiler
Compile HDL source files (Verilog/SystemVerilog and VHDL), executes both vlib and vlog internally.
use ruvsim::sim_compiler::{Compiler, CompilerCommand};
let compiler = Compiler::new("build/work_dir", "/path/to/modelsim/bin", CompilerCommand::Vlog)
.enable_system_verilog()
.set_work("work_lib")
.add_dependency("hdl/design.sv")
.add_dependency("hdl/testbench.sv")
.add_optimization(5);
compiler.run().expect("Compilation failed");
Additionally, you can leave the path to modelsim empty if it's already in your PATH.
let compiler = Compiler::new("build/work_dir", "", CompilerCommand::Vlog)
.enable_system_verilog()
.set_work("work_lib")
.add_dependency("hdl/design.sv")
.add_dependency("hdl/testbench.sv")
.add_optimization(5);
Runner
Controls the simulation runner; it's essentially a wrapper for the vsim command line interface.
use ruvsim::sim_runner::{Runner, RunnerBuilder};
// Create and configure a runner
let mut runner = RunnerBuilder::new()
.with_vsim_command("vsim")
.with_cwd("build/work_dir")
.work_lib("work_lib")
.top("testbench")
.enable_acc()
.batch_mode()
.build();
runner.wait_until_prompt_startup().expect("Failed to start");
// Control simulation
runner.run_for(100).expect("Run failed"); // Run for 100ns
runner.run_next().expect("Run failed"); // Run to next event
runner.run_all().expect("Run failed"); // Run to completion
// Query simulation state
let time = runner.get_time().expect("Failed to get time");
let status = runner.get_status().expect("Failed to get status");
Signal Inspection
Read and modify signal values:
use ruvsim::sim_types::{SimRadix, SimSignalDirection};
// Get signals by path pattern
let signals = runner.get_signals("/top/module/*", Some(SimSignalDirection::Output))
.expect("Failed to get signals");
// Examine a specific signal
let mut signal = runner.get_signal("/top/module/data", None)
.expect("Signal not found");
runner.examine_signal(&mut signal, SimRadix::Hexadecimal)
.expect("Failed to examine");
println!("Signal value: {:?}", signal.value);
// Force a signal to a new value
runner.force_signal(&signal, "ff", SimRadix::Hexadecimal)
.expect("Failed to force signal");
Breakpoints
Set and manage simulation breakpoints:
// Create a breakpoint at a source line
let bp = runner.create_breakpoint("testbench.sv", 42)
.expect("Failed to create breakpoint");
// Continue to next breakpoint
runner.run_continue().expect("Failed to continue");
// List all breakpoints
let breakpoints = runner.list_breakpoints()
.expect("Failed to list breakpoints");
// Delete a breakpoint
runner.delete_breakpoint("testbench.sv", 42)
.expect("Failed to delete");
Log Parsing
Search and filter simulation output:
// Find log lines matching a predicate
let matches = runner.get_log_matches(|line| line.contains("ERROR"))
.expect("Failed to search logs");
// Get all parsed output
let buffer = runner.parsed_buffer();
for line in buffer {
println!("{:?}: {}", line.line_type, line.content);
}
Python Bindings
Python bindings (Python 3.9+) provide the same functionality with a Pythonic interface.
Installation
pip install maturin
maturin develop --release
Or build a wheel:
maturin build --release
pip install target/wheels/ruvsim-*.whl
Usage
Compilation
from ruvsim import PyCompiler
# Create and configure compiler
compiler = PyCompiler("build/work_dir", "/path/to/modelsim/bin", "vlog")
compiler.enable_system_verilog()
compiler.set_work("work_lib")
compiler.add_dependencies(["hdl/design.sv", "hdl/testbench.sv"])
compiler.run()
Simulation Control
from ruvsim import PyRunner
# Create runner
runner = PyRunner(
"vsim",
["-work", "work_lib", "-voptargs=+acc", "testbench", "-c"],
"build/work_dir"
)
runner.wait_until_prompt_startup()
# Run simulation
runner.send_command("log -r /*") # Log all signals
runner.run_for(100) # Run for 100ns
runner.run_next() # Run to next event
runner.run_all() # Run to completion
# Query state
time = runner.get_time()
status = runner.get_status()
print(f"Time: {time}, Status: {status}")
Signal Access
# Get signals
nets = runner.get_nets("/top/module/*", direction="Output")
# Examine individual signal
signal = runner.get_net("/top/module/data")
runner.examine_net(signal, "hex")
print(f"Value: {signal.value}, Radix: {signal.radix}")
# Batch examine for performance
signals = runner.get_nets("/top/module/*")
runner.examine_nets_batch(signals, "binary")
for sig in signals:
print(f"{sig.name}: {sig.value}")
# Force signal values
runner.force_net(signal, "ff", "hex")
# Batch force for performance
runner.force_nets_batch(signals, ["01", "10", "11"], ["bin", "bin", "bin"])
# Get numeric value (for integer signals)
value = signal.numeric_value() # Returns Python int or None
Breakpoints
# Create breakpoint
bp = runner.create_breakpoint("testbench.sv", 42)
print(f"Breakpoint: {bp.name} at {bp.file}:{bp.line_num}")
# Continue to breakpoint
runner.run_continue()
# List and delete
breakpoints = runner.list_breakpoints()
runner.delete_breakpoint("testbench.sv", 42)
Log Searching
# Search by substring
matches = runner.get_log_matches_contains("ERROR")
for match in matches:
print(match)
# Search by regex
matches = runner.get_log_matches_regex(r"^# Time: \d+")
# Send command and expect output
results = runner.send_and_expect_contains("status", "running")
results = runner.send_and_expect_regex("examine signal", r"32'h[0-9a-f]+")
Memory Access
# List memory regions
mems = runner.list_mems()
for mem in mems:
print(f"{mem.name}: [{mem.left_bound}:{mem.right_bound}]")
Direct Commands
# Send arbitrary TCL commands
runner.send_command("restart -f")
runner.send_command_no_wait("run -all") # Non-blocking
# Parse output buffer
parsed = runner.parsed_buffer()
for line in parsed:
print(f"{line.line_type}: {line.content}")
# Get only latest output
latest = runner.latest_buffer()
Python API Classes
PyRunner: Main simulation controllerPyCompiler: HDL compilation interfacePySignal: Signal object with properties (name, value, radix, direction, bounds, drivers)PyDriver: Signal driver informationPyBreakpoint: Breakpoint representationPyMemory: Memory region informationPyParsedLine: Parsed simulator output line
Example: Complete Workflow
import os
from ruvsim import PyCompiler, PyRunner
# Setup directories
build_dir = "build/my_design"
os.makedirs(build_dir, exist_ok=True)
# Compile
compiler = PyCompiler(build_dir, "", "vlog")
compiler.enable_system_verilog()
compiler.set_work("work_my_design")
compiler.add_dependencies(["hdl/design.sv", "hdl/tb.sv"])
compiler.run()
# Run simulation
runner = PyRunner("vsim",
["-work", "work_my_design", "-voptargs=+acc", "tb", "-c"],
build_dir)
runner.wait_until_prompt_startup()
# Test scenario
runner.send_command("log -r /*")
runner.run_for(1000)
# Check results
matches = runner.get_log_matches_contains("TEST PASSED")
if matches:
print("✓ Test passed!")
else:
print("✗ Test failed!")
runner.finish()
RuvSim REST API
Overview
- Axum-based REST API to compile HDL deps, start a ModelSim/Questa session, inspect nets, run simulation steps, and query logs.
- OpenAPI docs served at /docs and /api-docs/openapi.json.
Environment
- RUVSIM_API_ADDR: Bind address (default 0.0.0.0:8080)
- RUVSIM_WORK_DIR: Required. Base directory for compilation and simulator CWD.
- RUVSIM_MODELSIM_PATH: Optional. Path to ModelSim/Questa bin folder for vlib/vlog/vcom.
- RUVSIM_VSIM_BIN: Optional. Simulator executable name (default "vsim").
- RUVSIM_SESSION_TTL_SECS: Optional. Idle eviction TTL in seconds (default 600).
Path policy
- The API rejects absolute dependency paths.
- deps must be relative to RUVSIM_WORK_DIR. The server resolves each dep as "${RUVSIM_WORK_DIR}/${dep}".
Key endpoints
- GET /health
- GET /sessions
- POST /sessions Body: { "work_lib": "work_SDRAM_tb", "top": "SDRAM_tb", "deps": ["hdl/SDRAM_tb.sv", "hdl/SDRAM.sv", "hdl/Dummy.sv", "hdl/Dummy_tb.sv"] } Returns: { "id": "" }
- GET /sessions/{id}/nets?path=/*&direction=All|Input|Output|Inout&examine=true&radix=binary
- POST /sessions/{id}/run Body: { "mode": "all" } | { "mode": "next" } | { "mode": "for", "ns": 100 }
- GET /sessions/{id}/logs?contains=NETS
- POST /sessions/{id}/examine Body: { "path": "/top/u0/signal", "radix": "hex" }
- POST /sessions/{id}/cmd Body: { "command": "run -next", "expect_contains": "VSIM" }
- DELETE /sessions/{id}
Notes
- Compilation uses vlog (SystemVerilog enabled). vcom is not exposed yet.
- work library is created under RUVSIM_WORK_DIR if not present.
- Errors are returned as { "message": "..." } with suitable HTTP status codes.
Examples
The examples/ directory contains several complete working examples demonstrating different features of ruvsim. Each example includes both Rust and Python implementations where applicable.
Dummy - Basic Simulation
Location: examples/dummy/
Run:
# Rust
cargo run --example dummy
# Python
python examples/dummy/main.py
Memory Access
Location: examples/mem/
Run:
cargo run --example mem
Breakpoints
Location: examples/breakpoints/
Run:
cargo run --example breakpoints
SERV MCU - Advanced Integration
Location: examples/serv_mcu/
Run:
# Rust
cargo run --example serv_mcu
# Python (requires: pip install bronzebeard)
python examples/serv_mcu/main.py
Running Examples
All examples assume ModelSim/Questa is in your system PATH. If not, set the path:
# For Rust examples
export PATH="/path/to/modelsim/bin:$PATH"
cargo run --example dummy
# For Python examples
python examples/dummy/main.py
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
File details
Details for the file ruvsim-0.1.3.tar.gz.
File metadata
- Download URL: ruvsim-0.1.3.tar.gz
- Upload date:
- Size: 96.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3a25c74e360153a8e46acbbe5b3f8f8ba0d8b35a8fd5a774157cc545ac6f265d
|
|
| MD5 |
bd0b1c6204c4a1e209c4f61e44f123d7
|
|
| BLAKE2b-256 |
e66bb0a48363967acb246130055a329dfb74b79a5e79f0b77f6d9c9dd542e538
|