Skip to content
VLSI Mentor

C Part II · Module 18 · C in SoC Verification

How C Code Gets into a Simulation

Ask how C gets into a simulation and the answer comes back as DPI, which is one of six routes and not the most common. Three of the six never put C inside the simulator at all. This chapter is the map at a block-diagram level — when the C runs, what couples it to the simulator, and what each coupling costs — so that an interface mechanism arrives as an answer to a stated problem. It also contains a C function genuinely executing inside a running simulator, cross-checked against the same function run standalone and against a published check value.

Advanced21 min readCPart IIVerificationArchitecture

Part II · Module 18 · Chapter 18.5 · How C Code Gets into a Simulation

1. The Question, Stated Properly

Chapters 18.1 to 18.4 have been about what the C is: a reference model, an adopted oracle, firmware. This chapter is about how it meets the simulation.

The question is usually asked as "how do I call C from SystemVerilog?", and that framing has already chosen an answer. The better question is:

What has to pass between the C and the simulation, and when?

Because the answers range from "a file, before the run starts" to "a function call, at a specific simulation time, with results that affect the next cycle" — and those need entirely different machinery. Chapter 18.1 §6 made this point and deferred it; this is where it gets settled.

2. Six Routes

Six ways C code can meet a simulation, compared on when the C runs, what couples it to the simulator, and what it costs. A data file: the C runs before or after the simulation, nothing couples them, and the cost is only that the exchange is not interactive. A pipe or socket: the C runs alongside as a separate process, coupled loosely by a protocol you define, costing a protocol to design and debug. DPI, the direct programming interface: the C runs inside the simulator during the run, coupled by a SystemVerilog language feature, costing a build integration and a type mapping. VPI or PLI, the older procedural interfaces: also inside the simulator, coupled by a C API, costing more code than DPI for the same job. Compiled together, as a tool that translates the design into C or C plus plus: the C runs as part of one program, coupled completely, and costs a change of simulator. Firmware on a processor model: the C runs on the design's own core, coupled only through the memory image, and costs a core model and a way to observe results.How C meets thesimulationWhen the C runsWhat couples themWhat it costsA data filestimulus in, log outbefore or afternothingjust a file formatnot interactiveA pipe or socketalongsidea separate processa protocol youdefinea protocol todebugDPIdirect programminginterfaceinside, duringthe runa SystemVerilogfeaturebuild plus a typemappingVPI or PLIthe older C interfacesinside, duringthe runa C APImore code thanDPIfor the same jobCompiled togetherthe design translatedto Cone programcompletelya change ofsimulatorFirmware on acore modelon the design'sown corethe memory imagenothing elsea core modeland a way to observe12
Figure 1 — six ways C meets a simulation, compared on when the C runs, what couples it to the simulator, and what that coupling costs. The second column is the one that decides most of it: only three of the six put C inside the simulator at all, and the top and bottom rows — a data file and firmware on a core model — are both extremely common and neither involves a simulator interface. Read the third column as the real cost of each route, because coupling is what you pay for: a file costs nothing and can be inspected with ordinary tools, while compiling the design and the C into one program couples them completely and takes your choice of simulator with it. DPI sits in the middle of the table rather than at the top, which is the point of the chapter.

Three of the six never put C inside the simulator, and two of those three are extremely common.

A data file is the one people skip past. The C runs as an ordinary program — compiled with an ordinary compiler, debugged with an ordinary debugger — and writes a file the testbench reads, or reads a log the simulation wrote. Nothing couples them except a format. Chapter 18.1 §5's generator and post-processor are this, and so is a great deal of real verification infrastructure.

Firmware on a core model does not involve an interface either. The C is compiled for the design's processor, loaded into a memory image, and executed by the core — Chapter 18.4's subject. The only coupling is the image.

A pipe or socket keeps the C as a separate process but lets it interact during the run. You define the protocol, which is both the cost and the freedom.

DPI, VPI and PLI put C inside the simulator's process, called during the run. They differ in ergonomics rather than in what they achieve.

Compiling together — a tool that translates the design into C or C++ and builds one program — gives the tightest coupling and the fewest moving parts, at the cost of being tied to that tool.

3. C Inside a Running Simulation, Measured

The interface routes are easy to describe and worth demonstrating, because "the C runs inside the simulator" is abstract until you have seen it happen.

