Skip to content

Verilog · Chapter 8.3 · System Tasks & Functions

Time Functions in Verilog — $time, $realtime, $stime

Three system functions return the current simulation time in Verilog, and they are not interchangeable. One returns a 64-bit integer rounded to the active time unit, one returns a 64-bit real value that is exact to the active precision, and one returns a 32-bit integer with far less headroom. The differences stay hidden until you need sub-unit precision, run a simulation past the 32-bit limit, or want a timestamp that fits neatly in a log line. This lesson explains each function, its return type, and how it interacts with the timescale that sets the units. It covers the rounding rules, the 32-bit overflow trap that bites long regressions, and the print formatting discipline that keeps timing logs readable instead of turning them into unreadable walls of numbers.

Foundation14 min readVerilog$time$realtime$stimeTimescaleSimulation

Chapter 8 · Section 8.3 · System Tasks & Functions

1. The Engineering Problem

A verification engineer adds timestamp prints to a long-running clock-recovery testbench. The simulation is configured with `timescale 1ns / 1ps — a precision fine enough to catch picosecond-scale clock drift. The engineer writes:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
initial begin
    forever begin
        @(posedge sample_clk);
        $display("[t=%0t] sample = %h", $time, sample);
    end
end

The log fills with prints. But the picosecond-scale drift the engineer is hunting for never appears in the output — every timestamp lands on an integer nanosecond. The 50 ps measurement window is invisible.

The issue: $time returns a 64-bit integer rounded to the active time_unit (1 ns here). Sub-unit precision is gone. To see picosecond resolution, the engineer needs $realtime — which returns a 64-bit real preserving the full time_precision (1 ps).

The fix is one character:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$display("[t=%0t] sample = %h", $realtime, sample);
//             ^^^^^^^^^ — real, not integer; preserves 1 ps resolution

Same %0t specifier, different function: $time → integer ns timestamps, $realtime → exact ns.ps timestamps. The clock-drift measurement now works.

This page covers all three time-query functions — their return types, their interaction with \timescale, the rounding semantics that hide sub-precision data, the 32-bit overflow trap that can corrupt long-running regression logs, and the %0t` discipline every readable log relies on.

2. The Three Functions at a Glance

FunctionReturnsWidthPrecisionUse case
$timeinteger64-bit signedrounded to time_unitGeneral-purpose timestamps; the default reach
$realtimereal64-bit IEEE-754exact to time_precisionSub-unit measurements (jitter, propagation delay)
$stimeinteger32-bit unsignedrounded to time_unitLegacy compatibility; overflow risk at long sim times

The four differentiators:

  • Return type — integer ($time, $stime) vs real ($realtime).
  • Bit width — 64-bit ($time, $realtime) vs 32-bit ($stime).
  • Precision — rounded to time_unit ($time, $stime) vs exact to time_precision ($realtime).
  • Overflow risk — none for the 64-bit functions; serious for $stime after 2³² × time_unit has elapsed.

The 64-bit time_unit count for $time overflows at simulated 2⁶³ × time_unit — for 1ns unit, ~292 years of simulated time. $stime overflows at 2³² × time_unit — for 1ns unit, just ~4.3 seconds of simulated time. Long-running RTL regressions hit $stime's ceiling routinely; $time's ceiling is astronomical.

3. $time — The 64-bit Integer Timestamp

The most-used member of the family.

3.1 Syntax

time-syntax.v — one form, no arguments
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
integer t;
t = $time;                                  // 64-bit signed integer
$display("now = %0t", $time);               // typical usage
$display("now = %0d ns", $time);            // explicit unit suffix

