The `timescale directive sets the time unit and precision of a simulation, but it does not decide how a time value looks when it is printed. That is the job of the system task $timeformat. It controls how the %t format specifier prints time in system tasks such as $display, $monitor, $write and $strobe: in which unit, with how many decimal places, with which suffix and in how wide a field.

9 min read | Beginner Level

What You'll Learn

  • The four arguments of $timeformat and the default format of %t
  • How the timescale and $timeformat together decide the printed time
  • The difference between %t and %0t, and how the minimum field width pads the output
  • Common $timeformat mistakes, such as a suffix that does not match the unit

Syntax


$timeformat(<unit_number>, <precision>, <suffix_string>, <minimum field width>);
ArgumentMeaningDefault if $timeformat is never called
unit_numberThe unit to print time in, as a power of ten of a second, from 0 (s) down to -15 (fs)The smallest time precision of all `timescale directives in the design
precisionNumber of digits printed after the decimal point0
suffix_stringText printed after the number, such as " ns"Empty string
minimum field widthMinimum number of characters for number and suffix together; shorter results are padded with leading spaces20

The most common unit numbers are listed below. Every power of ten in between is valid too, for example -10 for 100 ps or -11 for 10 ps.

Unit numberTime unit
-31 ms
-61 us
-91 ns
-121 ps
-151 fs
The call $timeformat(-9, 2, " ns", 10) with each argument labelled: -9 sets the unit to ns, 2 sets two decimal places, " ns" is the suffix and 10 is the minimum field width. For a simulation time of 10.512 ns, $display with %t prints [T=  10.51 ns]: the value is converted to ns, rounded to two decimal places, followed by the suffix, and padded with two leading spaces to a width of 10 characters

The setting is global: one call to $timeformat applies to every %t printed afterwards, in every module of the design, until the next call.

Synthesis result: None. $timeformat and $display are simulation-only system tasks, and synthesis tools ignore them. They are used in testbenches and models to produce readable log files.

Example #1: 1ns/1ps

The testbench below waits for a fractional delay, then prints the same time five times with different $timeformat settings. With a time unit of 1 ns and a precision of 1 ps, the delay #10.512351 is rounded to the nearest picosecond, which is 10.512 ns.


`timescale 1ns/1ps

module tb;
  reg a = 0;

  initial begin

    // Wait for some time - note that because precision is 1/1000 of
    // the main scale (1ns), this delay will be rounded at the 3rd
    // decimal position
    #10.512351;

    // Display current time with default timeformat parameters
    $display("[T=%0t] a=%0b", $realtime, a);

    // Change timeformat parameters and display again
    $timeformat(-9, 2, " ns");
    $display("[T=%0t] a=%0b", $realtime, a);

    // Remove the space in suffix, and extend fractional digits to 5
    $timeformat(-9, 5, "ns");
    $display("[T=%0t] a=%0b", $realtime, a);

    // Here suffix is wrong, it should not be "ns" because we are
    // setting display in "ps" (-12)
    $timeformat(-12, 3, " ns");
    $display("[T=%0t] a=%0b", $realtime, a);

    // Correct the suffix to ps
    $timeformat(-12, 2, " ps");
    $display("[T=%0t] a=%0b", $realtime, a);
  end
endmodule
Output
xcelium> run
[T=10512] a=0
[T=10.51 ns] a=0
[T=10.51200ns] a=0
[T=10512.000 ns] a=0
[T=10512.00 ps] a=0
xmsim: *W,RNQUIE: Simulation is complete.

Line by line:

  • Default: $timeformat has not been called, so time is printed in the smallest precision of the design (1 ps) with no decimals and no suffix: 10512.
  • (-9, 2, " ns"): the time is converted to ns and printed with 2 decimals, so the third decimal is dropped: 10.51 ns.
  • (-9, 5, "ns"): 5 decimals, and the suffix has no space, so it is joined to the number: 10.51200ns.
  • (-12, 3, " ns"): the number is now in ps, but the suffix still says ns. The output 10512.000 ns is wrong by a factor of 1000, because $timeformat prints the suffix exactly as written and never checks it.
  • (-12, 2, " ps"): number and suffix now agree: 10512.00 ps.

All the examples use %0t. The 0 removes the padding to the minimum field width, which is why no leading spaces appear even though the field width argument is left out.

IEEE 1364 defines $timeformat with all four arguments. Most simulators accept the shorter form used in this example, but passing all four, such as $timeformat(-9, 2, " ns", 10);, works with every tool.

Quick Check

At the same simulation time of 10.512 ns, what does $timeformat(-6, 4, " us", 0) print with %0t?

Show Answer

0.0105 us. 10.512 ns is 0.010512 us, and 4 decimal places round it to 0.0105.

Example #2: 1ns/100ps

This is the same example with a different timescale. Only the first line changes:


`timescale 1ns/100ps
Output
xcelium> run
[T=105] a=0
[T=10.50 ns] a=0
[T=10.50000ns] a=0
[T=10500.000 ns] a=0
[T=10500.00 ps] a=0
xmsim: *W,RNQUIE: Simulation is complete.

With a precision of 100 ps, #10.512351 is rounded to the nearest 0.1 ns, which is 10.5 ns. The default line prints 105 because the default unit is now the smallest precision of the design, 100 ps, and 10.5 ns is 105 of those. The other lines show 10.50 ns, 10.50000 ns and 10500 ps: $timeformat can add as many decimal places as you ask for, but the digits beyond the precision are always zero, because the simulator never stored them.

Example #3: 100ns/1ns

