Skip to content
VLSI Mentor

C Part II · Module 18 · C in SoC Verification

Where C Appears Around a Verification Environment

Ask where C fits in a verification flow and most answers come back as DPI — a C function called from SystemVerilog. That is one of five places C actually appears, and three of the other four are never called by the testbench at all. This chapter is the map: what the C is in each case, where it runs, who calls it, and why a team reaches outside SystemVerilog in the first place. Every category is backed by something built and run here, including a C function genuinely executing inside a simulation and firmware driving the peripheral model from Module 16.

Intermediate20 min readCPart IIVerificationArchitecture

Part II · Module 18 · Chapter 18.1 · Where C Appears Around a Verification Environment

1. A Different Question

Modules 14 to 17 asked one question: how does a C program drive hardware? Widths, byte order, alignment, registers, volatile. Everything in those four modules was about getting bytes and bits across a boundary into a device.

This module asks a different question, and the change is in what the code is for rather than in what the language does. In a verification flow the C is usually not driving anything. It is standing in for hardware, or checking it, or generating what will be fed to it.

The module's own description is "the architecture before the syntax", and that ordering is deliberate. The mechanics of calling C from SystemVerilog are a subject of their own; knowing them is no use if the C should not have been called from SystemVerilog in the first place. So this chapter is the map, and the only question it answers is where C appears and why.

2. Five Places

Five places C appears around a verification environment, each with what the C is, where it runs, and who calls it. A reference model is an independent implementation of what the design should do; it runs inside the simulator through a programming interface; the scoreboard calls it. Firmware is the software the product will actually ship; it runs on the processor inside the design, or on a model of it; the design's own processor calls it. A reused library is existing tested code such as a codec, a cryptographic routine or a compression algorithm; it runs inside the simulator or in a post-processing step; a model or a checker calls it. A test-data generator or post-processor runs outside the simulation entirely, as an ordinary program, and is called by the build or regression scripts. A device model stands in for hardware that is not part of this simulation, runs inside the simulator, and is called by the testbench.Where C appearsWhat it isWhere it runsWho calls itReference modelwhat the designSHOULD dowritten independentlyinside thesimulatorthe scoreboardFirmwarethe software thatshipsthe real thing, earlyon the processorin the designthe design itselfA reused libraryexisting testedcodecodec · crypto ·compressionin the simulatoror after ita model or acheckerGenerator orpost-processormakes or checksdata filesoutside thesimulationan ordinary programthe build scriptsor the regressionDevice modelstands in forabsent hardwareinside thesimulatorthe testbench12
Figure 1 — the five places C appears around a verification environment, with what the C actually is in each case, where it executes, and who invokes it. The fourth column is the one that surprises people: only two of the five are called by the testbench. Firmware is called by the design's own processor and the testbench may not know it exists; a generator or post-processor runs before or after the simulation as an ordinary program; and those two between them account for a great deal of the C in a real flow. The second column is the one to read when deciding whether a piece of work belongs here at all, because each entry describes something that either already exists or is genuinely easier to express in C — which is Chapter 18.6's whole subject.

Taking them in turn, each with the artefact that demonstrates it.

3. The Reference Model

A reference model is an implementation of what the design is supposed to do, written independently of the design, so that a scoreboard can compare the two and a disagreement means something.

It is the category with the most written about it and the most got wrong, which is why Chapter 18.2 is entirely about it. The short version of that chapter's measurement, because it sets up everything else in the module:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
headers exercised          : 4096
model A (from the spec)    : 3072 mismatches -> the bug is FOUND
model B (from the design)  : 0 mismatches    -> the bug is INVISIBLE

Two models of the same block against the same deliberately buggy design. One was written from the specification and found the bug on three quarters of all inputs. The other was written by reading the design's source — the common shortcut — and passed every single test while the design was broken.

That measurement is the reason this module exists before any syntax is taught.

4. Firmware

The second category is the one people forget, and it is not called from the testbench at all.

Firmware is the software that will ship in the product, running on the processor inside the design — or on a model of that processor — while the simulation runs. The testbench may not know it exists; the design's own core fetches and executes it.

The payoff is that you are exercising the hardware the way the product will, with the code the product will use, before any silicon exists. Register sequences that a directed testbench would never think to produce appear naturally, because the firmware does what firmware does.

Demonstrated here against the peripheral model from Chapter 16.1 — the same model, now driven by code written as firmware rather than as a test:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
static void uart_init(uint32_t divisor, uint32_t mode, uint32_t thresh) {
    uint32_t c = rd(DEV_CTRL);                       /* read-modify-write, 16.5 */
    c &= ~(F_MASK(CTRL_DIVISOR_POS, CTRL_DIVISOR_W)
         | F_MASK(CTRL_MODE_POS,    CTRL_MODE_W)
         | F_MASK(CTRL_THRESH_POS,  CTRL_THRESH_W));
    c |= PUT(divisor, CTRL_DIVISOR_POS, CTRL_DIVISOR_W)
       | PUT(mode,    CTRL_MODE_POS,    CTRL_MODE_W)
       | PUT(thresh,  CTRL_THRESH_POS,  CTRL_THRESH_W)
       | 0x1u;                                       /* ENABLE */
    wr(DEV_CTRL, c);
}
static uint8_t uart_getc(void) { return (uint8_t)rd(DEV_DATA); }  /* RC: read ONCE */
static void    uart_ack(uint32_t bits) { wr(DEV_IRQ, bits); }     /* W1C */
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
after init : CTRL = 0x00203e8b
firmware read : "Hi" (2 bytes)
pending before ack : 0x00000005
pending after  ack : 0x00000004   <- bit 2 preserved

