Part 7 · Advanced & Integration · Intermediate
DPI Imports
import "DPI-C" function/task syntax, pure and context qualifiers, SV-to-C type mapping, and svdpi.h.
Import syntax: functions and tasks
An import "DPI-C" declaration gives SystemVerilog a prototype for a C function. Imported functions must return in zero simulation time — they map to ordinary C functions. Imported tasks may consume time only in the limited sense that they can call exported SV tasks that block; the C code itself cannot wait on simulation events directly.
// Function import: zero-time, returns a value
import "DPI-C" function int compute_crc(input int unsigned data,
input int unsigned poly);
// Task import: void on the C side, may call exported SV tasks
import "DPI-C" task run_c_model(input int n_cycles);
// Renaming: SV name differs from C symbol
import "DPI-C" c_checksum = function int checksum(input byte data[]);
// void function
import "DPI-C" function void log_to_c(input string msg);pure and context qualifiers
Two optional qualifiers tell the simulator what the C function is allowed to do, which controls both correctness and optimization. pure promises the result depends only on the inputs — no side effects, no static state, no file I/O — so the simulator may cache or reorder calls. context promises the opposite: the function needs to know its SV call site, because it will call exported SV functions or use scope-sensitive svdpi APIs.
pure — side-effect free, result depends only on arguments; simulator may cache results and skip repeated calls. Math helpers, CRC, format converters.
context — the C code calls exported SV functions/tasks or uses svGetScope/svSetScope; the simulator must maintain call-site scope. Required for any callback into SV.
(no qualifier) — default: side effects allowed (file I/O, static state), but no calls back into SV. Most utility imports live here.
Type mapping: SV to C
DPI defines a fixed mapping between SV types and C types, declared in the standard header svdpi.h. Two-state SV types map to plain C integer types; four-state types map to svLogic and packed vectors to svLogicVecVal arrays that carry the X/Z encoding.
SV TYPE C TYPE (svdpi.h) NOTES
─────────────────────────────────────────────────────────────────
byte char 2-state, 8 bit
shortint short int 2-state, 16 bit
int int 2-state, 32 bit
longint long long 2-state, 64 bit
real double
shortreal float
string const char* simulator owns memory
chandle void* opaque C pointer
bit svBit (unsigned char) 2-state, 1 bit
logic / reg svLogic (unsigned char) 4-state, 1 bit
bit [N:0] (packed) svBitVecVal array 2-state vector
logic [N:0] (packed) svLogicVecVal array 4-state, aval/bval pairs
unpacked array [] svOpenArrayHandle open array (see later)
struct (packed) passed as vector bit layout preservedPrefer 2-state types (int, byte, bit [31:0]) at the DPI boundary. Four-state values force the C side to decode aval/bval pairs, and a C model has no meaningful interpretation of X anyway — resolve X-ness in SV before crossing.
Worked example: checksum import
A minimal but complete round trip: SV passes a data word and an accumulator to C, C returns the updated checksum. The C file is compiled into a shared object the simulator loads at startup.
SV side
// dpi_pkg.sv
package dpi_pkg;
import "DPI-C" pure function int unsigned
checksum_add(input int unsigned acc, input int unsigned data);
endpackage
module tb;
import dpi_pkg::*;
int unsigned csum;
initial begin
csum = 32'hFFFF_FFFF;
csum = checksum_add(csum, 32'hDEAD_BEEF);
csum = checksum_add(csum, 32'h0000_1234);
$display("checksum = %h", csum);
end
endmoduleC side
// checksum.c — compile: gcc -shared -fPIC -o checksum.so checksum.c
#include "svdpi.h"
unsigned int checksum_add(unsigned int acc, unsigned int data)
{
/* simple Fletcher-style fold; pure: no static state */
acc ^= data;
acc = (acc << 7) | (acc >> 25);
return acc;
}Walkthrough
The import is declared pure — no side effects, so the simulator may cache identical calls.
int unsigned maps to unsigned int; no svdpi types needed for plain scalars.
The C function name must match the SV import name (or use the rename form).
Compile C with -fPIC into a shared object; load with the simulator's -sv_lib or equivalent switch.
Key takeaways
import "DPI-C" function for zero-time calls; task imports only matter when C must call blocking exported SV tasks.
pure enables caching; context is mandatory when C calls back into SV — default is in between.
Keep the boundary 2-state: int/byte/bit map to plain C types, logic forces aval/bval decoding.
svdpi.h is the standard header — it defines svBit, svLogic, vector value types, and open-array APIs.
Common pitfalls
Marking a function pure when it has static state or I/O — simulator caching silently returns stale results.
Passing logic vectors when bit would do — C side must decode 4-state aval/bval pairs for no benefit.
Mismatched prototypes between SV import and C definition — often links fine, then corrupts the stack at runtime.
Assuming an imported task can wait on SV events — C code cannot block; only exported SV tasks it calls can.