Skip to content
VLSI Mentor

C Part II · Module 23 · Calling SystemVerilog from C

Simulation Time: What an Imported Function May Not Do

Two hundred million iterations of work ran inside a single call and the simulation clock did not move — before 5, after 5, a delta of zero, while the run took real wall-clock time. That is the constraint in one measurement: work is unlimited and free in simulated time, and advancing time, waiting for an edge, or letting another process run are all impossible rather than merely discouraged. What remains is scheduling, measured here as a callback registered at time 10 for twenty units ahead, firing at 30, from a call that returned immediately.

Expert24 min readCPart IIVerificationDPI-C

Part II · Module 23 · Chapter 23.5 · Simulation Time: What an Imported Function May Not Do

1. The Constraint, and Where It Comes From

Chapter 23.4 established the mechanism: a call is one instant, control is in exactly one place, and the simulator is suspended for the whole of it. This chapter measures what that rules out — and, more usefully, what it leaves.

From the LRM, the rule is stated directly: an imported function may not consume simulation time, and an imported task may. Chapter 23.2 §3 was explicit about the limit of what can be shown here: the binding available on this machine does not provide the blocking form, so the task's privilege is cited, not demonstrated.

What can be measured is the half that causes trouble, and it is the half nobody expects: how completely a call is sealed off from simulation time.

2. Work Is Free, and Unlimited

Measured, two hundred million iterations of arithmetic inside one call:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  [SV] calling $c_slow at time 5
  [C] 200 million iterations of host work
  [C] sim time before = 5, after = 5
  [SV] back at time 5, the call reported a delta of 0
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  real 0.78
  user 0.17

A delta of zero. The run spent real wall-clock time doing the work; the simulation clock did not move by any amount at all.

Nothing restricts how much a call may do. There is no quota, no timeout, and no point at which the simulator intervenes — Chapter 23.4 §4's single thread of control means there is nothing available to intervene with.

And measured, the ticker running alongside:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  [SV ticker] tick 1 at time 10
  [SV ticker] tick 2 at time 20
  [SV ticker] tick 3 at time 30
  [SV ticker] tick 4 at time 40

Ticks at 10, 20, 30, 40 — never inside a call.

3. Three Things That Are Impossible

Not discouraged, not slow — impossible, in the sense that no mechanism exists.

Advancing simulation time. There is nothing to call. Measured, the delta across 200 million iterations was 0, and it would be 0 across any amount of work.

Waiting for an event or a clock edge. A wait requires the waiting process to be suspended and something else to run, and Chapter 23.4 §5 measured that nothing else runs. A C function that blocks on a lock, a socket, a file or standard input does not slow the simulation — it stops it, at one timestamp, with no process able to make progress and no timeout.

Letting another process run. Same reason from the other direction: there is no moment between your entry and your return for a process to be scheduled in.

The three are one fact seen from three sides, and the fact is Chapter 23.4's: control is in C, and it is in exactly one place.

4. What Works: Ask, and Return

The one operation that reaches a later time is scheduling. Measured:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  [SV] calling $c_schedule(20) at time 10
  [C] registered a callback for now+20, and RETURNING at time 10
  [SV] back at time 10 (the call returned 10)
  [SV ticker] tick 2 at time 20
  [C] <<< the scheduled callback fired, sim time = 30 >>>
  [SV ticker] tick 3 at time 30

Read the sequence carefully, because every line is load-bearing.

The call registered a request and returned at time 10 — immediately, at the instant it was called.

The simulation then advanced, and the ticker ticked at 20, which it could only do because the call had returned.

The callback fired at time 30 — twenty units after it was registered, exactly as asked.

And the ticker carried on afterwards, at 30, interleaving correctly around the callback.

So the shape of every design that needs a later time is the same:

You do not wait. You arrange to be called again, and you return.

What a function called from SystemVerilog may and may not do with simulation time, each claim measured. Doing a great deal of work is allowed: two hundred million iterations ran inside one call and the simulation time before and after were both 5, a delta of zero, while the wall clock showed the run taking real time. Advancing simulation time is impossible: there is no mechanism, and the measured delta was zero. Waiting for an event is impossible for the same reason, and a function that blocks does not slow the simulation but stops it entirely at one timestamp. Letting other processes run is also impossible, measured by a ticker that ran at 10, 20, 30 and 40 and never inside a call. What is possible is scheduling: a callback registered for twenty units ahead, from a call at time 10, fired at time 30, and the call that registered it returned immediately at time 10. So the way to reach a later time is to ask for something and return, never to wait.What a call tries to dowith simulation timeCan it?measuredWhat was observedmeasuredDo a great deal of workthe thing C is there foryes, without limitnothing restricts how much200 million iterationsinside one calland the run took real wall-clocktimeAdvance simulation timethe thing people expect to be ableto donothere is no mechanism at allbefore 5, after 5, delta 0through all 200 million iterationsWait for an event or aclock edgethe one that costs runsnoand blocking does not slow therun, it stops itthe simulation would haltat one timestampwith no process able to makeprogressLet another process runthe same fact from another sidenothere is no moment for one to runinthe ticker ran at 10, 20,30 and 40never inside a callAsk for something later,then returnthe one thing that worksyesschedule, do not waitregistered at 10 for 20ahead, fired at 30and the call itself returned at 10Time advances only when you returnmeasured — work is free in simulation time and not in wall-clock time, and the only route to a later instant is toarrange to be called again12
Figure 1 — what a call may and may not do with simulation time, measured. The first row is the permission that surprises people in the other direction: there is no limit on how much work a call may do, and 200 million iterations went by inside one. The next three rows are the things that are impossible rather than merely discouraged, and the second of them is the one that costs runs — a C function that blocks does not slow the simulation, it halts it at a single timestamp with nothing able to make progress. The fifth row is what remains, and it is enough: a callback registered at time 10 for twenty units ahead fired at 30, while the call that registered it returned immediately. The bar at the bottom is the whole chapter — simulation time moves when you return, and not before.

