// //----------------------------------------------------------------------------- // 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 uvm_set_before_get_dap; typedef class uvm_text_recorder; //------------------------------------------------------------------------------ // // CLASS: uvm_tr_stream // // The ~uvm_tr_stream~ base class is a representation of a stream of records // within a . // // 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 // class. // virtual class 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 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 . function uvm_tr_database 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 . function string 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 . function string 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 , 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 call, followed by // on all open recorders within the // stream. function void 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 call, followed by // on all recorders within the stream. function void 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 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 ''", 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 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 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 ( 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 call. If ~do_open_recorder~ // returns a non-~null~ value, then the method will // be called in the recorder. // // Transaction recorders can only be opened if the stream is // ~open~ on the database (per ). Otherwise the // request will be ignored, and ~null~ will be returned. function uvm_recorder 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 s // // The 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 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 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 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 . // // 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 . // // 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 . // // 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 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 // . // // class 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 new(string name="unnamed-uvm_text_tr_stream"); super.new(name); endfunction : new // Group: Implementation Agnostic API // Function: do_open // Callback triggered via . // 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 . 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 . // 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