Of the simulators available on this machine — Icarus Verilog 13.0 and NVC 1.23.0 — Icarus supports VPI, the procedural interface. It does not support DPI, and neither does anything else available here, so this demonstration uses the one that runs. The chapter says so rather than presenting a DPI example that was never executed.

The C side registers a system function:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
#include <vpi_user.h>

static PLI_INT32 c_crc8_calltf(PLI_BYTE8 *user) {
    vpiHandle sys = vpi_handle(vpiSysTfCall, NULL);
    vpiHandle it  = vpi_iterate(vpiArgument, sys);
    s_vpi_value v; v.format = vpiIntVal;
    uint8_t buf[8]; unsigned n = 0;
    vpiHandle a;
    while ((a = vpi_scan(it)) != NULL && n < sizeof buf) {
        vpi_get_value(a, &v);
        buf[n++] = (uint8_t)v.value.integer;
    }
    s_vpi_value out; out.format = vpiIntVal;
    out.value.integer = crc8(buf, n);
    vpi_put_value(sys, &out, NULL, vpiNoDelay);
    return 0;
}

The Verilog side calls it like any system function:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
module tb;
  initial begin
    $display("CRC8 of 'A','B','C' from C = %0d (0x%02h)",
             $c_crc8(65,66,67), $c_crc8(65,66,67));
    $finish;
  end
endmodule

Built and run:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ iverilog-vpi hello_vpi.c
Compiling hello_vpi.c...
Making hello_vpi.vpi from  hello_vpi.o...
$ iverilog -o tb.vvp tb.v && vvp -M. -mhello_vpi tb.vvp
CRC8 of 'A','B','C' from C   = 82 (0x00000052)

82. Computed by a C function, inside a running simulation, from values the Verilog passed in.

What makes that evidence rather than a screenshot is the cross-check. The same crc8 function compiled as an ordinary program gives 82 for the same input, and over the standard vector "123456789" it gives 0xF4 — the published check value for CRC-8/SMBUS, which Chapter 18.3 §2 used to establish that this implementation is independently correct.

So the function is right, and it computes the same thing in both environments. That is the property you actually need from an interface: the C behaves the same inside the simulator as outside it, which is what lets you develop and debug it as ordinary code.

4. Choosing a Route

Three questions decide it, in order.

Does the C need to run during the simulation? If not, use a file. Generators, post-processors, expected-value tables and log analysers are all in this category, and reaching past it is the most common over-engineering in this area.

Does it need to respond to simulation state, or merely produce data? Producing data can still be a file. Responding — the testbench computes something, passes it to the model, and uses the answer in the next transaction — needs an in-process interface or a socket.

Is the C the stimulus rather than the checker? Then it may belong on a processor model as firmware, with no interface at all. Chapter 18.4 is that case.

Only after all three point the same way is the question which interface — and at that point the choice is largely made for you by the simulator you have and the language your testbench is written in.

5. What Each Interface Is For

A short, honest characterisation of the three in-process routes, since the differences are frequently overstated.

DPI is the modern answer when the testbench is SystemVerilog. It is a language feature rather than a library: you declare the C function's prototype in SystemVerilog and call it directly. The cost is a type mapping — SystemVerilog types and C types are not the same set — and that mapping is a subject in its own right.

VPI and PLI are older, procedural, and more verbose: §3's demonstration is thirty lines to expose one function that DPI would expose in two. What they offer in exchange is access to the simulation itself — walking the design hierarchy, reading and writing signals by name, registering callbacks on events. DPI is for calling functions; VPI is for inspecting and manipulating a simulation. That is the real distinction, and it is why VPI is still used for tools rather than for models.

Compiling together is a different model entirely: the design is translated into C or C++ and your code is linked into the same program. Coupling is total and so is performance, and the trade is that you have chosen your simulator permanently.

6. What This Chapter Deliberately Does Not Cover

The DPI row of Figure 1 has not been expanded, and that is the module's design rather than an omission.

Module 18's description is "the architecture before the syntax", and the syntax in question is substantial: import and export declarations, the mapping between SystemVerilog and C types, packed and open arrays, calling in both directions, and what happens to a logic value that is x when it reaches a C int. Part II's own description lists "DPI-C end to end" as a distinct subject for exactly that reason.

