SystemVerilog timescale Across Classes Illustrated

This is the first post in a series dealing with some ideas on working with the effects of the timescale directive in SystemVerilog.  We will start with the example and use it as the way to talk about why timescale can cause undesired behavior.

The follow up to the post is "The delay_ps task Solution to Passing Delays".

delay_realtime task


 `timescale 1ps/1ps
 // `timescale 1fs/1fs

 package shared;

 class helper;

     static task delay_realtime(realtime delay);
         real t0, t1;
         t0 = $realtime;
        
         $display("delay_realtime(%g)", delay);
         $printtimescale;
         #(delay);

         t1 = $realtime;
         if (t0 == t1) $error("%m timescale not precise enough");

     endtask // delay_realtime

 endclass // helper
    
 endpackage // shared

The above code defines a SystemVerilog package named shared and defines a class named helper and one static task named delay_realtime.  The intention is that this code could be used as a helper function from anywhere, and across classes, to insert delays.  The argument to delay_realtime  is of type realtime, which is how much time this task is supposed to wait.

There is a line at the top where we define the `timescale to be "1ps/1ps"; we will be altering this `timescale to illustrate what it does to task behavior.  We also have a single-line-check to make sure we inserted some measurable delay.  We will also see when we try to delay for a period smaller than timescale allows.

Now to the test itself.

Example Test of delay_realtime


 `timescale 1ps/1ps
 // `timescale 1fs/1fs

 module tb ();

     task print_time();
         $display("\n%f is tb time\n", $realtime);
     endtask
    
     initial begin

         $display("\n");
         $printtimescale;
         $display("\n");

         print_time();
         #2ps; $display("delay 2ps"); print_time();
         shared::helper::delay_realtime(2ps); print_time();
         shared::helper::delay_realtime(2fs); print_time();
         #2fs; $display("delay 2fs"); print_time();
        
         $finish();
        
     end
    
 endmodule

This sets up a module named tb that allows us to evaluate what happens when we use our delay_realtime task with different combinations of `timescale between the two files.  There is a very useful function called $printtimescale that is used in both files that will help us understand what `timescale is being used during each call.

Output 1: tb 1ps/1ps with shared 1ps/1ps


 Time scale of (tb) is  1ps /  1ps



 0.000000 is tb time
 
 delay 2ps
 
 2.000000 is tb time
 
 delay_realtime(2)
 Time scale of (shared.helper.delay_realtime) is  1ps /  1ps
 
 4.000000 is tb time
 
 delay_realtime(0)
 Time scale of (shared.helper.delay_realtime) is  1ps /  1ps
 ** Error: shared.helper.delay_realtime timescale not precise enough
    Time: 4 ps  Scope: shared.helper.delay_realtime File: delay_realtime.sv Line: 17
 
 4.000000 is tb time
 
 delay 2fs
 
 4.000000 is tb time

PASSED #2ps
PASSED delay_realtime(2ps)
FAILED delay_realtime(2fs) - error not enough precision
FAILED #2fs- not enough precision silent error

Output 2: tb 1ps/1ps with shared 1fs/1fs


 Time scale of (tb) is  1ps /  1ps
 
 
 
 0.000000 is tb time
 
 delay 2ps
 
 2.000000 is tb time
 
 delay_realtime(2)
 Time scale of (shared.helper.delay_realtime) is  1fs /  1fs
 
 2.002000 is tb time
 
 delay_realtime(0)
 Time scale of (shared.helper.delay_realtime) is  1fs /  1fs
 ** Error: shared.helper.delay_realtime timescale not precise enough
    Time: 2002 fs  Scope: shared.helper.delay_realtime File: delay_realtime.sv Line: 17
 
 2.002000 is tb time
 
 delay 2fs
 
 2.004000 is tb time

PASSED #2ps
FAILED delay_realtime(2ps) - improperly interpreted as 2fs delay
FAILED delay_realtime(2fs) - error not enough precision
PASSED #2fs 

Output 3: tb 1fs/1fs with shared 1ps/1ps


 Time scale of (tb) is  1fs /  1fs
 
 
 
 0.000000 is tb time
 
 delay 2ps
 
 2000.000000 is tb time
 
 delay_realtime(2000)
 Time scale of (shared.helper.delay_realtime) is  1ps /  1ps
 
 2002000.000000 is tb time
 
 delay_realtime(2)
 Time scale of (shared.helper.delay_realtime) is  1ps /  1ps
 
 2004000.000000 is tb time
 
 delay 2fs
 
 2004002.000000 is tb time

PASSED #2ps
FAILED delay_realtime(2ps) - improperly interpreted as 2us
PASSED delay_realtime(2fs) - improperly interpreted as 2ps
PASSED #2fs

Conclusion

Mixing of `timescale(s) causes problems if you are trying to pass delays as realtime types.  The realtime type was never meant to be treated this way.  realtime is actually a standard real type but with only local knowledge of the meaning of the units of time.  When you pass a realtime value into a class in a different package all you get is that real type value and no units.  Take the example below.


 `timescale 1fs/1fs
 
 ...
 
         shared::helper::delay_realtime(2ps); print_time();
 
 ...

 endmodule

Before it the test sends the "2ps" to the delay_realtime task the simulator will say to itself "Ah, you want to send 2ps to that task, but my timescale is 1fs.  That means I'll take 2ps/1fs and convert that to a standard real number which is 2000 and pass that through."  If you go back to the Output 3, you will see this happening.

This illustration shows mixing of timescales and passing realtime values requires careful attention.