//----------------------------------------------------------------------
// Copyright 2010-2011 Synopsys, Inc.
// Copyright 2011 Mentor Graphics Corporation
// All Rights Reserved Worldwide
//
// Licensed under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of
// the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in
// writing, software distributed under the License is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See
// the License for the specific language governing
// permissions and limitations under the License.
//----------------------------------------------------------------------
// CLASS: uvm_tlm_time
// Canonical time type that can be used in different timescales
//
// This time type is used to represent time values in a canonical
// form that can bridge initiators and targets located in different
// timescales and time precisions.
//
// For a detailed explanation of the purpose for this class,
// see <Why is this necessary>.
//
class [docs]uvm_tlm_time;
static local real m_resolution = 1.0e-12; // ps by default
local real m_res;
local time m_time; // Number of 'm_res' time units,
local string m_name;
// Function: set_time_resolution
// Set the default canonical time resolution.
//
// Must be a power of 10.
// When co-simulating with SystemC, it is recommended
// that default canonical time resolution be set to the
// SystemC time resolution.
//
// By default, the default resolution is 1.0e-12 (ps)
//
static function void [docs]set_time_resolution(real res);
// Actually, it does not *really* need to be a power of 10.
m_resolution = res;
endfunction
// Function: new
// Create a new canonical time value.
//
// The new value is initialized to 0.
// If a resolution is not specified,
// the default resolution,
// as specified by <set_time_resolution()>,
// is used.
function [docs]new(string name = "uvm_tlm_time", real res = 0);
m_name = name;
m_res = (res == 0) ? m_resolution : res;
reset();
endfunction
// Function: get_name
// Return the name of this instance
//
function string [docs]get_name();
return m_name;
endfunction
// Function: reset
// Reset the value to 0
function void [docs]reset();
m_time = 0;
endfunction
// Scale a timescaled value to 'm_res' units,
// the specified scale
local function real to_m_res(real t, time scaled, real secs);
// ToDo: Check resolution
return t/real'(scaled) * (secs/m_res);
endfunction
// Function: get_realtime
// Return the current canonical time value,
// scaled for the caller's timescale
//
// ~scaled~ must be a time literal value that corresponds
// to the number of seconds specified in ~secs~ (1ns by default).
// It must be a time literal value that is greater or equal
// to the current timescale.
//
//| #(delay.get_realtime(1ns));
//| #(delay.get_realtime(1fs, 1.0e-15));
//
function real [docs]get_realtime(time scaled, real secs = 1.0e-9);
return m_time*real'(scaled) * m_res/secs;
endfunction
// Function: incr
// Increment the time value by the specified number of scaled time unit
//
// ~t~ is a time value expressed in the scale and precision
// of the caller.
// ~scaled~ must be a time literal value that corresponds
// to the number of seconds specified in ~secs~ (1ns by default).
// It must be a time literal value that is greater or equal
// to the current timescale.
//
//| delay.incr(1.5ns, 1ns);
//| delay.incr(1.5ns, 1ps, 1.0e-12);
//
function void [docs]incr(real t, time scaled, real secs = 1.0e-9);
if (t < 0.0) begin
`uvm_error("UVM/TLM/TIMENEG", {"Cannot increment uvm_tlm_time variable ", m_name, " by a negative value"});
return;
end
if (scaled == 0) begin
`uvm_fatal("UVM/TLM/BADSCALE",
"uvm_tlm_time::incr() called with a scaled time literal that is smaller than the current timescale")
end
m_time += to_m_res(t, scaled, secs);
endfunction
// Function: decr
// Decrement the time value by the specified number of scaled time unit
//
// ~t~ is a time value expressed in the scale and precision
// of the caller.
// ~scaled~ must be a time literal value that corresponds
// to the number of seconds specified in ~secs~ (1ns by default).
// It must be a time literal value that is greater or equal
// to the current timescale.
//
//| delay.decr(200ps, 1ns);
//
function void [docs]decr(real t, time scaled, real secs);
if (t < 0.0) begin
`uvm_error("UVM/TLM/TIMENEG", {"Cannot decrement uvm_tlm_time variable ", m_name, " by a negative value"});
return;
end
if (scaled == 0) begin
`uvm_fatal("UVM/TLM/BADSCALE",
"uvm_tlm_time::decr() called with a scaled time literal that is smaller than the current timescale")
end
m_time -= to_m_res(t, scaled, secs);
if (m_time < 0.0) begin
`uvm_error("UVM/TLM/TOODECR", {"Cannot decrement uvm_tlm_time variable ", m_name, " to a negative value"});
reset();
end
endfunction
// Function: get_abstime
// Return the current canonical time value,
// in the number of specified time unit, regardless of the
// current timescale of the caller.
//
// ~secs~ is the number of seconds in the desired time unit
// e.g. 1e-9 for nanoseconds.
//
//| $write("%.3f ps\n", delay.get_abstime(1e-12));
//
function real [docs]get_abstime(real secs);
return m_time*m_res/secs;
endfunction
// Function: set_abstime
// Set the current canonical time value,
// to the number of specified time unit, regardless of the
// current timescale of the caller.
//
// ~secs~ is the number of seconds in the time unit in the value ~t~
// e.g. 1e-9 for nanoseconds.
//
//| delay.set_abstime(1.5, 1e-12));
//
function void [docs]set_abstime(real t, real secs);
m_time = t*secs/m_res;
endfunction
endclass
// Group: Why is this necessary
//
// Integers are not sufficient, on their own,
// to represent time without any ambiguity:
// you need to know the scale of that integer value.
// That scale is information conveyed outside of that integer.
// In SystemVerilog, it is based on the timescale
// that was active when the code was compiled.
// SystemVerilog properly scales time literals, but not integer values.
// That's because it does not know the difference between an integer
// that carries an integer value and an integer that carries a time value.
// The 'time' variables are simply 64-bit integers,
// they are not scaled back and forth to the underlying precision.
//
//| `timescale 1ns/1ps
//|
//| module m();
//|
//| time t;
//|
//| initial
//| begin
//| #1.5;
//| $write("T=%f ns (1.5)\n", $realtime());
//| t = 1.5;
//| #t;
//| $write("T=%f ns (3.0)\n", $realtime());
//| #10ps;
//| $write("T=%f ns (3.010)\n", $realtime());
//| t = 10ps;
//| #t;
//| $write("T=%f ns (3.020)\n", $realtime());
//| end
//| endmodule
//
// yields
//
//| T=1.500000 ns (1.5)
//| T=3.500000 ns (3.0)
//| T=3.510000 ns (3.010)
//| T=3.510000 ns (3.020)
//
// Within SystemVerilog, we have to worry about
// - different time scale
// - different time precision
//
// Because each endpoint in a socket
// could be coded in different packages
// and thus be executing under different timescale directives,
// a simple integer cannot be used to exchange time information
// across a socket.
//
// For example
//
//| `timescale 1ns/1ps
//|
//| package a_pkg;
//|
//| class a;
//| function void f(inout time t);
//| t += 10ns;
//| endfunction
//| endclass
//|
//| endpackage
//|
//|
//| `timescale 1ps/1ps
//|
//| program p;
//|
//| import a_pkg::*;
//|
//| time t;
//|
//| initial
//| begin
//| a A = new;
//| A.f(t);
//| #t;
//| $write("T=%0d ps (10,000)\n", $realtime());
//| end
//| endprogram
//
// yields
//
//| T=10 ps (10,000)
//
// Scaling is needed every time you make a procedural call
// to code that may interpret a time value in a different timescale.
//
// Using the uvm_tlm_time type
//
//| `timescale 1ns/1ps
//|
//| package a_pkg;
//|
//| import uvm_pkg::*;
//|
//| class a;
//| function void f(uvm_tlm_time t);
//| t.incr(10ns, 1ns);
//| endfunction
//| endclass
//|
//| endpackage
//|
//|
//| `timescale 1ps/1ps
//|
//| program p;
//|
//| import uvm_pkg::*;
//| import a_pkg::*;
//|
//| uvm_tlm_time t = new;
//|
//| initial
//| begin
//| a A = new;
//| A.f(t);
//| #(t.get_realtime(1ns));
//| $write("T=%0d ps (10,000)\n", $realtime());
//| end
//| endprogram
//
// yields
//
//| T=10000 ps (10,000)
//
// A similar procedure is required when crossing any simulator
// or language boundary,
// such as interfacing between SystemVerilog and SystemC.