3.2 Semantics

  • Returns a 64-bit signed integer. Even though simulation time is non-negative, the integer is signed (Verilog's integer keyword is signed by default; the 64-bit width is implementation-defined but specified ≥ 64 bits in IEEE 1364-2005).
  • Rounded to the active time_unit. With `timescale 1ns / 1ps, $time at simulation time 5.5 ns returns 6 (rounded to the nearest ns). Sub-unit precision is dropped.
  • No arguments. Always reads the current scope's active timescale.

3.3 The rounding rule

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
`timescale 1ns / 1ps      // unit = 1ns, precision = 1ps
 
module rounding_demo;
    initial begin
        #5;       $display("$time = %0t", $time);  // 5
        #0.5;     $display("$time = %0t", $time);  // 6  (rounds 5.5 → 6)
        #0.5;     $display("$time = %0t", $time);  // 6  (rounds 6.0 → 6)
        #0.0001;  $display("$time = %0t", $time);  // 6  (rounds 6.0001 → 6)
        $finish;
    end
endmodule

$time rounds at the time_unit boundary using banker's rounding (round half to even). Sub-unit delays accumulate inside the simulator (preserved in $realtime) but vanish in the integer return.

3.4 When to use

Almost every general-purpose timestamp print. Log lines, debug snapshots, test-progress banners. The default reach when sub-unit precision isn't needed.

4. $realtime — The 64-bit Real Timestamp

The function for sub-unit measurements.

4.1 Syntax

realtime-syntax.v
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
real t;
t = $realtime;                              // 64-bit IEEE-754 double
$display("now = %0t", $realtime);           // %0t formats reals too
$display("now = %0.3f ns", $realtime);      // explicit %f format

4.2 Semantics

  • Returns a 64-bit IEEE-754 double-precision real. The value is exact to the active time_precision (e.g., 1 ps under `timescale 1ns / 1ps).
  • No rounding to time_unit. Sub-unit delays are preserved.
  • Unit is still the timescale's time_unit. A $realtime of 5.5 under 1ns / 1ps means 5.5 ns, NOT 5.5 ps.

4.3 The precision contract

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
`timescale 1ns / 1ps
 
module realtime_demo;
    initial begin
        #5;       $display("$time=%0t  $realtime=%0t", $time, $realtime);
        // $time=5  $realtime=5
 
        #0.5;     $display("$time=%0t  $realtime=%0t", $time, $realtime);
        // $time=6  $realtime=5.5  ← integer rounded, real preserved
 
        #0.123;   $display("$time=%0t  $realtime=%0t", $time, $realtime);
        // $time=6  $realtime=5.623
 
        $finish;
    end
endmodule

$realtime preserves the sub-unit detail that $time drops. The trade-off: real arithmetic is slower than integer arithmetic, and IEEE-754 representations have rounding error at large values (millions of nanoseconds suffer floating-point precision loss).

4.4 When to use

Any time you need sub-time_unit resolution — clock jitter analysis, propagation-delay extraction, PLL settling-time measurement, real-time signal-processing simulation. Otherwise prefer $time (faster, integer-clean).

5. $stime — The 32-bit Compatibility Form

The legacy function with the overflow trap.

5.1 Syntax

stime-syntax.v
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
integer t;          // careful — assigning $stime to an integer is fine here
t = $stime;         // returns 32-bit unsigned
$display("now = %0t", $stime);

5.2 Semantics

  • Returns a 32-bit unsigned integer. Same rounding rule as $time.
  • Identical to $time modulo 2³². When $time ≤ 2³² − 1, $stime == $time. When $time ≥ 2³², $stime wraps.
  • Historically the only time function in early Verilog before 64-bit support was universal.

5.3 The overflow trap

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
`timescale 1ns / 1ps
 
module stime_overflow_demo;
    initial begin
        #4_000_000_000;     // 4 billion ns ≈ 4.3 seconds simulated
        $display("$time  = %0d", $time);    //  4000000000  (fine)
        $display("$stime = %0d", $stime);   //  4000000000  (still fine)
 
        #500_000_000;       // additional 0.5 s
        $display("$time  = %0d", $time);    //  4500000000  (fine)
        $display("$stime = %0d", $stime);   //   205032704  ← WRAPPED
        // $time exceeds 2³²-1 = 4_294_967_295; $stime wraps modulo 2³²
 
        $finish;
    end
endmodule

$stime wraps once simulated time exceeds 2³² × time_unit:

Timescale unit$stime overflows at
1fs4.3 µs simulated
1ps4.3 ms simulated
1ns4.3 s simulated
1us71.6 minutes simulated
1ms49.7 days simulated

For ASIC RTL using 1ns / 1ps timescale, a 4.3-second regression hits the overflow. Long-running stability simulations, post-silicon co-emulation, and gate-level annotated runs routinely exceed it.

5.4 When to use

Almost never in new code. The function exists for legacy compatibility with Verilog-1995 code that pre-dates 64-bit $time. New code should always use $time (or $realtime for sub-precision).

6. Interaction with `timescale

The three functions all read the active timescale to determine their unit and (for $realtime) precision.

6.1 The unit is the divisor

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
`timescale 1ns / 1ps   →  $time returns "units of 1 ns"      (#10$time=10)
`timescale 1us / 1ns   →  $time returns "units of 1 us"      (#10$time=10)
`timescale 1ms / 1us   →  $time returns "units of 1 ms"      (#10$time=10)

A #10 in source is always 10 × time_unit; $time after the delay always returns 10. The integer value doesn't change with the unit — only its meaning does. This is what makes timestamps portable across timescales as raw counts, even though the wall-clock duration each unit represents differs.

6.2 The precision floors $realtime resolution

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
`timescale 1ns / 1ns   →  $realtime is also rounded to 1 ns
`timescale 1ns / 1ps   →  $realtime exact to 1 ps  (3 sub-unit digits)
`timescale 1ns / 1fs   →  $realtime exact to 1 fs  (6 sub-unit digits)

$realtime's effective resolution is the timescale's time_precision, not the time_unit. Choosing precision finer than needed slows the simulation (covered in the 7.3 timescale-directive page) but gives $realtime more digits to print.

6.3 Cross-module timescale rules apply

Each module's $time / $realtime reflects its own \timescale. A module-A $timeof10under1ns / 1psand a module-B$timeof10under1us / 1ns` represent different wall-clock instants — even though both are simulated at the same simulator step. The 7.3 page covers the multi-module propagation rule; this page just notes that time functions inherit it.

7. Format Specifier %t

The print specifier built specifically for these functions.

7.1 %t vs %0t

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$display("t = %t",  $time);    // "t =                    10"    — default width is HUGE
$display("t = %0t", $time);    // "t = 10"                       — compact

The default %t width is implementation-defined but typically 20+ characters — every print becomes unreadable. Always use %0t for production log output.

7.2 Explicit format vs %t

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$display("t = %0d", $time);              // "t = 5500" — no time-aware formatting
$display("t = %0t", $time);              // "t = 5500" — same number, time-aware
$display("t = %0.3f ns", $realtime);     // "t = 5.500 ns" — explicit unit

%t is identical to %d when the argument is an integer ($time, $stime), but the difference appears with $realtime:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$display("%0t", $realtime);              // "5500"   — integer-style print
$display("%0.3f", $realtime);            // "5.500"  — decimal-style print
$display("%g", $realtime);               // "5.5"    — compact-style print

Use %0t for uniform time-stamp output; use %0.3f or %g when you need decimal point clarity.

7.3 Vendor-specific $timeformat

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
initial begin
    $timeformat(-9, 3, " ns", 10);
    // (-9 = nanoseconds, 3 = decimals, " ns" = suffix, 10 = min width)
    $display("now = %t", $realtime);  // "    5.500 ns"  — auto-formatted
end

$timeformat (IEEE 1364-2005 §17.8.1) configures how %t formats time. Sets the unit indicator, decimal precision, suffix, and minimum width. Useful in shared library tasks where every timestamp print should match a project convention.

8. Visual Explanation

Three figures cover the time-function model.

8.1 Visual A — function family at a glance

The three functions and their differentiators.

Time-query family

data flow
Time-query familysimulation timewhat the simulator's clock holds$time64-bit int, rounded to time_unit$realtime64-bit real, exact to time_precision$stime32-bit int — overflow risk after 2³² units
Same underlying clock, three projections. $time is the default. $realtime gives sub-unit precision. $stime is the 32-bit legacy form — avoid in new code (overflow at ~4.3 s of simulated time under `1ns / 1ps`).

8.2 Visual B — precision rounding

What $time shows vs what $realtime shows at the same simulator step.

Precision rounding across time functionssimulator step 5.623nsinternal clock$time = 6rounded to ns$realtime = 5.623exact$stime = 6rounded to ns (32-bit)12
At simulated time 5.623 ns under `timescale 1ns / 1ps, $time rounds to 6 (nearest 1ns); $realtime preserves 5.623; $stime matches $time. Sub-unit detail is preserved by the real-valued function only. For jitter / propagation-delay / sub-precision measurements, use $realtime — $time silently quantises the data.

8.3 Visual C — $stime overflow at 4.3 s simulated time

Why long-running regressions can't use the 32-bit function.

$stime overflow at 4.3 seconds simulated time$time = 4,000,000,000 $stime = 4,000,000,000$time = 4,000,000,000$stime =…before 4.3 s — both agree$time = 4,294,967,295 $stime = 4,294,967,295$time = 4,294,967,295$stime =…ceiling of 2³²-1$time = 4,500,000,000$stime = 205,032,704$stime wrapped12
Simulated time crosses 2³² = 4,294,967,296 time_units. Under `timescale 1ns / 1ps that's about 4.3 seconds of simulated time. $time keeps incrementing cleanly to 4,500,000,000. $stime wraps modulo 2³² and reports 205,032,704 — totally wrong. Log analysis based on $stime corrupts; regressions report nonsense timestamps. Use $time everywhere new code is written.

9. Industry Use Cases

Five patterns where the time-query family shows up in production.

9.1 Event-time logging

Every meaningful event prints with a timestamp:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
always @(posedge clk) begin
    if (transaction_complete) begin
        $display("[t=%0t] xact %0d complete: data=%h", $time, id, data);
    end
end

Universal pattern across every testbench. $time + %0t is the canonical form.

9.2 Sub-precision jitter measurement

Measuring clock jitter requires $realtime:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
real prev_edge_time, jitter_ps;
 
always @(posedge sample_clk) begin
    jitter_ps = ($realtime - prev_edge_time) * 1000.0 - NOMINAL_PERIOD_PS;
    prev_edge_time = $realtime;
    $display("[t=%0t] jitter = %0.3f ps", $realtime, jitter_ps);
end

$time would round to nanosecond boundaries; the picosecond-scale jitter signal would vanish.

9.3 Propagation-delay characterisation

Extract propagation delays from event timing:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
real input_edge, output_edge;
 
always @(input_signal)  input_edge  = $realtime;
always @(output_signal) begin
    output_edge = $realtime;
    $display("propagation = %0.3f ps", (output_edge - input_edge) * 1000.0);
end

9.4 Test-progress tracking

Long-running tests print progress markers:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
initial begin
    forever begin
        #1_000_000;   // every 1 ms simulated
        $display("[t=%0t] progress: %0d cycles complete", $time, cycle_count);
    end
end

$time survives even multi-day simulations on a 64-bit integer; $stime would wrap.

9.5 Timeout enforcement

Detect runaway tests:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
parameter MAX_TIME = 100_000_000;   // 100 ms simulated
 
always @(posedge clk) begin
    if ($time > MAX_TIME) begin
        $display("[FATAL] timeout at t=%0t — test ran > %0d", $time, MAX_TIME);
        $finish(1);
    end
end

$time's comparison is integer; $realtime's would be IEEE-754 double — both work, but integer comparison is exact while double comparison can have rounding artefacts at large values.

10. Common Mistakes

Six pitfalls that catch every working engineer at least once.

10.1 %t produces unreadable wide output

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$display("t = %t", $time);
// Prints: "t =                    100"   ← 20+ char field, hard to read

Default %t width is implementation-defined and typically huge. Always use %0t for compact output.

10.2 $time drops sub-unit precision

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
`timescale 1ns / 1ps
initial begin
    #5.5 $display("t = %0t", $time);   // prints "t = 6", not "t = 5.5"
end

$time rounds to time_unit. For sub-unit values, use $realtime.

10.3 $stime silently wraps in long simulations

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
`timescale 1ns / 1ps
initial begin
    #5_000_000_000;
    $display("t = %0d (stime = %0d)", $time, $stime);
    // "t = 5000000000 (stime = 705032704)"  ← stime wrapped
end

Use $time for any simulation that might exceed 2³² × time_unit.

10.4 Comparing real time with integer time

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
real t_real;
integer t_int;
 
t_real = $realtime;
t_int  = $time;
 
if (t_real == t_int)  // ← may be false even at same simulator step
    $display("equal");

$time is rounded; $realtime is exact. They equal only when the simulator step is on an integer time_unit boundary. Don't compare values from the two functions directly.

10.5 $realtime precision loss at large values

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
real t;
t = $realtime;
// At simulated time 1_000_000_000 ns (1 second) with 1ps precision,
// the IEEE-754 double has ~15 significant digits — picosecond resolution
// is preserved. At simulated 100_000_000_000 ns (100 seconds), the
// representable precision degrades to ~10s of picoseconds.

Long-running picosecond-precision simulations eventually lose precision in $realtime's IEEE-754 representation. For jitter measurement of relative differences (event-to-event), this rarely matters; for absolute long-time precision it does.

10.6 Forgetting \timescalemakes$time` undefined

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// No `timescale at top of file
module no_timescale;
    initial #10 $display("t = %0t", $time);
    // What unit is "10"? Vendor-defined default. Behaviour varies.
endmodule

$time depends on \timescale`. Without it, the unit is implementation-defined. Always declare a timescale (the 7.3 page covers the discipline).

11. Debugging Lab

Three time-function debug post-mortems

12. Coding Guidelines

Six rules teams converge on.

  1. $time is the default reach. Reach for it first; switch to $realtime only when sub-unit precision is required.
  2. $realtime for sub-unit measurements. Jitter, propagation delay, PLL settling time, any value smaller than one time_unit.
  3. Avoid $stime. Only use it for legacy compatibility with Verilog-1995 code; new code uses $time (64-bit, no overflow risk).
  4. %0t for every print. Default %t width is unreadable; the leading 0 forces minimum-width.
  5. **Always declare \timescale.** Without it, $time` and its siblings have implementation-defined behaviour.
  6. Don't compare $time with $realtime directly. Different precisions; == is unreliable. Convert one side to the other's representation first, or use a tolerance band.

13. Interview Q&A

14. Exercises

Three exercises that turn time-function choice into reflex.

Exercise 1 — Predict the output

For each snippet, predict the exact printed output.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Snippet A
`timescale 1ns / 1ps
initial begin
    #5      $display("A: %0t", $time);
    #0.5    $display("B: %0t", $time);
    #0.5    $display("C: %0t", $time);
    #0.001  $display("D: %0t", $time);
    $finish;
end
 
// Snippet B
`timescale 1ns / 1ps
initial begin
    #5      $display("A: %0t / %0t", $time, $realtime);
    #0.5    $display("B: %0t / %0t", $time, $realtime);
    #0.123  $display("C: %0t / %0t", $time, $realtime);
    $finish;
end
 
// Snippet C
`timescale 1ns / 1ps
initial begin
    #5_000_000_000     $display("A: time=%0d stime=%0d", $time, $stime);
    $finish;
end

Hints. Apply the rounding rule to A. Apply the $time vs $realtime distinction to B. Apply the 32-bit overflow rule to C.

Exercise 2 — Pick the right function

For each requirement, name the best function (and any format specifier needed).

#Requirement
1Print a per-cycle bus event timestamp in a log line
2Measure the time between two posedge clk events to 1 ps resolution
3Detect if simulation has run more than 100 ms
4Print the current time formatted as "5.500 ns"
5Time-stamp a 5-day-long gate-level regression
6Match Verilog-1995 codebase conventions
7Compute clock jitter as the deviation from a nominal period

Exercise 3 — Fix the time bugs

This testbench has four time-function bugs. Identify and fix.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
`timescale 1ns / 1ps
 
module buggy_time_tb;
    integer prev_edge, jitter;
    real    long_sim_t;
 
    always @(posedge clk) begin
        jitter = $time - prev_edge - 10;            // Bug 1?
        $display("t = %t   jitter = %0d ps", $time, jitter * 1000);  // Bug 2?
        prev_edge = $time;
    end
 
    initial begin
        #4_500_000_000;
        $display("end of test at $stime = %0d", $stime);    // Bug 3?
    end
 
    initial begin
        long_sim_t = $time;                          // Bug 4?
        $display("long_sim_t = %0.3f", long_sim_t);
    end
endmodule

Hints. Bug 1 is a precision issue. Bug 2 is a format issue. Bug 3 is an overflow issue. Bug 4 is a type-mismatch issue.

15. Summary

Verilog's time-query family has three members differentiated by return type, bit width, and precision.

The three functions:

  • $time — 64-bit signed integer, rounded to time_unit. The default.
  • $realtime — 64-bit IEEE-754 real, exact to time_precision. For sub-unit measurements.
  • $stime — 32-bit unsigned integer; same semantics as $time minus 32 bits of headroom. Avoid in new code (overflows at 2³² × time_unit).

The interaction with \timescale`:

  • The unit is the timescale's time_unit. $time = 10 under 1ns / 1ps means 10 ns.
  • Precision floor: $time rounds to time_unit; $realtime rounds to time_precision.
  • Multi-module timescales propagate the same way as in §7.3 — each module's time functions reflect its own timescale.

The %t format discipline:

  • Always use %0t, never bare %t. Default %t width is implementation-defined (typically 20+ chars) and produces unreadable logs.
  • Use %0.3f for decimal-style $realtime prints.
  • Use $timeformat(-9, 3, " ns", 0) to configure %t globally.

The overflow risk:

Timescale unit$stime overflows at
1fs4.3 µs simulated
1ps4.3 ms simulated
1ns4.3 s simulated
1us71.6 minutes simulated
1ms49.7 days simulated

$time (64-bit) is essentially overflow-free — at 1ns / 1ps, the ceiling is 292 years of simulated time.

The day-to-day discipline:

  • $time for general-purpose timestamps. Reach for it first.
  • $realtime for sub-unit precision. Jitter, propagation delay, PLL settling.
  • Avoid $stime. Legacy only; wraps in long simulations.
  • %0t for every print. Bare %t is unreadable.
  • Always declare \timescale`. Without it, time-function behaviour is implementation-defined.

The next two sub-pages close Chapter 8: 8.4 Conditional Simulation covers the assertion-and-severity layer ($assert, $error, $warning, $fatal, $info), and 8.5 Simulation Control closes the chapter with $finish / $stop / $exit.