The Return of `timescale

If you thought that timescale didn’t matter in SystemVerilog – think again!
And truth be told – I thought that timescale was obviated with SystemVerilog’s new features – and I’m having to think again!

So I had thought that we had finally rid ourselves of the `timescale albatross with SystemVerilog.

If you don’t remember the (old) issue here’s a refresher:
`timescale is a compiler directive used to configure the units and precision of a # delay in Verilog. The syntax is something like this:

`timescale 1ns / 10ps

which means that the time unit of a delay is 1ns and the precision is 10ps (or two decimals of precision in ns  as x.xx ns).
Since a delay in Verilog doesn’t have units it relies on the timescale to determine how long the delay will last in simulation.
No problem, right?

Here’s the issue: a `timescale directive has file scope and is “sticky” – once a `timescale is found while compiling everything in that file and following files has that timeunit and timeprecision value until the next `timescale is encountered.

And that was the issue – if you didn’t include a `timescale in your file then you relied on compilation order to get your timescale.  If the compilation order changed or the upstream file changed it’s unit then all of a sudden your delays were wrong.

Introducing SystemVerilog:
SystemVerilog solved this problem in two ways:

  1. delays now have units!  So I no longer rely on the timescale time unit to determine how long my delay is.  Instead of #1;  I now say #1ns.
  2. introduction of timeunit and timeprecision keywords – these allow the specification of a timescale within the declaration body of a module, interface, or package; no longer do we rely on a `timescale compiler directive.

Unfortunately the problem wasn’t solved…
While delays can be specified for units – effectively obviating the need for a timescale – there’s a (minor) catch.
If a delay is specified that is more precise than a previously declared timescale then it is rounded to match the timescale precision.
Example:

`timescale 1ns / 100ps
...
#1.75ns  // rounded to 1.8ns

But that’s a minor hiccup – compared to this:
The LRM doesn’t explicitly define what happens when the timeunits aren’t aligned. It seems that when a time (or realtime) typed variable is passed from one timescale domain to another the units are CHANGED to the destination timescale.
This is crazy.  If I define a realtime variable in a package with a timescale and then pass that variable to a function that is *defined* in another package (or file) timescale the time *units* are changed to match the destination timescale!
What does that mean?
It means that if I have 1.75ns in a 1ns/1ps timescale and pass it to a class declared in a 1ms/1us timescale that the time will be changed to 1.75ms.

An example follows – after the break.

Example: (from the code posted here)

package pkg2;
   timeunit 1us;
   timeprecision 1ns;
   class myclass7;
      function new();
         $printtimescale;
      endfunction: new
      task dodelay(realtime delay);
         realtime predelay;
         realtime postdelay;
         realtime elapsed;
         predelay = $realtime;
         #delay;
         postdelay = $realtime;
         elapsed = postdelay - predelay;
         $display("Delay = %t, Actual Delay = %t, Pre = %t, Post = %t", delay, elapsed, predelay, postdelay);
         $printtimescale;
      endtask:dodelay
   endclass: myclass7
endpackage: pkg2

program main;
   timeunit 1ns;
   timeprecision 100ps;

   import pkg2::*;

   initial begin:testsequence
      realtime delay;
      realtime predelay;
      realtime postdelay;
      realtime elapsed;

      myclass7 mc7;

      $timeformat(-9,     // units_number: -9 is ns
                   5,     // precision_number: number of ints after decimal
                   " ns", // suffix_string: display this suffix after showing realtime value
                   10     // minimum_field_width:
                 );

      $display("Construct and show timescale");
      $printtimescale;
      mc7 = new();

      $display("Delay and show timescale");
      delay = 2.75ns;
      predelay = $realtime;
      #delay;
      postdelay = $realtime;
      elapsed = postdelay - predelay;
      $display("Delay = %t, Actual Delay = %t, Pre = %t, Post = %t", delay, elapsed, predelay, postdelay);

      mc7.dodelay(2.75ns);

      $finish;

   end:testsequence
endprogram:main

Output:

Construct and show timescale
TimeScale of main is 1 ns / 100 ps
TimeScale of pkg2 is 1 us / 1 ns
Delay and show timescale
Delay = 2.75000 ns, Actual Delay = 2.80000 ns, Pre = 0.00000 ns, Post = 2.80000 ns
TimeScale of $unit is 1 ms / 100 us
Delay = 2750.00000 ns, Actual Delay = 2750.00000 ns, Pre = 16800003.80275 ns, Post = 16802753.80275 ns
TimeScale of pkg2 is 1 us / 1 ns

So units only have meaning within the timescale where the delay is defined.  If you pass that realtime (or time) variable to a scope with different timeunits then you won’t be getting the time you expected. And the fix? Apparently you’ll need to make sure that everything in your design has the same timeunits and precision!  Or, at a minimum, ensure that anywhere you are passing time/realtime variables that you are passing them between scopes with the same timeunit.
This is something that should be quickly corrected in the LRM and simulators.

