Skip to content
VLSI Mentor

C Part II · Module 19 · DPI-C Foundations

Why DPI-C Exists

Chapter 18.5 mapped six ways C can meet a simulation and expanded five of them. DPI is the sixth, and it exists to fill one specific gap: C that must run during the simulation, be called like a function rather than through an API, from a SystemVerilog testbench. Its predecessor already filled that gap procedurally — thirty lines of registration and handle-walking to expose a single function — so DPI's contribution is ergonomic, which is a smaller claim than it is usually given and a real one. This chapter also establishes what this module can and cannot demonstrate, because DPI does not run on the machine these pages are measured on.

Advanced20 min readCPart IIVerificationDPI-C

Part II · Module 19 · Chapter 19.1 · Why DPI-C Exists

1. The Gap It Fills

Chapter 18.5 laid out six ways C can meet a simulation and expanded five of them. Recalling the shape of that table:

  • a data file — no coupling, not interactive
  • a pipe or socket — a separate process, a protocol you define
  • DPI — in-process, during the run
  • VPI or PLI — in-process, during the run, through a C API
  • compiled together — one program, total coupling
  • firmware on a core model — no interface at all

DPI exists for a gap defined by three conditions holding at once:

  1. the C must run during the simulation, not before or after
  2. it is shaped like a function call — arguments in, a result out
  3. the testbench is SystemVerilog

Drop any one and something else is better. If the C does not need to run during the simulation, Chapter 18.5 §3's callout argued for a file and argued hard. If the C is the stimulus rather than a callee, Chapter 18.4's firmware route needs no interface. If the testbench is not SystemVerilog, DPI is not available to you.

That is a narrow gap, and it is a real and common one: a reference model called once per transaction, an adopted algorithm invoked on each packet, a checker that needs an expected value computed now.

2. What Came Before, and What DPI Changed

The gap was already filled. Chapter 18.5 §3 demonstrated it — a C function executing inside a running Icarus simulation, reached through VPI, the procedural interface that predates DPI:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ vvp -M. -mhello_vpi tb.vvp
CRC8 of 'A','B','C' from C   = 82 (0x00000052)

That works, and it is worth looking at what it cost. Exposing one function required: including a vendor header, writing a calltf handler, obtaining a handle to the system-function call, iterating the argument list, extracting each argument into an s_vpi_value, computing, packing the result into another s_vpi_value, calling vpi_put_value, writing a sizetf, filling in a registration structure, and exporting a vlog_startup_routines array. Around thirty lines of interface code around a nine-line CRC.

The DPI equivalent of the same thing is a declaration on the SystemVerilog side and a C function with no interface code at all — the C is an ordinary function that knows nothing about simulators.

So DPI's contribution is ergonomic. That is a smaller claim than it is usually given and it is not a small thing: thirty lines of marshalling per function is thirty lines that can be wrong, and the VPI version's argument extraction is exactly the kind of code where a width or a signedness error hides.

3. What Crosses the Boundary

The module's description asks what crosses it, who calls whom, how the build is put together, and where the C code actually runs — four questions that are the module's four subjects. The first is worth a preview, because it determines how much of this is C at all.

What crosses is values, and the types on each side are different sets. SystemVerilog has bit, logic, four-state values with x and z, packed and unpacked arrays, and int which is specified as a 32-bit two's complement signed integer. C has its own types, with Chapter 14.2's exactness and Chapter 14.1's implementation-defined edges.

For the simple scalar cases the mapping is direct and needs no machinery: SystemVerilog's int maps to C's int, and on this machine sizeof(int) is 4, which is what the mapping requires. Measured:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
int c_add(int a, int b) { return a + b; }
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
c_add(40,2)        = 42
sizeof(int) = 4   what SV 'int' maps to

That C function is the whole C side of a DPI import. No header, no attribute, no registration, no awareness of a simulator. Chapter 19.2 §3 returns to this, because it is the property that makes most of this module's C testable without any simulator at all.

Where it stops being simple is four-state values, packed arrays and open arrays — the cases where SystemVerilog has something C does not — and those need svdpi.h and a vocabulary of their own. Chapter 19.3 begins that; the harder cases are a later module's.

4. What This Module Can Demonstrate

Chapter 18.5 reported which simulators are available here, and Module 19 has to be straightforward about the consequence.

DPI cannot be executed on this machine. Verified rather than assumed:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ iverilog -g2012 -o dpi.vvp dpi_tb.sv        # import inside a module
dpi_tb.sv:2: syntax error
dpi_tb.sv:2: error: Invalid module item.

$ iverilog -g2012 -o dpi2.vvp dpi2.sv         # import at $unit scope
dpi2.sv:1: syntax error
I give up.

man iverilog mentions DPI zero times, and no svdpi.h exists anywhere on the system. NVC offers VHPI — VHDL's foreign interface — not DPI. Verilator, GHDL and the commercial simulators are not installed.

What remains measurable is more than it first appears, and §5 is the list.

5. What Is Still Measured

Five things, all of which were run for this module:

The C side is ordinary C. c_add and a CRC compiled with -Wall -Wextra -Wpedantic -Wconversion -Wsign-conversion and run under AddressSanitizer and UndefinedBehaviorSanitizer, clean, producing 42 and the published check value 0xF4. No DPI header involved.

The build artefact. A shared object built from that C, exporting the symbols a simulator would look for:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ nm -gU libc.dylib
   _c_add
   _c_crc8

The symbol-name hazard. The identical source compiled as C++ exports __Z5c_addii and __Z6c_crc8PKhi, which no simulator will find — and extern "C" restores the plain names. Chapter 19.6's subject, fully measurable.

