Install RyuSim, compile a counter, and test it three ways: with a SystemVerilog testbench, a UVM test, and a cocotb test.
clang++-19, clang++-18 and clang++ on PATH, in that order.zlib1g-dev or zlib-devel). Every simulation links zlib for the FST waveform writer. The runtime library libz.so.1 is not enough: the linker needs the libz.so symlink from the development package.make, and Python 3.9 or newer with the venv module and the shared library libpython. cocotb loads libpython into the simulation; on Debian and Ubuntu it comes with python3-dev, which the stock Python install does not include.The distribution's clang package is new enough: Clang 18 on Ubuntu 24.04, Clang 19 on Debian 13.
sudo apt update && sudo apt install clang ninja-build cmake make curl python3-venv python3-dev zlib1g-dev
These releases ship Clang 14, which cannot compile RyuSim's generated code. Install Clang 18 from apt.llvm.org with its llvm.sh script, then the other tools:
sudo apt update && sudo apt install lsb-release wget software-properties-common gnupg
wget -qO- https://apt.llvm.org/llvm.sh | sudo bash -s -- 18
sudo apt install ninja-build cmake make curl python3-venv python3-dev zlib1g-dev
llvm.sh installs the compiler as clang++-18, which RyuSim finds by that name.
sudo dnf install clang ninja-build cmake make python3 zlib-devel
ninja-build is in the CRB repository. The crb command that enables it comes with epel-release.
sudo dnf install epel-release && sudo /usr/bin/crb enable && sudo dnf install clang ninja-build cmake make python3 zlib-devel
On Red Hat Enterprise Linux, ninja-build is in the CodeReady Linux Builder repository, which subscription-manager enables on a registered system. These lines follow Red Hat's documented steps; RyuSim's CI tests Rocky Linux, not RHEL itself. On RHEL 10, write rhel-10 in place of rhel-9.
sudo subscription-manager repos --enable codeready-builder-for-rhel-9-$(arch)-rpms
sudo dnf install clang ninja-build cmake make python3 zlib-devel
On Rocky Linux and RHEL 9, the clang package reached version 18 in 9.5. Minor releases 9.3 and 9.4 ship Clang 16 and 17. A 9.x host that follows the current repositories installs a newer clang. A host held at 9.4 or older does not; run sudo dnf update to move it to 9.5 or later first. Then check that the version is 18 or newer:
clang++ --version
curl -fsSL https://ryusim.com/install.sh | bash
The installer picks the build for your glibc, checks it against the release's SHA-256 checksum file, and checks that the binary runs before it copies anything. If the checksum file or sha256sum is missing, it prints a NOTE: line and skips that check; see Downloads. Run as a regular user, it installs into ~/.ryusim. Run as root, it installs into /opt/ryusim and links ryusim into /usr/local/bin. It ends by checking for clang++, cmake, python3 and a linkable zlib, and names any that are missing.
After a regular-user install, put ~/.ryusim/bin on your PATH. Add the same line to ~/.bashrc to keep it in new shells.
export PATH="$HOME/.ryusim/bin:$PATH"
Check the install:
ryusim --version
Other install options (a pinned version, a manual download) are on the Downloads page.
Save this counter as counter.sv:
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
RyuSim parses the design, generates C++ into obj_dir/, and builds it. The output ends with:
Build successful!
Simulation executable: "obj_dir/build/libcounter.so"
Standalone executable: "obj_dir/build/counter_sim" (alias of the .so)
The first compile on a machine also builds precompiled headers into ~/.cache/ryusim/pch. Later compiles reuse them.
A testbench written in SystemVerilog compiles into the same executable as the design and runs with no Python and no VPI. The compliance matrix lists which parts of the language RyuSim supports.
Save this testbench as counter_tb.sv. It generates a clock, releases reset, waits ten rising edges, and checks the count 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
Run the executable:
./obj_dir/build/counter_tb_sim --seed 1
Output:
RyuSim - Simulation of counter_tb
RyuSim: effective root seed = 1 (source: --seed)
COUNTER_TB_PASS: count=10 at t=106
Simulation complete. Time: 106000
--seed sets the root seed for randomization. Without it, the executable picks a new seed on each run and prints it on the second line, so a failing run can be repeated. The executable exits with status 0 when the test passes. A failed assert calls $fatal, and the exit status is 1, so the executable works as a CI step on its own.
RyuSim compiles the Accellera UVM library from source, including its C DPI helpers, without patches. RyuSim's own tests use two UVM packages: Accellera UVM 1.2 and the IEEE 1800.2-2020 reference implementation (accellera-official/uvm-core, tag 2020.3.1). This example uses UVM 1.2. Download uvm-1.2.tar.gz from Accellera, unpack it, and point UVM_HOME at it. Replace /path/to/uvm-1.2 with the directory you unpacked:
export UVM_HOME=/path/to/uvm-1.2
The blocks in this section are not run by the site's CI, because they need that download.
Save a test as uvm_hello.sv. The test registers with the UVM factory, and run_test() starts it:
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 with the UVM package. Pass uvm_pkg.sv and the stock uvm_dpi.cc as sources, and add the package directory with -I. --dpi-define QUESTA defines QUESTA for the C sources only; it selects UVM's HDL backdoor code that uses standard VPI calls, which RyuSim implements.
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 UVM package takes minutes. The C++ build runs one job per CPU core, up to 8; set the count with -j N, and lower it if the build runs out of memory.
Choose the test with +UVM_TESTNAME. RyuSim passes plusargs to UVM's uvm_cmdline_processor:
./obj_dir/build/uvm_hello_sim +UVM_TESTNAME=hello_test
UVM prints its release notes, runs its phases, and ends with the report summary. [...] marks omitted lines:
[...]
UVM_INFO @ 0: reporter [RNTST] Running test hello_test...
WARNING: container: associative array read of nonexistent entry (Table 7-1)
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
The UVM_WARNING, UVM_ERROR and UVM_FATAL counts are zero, and the exit status is 0. The WARNING: container line comes from RyuSim, not UVM. UVM reads an associative-array entry that does not exist, and IEEE 1800-2023 clause 7.8.6 requires a warning for that read; the read returns the default value from Table 7-1.
UVM can also be built without its DPI code. RyuSim's UVM smoke tests compile and elaborate uvm_pkg with -DUVM_NO_DPI -DUVM_REGEX_NO_DPI -DUVM_CMDLINE_NO_DPI. That build has no regular-expression matching, no HDL backdoor and no command-line processor, so +UVM_TESTNAME does not work in it.
cocotb testbenches drive RyuSim through VPI. Compatibility for existing cocotb testbenches is a commitment across RyuSim releases.
Install the cocotbext-ryusim package. It depends on a released version of cocotb, so pip installs both. Use a virtual environment: Ubuntu, Debian and Fedora block pip install into the system Python (PEP 668).
python3 -m venv .venv
source .venv/bin/activate
pip install cocotbext-ryusim
Save the test as 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()}"
Save this Makefile in the same directory. cocotbext-ryusim-config --makefiles prints the directory that holds the package's Makefile.sim, and SIM=ryusim selects RyuSim:
SIM ?= ryusim
TOPLEVEL_LANG := verilog
VERILOG_SOURCES = $(PWD)/counter.sv
COCOTB_TOPLEVEL = counter
COCOTB_TEST_MODULES = test_counter
include $(shell cocotbext-ryusim-config --makefiles)/Makefile.sim
Run it:
make
make compiles the design into sim_build/, starts the simulation with cocotb loaded, and prints a results table. The last row reads:
** TESTS=1 PASS=1 FAIL=0 SKIP=0 105.00 0.00 163174.97 **
The cocotb guide runs a test through the Python Runner, pytest and cocotb-test as well, and lists the settings they share. If cocotb does not start, see Troubleshooting. The cocotb documentation covers writing tests.
ryusim compile option.