Append-only. One ## NN_name section per step.
.cfiles on theverilatorcommand line are compiled with$(CXX)(clang warns "treating 'c' input as 'c++'" on a cache miss). Every imported function needs C linkage:extern "C"or#include "V<top>__Dpi.h"before the definitions.V<top>__Dpi.h(in the-Mdir) holds oneextern "C"prototype per import. Including it turns a return-type mismatch into "functions that differ only in their return type cannot be overloaded". A parameter mismatch defined outsideextern "C"silently becomes a C++ overload → link errorUndefined symbols "_negate".- Missing / mangled /
staticsymbol → macOS ld:Undefined symbols for architecture x86_64: "_factorial", referenced from: ...Vdpiimwrap_top__DOT__factorial_TOP.... - Each import gets a generated wrapper
Vtop___024root____Vdpiimwrap_<scope>__DOT__<name>_TOPthat converts types and passes a temporary for eachoutput(copied back after the call). import "DPI"(1800-2005 spelling):%Error-UNSUPPORTED: Unsupported DPI type 'DPI': Use 'DPI-C'.import "DPI-C" \expect = function int fexpect();works (c_name must be escaped when it is an SV keyword).pure: no call elimination observed (unused result,f(2)+f(2), loop) — every call executes.- Importing libc
strlendirectly fails: Verilator emitsunsigned long long strlen(const char*)forlongint unsigned, which clashes withsize_t strlenfrom<string.h>(included by the generated code). No SV type maps tounsigned long; use a wrapper. libmsin/pow/fabs/log10and libcaccessimport fine. - Same C name imported with two different signatures in two packages:
Duplicate declaration of DPI function with different signature(followed by an internal error). - SV gotcha seen while writing labs:
$display(c ? "PASS" : "FAIL")prints a number (the ternary yields an integral, not a string).
.cfiles passed toverilator --binaryare compiled with the C++ compiler (clang:treating 'c' input as 'c++' when in C++ mode, this behavior is deprecated). DPI functions therefore needextern "C", and C idioms invalid in C++ (implicitvoid*casts frommalloc) fail.- The generated
V<top>__Dpi.his in the Mdir and the C files compile with-I.from there, so#include "Vtop__Dpi.h"works with no extra flag. It declareschandleasvoid*and names parameters after the SV formals. Including it turns the book'scounter7(c7* inst, ...)(Sample 12.9) intoerror: conflicting types for 'counter7'; without it the mismatch links silently (C linkage has no parameter types). - Missing
extern "C"on C++ wrappers: link fails with"_counter7_new", referenced from: ... ld: symbol(s) not found(macOS adds the leading_). - Internally Verilator passes
chandleas a 64-bitQData(unsigned long long) in itsVdpiimwrap_*wrappers. - Two always blocks that call the same import in the same time step: Verilator 5.052 called the second-declared instance first (u2 before u1).
finalblocks:top's ran before the instances' (u2, then u1). Both orders are unspecified by the LRM. - A
pureimport with hidden state (a call counter): Verilator 5.052 still executed all 13 calls (a merged duplicate, a discardedvoid'()result, and a loop whose results are overwritten). The LRM allows eliminating them; do not rely on either behavior. --threads 2: with the default--threads-dpi pure, pure imports were called from 2 OS threads and non-pure imports from 1.--threads-dpi noneserializes them to 1 thread.--threads-dpi allgave the same counts aspurein this design (non-pure calls stayed on 1 thread). Results were identical in every mode.chandlerules (IEEE 1800-2023 6.14) underverilator --lint-only -Wall:int x = honly warns (WIDTHTRUNC, 64→32 bits);h + 1,assign g = hand@(h)are accepted silently; onlychandleinside a packed struct is an error. Verilator models chandle as a 64-bit integer, so code that relies on this will not port to commercial simulators.import "DPI-C" function void bump(ref int x);is accepted silently (no warning even with-Wall) and mapped tovoid bump(int* x). IEEE 1800 forbidsrefin imports; commercial tools reject it.- Imports in
always_comb/assign: exactly one call per input change per call site (2 sites, 10 changes → 2 + 20 calls). Event-driven simulators may evaluate more often; keep stateful models out of combinational code. - Including
Vtop__Dpi.hrescues C++ wrappers written withoutextern "C": the header's earlierextern "C"declarations give the later definitions C linkage (labnocxxhbuild links and runs correctly).
shortrealin a DPI signature: Verilator warnsSHORTREALand generatesdoubleinVtop__Dpi.h(LRM:float). A C definition withfloatlinks silently and reads garbage (0.1 → -1.59e-23). IncludingVtop__Dpi.hturns it into a compile error.refin an import is accepted silently (generatesint*); the LRM forbids it.- A
1'bzliteral as a DPI argument:%Error-UNSUPPORTED: Unsupported tristate construct: ARG. X/Z held in variables arrive as 0 (2-state); C-writtenbvalbits are dropped on output. - Output arguments are uninitialized stack locals in the generated wrapper (
svBitVecVal v__Vcvt[2];) and are copied back unconditionally: an output C never writes overwrites the SV variable with stale stack data (measured: the previous call's 40-bit temp). - Copy-back masks to the declared width (
VL_SET_Q_SVBV(40, ...), scalars1U & x); input chunks arrive with zero unused bits and no sign extension. The LRM guarantees neither. SV_GET_SIGNED_BITS(v, N)invltstd/svdpi.h(Annex I text) tests bit N instead of N-1:SV_GET_SIGNED_BITS(0x80, 8)= 128.- Result types:
bit [31:0]→svBitVecVal;bit [32:0]and any 4-state vector (logic [6:0],integer,time) are errors citing IEEE 1800-2023 35.5.5. - Verilator skips verilation (and its warnings) when the
-Mdiris up to date: a check that greps warnings needs a clean build. - SV gotcha seen in testbenches:
$display(c ? "PASS" : "FAIL")prints a number (the string literal is integral in an expression).
- Open-array actuals must be fixed-size unpacked arrays: a dynamic array (or queue) is rejected
(
Unsupported: DPI argument of type DYNARRAYDTYPE, or an internal errorPassing dynamic array or queue as actual argument to DPI open array is not yet supporteddepending on where it is declared). - An assignment pattern
'{...}as an open-array actual:Unsupported: Assignment pattern applies against non-struct/union data type: 'UNSIZEDARRAYDTYPE'. Assign it to a fixed array variable first. svGetArrayPtr()of an input fixed array points at the SV variable itself (writes in SV after the call are visible through a pointer kept by C/Python). An output open array is a temporary on the C stack (different address on every call), copied back after the call returns.- Unpacked-array argument shape must match exactly:
logic signed [15:0] c[8]->input shortint x[8]is%Error-UNSUPPORTED: Shape of the argument does not match the shape of the parameter. Same family: ashortintarray on alogic signed [15:0]array port isArray element types are not equivalent. $errorstops the simulation (Verilog $stop, exit code 1) unless+verilator+error+limit+N.$display(cond ? "PASS" : "FAIL")prints a number, not the string (the argument is not a format literal).- A comment that starts with
// Verilator:is parsed as a pragma (Unknown verilator comment). - Variable with initializer declared inside a loop body of an
initialblock is static: the initializer runs once at time 0 (int len = $urandom_range(...)was always 0). Useautomatic. No warning. - Verilator compiles
.cfiles as C++ (clang warnstreating 'c' input as 'c++');#include "Vtop__Dpi.h"works because--top-module topmakes the prefixVtop.
- Open arrays only take fixed-size actuals. Passing a dynamic array (or queue) to an
input T a[]DPI formal fails at elaboration:%Error-UNSUPPORTED: Unsupported: DPI argument of type DYNARRAYDTYPE(pointing at the import), followed byInternal Error: ../V3Task.cpp:759. Fixed-size unpacked arrays work (svSize,svLow,svGetArrayPtr,svGetArrElemPtr1all fine, also foroutput). Workaround: copy into a fixed buffer and pass the length as a separateint(seegolden_pkg.sv). randomize()runs an external SMT solver (z3): ~5-10 ms per call on this machine, CPU mostly idle (simcpu 0.68 svswalltime 12.9 sfor 2500 calls). Parallel sims contend for it. It dwarfs DPI cost (a DPI call into C is ~2-10 ns); use$urandomstimulus when measuring anything else.- Sized negative literal in
dist:x dist {-16'sh8000 := 5}on ashortintis unsatisfiable (%Warning-UNSATCONSTR,randomize()returns 0). Plain-32768works. - User
.cfiles are compiled with the C++ compiler (clang: warning: treating 'c' input as 'c++') from inside the-Mdir, so#include "Vtop__Dpi.h"works and gives C linkage + prototype checking for free.
- Open-array actuals: only fixed-size arrays. Dynamic array →
%Error-UNSUPPORTED: Unsupported: DPI argument of type DYNARRAYDTYPE; queue →... QUEUEDTYPE; slicea[1:2]→%Error: Internal: Unexpected Call. Workaround: copy through a fixed buffer + explicit count. - Unpacked struct as DPI argument →
Unsupported: DPI argument of type REFDTYPE '<name>'. Use packed structs (svBitVecVal) or separate members. - Packed open-array elements are stored natively: bit[7:0] = 1 byte, bit[12:0] = 2, bit[63:0] = 8, bit[99:0] = 16 (32-bit words). A
svBitVecVal*cast on asvGetArrElemPtr1result is wrong for widths ≤ 32 (reads neighbors); usesvGetBitArrElem1VecVal. - Flat storage of unpacked arrays: low index first, row-major (
int v[10:1]has v[1] at offset 0). outputarrays: open-array temporary arrives zero-filled and is copied back whole (unwritten elements become 0); fixed-size output arrays return garbage in unwritten elements.inoutkeeps them.svGetArrElemPtr*out of range or with the wrong number of indices: run-time%Warning: DPI svOpenArrayHandle function ...and NULL.- Returned strings are copied into std::string immediately (one static buffer survives two calls in one
$display). - 4-state:
bvalwritten by C is ignored;svGetLogicArrElem*VecValwritesbval = 0. - C compiler warnings (e.g.
-Wreturn-stack-address) only appear in the Verilator build log; with ccache they are replayed on cache hits. bufis a Verilog keyword (gate primitive):int buf[256];is a syntax error.
- Timing controls anywhere below an exported task (
#,@, or nested in a called task) are a compile error:%Error-UNSUPPORTED: ... Unsupported: Timing controls inside DPI-exported tasks. Book Sample 12.40 does not build. Portable alternative: pull model (SV loops and waits, C is a zero-time iterator), see05_export_and_context/lab/mem_pull.*. - Generated header declares exported tasks as
void(LRM Annex H:intdisable flag); imported tasks asint.svIsDisabledState()links and returns 0. - Imported function calling an exported zero-time task: accepted (LRM: illegal).
- Non-context import calling an export: fatal only if no scope was ever set
(
Testbench C called 'X' but scope wasn't set ... See IEEE 1800-2023 35.5.3., preceded by a%Warning: DPI C Function called by Verilog DPI import with missing 'context' keyword.); if an earlier context call left a scope, the export of THAT instance runs silently. svGetScopeFromNametakes the%mname (top.b2,top.gen[0].u);TOP.top.b2and relative names return NULL.svPutUserData/svGetUserData,svGetCallerInfo(path as on the command line) work.pureis not exploited: discarded/duplicated/loop-invariant pure calls all execute (also with -O3). A DPI function in a continuous assign ran 11 times for 5 input changes.- Cost per call (5 M calls, idle machine): generic ≈ pure ≈ 1.3–1.8 ns, context ≈ 10 ns, context + 1 export ≈ 18–21 ns.
- Unwritten
outputargs come back as garbage: the import wrapper passes an uninitialized temp (o__Vcvt). $errordefaults to$stop(abort) at the first error;+verilator+error+limit+Nkeeps going.
.cfiles passed toverilator --binaryare compiled with the C++ compiler (__cplusplus=201703; clang warns "treating 'c' input as 'c++'"). IncludeVtop__Dpi.hso definitions getextern "C"from the prototypes.- Open-array formal (
input byte unsigned d[]) with a dynamic array actual:%Error-UNSUPPORTED: Unsupported: DPI argument of type DYNARRAYDTYPE(+ an internal error in V3Task). A fixed-size actual works. - A
//comment whose first word isVerilator:(any case) is parsed as a pragma:%Error-BADVLTPRAGMA: Unknown verilator comment. Don't start comments with the tool's name. $finishdoes not stop the calling process: statements after it in the sameinitialstill execute, thenfinalblocks run.$errorwith the default error limit becomes$stop: "Aborting...", exit status 1,finalblocks do not run.+verilator+error+limit+Nkeeps going ("$stop, ignored due to +verilator+error+limit").--threads 4with the default--threads-dpi pure: non-pure DPI calls are serialized onto ONE thread that is NOT the thread that raninitial(3200/3200 calls off the init thread).pureimports or--threads-dpi allspread calls over 4 threads. Any thread-affine C library (CPython) needs per-call thread attach (PyGILState_Ensure).- Embedded CPython 3.14:
python3-config --ldflagslacks-lpython3.14(use--embed); numpy imports fine in the Verilator binary (~0.1 s) but only once per process (re-init + import → "cannot load module more than once per process"). - macOS:
WEXITSTATUS(system(cmd))does not compile as C++ ("cannot take the address of an rvalue");<wait.h>(Spear Sample 12.52) is Linux-only, use<sys/wait.h>. - Unpacked array port connections with non-equivalent element types are checked inconsistently: a whole
shortint c[8]on alogic signed [15:0] coef [8]port fails (Array element types are not equivalent), but a sub-arrayshortint c[2][8]->.coef(c[u])builds and simulates. Both are illegal per the LRM. - Calling any CPython API (e.g.
PyLong_FromNativeBytes) before the interpreter is initialized segfaults the simulator on the first DPI call: make every bridge entry point go through the function that initializes.
- Step 08 now links step 06's
golden_pkg.svby relative path; Python-only extras live inpy_extra_pkg.sv. One build line per backend, no SV change. - 06's
golden.candwall.crely on#include "Vtop__Dpi.h"for C linkage (noextern "C"of their own). A testbench that linkswall.cwithout importingwall_sstill builds: the definition just gets C++ linkage and is unused. - A contract with no shutdown call: the bridge registers
atexit(Py_FinalizeEx wrapper)on first init. Verilator'smain()returns afterfinalblocks, so Python's buffered stdout is flushed andFir8.__del__runs. - Bug selection without SV or rebuild:
GOLDEN_BUG=fir_trunc vlr ...(bash passes a prefix assignment to a function's child processes); the C twin is-CFLAGS -DMUTANT=1(rebuild).
Checked against IEEE 1800-2023 clause 35 and Annexes H, I, J (2026-10-15). Older lines above stay as written; these supersede them.
- Return types:
bit [6:0]/bit [31:0]results are NOT legal per §35.5.5 and H.8.9 (only byte…string and scalar bit/logic). Verilator accepting packed bit results ≤ 32 bits is an extension, so Spear §12.1.4 is not an erratum. The LRM's own §35.5.4 examplefunction bit [15:0] getStimulus()contradicts its normative list. import "DPI"(02 notes above: "1800-2005 spelling"): it selects the deprecated SV3.1a packed-argument passing (opaque handles, H.14). §35.5.4 requires a warning or an error, so Verilator's error is compliant.- Unpacked array element order is fixed by H.7.3 and H.7.6(c): the lower index goes first. Verilator follows it; it is not implementation-defined.
- Packed open-array elements: H.7.3 requires canonical form; H.12.4 requires
svGetArrElemPtr*to return NULL if the storage differs. Verilator's native storage with non-NULL pointers is an LRM deviation. - Exported tasks declared
voidinVtop__Dpi.h: deviation from §35.8 / H.8.2 (same header as an import task,int). - Export from a non-context import: an error per §35.5.3, behavior undefined per H.9.3 (user error, not implementation-defined).
svDimensions: H.12.2 defers to §20.7 ($dimensionscounts packed + unpacked). Verilator returns unpacked dims only; the LRM is ambiguous.puremisuse (new, 01pure_rules.sv):pure function void,pure task, andpurewith anoutputall build and run with no diagnostic (illegal per §35.5.1.3/§35.5.2).- Shared C names (new, 01
cid.sv, 05xshared.sv): one C name in two scopes with an identical signature works. Changing only thecontextqualifier givesDuplicate declaration of DPI function with different signature(then an internal error). Two modules exportinghello+svSetScopeworks. - C++ exception through an import (new, 03
exc.cpp): libc++terminating due to uncaught exception, exit 134, nofinalblocks (§35.5.1.6: undefined). try/catch in the wrapper fixes it. - Time API (new, 05
svtime.c):svGetTimeignorestypeand scope: always sim time in precision units (25 ns → 25000 at 1 ps),realnever written.svGetTimeUnitreports −9 for a module withtimeunit 1us(the scope table stores the global unit).svGetTimePrecisionignores the scope. svDpiVersion()returns"1800-2005"(02ver.c).- Dynamic-array formal on an export (new, 05
xdyn.sv):%Error: Verilator internal fault, sorry.instead of a diagnostic (erroneous per §35.5.6).