bus transactions issued by the firmware: 6 reads, 2 writes

Every line of that firmware is Part II material: fixed-width types from 14.2, the field accessors from 15.4, the read-modify-write discipline from 16.5, the single read of a read-to-clear register from 16.6, and a write-one-to-clear acknowledgement that left the unrelated pending bit intact. Nothing in this module changes how the C is written — only what it is for.

Chapter 18.4 is this category properly.

5. Libraries, Generators and Device Models

A reused library is the cheapest category and the most under-used. If the design implements a standard — a CRC, a cipher, a compression format, a floating-point operation — then a correct implementation already exists, has been used by thousands of people, and did not come from your project's understanding of the specification. Chapter 18.3 is this, and it is worth noticing that "we already have a trustworthy implementation" is a much stronger starting position than "we will write a model".

A generator or post-processor runs outside the simulation entirely. It builds a stimulus file before the run or analyses a log after it, as an ordinary C program compiled with an ordinary compiler. No simulator interface is involved. This is the category people skip past on the way to something more sophisticated, and it is frequently the right answer — Chapter 18.6's decision framework returns to it.

A device model stands in for hardware that is not part of this simulation: a memory, a PHY, an attached peripheral. The model from Chapter 16.1 is exactly this, and it was built there in order to make that module's measurements runnable. Its registers implement their documented access types, so firmware written against it exercises the same W1C and read-to-clear behaviour it will meet in silicon.

6. The Misconception: C in Verification Means DPI

"Using C in verification means writing DPI functions that SystemVerilog calls."

DPI is one transport, for one or two of the five categories. Three of the five are not called from SystemVerilog at all:

  • firmware is executed by the design's own processor
  • a generator runs before the simulation starts
  • a post-processor runs after it finishes

Conflating the category with the transport has a practical cost, and it runs in one direction: teams reach for an interface mechanism when a file would have done. Writing stimulus to a file and reading it in is simpler, debuggable with ordinary tools, and does not couple your C to a simulator's lifetime. It is the right answer more often than its reputation suggests.

And the transport question is genuinely separate from the architecture question. Chapter 18.5 covers how C gets into a simulation and demonstrates it with a C function executing inside a running simulator — but it comes after 18.2, 18.3 and 18.4, because which of the five categories you are in determines whether you need a transport at all.

7. What You Should Be Able to Say Now

  1. The question changes, not the language. In Part II's earlier modules C drove hardware; here it stands in for hardware, checks it, or generates what feeds it.
  2. Five places C appears: reference model, firmware, a reused library, a generator or post-processor, and a device model.
  3. Only two of the five are called by the testbench. Firmware is run by the design's own processor; a generator and a post-processor run outside the simulation entirely.
  4. A reference model is worth having only if it is independent. Measured: over 4096 inputs against a buggy design, a spec-derived model found 3072 mismatches and a design-derived model found 0.
  5. Firmware is the software that ships, exercised against the RTL before silicon, producing register sequences a directed test would not invent.
  6. Measured: firmware against the Chapter 16.1 model configured CTRL to 0x00203e8b, read "Hi", and acknowledged with W1C leaving pending at 0x00000004 — 6 reads and 2 writes.
  7. Nothing about how the C is written changes. The firmware uses 14.2's types, 15.4's accessors, 16.5's read-modify-write and 16.6's access types unchanged.
  8. "C in verification means DPI" is wrong. DPI is one transport for one or two categories, and the cost of the confusion is reaching for an interface when a file would do.
  9. Four honest reasons to reach outside SystemVerilog: the code exists · the same code ships · independence is the point · the software team is the audience. Three are about provenance and audience, not about the language.

8. Exercises

  1. For a block you have worked on, list which of the five categories appear in its verification environment. If any are missing, say whether that is a deliberate choice.
  2. Take §4's firmware and add a function that sets only the mode, leaving the divisor alone. Verify against the Chapter 16.1 model that the divisor survives, and count the transactions.
  3. §6 claims three of the five categories are not called from SystemVerilog. Name them and, for each, say what does invoke it.
  4. A team proposes writing a C model of a block whose RTL is already written and whose author is on the same team. Using §3's measurement and the callout, list what you would ask before agreeing.
  5. Name one standard your designs implement for which a trustworthy C implementation already exists. What would you have to check before using it as a reference?
  6. A colleague wants to use DPI to pass a stimulus array into a simulation. Give the file-based alternative and two reasons it might be preferable.
  7. Write down the four reasons from the callout in your own words, then mark which ones apply to the most recent verification task you worked on.

9. Summary

  • The code's purpose changes, not the language. C here stands in for hardware, checks it, or feeds it.
  • Five places: reference model · firmware · a reused library · a generator or post-processor · a device model.
  • Only two are called by the testbench.
  • A reference model must be independent — measured 3072 mismatches versus 0 on the same buggy design.
  • Firmware is what ships, run by the design's own processor, and it produces sequences a directed test would not.
  • Measured firmware run: CTRL = 0x00203e8b, read "Hi", W1C ack left pending 0x00000004, 6 reads + 2 writes.
  • The C is written exactly as Part II taught it — types, fields, RMW, access types.
  • DPI is a transport, not the category. The cost of confusing them is an interface where a file would do.
  • Four reasons to reach outside SV: it exists · it ships · independence · the software team. Three are about provenance, not the language.

The first of the five is the one with the most written about it and the most at stake, because it is the only one whose entire value depends on where it came from. A model that agrees with the design is either a vindication or a tautology, and the measurement in §3 shows those two look identical from the outside.

Where this fits

Part of the C Programming curriculum.