What pure licenses. Chapter 19.5's concept has an exact C analogue: a function declared __attribute__((const)) had two calls collapsed into one at -O2, where the undeclared version emitted both.

Shared static state. A counter inside a shared object, called from two places, gave 1 2 3 then 4 — one counter, not one per caller. Chapter 19.7's subject.

And VPI stands in for the architecture: Chapter 18.5's $c_crc8 returning 82 from inside a running simulation is a genuine demonstration that C executes in a simulator's process, which is the structural fact DPI also relies on.

6. The Misconception: DPI Is How You Use C With a Simulator

"DPI is the mechanism for using C with a simulator."

Chapter 18.5 measured six routes and this chapter has named the three conditions under which DPI is the right one. Outside that intersection it is overhead, and the overhead is not hypothetical: a build integration to maintain, a type mapping to get right, C that can only be exercised inside a simulator process, and results that do not outlive the run.

The belief persists for the reason Chapter 18.5 §7 identified — DPI is the row that gets taught, because it is a language feature with a specification and the others are not interesting enough to write about.

What is genuinely true, and is narrower:

DPI is the right mechanism when the C must run during the simulation, is shaped like a function call, and the testbench is SystemVerilog. That is a common case and it is not the general case.

And one thing worth carrying into the rest of the module: DPI's existence does not change any of Chapter 18's architecture. A reference model reached through DPI is subject to Chapter 18.2's independence question exactly as before — the measurement there was 3072 mismatches against 0, and the transport had nothing to do with it. Getting the interface right makes a bad model reachable, not good.

7. What You Should Be Able to Say Now

  1. DPI fills a gap defined by three conditions holding together: the C runs during the simulation, is shaped like a function call, and the testbench is SystemVerilog. Drop any one and Chapter 18.5's table has a better row.
  2. The gap was already filled by VPI. Chapter 18.5 measured a C function returning 82 from inside a running simulation through it.
  3. DPI's contribution is ergonomic — around thirty lines of registration and marshalling per function, replaced by a declaration and an unmarked C function. A smaller claim than usual, and a real one.
  4. DPI and VPI are for different jobs. DPI calls functions; VPI inspects and manipulates a simulation. "Which should I use?" is usually not a real question.
  5. What crosses is values, between two different type systems. The simple scalars map directly — SystemVerilog int to C int, sizeof(int) measured as 4.
  6. The C side of an import is an ordinary function — no header, no attribute, no registration. Measured: c_add(40,2) = 42, CRC check value 0xF4, clean under sanitizers.
  7. DPI cannot be executed here. Verified: Icarus rejects import "DPI-C" at module scope and $unit scope, mentions DPI zero times in its manual, and no svdpi.h exists on the system. NVC offers VHPI.
  8. Five things remain measured: the C side, the shared object, the symbol-mangling hazard, what pure licenses, and shared static state.
  9. Claims in this module are marked measured or from the LRM — because if you cannot tell which were checked, none are worth more than the weakest.
  10. DPI does not change Module 18's architecture. A model reached through DPI faces the same independence question; a good interface makes a bad model reachable, not good.

8. Exercises

  1. Write int c_add(int, int) and compile it with -Wall -Wextra -Wpedantic -Wconversion under ASan and UBSan. Confirm it needs no DPI header. What does that tell you about where the C half of a DPI interface can be developed?
  2. Build the same C into a shared object and list its exported symbols. Then build it as C++ and compare. Which spelling would a simulator find?
  3. For each of Chapter 18.5's six routes, say whether DPI's three conditions hold. How many rows survive all three?
  4. Count the interface lines in Chapter 18.5 §3's VPI example that would disappear under DPI. Which of them look most likely to contain a bug?
  5. Name one job VPI can do that DPI cannot, and one that DPI does more cleanly.
  6. On your own machine, test whether your simulator accepts import "DPI-C" at module scope and at $unit scope. Record both results.
  7. A team adopts DPI and its reference model still fails to find a known bug. Using Chapter 18.2, explain why that is unsurprising and what you would check.

9. Summary

  • DPI fills one gap: C running during the simulation · shaped like a function call · a SystemVerilog testbench. All three, or use another of 18.5's six routes.
  • VPI already filled it — measured, a C function returned 82 from inside a running simulation.
  • DPI's contribution is ergonomic: ~30 lines of marshalling per function replaced by a declaration and an unmarked C function.
  • DPI calls functions; VPI inspects simulations. Different jobs, not successive generations.
  • Values cross, between two type systems. Simple scalars map directly; sizeof(int) measured 4.
  • The C side is ordinary C — measured 42 and 0xF4, clean under sanitizers, no DPI header.
  • DPI cannot run here — verified at both scopes, zero manual mentions, no svdpi.h; NVC has VHPI.
  • Still measured: the C side · the shared object · the C++ mangling hazard · what pure licenses · shared static state.
  • Claims are marked measured or from the LRM.
  • The transport does not fix the architecture — 18.2's independence question is unchanged.

The gap is now stated and so is what this module can and cannot show. The boundary itself has two directions, and the second one — C calling into SystemVerilog — is usually taught last or not at all, which leaves people believing the traffic only runs one way.

Standards & specifications

Governing standard
IEEE Std 1800 (SystemVerilog)(opens IEEE in a new tab)

Defines SystemVerilog language semantics — syntax, data types, scheduling and the behaviour a conforming simulator must produce. It does not define tool-specific synthesis support or vendor methodology.

This page also covers RTL structure, verification approach and debugging technique. Those are engineering practice built on the standard, not requirements the standard itself imposes.

Where this fits

Part of the C Programming curriculum.