OpenVAF is a Verilog-A compiler written by Pascal Kuthe. The compiler outputs a dynamic library whose functionality can be accessed via the OSDI API (version 0.3). The original compiler received no support since end of 2023. This fork of the original repository was started by Árpád Bűrmen in early 2024. Since then several small bugs were fixed that prevented the use of OpenVAF for building SPICE3-equivalent device models.
To add new functionality to OpenVAF the OSDI interface has been modified. Consequently the current version of OSDI API is 0.4. OSDI API 0.4 differs from version 0.3 in the module descriptor. It also exports OSDI_DESCRIPTOR_SIZE which can be used to traverse the array of descriptors without relying on the definition of the OsdiDescriptor structure (i.e. size of the structure in the OSDI header file used by the simulator). New members are added after the first part of the descriptor which still complies with the OSDI 0.3 specification. Simulators that support only OSDI 0.3 can still use models exposing the newer OSDI API by applying some minor changes.
The last version of OpenVAF before the project was renamed to OpenVAF-reloaded and the binary was renamed to openvaf-r is tagged with osdi_0.3. The master branch includes several extensions of the compiler and exposes the OSDI 0.4 API in the generated models. The models generated by the compiler in the branches/osdi_0.3 branch expose the old OSDI 0.3 API. This branch does not include compiler extensions as they depend on OSDI API 0.4. Both branches include all the bugfixes up to December 2024. As of December 2024 the branches/osdi_0.3 branch is no longer maintained.
In OSDI 0.4 new members are added to the module descriptor data structure after the members defined in the OSDI 0.3 specification. The descriptor (if cast to the declaration given in the OSDI 0.3 header file) remains compatible with OSDI 0.3 and should work just like before. Simulators using OSDI API 0.3 can be adapted to use version 0.4 by applying the following changes
- allowing major.minor version >=0.4 beside 0.3,
- reading the
OSDI_DESCRIPTOR_SIZEsymbol of typeuint32specifying the descriptor size, - making sure the table of descriptors (pointed to by the
OSDI_DESCRIPTORSsymbol) is traversed in steps of sizeOSDI_DESCRIPTOR_SIZEinstead ofsizeof(OsdiDescriptor), and - casting each descriptor to the structure declared in the OSDI header file, version 0.3.
This is the current state of OSDI 0.4 support
| Simulator | OSDI version supported | Comment |
|---|---|---|
| Ngspice 43 | 0.3 | |
| Ngspice >=44 | 0.3 & 0.4 | uses only 0.3 features |
| SPICE OPUS 3.0 | 0.3 | |
| VACASK 0.1.2 | 0.3 | |
| VACASK >=0.2 | 0.4 |
If you know of any other simulator supporting OSDI models generated by OpenVAF, let me know.
Some internals of the OpenVAF compiler are documented in the internals.md file.
- OSDI descriptor size for traversing the OSDI descriptor table in simulators not supporting OSDI 0.4
- Support for reading param given flags of parameters in the instance and model data structures. This is pretty much self-explanatory. Look at the OSDI 0.4 header file. This one takes care of issue #76 in the original repository.
- Support for writing nonzero resistive and reactive Jacobian contributions to an array of doubles.
- List of model inputs (node pairs).
- Functions for loading Jacobians with offset (for harmonic balance analysis).
- --dump-unopt-mir, --dump-mir, --dump-unopt-ir, and --dump-ir options for dumpring the (unoptimized) MIR and LLVM IR.
- Support for $fatal, $finish, and $stop.
- Loops no longer crash the compiler.
- Natures, disciplines, and the corresponding attributes exposed in OSDI API.
- Natures of unknowns and residuals exposed in OSDI descriptor. TODO: switch branches and implicit equations.
- $bound_step() fixed.
- Initalization of instance parameters from model defaults now works.
- Access to noise source type and parameters (white and flicker noise).
Yes, binaries for 64-bit Linux and Windows are available here. The naming scheme of the binaries is
openvaf-reloaded-<version>-<platform>
The version name is generated with git --describe. The OpenVAF-reloaded that produces models with the OSDI API 0.3 is version osdi_0.3. All newer versions (osdi_0.4) produce models with OSDI API 0.4.
If the binary is named openvaf it comes from the branches/osdi_0.3 branch and produces models with the OSDI 0.3 API (note that this branch is no longer maintained). If the binary is named openvaf-r it comes from the master branch and produces models with the OSDI 0.4 API.
OpenVAF-reloaded supports multiple LLVM versions from 18 to 21. You can choose which version to use at build time via Cargo features:
| Feature | LLVM Version | Environment Variable | llvm-sys crate |
|---|---|---|---|
llvm18 |
LLVM 18.1.x | LLVM_SYS_181_PREFIX |
181.2.0 |
llvm19 |
LLVM 19.1.x | LLVM_SYS_191_PREFIX |
191.0.0 |
llvm20 |
LLVM 20.1.x | LLVM_SYS_201_PREFIX |
201.0.1 |
llvm21 |
LLVM 21.1.x | LLVM_SYS_211_PREFIX |
211.0.0 |
Note: There is no default LLVM version. You must specify the version explicitly using --features llvmXX or use the ./configure script for auto-detection (see Building section).
Ubuntu Noble (24.04) ships with LLVM 18 as a system package, making it a convenient choice for those platforms.
Note: The llvm-sys crate version follows the pattern MAJORminor.patch, where MAJOR is the LLVM major version and minor is the minor version (e.g., 181 = LLVM 18.1, 211 = LLVM 21.1).
Everything was tested under Debian 13. First, install Rust as ordinary user (files will go to ~/.cargo and ~/.rustup).
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | shDuring installation select "Customize installation" and set profile to "complete".
Make sure the Bash login script is read again (either log out and in again, or type source ~/.bashrc) Now you are good to go.
On Ubuntu Noble (24.04) or similar, LLVM 18 is available from system packages:
sudo apt-get install llvm-18 llvm-18-dev libclang-18-dev clang-18Set up the environment:
export LLVM_SYS_181_PREFIX=/usr/lib/llvm-18
export PATH=/usr/lib/llvm-18/bin:$PATHFor LLVM 21 on Ubuntu/Debian, use the official LLVM apt repository:
wget https://apt.llvm.org/llvm.sh
chmod +x llvm.sh
sudo ./llvm.sh 21
sudo apt-get install llvm-21 llvm-21-dev libclang-21-devSet up the environment:
export LLVM_SYS_211_PREFIX=/usr/lib/llvm-21
export PATH=/usr/lib/llvm-21/bin:$PATHIf you need to build LLVM from source, download LLVM 21.1.6 sources (or 18.1.8 for LLVM 18). Unpack them and create a build directory:
cmake -S <path to souces>/llvm -B <path to build dir> -DCMAKE_INSTALL_PREFIX=<LLVM install directory> -DCMAKE_BUILD_TYPE=Release -DLLVM_TARGETS_TO_BUILD="X86;ARM;AArch64" -DLLVM_ENABLE_PROJECTS="llvm;clang;lld"
Enter the build directory and type:
make -j <number of processors to use>
make install
Download rustup, run it to install Rust. During installation select "Customize installation" and set profile to "complete".
Install Visual Studio 2022 Community Edition. Make sure you install CMake Tools that come with VS2022 (also installs Ninja).
Build LLVM and Clang. Download LLVM 21.1.6 sources sources (get the .zip file). Unpack the sources (this creates directory llvm-project-llvmorg-21.1.6). Create a build directory and decide where you want to install LLVM.
Start Visual Studio x64 native command prompt. Run CMake, use Ninja as build system. Do not use default (nmake) because for me it always built the Debug version, even when I specified Release.
cmake -G Ninja -S path_to_sources/llvm -B path_to_build_dir -DCMAKE_INSTALL_PREFIX=LLVM_install_directory -DCMAKE_BUILD_TYPE=Release -DLLVM_TARGETS_TO_BUILD="X86;ARM;AArch64" -DLLVM_ENABLE_PROJECTS="llvm;clang;lld"Enter build directory and run Ninja (build and install)
ninja
ninja install Add the LLVM binary directory (<LLVM install directory>\bin) to the PATH. Set the LLVM_SYS_211_PREFIX environmental variable to <LLVM install directory>.
Restart command prompt. Now you are good to go.
Install Homebrew if not already installed.
brew install llvm@18
export LLVM_SYS_181_PREFIX=$(brew --prefix llvm@18)
export PATH="$(brew --prefix llvm@18)/bin:$PATH"brew install llvm
export LLVM_SYS_211_PREFIX=$(brew --prefix llvm)
export PATH="$(brew --prefix llvm)/bin:$PATH"Add the appropriate export commands to ~/.zshrc (or ~/.bashrc) to make them permanent.
Install Rust if not already installed:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | shDuring installation select "Customize installation" and set profile to "complete".
Now you are good to go.
The easiest way to build is using the configure script which auto-detects your LLVM installation:
./configure # Auto-detect LLVM version
./build.sh --release # Build release versionThe configure script will:
- Search for LLVM installations (via environment variables, PATH, or Homebrew on macOS)
- Select the newest available version (21 > 20 > 19 > 18)
- Save the configuration to
.llvm-version
You can also force a specific version:
./configure --llvm=18 # Force LLVM 18If you prefer not to use the configure script, specify the LLVM version explicitly:
# Set environment variable for your LLVM version
export LLVM_SYS_211_PREFIX=/path/to/llvm-21 # For LLVM 21
# Or: export LLVM_SYS_181_PREFIX=/path/to/llvm-18 # For LLVM 18
# Make sure LLVM binaries are in the system path
# Build with explicit feature flag
cargo build --release --features llvm21
# Or: cargo build --release --features llvm18To build the debug version:
cargo build --features llvm21The release binary can be found in target/release while the debug binary is built in target/debug.
You will need two extensions: CodeLLDB (under Linux) / Microsoft C++ (under Windows) and rust-analyzer. In the .vscode directory there are two files: launch-openvaf-r.json (for working with the master branch) and launch-openvaf.json (for working with the branches/osdi_0.3 branch). Copy the one that matches your branch to launch.json. There are two debug setups available in that file (Linux and Windows). Set your breakpoints and run the program. If there are any changes since the last build they will be applied upon which the program will be started and then stop at the first breakpoint.
The debug configuration disables rayon running the .osdi file build process in parallel so that debugging the last step of compilation is somewhat easier.
Pascal has set up a test suite for OpenVAF.
If you've run ./configure, use the build script:
./build.sh --test # Debug tests
./build.sh --test --release # Release testsTo run the tests manually, specify the LLVM version explicitly:
# Debug tests
cargo test --features llvm21
# Release tests
cargo test --release --features llvm21By default only fast tests are run. To run all tests set the RUN_SLOW_TEST variable to 1, e.g.
RUN_SLOW_TESTS=1 cargo testIntegration tests compile real-world Verilog-A models (BSIM, HiSIM, PSP, MEXTRAM, etc.) and verify the generated OSDI libraries. These tests are disabled by default but can be enabled with:
RUN_DEV_TESTS=1 cargo test --release --features llvm21 --test integrationOr using the build script:
RUN_DEV_TESTS=1 ./build.sh --test --release -- --test integrationYour changes may fail some tests although they are correct. Consider the case you changed the MIR generator. The expected test results assume MIR is generated the way Pascal did it. If you are sure your changes are correct you can update the expected values (stored in openvaf/test_data as files ending with .snap). To do this set the UPDATE_EXPECT variable 1, e.g.
UPDATE_EXPECT=1 cargo test --release --features llvm21Unfortunately not all expected results are in .snap files. Some are hard-coded in the test sources, e.g. see openvaf/mir_autodiff/src/builder/tests.rs. You will have to update these expected values manually.
Kudos to Pascal Kuthe for the great work he did.
Geoffrey Coram and Dietmar Warning are authors of several bugfixes included in OpenVAF-reloaded.
Kreijstal ported OpenVAF to LLVM 18.
Rob Taylor contributed CI improvements, MACOS support, and LLVM version support beyond LLVM 18.
This work is free software and licensed under the GPL-3.0 license.
It contains code that is derived from rustc and rust-analyzer. These projects are both licensed under the MIT license. As required a copy of the license and disclaimer can be found in copyright/LICENSE_MIT.
Many models in integration tests folder are not licensed under a GPL compatible license. All of those models contain explicit license information. They do not end up in the openvaf binary in any way and therefore do not affect the license of the entire project. Integration tests without explicit model information (either in the model files or in a dedicated LICENSE file) fall under GPLv3.0 like the rest of the repo.