5. How Designs Work Within the Limit

The purpose's third clause, and the part worth taking away. Three patterns, all already measured in this track.

Schedule and return. §4's measurement. The model asks to be called at a later time and gives control back immediately. Everything it needs to remember until then lives in its own state — Chapter 22.7's handle, not a file-scope variable.

Keep state and answer the next call differently. A model that must behave as though it had waited can simply remember where it was. The testbench calls it once per cycle; the model returns "not ready" until its condition holds. Chapter 22.7 measured the design question this raises — which stream's state is it — and answered it with a C-issued handle.

Let the testbench own the waiting. The cleanest of the three, and Chapter 23.1 §2 measured it as design (b): the C records what it found, the testbench fetches each item, and the testbench's loop is where any waiting happens. It cost three extra calls and it was the only one of the three designs that let the testbench act on every event, precisely because each fetch is a separate call with simulation time available between them.

The common shape is worth naming: the waiting belongs to SystemVerilog, and the computing belongs to C. A design that wants to wait in C is a design that has put the two in the wrong places.

6. The Misconception: My Slow Model Is Slowing the Simulation Down

"The C model is expensive, so it is dragging the simulation out. I should optimise it or the test will miss its deadline."

Half right, in a way that matters.

It is not slowing the simulation down, in the sense that word usually carries. Measured: 200 million iterations with a simulation-time delta of 0. No signal moved later, no timeout came closer, no deadline inside the simulated world got harder to meet. The simulated behaviour is bit-for-bit identical to a run with an instantaneous model.

It is making the run take longer. Measured in wall-clock time, and that is a real cost with real consequences for a regression.

The two are worth keeping apart because the fixes are different. A simulated-time problem is fixed by changing the design or the test. A wall-clock problem is fixed by making the C faster, calling it less often, or moving work out of the per-cycle path — and Chapter 23.1 §2's measurement is directly relevant, since design (b) made four calls where design (a) made one.

The accurate position:

A call costs wall-clock time and exactly zero simulation time. The model cannot affect when anything happens in the simulated world; it can only affect how long you wait to find out. Measure both, and never read a report in one unit as evidence about the other.

And the limit worth stating: this is why a C function that blocks is categorically different from one that is merely slow. A slow function finishes and the simulation proceeds. A blocking one does not finish, so the simulation never proceeds at all — §3, and the reason the distinction is not a matter of degree.

7. What You Should Be Able to Say Now

  1. From the LRM: an imported function may not consume simulation time; an imported task may. The task's privilege is cited, not demonstrated here (23.2 §3).
  2. Work inside a call is unlimited — measured, 200 million iterations with no intervention possible.
  3. And free in simulation time — measured, before 5, after 5, delta 0.
  4. Three impossibilities: advancing time · waiting for an event · letting another process run. All one fact from 23.4.
  5. Measured: the ticker ran at 10, 20, 30, 40 and never inside a call.
  6. A blocking call stops the simulation, at one timestamp, with no timeout — it does not slow it.
  7. Scheduling works — measured, registered at 10 for +20, fired at 30, with the registering call returning at 10.
  8. The shape of every solution: you do not wait, you arrange to be called again and return.
  9. Three patterns: schedule and return · keep state and answer the next call differently (22.7) · let the testbench own the waiting (23.1's fetch loop).
  10. The waiting belongs to SystemVerilog; the computing belongs to C.
  11. Wall-clock and simulated time are independent — a model can dominate the run's cost and be invisible in every simulated-time report.

8. Exercises

  1. Run a large amount of work inside a call and print the simulation time before and after. What delta do you get, and what does the wall clock say?
  2. Add a process that prints periodically. Does any of its output appear inside the call? Relate your answer to Chapter 23.4 §3.
  3. Register a callback for a later time and return immediately. At what time does it fire, and what ran in between?
  4. §3 says a blocking call stops rather than slows the simulation. Describe what you would observe from outside the run, and how you would distinguish it from an infinite loop in the design.
  5. Take a design that "needs to wait in C" and rewrite it using each of §5's three patterns. Which is simplest, and what does it cost in calls?
  6. A regression has slowed by 40% with no change in simulated time per test. List the possible causes, and say which one this chapter is about.
  7. Why is a slow C model unable to change the result of a timeout inside the simulation? Answer using §2's measurement.
  8. Explain, to someone who has not read Chapter 23.4, why "the simulator will just run something else while my C works" is not available.

9. Summary

  • From the LRM: a function may never consume simulation time; a task may — the latter cited, not shown here.
  • Work is unlimited — measured 200 million iterations in one call.
  • And costs zero simulation time — measured before 5, after 5, delta 0, with real 0.78 on the wall clock.
  • Impossible: advancing time · waiting · yielding to another process. Measured: the ticker ran at 10, 20, 30, 40, never inside a call.
  • A blocking call halts the simulation at one timestamp — a difference in kind, not degree.
  • Scheduling works — measured, registered at 10 for +20, fired at 30, call returned at 10.
  • You do not wait; you arrange to be called again and return.
  • Three patterns: schedule and return · keep state and answer differently next time · let the testbench own the waiting.
  • The waiting belongs to SystemVerilog, the computing to C.
  • Wall-clock and simulated time are independent — a dominant model can be invisible in every simulated-time report.

Three chapters of constraints — one instant, one scope, no waiting — and they all point the same way: a callback is predictable exactly to the extent that someone decided where it lands, what it is allowed to do, and what happens when it cannot do it. Turning that into something a team can maintain is the last thing this module owes.

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.