//
//-----------------------------------------------------------------------------
// Copyright 2007-2011 Mentor Graphics Corporation
// Copyright 2007-2011 Cadence Design Systems, Inc.
// Copyright 2010 Synopsys, Inc.
// Copyright 2013 NVIDIA 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.
//-----------------------------------------------------------------------------
//------------------------------------------------------------------------------
// File: Transaction Recording Streams
//
// class- m_uvm_tr_stream_cfg
// Undocumented helper class for storing stream
// initialization values.
class m_uvm_tr_stream_cfg;
uvm_tr_database db;
string scope;
string stream_type_name;
endclass : m_uvm_tr_stream_cfg
typedef class [docs]uvm_set_before_get_dap;
typedef class [docs]uvm_text_recorder;
//------------------------------------------------------------------------------
//
// CLASS: uvm_tr_stream
//
// The ~uvm_tr_stream~ base class is a representation of a stream of records
// within a <uvm_tr_database>.
//
// The record stream is intended to hide the underlying database implementation
// from the end user, as these details are often vendor or tool-specific.
//
// The ~uvm_tr_stream~ class is pure virtual, and must be extended with an
// implementation. A default text-based implementation is provided via the
// <uvm_text_tr_stream> class.
//
virtual class [docs]uvm_tr_stream extends uvm_object;
// Variable- m_cfg_dap
// Data access protected reference to the DB
local uvm_set_before_get_dap#(m_uvm_tr_stream_cfg) m_cfg_dap;
// Variable- m_records
// Active records in the stream (active == open or closed)
local bit m_records[uvm_recorder];
// Variable- m_warn_null_cfg
// Used to limit the number of warnings
local bit m_warn_null_cfg;
// Variable- m_is_opened
// Used to indicate stream is open
local bit m_is_opened;
// Variable- m_is_closed
// Used to indicate stream is closed
local bit m_is_closed;
// !m_is_opened && !m_is_closed == m_is_freed
// Function: new
// Constructor
//
// Parameters:
// name - Stream instance name
function [docs]new(string name="unnamed-uvm_tr_stream");
super.new(name);
m_cfg_dap = new("cfg_dap");
endfunction : new
// Variable- m_ids_by_stream
// An associative array of integers, indexed by uvm_tr_streams. This
// provides a unique 'id' or 'handle' for each stream, which can be
// used to identify the stream.
//
// By default, neither ~m_ids_by_stream~ or ~m_streams_by_id~ are
// used. Streams are only placed in the arrays when the user
// attempts to determine the id for a stream.
local static integer m_ids_by_stream[uvm_tr_stream];
// Group: Configuration API
// Function: get_db
// Returns a reference to the database which contains this
// stream.
//
// A warning will be asserted if get_db is called prior to
// the stream being initialized via <do_open>.
function uvm_tr_database [docs]get_db();
m_uvm_tr_stream_cfg m_cfg;
if (!m_cfg_dap.try_get(m_cfg)) begin
if (m_warn_null_cfg == 1)
`uvm_warning("UVM/REC_STR/NO_CFG",
$sformatf("attempt to retrieve DB from '%s' before it was set!",
get_name()))
m_warn_null_cfg = 0;
return null;
end
return m_cfg.db;
endfunction : get_db
// Function: get_scope
// Returns the ~scope~ supplied when opening this stream.
//
// A warning will be asserted if get_scope is called prior to
// the stream being initialized via <do_open>.
function string [docs]get_scope();
m_uvm_tr_stream_cfg m_cfg;
if (!m_cfg_dap.try_get(m_cfg)) begin
if (m_warn_null_cfg == 1)
`uvm_warning("UVM/REC_STR/NO_CFG",
$sformatf("attempt to retrieve scope from '%s' before it was set!",
get_name()))
m_warn_null_cfg = 0;
return "";
end
return m_cfg.scope;
endfunction : get_scope
// Function: get_stream_type_name
// Returns a reference to the database which contains this
// stream.
//
// A warning will be asserted if get_stream_type_name is called prior to
// the stream being initialized via <do_open>.
function string [docs]get_stream_type_name();
m_uvm_tr_stream_cfg m_cfg;
if (!m_cfg_dap.try_get(m_cfg)) begin
if (m_warn_null_cfg == 1)
`uvm_warning("UVM/REC_STR/NO_CFG",
$sformatf("attempt to retrieve STREAM_TYPE_NAME from '%s' before it was set!",
get_name()))
m_warn_null_cfg = 0;
return "";
end
return m_cfg.stream_type_name;
endfunction : get_stream_type_name
// Group: Stream API
//
// Once a stream has been opened via <uvm_tr_database::open_stream>, the user
// can ~close~ the stream.
//
// Due to the fact that many database implementations will require crossing
// a language boundary, an additional step of ~freeing~ the stream is required.
//
// A ~link~ can be established within the database any time between "Open" and
// "Free", however it is illegal to establish a link after "Freeing" the stream.
//
// Function: close
// Closes this stream.
//
// Closing a stream closes all open recorders in the stream.
//
// This method will trigger a <do_close> call, followed by
// <uvm_recorder::close> on all open recorders within the
// stream.
function void [docs]close();
if (!is_open())
return;
do_close();
foreach (m_records[idx])
if (idx.is_open())
idx.close();
m_is_opened = 0;
m_is_closed = 1;
endfunction : close
// Function: free
// Frees this stream.
//
// Freeing a stream indicates that the database can free any
// references to the stream (including references to records
// within the stream).
//
// This method will trigger a <do_free> call, followed by
// <uvm_recorder::free> on all recorders within the stream.
function void [docs]free();
process p;
string s;
uvm_tr_database db;
if (!is_open() && !is_closed())
return;
if (is_open())
close();
do_free();
foreach (m_records[idx])
idx.free();
// Clear out internal state
db = get_db();
m_is_closed = 0;
p = process::self();
if(p != null)
s = p.get_randstate();
m_cfg_dap = new("cfg_dap");
if(p != null)
p.set_randstate(s);
m_warn_null_cfg = 1;
if (m_ids_by_stream.exists(this))
m_free_id(m_ids_by_stream[this]);
// Clear out DB state
if (db != null)
db.m_free_stream(this);
endfunction : free
// Function- m_do_open
// Initializes the state of the stream
//
// Parameters-
// db - Database which the stream belongs to
// scope - Optional scope
// stream_type_name - Optional type name for the stream
//
// This method will trigger a <do_open> call.
//
// An error will be asserted if-
// - m_do_open is called more than once without the stream
// being ~freed~ between.
// - m_do_open is passed a ~null~ db
function void m_do_open(uvm_tr_database db,
string scope="",
string stream_type_name="");
m_uvm_tr_stream_cfg m_cfg;
uvm_tr_database m_db;
if (db == null) begin
`uvm_error("UVM/REC_STR/NULL_DB",
$sformatf("Illegal attempt to set DB for '%s' to '<null>'",
this.get_full_name()))
return;
end
if (m_cfg_dap.try_get(m_cfg)) begin
`uvm_error("UVM/REC_STR/RE_CFG",
$sformatf("Illegal attempt to re-open '%s'",
this.get_full_name()))
end
else begin
// Never set before
m_cfg = new();
m_cfg.db = db;
m_cfg.scope = scope;
m_cfg.stream_type_name = stream_type_name;
m_cfg_dap.set(m_cfg);
m_is_opened = 1;
do_open(db, scope, stream_type_name);
end
endfunction : m_do_open
// Function: is_open
// Returns true if this ~uvm_tr_stream~ was opened on the database,
// but has not yet been closed.
//
function bit [docs]is_open();
return m_is_opened;
endfunction : is_open
// Function: is_closed
// Returns true if this ~uvm_tr_stream~ was closed on the database,
// but has not yet been freed.
//
function bit [docs]is_closed();
return m_is_closed;
endfunction : is_closed
// Group: Transaction Recorder API
//
// New recorders can be opened prior to the stream being ~closed~.
//
// Once a stream has been closed, requests to open a new recorder
// will be ignored (<open_recorder> will return ~null~).
//
// Function: open_recorder
// Marks the opening of a new transaction recorder on the stream.
//
// Parameters:
// name - A name for the new transaction
// open_time - Optional time to record as the opening of this transaction
// type_name - Optional type name for the transaction
//
// If ~open_time~ is omitted (or set to 0), then the stream will use
// the current time.
//
// This method will trigger a <do_open_recorder> call. If ~do_open_recorder~
// returns a non-~null~ value, then the <uvm_recorder::do_open> method will
// be called in the recorder.
//
// Transaction recorders can only be opened if the stream is
// ~open~ on the database (per <is_open>). Otherwise the
// request will be ignored, and ~null~ will be returned.
function uvm_recorder [docs]open_recorder(string name,
time open_time = 0,
string type_name="");
time m_time = (open_time == 0) ? $time : open_time;
// Check to make sure we're open
if (!is_open())
return null;
else begin
process p = process::self();
string s;
if (p != null)
s = p.get_randstate();
open_recorder = do_open_recorder(name,
m_time,
type_name);
if (open_recorder != null) begin
m_records[open_recorder] = 1;
open_recorder.m_do_open(this, m_time, type_name);
end
if (p != null)
p.set_randstate(s);
end
endfunction : open_recorder
// Function- m_free_recorder
// Removes recorder from the internal array
function void m_free_recorder(uvm_recorder recorder);
if (m_records.exists(recorder))
m_records.delete(recorder);
endfunction : m_free_recorder
// Function: get_recorders
// Provides a queue of all transactions within the stream.
//
// Parameters:
// q - A reference to the queue of <uvm_recorder>s
//
// The <get_recorders> method returns the size of the queue,
// such that the user can conditionally process the elements.
//
// | uvm_recorder tr_q[$];
// | if (my_stream.get_recorders(tr_q)) begin
// | // Process the queue...
// | end
//
function unsigned [docs]get_recorders(ref uvm_recorder q[$]);
// Clear out the queue first...
q.delete();
// Fill in the values
foreach (m_records[idx])
q.push_back(idx);
// Finally return the size of the queue
return q.size();
endfunction : get_recorders
// Group: Handles
// Variable- m_streams_by_id
// A corollary to ~m_ids_by_stream~, this indexes the streams by their
// unique ids.
local static uvm_tr_stream m_streams_by_id[integer];
// Function: get_handle
// Returns a unique ID for this stream.
//
// A value of ~0~ indicates that the recorder has been ~freed~,
// and no longer has a valid ID.
//
function integer [docs]get_handle();
if (!is_open() && !is_closed()) begin
return 0;
end
else begin
integer handle = get_inst_id();
// Check for the weird case where our handle changed.
if (m_ids_by_stream.exists(this) && m_ids_by_stream[this] != handle)
m_streams_by_id.delete(m_ids_by_stream[this]);
m_streams_by_id[handle] = this;
m_ids_by_stream[this] = handle;
return handle;
end
endfunction : get_handle
// Function- m_get_handle
// Provided to allow implementation-specific handles which are not
// identical to the built-in handles.
//
// This is an implementation detail of the UVM library, which allows
// for vendors to (optionally) put vendor-specific methods into the library.
virtual function integer m_get_handle();
return get_handle();
endfunction : m_get_handle
// Function: get_stream_from_handle
// Static accessor, returns a stream reference for a given unique id.
//
// If no stream exists with the given ~id~, or if the
// stream with that ~id~ has been freed, then ~null~ is
// returned.
//
static function uvm_tr_stream [docs]get_stream_from_handle(integer id);
if (id == 0)
return null;
if ($isunknown(id) || !m_streams_by_id.exists(id))
return null;
return m_streams_by_id[id];
endfunction : get_stream_from_handle
// Function- m_free_id
// Frees the id/stream link (memory cleanup)
//
static function void m_free_id(integer id);
uvm_tr_stream stream;
if (!$isunknown(id) && m_streams_by_id.exists(id))
stream = m_streams_by_id[id];
if (stream != null) begin
m_streams_by_id.delete(id);
m_ids_by_stream.delete(stream);
end
endfunction : m_free_id
// Group: Implementation Agnostic API
//
// Function: do_open
// Callback triggered via <uvm_tr_database::open_stream>.
//
// Parameters:
// db - Database which the stream belongs to
// scope - Optional scope
// stream_type_name - Optional type name for the stream
//
// The ~do_open~ callback can be used to initialize any internal
// state within the stream, as well as providing a location to
// record any initial information about the stream.
protected virtual function void do_open(uvm_tr_database db,
string scope,
string stream_type_name);
endfunction : do_open
// Function: do_close
// Callback triggered via <close>.
//
// The ~do_close~ callback can be used to set internal state
// within the stream, as well as providing a location to
// record any closing information.
protected virtual function void do_close();
endfunction : do_close
// Function: do_free
// Callback triggered via <free>.
//
// The ~do_free~ callback can be used to release the internal
// state within the stream, as well as providing a location
// to record any "freeing" information.
protected virtual function void do_free();
endfunction : do_free
// Function: do_open_recorder
// Marks the beginning of a new record in the stream.
//
// Backend implementation of <open_recorder>
protected virtual function uvm_recorder do_open_recorder(string name,
time open_time,
string type_name);
return null;
endfunction : do_open_recorder
endclass : uvm_tr_stream
//------------------------------------------------------------------------------
//
// CLASS: uvm_text_tr_stream
//
// The ~uvm_text_tr_stream~ is the default stream implementation for the
// <uvm_text_tr_database>.
//
//
class [docs]uvm_text_tr_stream extends uvm_tr_stream;
// Variable- m_text_db
// Internal reference to the text-based backend
local uvm_text_tr_database m_text_db;
`uvm_object_utils_begin(uvm_text_tr_stream)
`uvm_object_utils_end
// Function: new
// Constructor
//
// Parameters:
// name - Instance name
function [docs]new(string name="unnamed-uvm_text_tr_stream");
super.new(name);
endfunction : new
// Group: Implementation Agnostic API
// Function: do_open
// Callback triggered via <uvm_tr_database::open_stream>.
//
protected virtual function void do_open(uvm_tr_database db,
string scope,
string stream_type_name);
$cast(m_text_db, db);
if (m_text_db.open_db())
$fdisplay(m_text_db.m_file,
" CREATE_STREAM @%0t {NAME:%s T:%s SCOPE:%s STREAM:%0d}",
$time,
this.get_name(),
stream_type_name,
scope,
this.get_handle());
endfunction : do_open
// Function: do_close
// Callback triggered via <uvm_tr_stream::close>.
protected virtual function void do_close();
if (m_text_db.open_db())
$fdisplay(m_text_db.m_file,
" CLOSE_STREAM @%0t {NAME:%s T:%s SCOPE:%s STREAM:%0d}",
$time,
this.get_name(),
this.get_stream_type_name(),
this.get_scope(),
this.get_handle());
endfunction : do_close
// Function: do_free
// Callback triggered via <uvm_tr_stream::free>.
//
protected virtual function void do_free();
if (m_text_db.open_db())
$fdisplay(m_text_db.m_file,
" FREE_STREAM @%0t {NAME:%s T:%s SCOPE:%s STREAM:%0d}",
$time,
this.get_name(),
this.get_stream_type_name(),
this.get_scope(),
this.get_handle());
m_text_db = null;
return;
endfunction : do_free
// Function: do_open_recorder
// Marks the beginning of a new record in the stream
//
// Text-backend specific implementation.
protected virtual function uvm_recorder do_open_recorder(string name,
time open_time,
string type_name);
if (m_text_db.open_db()) begin
return uvm_text_recorder::type_id::create(name);
end
return null;
endfunction : do_open_recorder
endclass : uvm_text_tr_stream