Now the time unit is 100 ns and the precision is 1 ns:


`timescale 100ns/1ns
Output
xcelium> run
[T=1051] a=0
[T=1051.00 ns] a=0
[T=1051.00000ns] a=0
[T=1051000.000 ns] a=0
[T=1051000.00 ps] a=0
xmsim: *W,RNQUIE: Simulation is complete.

The delay #10.512351 now means 10.512351 × 100 ns = 1051.2351 ns, which is rounded to the precision of 1 ns, giving 1051 ns. In the same way, #10 would mean 1000 ns. The default line prints in the precision of 1 ns, so it shows 1051, and the ns and ps lines show the same time with more decimals.

Quick Check

In Example #3, the fourth line prints 1051000.000 ns. What is the actual simulation time, and what is wrong with this line?

Show Answer

The time is 1051 ns. The unit number -12 prints the value in picoseconds, 1051000, but the suffix still says ns, so the line reads as a time a thousand times too large. It is the same mistake as in Example #1.

%t vs %0t and the Minimum Field Width

With plain %t, the time is right-aligned in a field of at least the minimum field width, 20 characters by default. With %0t, the padding is removed:


`timescale 1ns/1ps

module tb;
  initial begin
    #10.512;
    $display("[T=%t]",  $realtime);    // Default width of 20
    $display("[T=%0t]", $realtime);    // No padding

    $timeformat(-9, 2, " ns", 10);
    $display("[T=%t]",  $realtime);    // Width of 10
  end
endmodule
[T=               10512]
[T=10512]
[T=  10.51 ns]

The padding is useful in log files: when every time stamp has the same width, the messages after it line up in columns and are easier to scan. %0t is more compact and is common in short messages.

Consistent Log Files in Regressions

A verification regression runs thousands of tests and produces thousands of log files, which are searched by scripts and compared between runs. Testbenches therefore call $timeformat once at time 0, typically $timeformat(-9, 3, " ns", 12); in the top-level testbench, so that every message from every component prints time in the same unit and width, regardless of the timescale of the module that prints it.

Matching Logs to Waveforms

When debugging, engineers jump from an error message in the log to the same moment in the waveform viewer. If the log prints 10512 while the waveform cursor shows 10.512 ns, it is easy to look at the wrong place. Printing time with an explicit unit and suffix that match the waveform viewer avoids this confusion.

Common Beginner Mistakes

Mistake #1: A suffix that does not match the unit number

Wrong: $timeformat(-12, 3, " ns", 10);
Right: $timeformat(-12, 3, " ps", 10); or $timeformat(-9, 3, " ns", 10);

Why? The suffix is only text. $timeformat does not check that it matches the unit, so every time stamp in the log is off by a factor of 1000, as in the fourth line of each example.

Mistake #2: Expecting more decimals than the precision can provide

Wrong: Using `timescale 1ns/100ps and $timeformat(-9, 5, ...), expecting to see 10.51235 ns
Right: Choose a finer time precision in `timescale if the extra digits matter

Why? $timeformat only changes how time is printed. The simulator has already rounded the time to the precision, so Example #2 prints 10.50000ns instead of 10.51235.

Mistake #3: Printing $time instead of $realtime

Wrong: $display("%t", $time); in a 1ns/1ps module at 10.512 ns
Right: $display("%t", $realtime);

Why? $time returns an integer number of the module's time units, so 10.512 ns becomes 11 before %t ever sees it. With $timeformat(-9, 3, " ns", 10), the line would print 11.000 ns.

Mistake #4: Calling $timeformat in several places

Wrong: Calling $timeformat inside several modules or testbench components, each with its own settings
Right: Call it once, at time 0, in the top-level testbench

Why? $timeformat is global. Whichever call runs last wins for the entire simulation, so the format in the log depends on the order in which modules happen to execute.

Practice Exercise

Challenge: Predict the Output

Predict the three lines printed by this code:


`timescale 1ns/10ps

module tb;
  initial begin
    #3.14159;
    $display("[%0t]", $realtime);
    $timeformat(-9, 1, " ns", 0);
    $display("[%0t]", $realtime);
    $timeformat(-12, 0, " ps", 0);
    $display("[%0t]", $realtime);
  end
endmodule
Hint

First round the delay to the precision of 10 ps. Then work out the default unit, which is the smallest precision in the design.

Solution
[314]
[3.1 ns]
[3140 ps]

#3.14159 is rounded to the nearest 10 ps, giving 3.14 ns. By default, time is printed in units of the precision, 10 ps, so 3.14 ns prints as 314. With (-9, 1, " ns", 0), it prints in ns with one decimal: 3.1 ns. With (-12, 0, " ps", 0), it prints in ps with no decimals: 3140 ps. The last line shows that the original 0.00159 ns was lost when the delay was rounded, not when it was printed.

Summary

In this tutorial, you learned:

  • $timeformat(unit, precision, suffix, width) controls how %t prints time in $display, $monitor, $write and $strobe
  • Without $timeformat, time is printed in the smallest precision of the design, with no decimals, no suffix and a width of 20
  • The unit number is a power of ten of a second, such as -9 for ns and -12 for ps
  • The suffix is plain text and must be kept consistent with the unit number
  • Decimal places beyond the timescale precision are always zero, because the time was already rounded
  • %0t removes the padding to the minimum field width; plain %t keeps time stamps aligned
  • $timeformat is global, so call it once at the start of the top-level testbench
Did you find what you were looking for?

Quiz

Test Your Knowledge

1 of 5 Score: 0 / 5