This post will help you to understand the difference between real, realtime and shortreal data types of SystemVerilog and its usage.
We faced some issue with real and realtime variable while writing a timing check.
Below is a simplified example of that check.
`timescale 1ns/1fs;
module test;
real a,b;
realtime t1, t2;
initial
begin
#1ns;
t1 = $realtime;
#1.8ns;
t2 = $realtime;
b = 1.8;
a = t2 - t1;
if(a == b)
$display("PASS a = %f b = %f", a,b);
else
$display("FAIL a = %f b = %f", a,b);
end
endmodule
and here is what we got the display
FAIL a = 1.800000 b = 1.800000
How that happened !!! is really 1.800000 != 1.800000 !!!!
Now let's try something else, instead of using real we use shortreal
`timescale 1ns/1fs;
module test;
shortreal a,b;
realtime t1, t2;
initial
begin
#1ns;
t1 = $realtime;
#1.8ns;
t2 = $realtime;
b = 1.8;
a = t2 - t1;
if(a == b)
$display("PASS a = %f b = %f", a,b);
else
$display("FAIL a = %f b = %f", a,b);
end
endmodule
Now the result was as expected !!
PASS a = 1.800000 b = 1.800000
To understand this lets go to SystemVerilog LRM. As per LRM
The real data type is from Verilog-2001, and is the same as a C double.
The shortreal data type is a SystemVerilog data type, and is the same as a C float.
So float is 32 bit data type and double is 64 bit data type. This sounds cool but still how 1.800000 != 1.800000
No ! it's not only about being 32 or 64 bit data type its more about precision."Precision is the main difference where float is a single precision (32 bit) floating point data type, double is a double precision (64 bit) floating point data type ".
To understand this difference let's go beyond and print the values with more number of digits after decimal point.
In below example we have used both real and shortreal to see the difference.
`timescale 1ns/1fs;
module test;
real a,b;
shortreal c,d;
realtime t1, t2, t3, t4;
initial
begin
#1ns;
t1 = $realtime;
#1.8ns;
t2 = $realtime;
b = 1.8;
a = t2-t1;
if(a == b)
$display("Case1: PASS \na = %1.100f \nb = %1.100f", a,b);
else
$display("Case1: FAIL \na = %1.100f \nb = %1.100f", a,b);
end
initial
begin
#1ns;
t3 = $realtime;
#1.8ns;
t4 = $realtime;
d = 1.8;
c = t2-t1;
if(c == d)
$display("Case2: PASS \nc = %1.100f \nd = %1.100f", c,d);
else
$display("Case2: FAIL \nc = %1.100f \nd = %1.100f", c,d);
end
endmodule
Here is what the display is
Case1: FAIL
a = 1.7999999999999998223643160599749535322189331054687500000000000000000000000000000000000000000000000000
b = 1.8000000000000000444089209850062616169452667236328125000000000000000000000000000000000000000000000000
Case2: PASS
c = 1.7999999523162841796875000000000000000000000000000000000000000000000000000000000000000000000000000000
d = 1.7999999523162841796875000000000000000000000000000000000000000000000000000000000000000000000000000000
It's clearly seen that the 64 bit real variable has more precision that that or 32 bit shortreal variable.
Here one more thing to consider is that we are taking difference of time. Floating point math is not exact. Simple values like 0.1 cannot be precisely represented using binary floating point numbers, and the limited precision of floating point numbers means that slight changes in the order of operations or the precision of intermediates can change the result. That means that comparing two floats to see if they are equal is usually not what you want.
The things will not matter much if you are doing calculations in nanoseconds so we suggest to use shortreal instead of real.
Thank you for this great example. Im new to systemverilog and was looking for such a concrete explanation. Normally when you need to compare to floating point numbers, you should consider comparing if their difference is greater/less than a certain machine/standard specific epsilon value (eps).
ReplyDeleteChristian