//----------------------------------------------------------------------
// Copyright 2018 Cadence Design Systems, Inc.
// Copyright 2017-2018 Cisco Systems, Inc.
// Copyright 2021-2023 Marvell International Ltd.
// Copyright 2018-2022 NVIDIA Corporation
// Copyright 2017-2018 Verific
//   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.
//----------------------------------------------------------------------

//----------------------------------------------------------------------
// Title -- NODOCS -- Resources
//
// Topic: Intro
//
// A resource is a parameterized container that holds arbitrary data.
// Resources can be used to configure components, supply data to
// sequences, or enable sharing of information across disparate parts of
// a testbench.  They are stored using scoping information so their
// visibility can be constrained to certain parts of the testbench.
// Resource containers can hold any type of data, constrained only by
// the data types available in SystemVerilog.  Resources can contain
// scalar objects, class handles, queues, lists, or even virtual
// interfaces.
//
// Resources are stored in a resource database so that each resource can
// be retrieved by name or by type. The database has both a name table
// and a type table and each resource is entered into both. The database
// is globally accessible.
//
// Each resource has a set of scopes over which it is visible.  The set
// of scopes is represented as a regular expression.  When a resource is
// looked up the scope of the entity doing the looking up is supplied to
// the lookup function.  This is called the ~current scope~.  If the
// current scope is in the set of scopes over which a resource is
// visible then the resource can be retuned in the lookup.
//
// Resources can be looked up by name or by type. To support type lookup
// each resource has a static type handle that uniquely identifies the
// type of each specialized resource container.
//
// Multiple resources that have the same name are stored in a queue.
// Each resource is pushed into a queue with the first one at the front
// of the queue and each subsequent one behind it.  The same happens for
// multiple resources that have the same type.  The resource queues are
// searched front to back, so those placed earlier in the queue have
// precedence over those placed later.
//
// The precedence of resources with the same name or same type can be
// altered.  One way is to set the ~precedence~ member of the resource
// container to any arbitrary value.  The search algorithm will return
// the resource with the highest precedence.  In the case where there
// are multiple resources that match the search criteria and have the
// same (highest) precedence, the earliest one located in the queue will
// be one returned.  Another way to change the precedence is to use the
// set_priority function to move a resource to either the front or back
// of the queue.
//
// The classes defined here form the low level layer of the resource
// database.  The classes include the resource container and the database
// that holds the containers.  The following set of classes are defined
// here:
//
// <uvm_resource_types>: A class without methods or members, only
// typedefs and enums. These types and enums are used throughout the
// resources facility.  Putting the types in a class keeps them confined
// to a specific name space.
//
// <uvm_resource_options>: policy class for setting options, such
// as auditing, which effect resources.
//
// <uvm_resource_base>: the base (untyped) resource class living in the
// resource database.  This class includes the interface for setting a
// resource as read-only, notification, scope management, altering
// search priority, and managing auditing.
//
//----------------------------------------------------------------------

typedef class uvm_resource_base; // forward reference