If you use a simulator other than VCS – please give the posted example a try. Let me know if this issue is in other simulators as well.

Complete example with makefile – here.

Another issue – classes and timescale:

A task in a class can have delays – but the LRM doesn’t define how delays are handled in a class. It appears that the only way to set the timescale of a class is with the `timescale compiler directive within the file that contains the class. Further, some classes (like the timer classes provided here) the only delays are specified at construction. (The IDV SystemVerilog Timers have only one # delay – the countdown time. This delay is specified, with units, on construction of the timer. This delay needs to be only as precise as the user constructing the timer requires.) Meaning that the timescale shouldn’t be attached to the class – but to the object that is the constructed class.  In this case it would be preferred that the timescale should be inherited from the constructing parent class / program / module. In the posted example you’ll see that I tried to set the timeunit/timescale of the class at construction with little luck. If you know how to do this – please let me know.

So guess what: `timescale is back!  And with a vengeance!  Don’t be lured into thinking your units are being adhered to…

-`dang

5 Responses to “The Return of `timescale”

  1. hempi Says:

    Competitor Simulator 1:
    # Construct and show timescale
    # Time scale of (main) is 1ns / 100ps
    # Time scale of (pkg2) is 1us / 1ns
    # Delay and show timescale
    # Delay = 2.80000 ns, Actual Delay = 2.80000 ns, Pre = 0.00000 ns, Post = 2.80000 ns
    # Delay = 2800.00000 ns, Actual Delay = 2800.00000 ns, Pre = 2.80000 ns, Post = 2802.80000 ns
    # Time scale of (pkg2) is 1us / 1ns

    Competitor Simulator 2:
    Construct and show timescale
    Time scale of (main) is 1ns / 100ps
    Time scale of (pkg2) is 1us / 1ns
    Delay and show timescale
    Delay = 2.80000 ns, Actual Delay = 2.80000 ns, Pre = 0.00000 ns, Post = 2.80000 ns
    Delay = 2800.00000 ns, Actual Delay = 2800.00000 ns, Pre = 2.80000 ns, Post = 2802.80000 ns
    Time scale of (pkg2) is 1us / 1ns

  2. Sean Says:

    @hempi

    Thanks for the reply!
    It looks like all of the (major) simulators have the same behavior: units are converted to the destination scope units.

    -Sean
    IDV

  3. Petr Says:

    Here is a Mentor statement about this issue:

    The issue about communicating time information across module
    boundaries is independent of `timescale versus timeunit. I responded
    to this issue a while back:

    Communicating time values across multiple timescale domains has been a
    ‘gotcha’ in Verilog since day one. Timescales only apply to scaling of
    literal time values, not values of variables or passed arguments.

    Therefore, you should always use a literal time value in any
    expression involving time to a normalized time unit – 1ns, for example.

    –file with timescale/timeunit X

    time tv;

    tv = 10ns / 1ns; // 10 – regardless of the current timescale

    delay_task(tv); // wait 10 ns
    delay_task(100ms/1ns); // wait 100ms
    ————————–

    — file with timescale/timeunit Y

    task delay_task(time t);
    #(1ns * t); // will scale to the correct time delay endtask

    Even if the language were enhanced to provide ‘raw’ simulation time
    conversion function, both the call site and the callee would have to
    perform the conversion and agree that the timeout argument was in ‘raw’
    time units. That’s no different in terms of complexity/typing than
    agreeing on 1ns, but the latter is already supported in SV.

  4. Sean Says:

    @Petr

    This is similar to the convention that I’ve taken. But rather than pass values as ‘time’ I pass them as either ‘int’ or ‘real’ depending on what I need. I normalize on a per method basis – and comment that method accordingly.

    BUT – the SystemVerilog gotcha is a new one! Since I can now apply the units on assignment it’s important to know that the units are applied on assignment in the current timescape and NOT stored with the value.

    It sure would be nice if the units were stored independently – such that the time could be passed between timescale domains without any unexpected corruption.

    -Sean

  5. arno Says:

    Still today OCT-2010 these issues are not solved …
    IUS92-s026

    Construct and show timescale
    Time scale of (main) is 1ns / 100ps
    Time scale of (worklib.pkg2::myclass7) is 1us / 1ns
    Delay and show timescale
    Delay = 2.80000 ns, Actual Delay = 2.80000 ns, Pre = 0.00000 ns, Post = 2.80000 ns
    Delay = 2800.00000 ns, Actual Delay = 2800.00000 ns, Pre = 2.80000 ns, Post = 2802.80000 ns
    Time scale of (worklib.pkg2::myclass7) is 1us / 1ns
    Simulation complete via $finish(1) at time 2802800 PS + 1
    ./tb.sv:56 $finish;
    ncsim> exit
    TOOL: irun 09.20-s026: Exiting on Oct 19, 2010 at 11:01:23 BST (total: 00:00:19)

Leave a Reply