Getting Started

Install RyuSim, compile your first design, and run a native SystemVerilog testbench, a UVM test, or a cocotb testbench.

Prerequisites

Install the required build tools for your distribution. The installer will warn if these are missing, but installing them first avoids interruption.

zlib development files (zlib1g-dev / zlib-devel) are required: every simulation RyuSim generates links zlib through the runtime's FST waveform writer. The runtime libz.so.1 most systems already have is not sufficient — linking needs the libz.so symlink that ships in the development package.

Ubuntu / Debian

The distro clang package is older than the required Clang 18, so install it from apt.llvm.org:

wget -qO- https://apt.llvm.org/llvm.sh | sudo bash -s -- 18 && sudo apt install cmake python3 python3-pip zlib1g-dev

Fedora / Rocky Linux 10+

sudo dnf install clang cmake python3 python3-pip zlib-devel

Rocky Linux / RHEL 9

sudo dnf install epel-release && sudo /usr/bin/crb enable && sudo dnf install clang cmake python3 python3-pip zlib-devel

Install RyuSim

curl -fsSL https://ryusim.com/install.sh | bash

The installer auto-detects your architecture and glibc version. See Downloads for the full list of supported distributions.

Compile your first design

Create a simple counter design:

module counter ( input logic clk, input logic rst, output logic [7:0] count ); always_ff @(posedge clk) begin if (rst) count <= 8'h0; else count <= count + 1; end endmodule

Compile it:

ryusim compile counter.sv --top counter

Run a native SystemVerilog testbench

RyuSim v2 delivers full IEEE 1800-2023 testbench-language compliance; gate-level modeling (cl. 28–33) and the VPI assertion/coverage/data-read APIs (cl. 39–41) ship in v2.x — see the per-clause compliance matrix. Testbenches written in SystemVerilog itself are the primary flow: ryusim compile builds a standalone executable that runs with no Python and no VPI in the loop.

Add a self-checking testbench next to the design, counter_tb.sv. It generates a clock, releases reset, counts ten rising edges, and checks the result with an immediate assertion:

`timescale 1ns/1ns module counter_tb; logic clk = 1'b0; logic rst; logic [7:0] count; counter dut (.clk(clk), .rst(rst), .count(count)); always #5 clk = ~clk; // 100 MHz free-running clock initial begin rst = 1'b1; @(negedge clk); rst = 1'b0; repeat (10) @(posedge clk); #1; // let the NBA update settle assert (count == 8'd10) else $fatal(1, "expected count=10, got %0d", count); $display("COUNTER_TB_PASS: count=%0d at t=%0t", count, $time); $finish; end endmodule

Compile the design and the testbench together, with the testbench as the top module:

ryusim compile counter.sv counter_tb.sv --top counter_tb

Then run the standalone executable RyuSim built:

./obj_dir/build/counter_tb_sim

Expected output:

RyuSim - Simulation of counter_tb RyuSim: effective root seed = 14916093976784742661 (source: randomly generated) COUNTER_TB_PASS: count=10 at t=106 Simulation complete. Time: 106000

The root seed is drawn fresh on every run unless you pin it, so that line will differ. The binary exits 0 on success; a failing assert reports through $fatal and exits non-zero, which is what makes these testbenches usable directly as CI gates.

Run a UVM test

RyuSim compiles and runs the Accellera UVM library natively, including the UVM DPI helpers. RyuSim's own verification pins two packages: Accellera UVM 1.2 (the primary package) and the IEEE 1800.2-2020 reference implementation (accellera-official/uvm-core, tag 2020.3.1). The examples below use UVM 1.2; unpack the Accellera distribution and point UVM_HOME at it:

export UVM_HOME=/path/to/uvm-1.2

Write uvm_hello.sv — a test registered with the UVM factory and started by run_test():

module uvm_hello; import uvm_pkg::*; `include "uvm_macros.svh" class hello_test extends uvm_test; `uvm_component_utils(hello_test) function new(string name, uvm_component parent); super.new(name, parent); endfunction task run_phase(uvm_phase phase); phase.raise_objection(this); `uvm_info("HELLO", "Hello, world from UVM on RyuSim", UVM_LOW) phase.drop_objection(this); endtask endclass initial run_test(); endmodule