//----------------------------------------------------------------------
// Class -- NODOCS -- uvm_resource_types
//
// Provides typedefs and enums used throughout the resources facility.
// This class has no members or methods, only typedefs.  It's used in
// lieu of package-scope types.  When needed, other classes can use
// these types by prefixing their usage with uvm_resource_types::.  E.g.
//
//|  uvm_resource_types::rsrc_q_t queue;
//
//----------------------------------------------------------------------
class uvm_resource_types;

  // types uses for setting overrides
  typedef bit[1:0] override_t;
  typedef enum override_t { TYPE_OVERRIDE = 2'b01,
                            NAME_OVERRIDE = 2'b10 } override_e;

  // general purpose queue of resource
  typedef uvm_queue#(uvm_resource_base) rsrc_q_t;

  // SV queue of resources
  typedef uvm_resource_base rsrc_sv_q_t[$];
  typedef uvm_shared#(rsrc_sv_q_t) rsrc_shared_q_t;

  // enum for setting resource search priority
  typedef enum { PRI_HIGH, PRI_LOW } priority_e;

  // access record for resources.  A set of these is stored for each
  // resource by accessing object.  It's updated for each read/write.
  typedef struct
  {
    time read_time;
    time write_time;
    int unsigned read_count;
    int unsigned write_count;
  } access_t;

endclass

//----------------------------------------------------------------------
// Class -- NODOCS -- uvm_resource_options
//
// Provides a namespace for managing options for the
// resources facility.  The only thing allowed in this class is static
// local data members and static functions for manipulating and
// retrieving the value of the data members.  The static local data
// members represent options and settings that control the behavior of
// the resources facility.

// Options include:
//
//  * auditing:  on/off
//
//    The default for auditing is on.  You may wish to turn it off to
//    for performance reasons.  With auditing off memory is not
//    consumed for storage of auditing information and time is not
//    spent collecting and storing auditing information.  Of course,
//    during the period when auditing is off no audit trail information
//    is available
//
//----------------------------------------------------------------------
class uvm_resource_options;

  static local bit auditing = 1;

  // Function -- NODOCS -- turn_on_auditing
  //
  // Turn auditing on for the resource database. This causes all
  // reads and writes to the database to store information about
  // the accesses. Auditing is turned on by default.

  static function void turn_on_auditing();
    auditing = 1;
  endfunction

  // Function -- NODOCS -- turn_off_auditing
  //
  // Turn auditing off for the resource database. If auditing is turned off,
  // it is not possible to get extra information about resource
  // database accesses.

  static function void turn_off_auditing();
    auditing = 0;
  endfunction

  // Function -- NODOCS -- is_auditing
  //
  // Returns 1 if the auditing facility is on and 0 if it is off.

  static function bit is_auditing();
    return auditing;
  endfunction
endclass

//----------------------------------------------------------------------
// Title: Resource Base Classes
//

//----------------------------------------------------------------------
// Class: uvm_resource_debug
//
// Class provided to facilitate overriding of debug functions
// in the parameterized uvm_resource class.
//
// @uvm-accellera The details of this API are specific to the Accellera implementation and are not being considered for contribution to 1800.2
class uvm_resource_debug extends uvm_object ;

  uvm_resource_types::access_t access[string];

  `uvm_object_utils(uvm_resource_debug)

  function new(string name = "");
    super.new(name);
  endfunction

  // Function: record_read_access
  //
  // Record the read access information for this resource for debug purposes.
  // This information is used by <print_accessors> function.
  //
  // @uvm-accellera The details of this API are specific to the Accellera implementation, and are not being considered for contribution to 1800.2

  virtual function void record_read_access(uvm_object accessor = null);

    string str;
    uvm_resource_types::access_t access_record;

    // If an accessor is supplied, then use its name
	// as the database entry for the accessor record.
	// Otherwise, use "<empty>" as the database entry.
    if(accessor != null)
      str = accessor.get_full_name();
    else
      str = "<empty>";

    // Create a new accessor record if one does not exist
    if(access.exists(str))
      access_record = access[str];
    else
      init_access_record(access_record);

    // Update the accessor record
    access_record.read_count++;
    access_record.read_time = $realtime;
    access[str] = access_record;

  endfunction

  // Function: record_write_access
  //
  // Record the write access information for this resource for debug purposes.
  // This information is used by <print_accessors> function.
  //
  // @uvm-accellera The details of this API are specific to the Accellera implementation, and are not being considered for contribution to 1800.2

  virtual function void record_write_access(uvm_object accessor = null);

    string str;

    // If an accessor object is supplied then get the accessor record.
    // Otherwise create a new access record.  In either case populate
    // the access record with information about this access.  

      if(accessor != null) begin
        uvm_resource_types::access_t access_record;
        string str;
        str = accessor.get_full_name();
        if(access.exists(str))
          access_record = access[str];
        else
          init_access_record(access_record);
        access_record.write_count++;
        access_record.write_time = $realtime;
        access[str] = access_record;
      end
  endfunction

  // Function: print_accessors
  //
  // Print the read/write access history of the resource, using the accessor 
  // argument <accessor> which is passed to the <uvm_resource#(T)::read> 
  // and <uvm_resource#(T)::write> 
  //
  // @uvm-accellera The details of this API are specific to the Accellera implementation, and are not being considered for contribution to 1800.2

  virtual function void print_accessors();

    string str;
    uvm_component comp;
    uvm_resource_types::access_t access_record;
    string qs[$];
    
    if(access.num() == 0)
      return;

    foreach (access[i]) begin
      str = i;
      access_record = access[str];
      qs.push_back($sformatf("%s reads: %0d @ %0t  writes: %0d @ %0t\n",str,
               access_record.read_count,
               access_record.read_time,
               access_record.write_count,
               access_record.write_time));
    end
    `uvm_info("UVM/RESOURCE/ACCESSOR",`UVM_STRING_QUEUE_STREAMING_PACK(qs),UVM_NONE)

  endfunction

  // Function -- NODOCS -- init_access_record
  //
  // Initialize a new access record
  //
  function void init_access_record (inout uvm_resource_types::access_t access_record);
    access_record.read_time = 0;
    access_record.write_time = 0;
    access_record.read_count = 0;
    access_record.write_count = 0;
  endfunction

endclass

//----------------------------------------------------------------------
// Class -- NODOCS -- uvm_resource_base
//
// Non-parameterized base class for resources.  Supports interfaces for
// scope matching, and virtual functions for printing the resource and
// for printing the accessor list
//----------------------------------------------------------------------

//----------------------------------------------------------------------
// Class: uvm_resource_base
//
// The library implements the following public API beyond what is 
// documented in 1800.2.
//----------------------------------------------------------------------

// @uvm-ieee 1800.2-2020 auto C.2.3.1
virtual class uvm_resource_base extends uvm_object;

  protected bit modified;
  protected bit read_only;

  // instance of delegate class for supporting resource debug
  uvm_resource_debug dbg ;

  protected string scope;

  //@uvm-compat provided for compatibility with 1.2
  int unsigned precedence;
  //@uvm-compat provided for compatibility with 1.2
  static int unsigned default_precedence = 1000;

  // @uvm-ieee 1800.2-2020 auto C.2.3.2.1
  function new(string name = "",string s = "<not provided>"); //string argument added for compatibility
    super.new(name);
    modified = 0;
    read_only = 0;
    // begin lines provided for compatibility with 1.2
    if (s == "<not provided>") scope = s; //don't call glob_to_re unless legacy usage
    else scope = uvm_glob_to_re(s) ; 
    precedence = default_precedence ;
    // end lines provided for compatibility with 1.2
  endfunction

  // Function -- NODOCS -- get_type_handle
  //
  // Pure virtual function that returns the type handle of the resource
  // container.

  // @uvm-ieee 1800.2-2020 auto C.2.3.2.2
  pure virtual function uvm_resource_base get_type_handle();


  //---------------------------
  // Group -- NODOCS -- Read-only Interface
  //---------------------------

  // Function -- NODOCS -- set_read_only
  //
  // Establishes this resource as a read-only resource.  An attempt
  // to call <uvm_resource#(T)::write> on the resource will cause an error.

  // @uvm-ieee 1800.2-2020 auto C.2.3.3.1
  function void set_read_only();
    read_only = 1;
  endfunction

  // function set_read_write
  //
  // Returns the resource to normal read-write capability.
  
  // Implementation question: Not sure if this function is necessary.  
  // Once a resource is set to read_only no one should be able to change 
  // that.  If anyone can flip the read_only bit then the resource is not 
  // truly read_only.

  function void set_read_write();
    read_only = 0;
  endfunction


  // @uvm-ieee 1800.2-2020 auto C.2.3.3.2
  function bit is_read_only();
    return read_only;
  endfunction


  //--------------------
  // Group -- NODOCS -- Notification
  //--------------------

  // Task -- NODOCS -- wait_modified
  //
  // This task blocks until the resource has been modified -- that is, a
  // <uvm_resource#(T)::write> operation has been performed.  When a 
  // <uvm_resource#(T)::write> is performed the modified bit is set which 
  // releases the block.  Wait_modified() then clears the modified bit so 
  // it can be called repeatedly.

  // @uvm-ieee 1800.2-2020 auto C.2.3.4
  task wait_modified();
    wait (modified == 1);
    modified = 0;
  endtask

  //-------------------------
  // Group -- NODOCS -- Utility Functions
  //-------------------------

  // function convert2string
  //
  // Create a string representation of the resource value.  By default
  // we don't know how to do this so we just return a "?".  Resource
  // specializations are expected to override this function to produce a
  // proper string representation of the resource value.

  function string convert2string();
    return $sformatf("(%s) %s", m_value_type_name(), m_value_as_string());
  endfunction // convert2string

  // Helper for printing externally, non-LRM
  pure virtual function string m_value_type_name();
  pure virtual function string m_value_as_string();
  
  function void do_print(uvm_printer printer);
    super.do_print(printer);
    printer.print_generic_element("val", m_value_type_name(), "", m_value_as_string());
  endfunction : do_print

  //-------------------
  // Group: Audit Trail
  //-------------------
  //
  // To find out what is happening as the simulation proceeds, an audit 
  // trail of each read and write is kept. The <uvm_resource#(T)::read> and 
  // <uvm_resource#(T)::write> methods each take an accessor argument.  This is a
  // handle to the object that performed that resource access.
  //
  //|    function T read(uvm_object accessor = null);
  //|    function void write(T t, uvm_object accessor = null);
  //
  // The accessor can by anything as long as it is derived from
  // uvm_object.  The accessor object can be a component or a sequence
  // or whatever object from which a read or write was invoked.
  // Typically the ~this~ handle is used as the
  // accessor.  For example:
  //
  //|    uvm_resource#(int) rint;
  //|    int i;
  //|    ...
  //|    rint.write(7, this);
  //|    i = rint.read(this);
  //
  // The accessor's ~get_full_name()~ is stored as part of the audit trail. 
  // This way you can find out what object performed each resource access.
  // Each audit record also includes the time of the access (simulation time)
  // and the particular operation performed (read or write).
  //
  // Auditing is controlled through the <uvm_resource_options> class.

  // Function: record_read_access
  //
  // Delegates to the <uvm_resource_debug::record_read_access function to 
  // record read access information for this resource for debug purposes,
  // information used by <print_accessors> function.
  //
  // @uvm-accellera The details of this API are specific to the Accellera implementation, and are not being considered for contribution to 1800.2

  function void record_read_access(uvm_object accessor = null);
     if (dbg==null) dbg = uvm_resource_debug::type_id::create("dbg") ; 
     dbg.record_read_access(accessor);
  endfunction

  // Function: record_write_access
  //
  // Delegates to the <uvm_resource_debug::record_write_access function to 
  // record write access information for this resource for debug purposes,
  // information used by <print_accessors> function.
  //
  // @uvm-accellera The details of this API are specific to the Accellera implementation, and are not being considered for contribution to 1800.2

  function void record_write_access(uvm_object accessor = null);
     if (dbg==null) dbg = uvm_resource_debug::type_id::create("dbg") ; 
     dbg.record_write_access(accessor);
  endfunction

  // Function: print_accessors
  //
  // Delegates to the <uvm_resource_debug::print_access function to 
  // print the read/write access history of the resource, using the accessor 
  // argument <accessor> which is passed to the <uvm_resource#(T)::read> 
  // and <uvm_resource#(T)::write> 
  //
  // @uvm-accellera The details of this API are specific to the Accellera implementation, and are not being considered for contribution to 1800.2

  virtual function void print_accessors();
     // if dbg is null, we never called record_xxx_access and so there is nothing to print
     if (dbg != null) dbg.print_accessors();
  endfunction

  function void init_access_record (inout uvm_resource_types::access_t access_record);
     if (dbg==null) dbg = uvm_resource_debug::type_id::create("dbg") ; 
     dbg.init_access_record(access_record);
  endfunction

  //@uvm-compat provided for compatibility with 1.2
  function void set_scope(string s);
    scope = uvm_glob_to_re(s);
  endfunction

  function void m_set_scope(string s); 
    scope = s;
  endfunction // m_set_scope

  //@uvm-compat provided for compatibility with 1.2
  function string get_scope();
    return scope;
  endfunction

  //@uvm-compat provided for compatibility with 1.2
  function bit match_scope(string s);
    return uvm_is_match(scope, s);
  endfunction

  //@uvm-compat provided for compatibility with 1.2
  pure virtual function void set_priority (uvm_resource_types::priority_e pri);

endclass