The reason to see the map first is in this chapter's purpose: so that DPI arrives as one answer to a stated problem. A reader who meets DPI first tends to use it for everything, including the four rows of Figure 1 where something simpler would do — and Chapter 18.1 §6 named the cost of that confusion.

7. The Misconception: Getting C In Means DPI

"Using C with a simulation means DPI. That is the mechanism."

DPI is one row of six, and three of the other five involve no simulator interface at all.

The belief persists for a straightforward reason: DPI is the row that gets taught. It is a language feature with a specification, it appears in training material, and it is the interesting part. A file is not interesting, so nobody writes a chapter about it — and it remains the right answer for a large fraction of real work.

The cost of the confusion is concrete and runs in one direction. Teams build an interface where a file would have done, and acquire: a build integration to maintain, a type mapping to get right, C that can only be run inside a simulator, and model output that cannot be inspected after the run. None of those is catastrophic and all of them are avoidable.

The corrected position:

Ask what has to pass and when. Use a file unless something genuinely has to happen during the run; use an interface when it does; use firmware when the C is the stimulus rather than the checker.

8. What You Should Be Able to Say Now

  1. The question is what has to pass, and when — not "how do I call C from SystemVerilog?", which has already chosen an answer.
  2. Six routes: a data file · a pipe or socket · DPI · VPI/PLI · compiled together · firmware on a core model.
  3. Only three put C inside the simulator, and the file and firmware routes are both very common.
  4. Measured: a C function executed inside Icarus Verilog via VPI returned 82 for $c_crc8(65,66,67); the same function standalone returns 82, and crc8("123456789") is 0xF4, the published CRC-8/SMBUS check value.
  5. That cross-check is what makes it evidence — the C behaves the same inside the simulator as outside, which is what lets you develop it as ordinary code.
  6. DPI cannot be demonstrated here; only Icarus and NVC are available and Icarus supports VPI. The chapter uses what runs.
  7. The file route's advantages: nothing to couple, output you can open and diff, ordinary debugging and sanitizers, reviewable without a simulator. Its limitation is that it is not interactive.
  8. Three questions, in order: does it run during the simulation? · does it respond to simulation state, or merely produce data? · is the C the stimulus rather than the checker?
  9. DPI calls functions; VPI inspects and manipulates a simulation. That is the real distinction, not ergonomics.
  10. DPI syntax is deliberately deferred — "the architecture before the syntax", so that it arrives as an answer to a stated problem.

9. Exercises

  1. For each of Figure 1's six rows, name a task from Chapters 18.1 to 18.4 that would suit it. One row should get two.
  2. Take §3's VPI example and change the C function to compute a different CRC variant from Chapter 18.3. Confirm the simulation's answer matches the standalone one.
  3. Rewrite the same job as a file: a C program that emits expected CRCs, and a testbench that reads them. List what you gained and what you lost.
  4. Apply §4's three questions to a verification task you have worked on. Which route do they indicate, and which one was actually used?
  5. §5 claims DPI and VPI differ in purpose rather than ergonomics. Give one job that VPI can do and DPI cannot.
  6. A colleague proposes DPI to pass a fixed table of 10,000 expected values into a simulation. Write the three-sentence reply, naming the route you would use.
  7. §3 cross-checked the C two ways before trusting it. For an interface of your own, write the equivalent cross-check you would run first.

10. Summary

  • Ask what has to pass and when, not how to call C from SystemVerilog.
  • Six routes: file · pipe or socket · DPI · VPI/PLI · compiled together · firmware on a core model. Only three put C inside the simulator.
  • Measured: C executed inside Icarus via VPI gave 82, matching the standalone run and the published 0xF4 check value — so the C behaves identically in both environments.
  • DPI is not demonstrable here; Icarus's VPI is. Use what runs and say so.
  • A file costs nothing to couple, can be inspected and diffed, debugs normally, and reviews without a simulator — and is not interactive, which is the stated problem an interface answers.
  • Three questions: during the run? · respond or just produce? · stimulus or checker?
  • DPI calls functions; VPI inspects simulations.
  • The syntax is deferred on purpose, so the mechanism arrives as an answer rather than as the only thing you were shown.

Every chapter in this module has quietly been answering the same underlying question — whether a particular piece of work belongs in C at all — and has answered it one case at a time. The last chapter makes it a decision you can defend, on evidence rather than on which language the person deciding happens to prefer.

Where this fits

Part of the C Programming curriculum.