Compile it against the package. uvm_pkg.sv and the stock uvm_dpi.cc are passed as sources, -I adds the package include directory, and --dpi-define QUESTA selects the UVM HDL backdoor backend that is written against standard VPI — the one RyuSim implements. The package source itself is never patched:

ryusim compile uvm_hello.sv \ $UVM_HOME/src/uvm_pkg.sv $UVM_HOME/src/dpi/uvm_dpi.cc \ -I $UVM_HOME/src --dpi-define QUESTA --top uvm_hello

Compiling the whole UVM package is a substantial one-off cost — minutes to tens of minutes depending on the machine, almost all of it in the C++ build phase. RyuSim runs that phase at up to 8 parallel jobs by default; -j N sets it explicitly, and lowering it is the usual fix if the build runs out of memory on the generated class header.

Select the test on the command line with +UVM_TESTNAME, exactly as with any other simulator — RyuSim's plusarg handling feeds the standard uvm_cmdline_processor:

./obj_dir/build/uvm_hello_sim +UVM_TESTNAME=hello_test

UVM prints its release-notes banner, runs the full phase ladder, and ends with the report summary. Abridged ([...] marks omitted lines):

[...] UVM_INFO @ 0: reporter [RNTST] Running test hello_test... UVM_INFO uvm_hello.sv(14) @ 0: uvm_test_top [HELLO] Hello, world from UVM on RyuSim [...] --- UVM Report Summary --- ** Report counts by severity UVM_INFO : 4 ** Report counts by id [HELLO] 1 [RNTST] 1 [TEST_DONE] 1 [UVM/RELNOTES] 1

Zero UVM_WARNING, UVM_ERROR and UVM_FATAL counts, and the binary exits 0.

UVM's DPI-free configuration is also verified — RyuSim's smoke matrix keeps uvm_pkg compiling and elaborating clean with -DUVM_NO_DPI -DUVM_REGEX_NO_DPI -DUVM_CMDLINE_NO_DPI defined. That configuration drops the UVM DPI helpers (regex matching, the HDL backdoor and the command-line processor), so +UVM_TESTNAME selection is not available in it.

Run with cocotb

Python testbenches with cocotb remain fully supported over RyuSim's VPI interface, and back-compatibility for existing cocotb testbenches is contractual — v1 testbenches keep working unchanged on v2.

Note: as of the v2.0 release, RyuSim support has not landed in upstream cocotb. Install cocotb from the Seiraiyu fork first:

pip install git+https://github.com/Seiraiyu/cocotb.git@feat/ryusim-simulator-support

The fork adds the RyuSim runner and Makefile.ryusim to cocotb's build infrastructure. Once upstream cocotb includes RyuSim support, plain pip install cocotb will be enough — see Downloads.

Create a testbench file test_counter.py:

import cocotb from cocotb.triggers import RisingEdge, FallingEdge from cocotb.clock import Clock @cocotb.test() async def test_counter_counts(dut): """Check that the counter increments on each clock edge.""" cocotb.start_soon(Clock(dut.clk, 10, unit="ns").start()) dut.rst.value = 1 await RisingEdge(dut.clk) await FallingEdge(dut.clk) dut.rst.value = 0 for _ in range(10): await RisingEdge(dut.clk) await FallingEdge(dut.clk) # let NBA updates settle assert dut.count.value.to_unsigned() == 10, f"Expected 10, got {dut.count.value.to_unsigned()}"

Create a Makefile in the same directory:

SIM ?= ryusim TOPLEVEL_LANG := verilog VERILOG_SOURCES = $(PWD)/counter.sv TOPLEVEL = counter COCOTB_TEST_MODULES = test_counter include $(shell cocotb-config --makefiles)/Makefile.sim

Run the test:

make

cocotb will compile the design with RyuSim, load the VPI testbench, and report results. See the cocotb documentation for triggers, coroutines, bus drivers, and more.