Verification

SystemVerilog DPI

By CourseTron Team ·  8 min read ·  Updated Oct 2026 💬 𝕏 in f
Table of Contents
    ? Print / Save as PDF

    In short: SystemVerilog DPI (Direct Programming Interface) lets your testbench call C/C++ functions directly through import "DPI-C" and lets C call back into SV through export "DPI-C", which is the standard way to bolt a C/C++ golden reference model onto an RTL verification environment without the overhead of VPI/PLI.

    Ask any verification engineer how they check a complex arithmetic block, a crypto core, or a video codec, and the answer almost always involves a reference model written in C. The DPI is the bridge that connects that software model to the simulated hardware. It replaced the clumsy PLI/VPI task-call mechanism with something that feels like a normal function call on both sides, and today it is a bread-and-butter skill expected in nearly every UVM interview.

    Why DPI exists

    Before DPI (added in the IEEE 1800-2005 standard), connecting C to Verilog meant the PLI or VPI: you registered system tasks, parsed argument lists by hand with tf_/acc_/vpi_ calls, and paid a heavy per-call cost. DPI threw that out. An imported C function is declared in SystemVerilog with a prototype, and the simulator links it like an ordinary subroutine. There is no argument marshalling boilerplate: the compiler maps SV types to C types for you.

    The practical payoff is the reference-model pattern. You run the RTL (DUT) and a C model in lockstep, feed both the same stimulus, and compare outputs cycle by cycle or transaction by transaction. The C model is usually far easier to write, debug, and maintain than an equivalent behavioural SV model, and it can often be the very same code the architects used to define the algorithm.

    Standard, not vendor-specific: DPI is part of IEEE 1800. Synopsys VCS, Cadence Xcelium, and Siemens Questa all support import "DPI-C" and export "DPI-C" with the same syntax — your code stays portable across simulators.

    Import: calling C from SystemVerilog

    An import declaration gives SV a prototype for a C function. The simplest form:

    import "DPI-C" function int add_c(input int a, input int b);

    You then call add_c(x, y) anywhere a normal function call is legal. On the C side you write a matching int add_c(int a, int b). Two context qualifiers matter in interviews:

    • pure — the function has no side effects and its result depends only on its inputs. The simulator may cache or optimise away calls. Use only for true mathematical functions.
    • context — the function needs to know which SV instance called it (it will call back into SV via an export, or use svGetScope). Slightly slower but required for callbacks.
    DPI data path: import and export SystemVerilog testbench / UVM scoreboard DUT monitor C / C++ reference model golden_model() sv_notify() call import "DPI-C" export "DPI-C" CourseTron · coursetron.com
    DPI data path: SV imports the C reference model (blue), C exports back into the SV scoreboard (orange).

    Export: calling SystemVerilog from C

    The export direction is less common but powerful. You declare export "DPI-C" function sv_notify; inside the SV scope, and the C code — as long as it was reached through a context import — can call sv_notify(). This is how a C model pushes an expected result straight into the SV scoreboard, or how a C-based transactor raises an SV event. The rule students trip on: an exported function runs in the SV scope that was active when the enclosing context import was called, so you often capture the scope with svScope s = svGetScope(); and restore it with svSetScope(s) before the callback.

    Type mapping and the open-array trap

    DPI maps scalar types cleanly: SV int ↔ C int, byte ↔ char, real ↔ double, string ↔ const char*. The subtlety is 4-state vs 2-state. A 2-state SV type (int, bit) passes as a plain C integer. A 4-state type (logic, integer) carries X and Z, so it maps to the canonical svLogicVecVal struct with separate aval/bval words — you must use the svdpi.h accessor macros, not a raw cast.

    SystemVerilogC type (svdpi.h)Notes
    intint2-state, direct
    bit [31:0]svBitVecVal*packed 2-state array
    logic [31:0]svLogicVecVal*4-state, aval/bval
    realdoubleIEEE-754
    stringconst char*copy it; SV owns the buffer
    open array []svOpenArrayHandleuse svSize/svGetArrElemPtr
    4-state logic bit as aval/bval (svLogicVecVal) 4-state valueaval bitbval bitsvLogic000sv_0110sv_1Z01sv_zX11sv_x bval = 1 flags an unknown or high-impedance bit (X or Z) CourseTron · coursetron.com
    A 4-state bit splits into aval and bval; bval set means X or Z, which is why logic vectors need the svdpi.h accessors.

    Memory ownership: a string passed from SV into C is valid only for the duration of the call. If C needs it later, strdup it. Returning a pointer into a C local is a classic use-after-free bug that shows up as random mismatches.

    A worked reference-model example

    Suppose you verify a 32-bit CRC engine. The C model is twenty lines; the SV scoreboard imports it:

    import "DPI-C" function int crc32_ref(input int unsigned data, input int unsigned seed);

    In the monitor callback you compute exp = crc32_ref(pkt.data, pkt.seed); and compare against the DUT output. When they diverge, you have the expected value for free because the same C file is the architectural spec. This pattern scales to floating-point units compared against a C double model, or a DSP block checked against a fixed-point C routine.

    Speed tradeoffs

    DPI is fast — a 2-state import call costs on the order of a normal function call plus a thin trampoline. But it is not free, and the cost structure drives design choices:

    • Call granularity. Calling C once per transaction (e.g. per packet) is negligible. Calling it once per signal toggle in a always block can dominate runtime — batch work into C instead.
    • 4-state conversion. Passing logic vectors forces aval/bval packing on every call. Prefer 2-state int/bit arguments when X/Z are not meaningful.
    • context overhead. Context imports carry scope bookkeeping; mark a function context only when it actually calls back.
    • pure wins. A pure function lets the simulator skip redundant calls entirely — measurable on hot math paths.

    As a rule of thumb, well-structured DPI reference modelling adds only low-single-digit percent to simulation time, whereas the old PLI path could add 10× on call-heavy code. That gap is exactly why DPI exists.

    One transaction across the DPI boundary SV driver C model data in (import call) expected result back CourseTron · coursetron.com
    Each transaction crosses the DPI boundary into the C reference model, which returns the expected result to the scoreboard.

    Build and linking

    All three big simulators compile the C alongside the SV. In Questa you point vlog/vsim at the C with -dpiheader and -sv_lib; VCS auto-generates a header and links the object; Xcelium uses -dpi plus the generated dpi_stub. A common gotcha: name mangling. Compile your C as C (not C++), or wrap prototypes in extern "C", or the linker will not find the symbol.

    Ready to build real UVM environments with C reference models and golden checkers? Our CourseTron Verification track walks you through DPI, scoreboards, and coverage closure with hands-on labs.

    If you are deciding where DPI fits in the bigger picture, read our guide to UVM testbench architecture and the companion piece on reference models and scoreboards. Teams building production flows often pair in-house training with a design partner such as Avecas design services for tapeout-grade verification support.

    Frequently asked questions

    What is the difference between DPI and PLI/VPI?

    PLI/VPI are callback APIs that require you to register system tasks and unpack arguments manually, with high per-call overhead. DPI links C functions as ordinary subroutines with automatic type mapping, so it is both simpler to write and much faster.

    When should I use pure vs context?

    Use pure for side-effect-free math whose output depends only on inputs — the simulator may optimise the calls. Use context when the C function calls back into SV via an export or needs the caller's scope. If unsure and no callback is involved, leave it unqualified.

    How do I pass a 4-state logic vector to C?

    It maps to svLogicVecVal with separate aval/bval words for X/Z. Include svdpi.h and use the provided accessor macros rather than casting to a plain integer, otherwise X and Z values are silently lost.

    Is DPI portable across Synopsys, Cadence, and Siemens?

    Yes. DPI is defined in IEEE 1800, so the import/export syntax is identical across VCS, Xcelium, and Questa. Only the build/link flags differ per simulator.

    Advertisement