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.