//
//------------------------------------------------------------------------------
// Copyright 2007-2014 Mentor Graphics Corporation
// Copyright 2014 Semifore
// Copyright 2018 Qualcomm, Inc.
// Copyright 2014 Intel Corporation
// Copyright 2018 Synopsys, Inc.
// Copyright 2007-2018 Cadence Design Systems, Inc.
// Copyright 2013-2018 NVIDIA Corporation
// Copyright 2017-2018 Cisco Systems, Inc.
//   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 -- NODOCS -- uvm_packer
//
// The uvm_packer class provides a policy object for packing and unpacking
// uvm_objects. The policies determine how packing and unpacking should be done.
// Packing an object causes the object to be placed into a bit (byte or int)
// array. If the `uvm_field_* macro are used to implement pack and unpack,
// by default no metadata information is stored for the packing of dynamic
// objects (strings, arrays, class objects).
//
//-------------------------------------------------------------------------------

typedef bit signed [(`UVM_PACKER_MAX_BYTES*8)-1:0] uvm_pack_bitstream_t;

// Class: uvm_packer
// Implementation of uvm_packer, as defined in section
// 16.5.1 of 1800.2-2017

// @uvm-ieee 1800.2-2017 auto 16.5.1
class uvm_packer extends uvm_policy;

   // @uvm-ieee 1800.2-2017 auto 16.5.2.3
   `uvm_object_utils(uvm_packer)

    uvm_factory m_factory;	
    local uvm_object m_object_references[int];
   

  // Function: set_packed_*
  // Implementation of P1800.2 16.5.3.1
  //
  // The LRM specifies the set_packed_* methods as being
  // signed, whereas the <uvm_object::unpack> methods are specified
  // as unsigned.  This is being tracked in Mantis 6423.
  //
  // The reference implementation has implemented these methods
  // as unsigned so as to remain consistent.
  //
  //| virtual function void set_packed_bits( ref bit unsigned stream[] );
  //| virtual function void set_packed_bytes( ref byte unsigned stream[] );
  //| virtual function void set_packed_ints( ref int unsigned stream[] );
  //| virtual function void set_packed_longints( ref longint unsigned stream[] );
  //
  // @uvm-contrib This API is being considered for potential contribution to 1800.2
   
  // @uvm-ieee 1800.2-2017 auto 16.5.3.1
  extern virtual function void set_packed_bits (ref bit unsigned stream[]);

  // @uvm-ieee 1800.2-2017 auto 16.5.3.1
  extern virtual function void set_packed_bytes (ref byte unsigned stream[]);

  // @uvm-ieee 1800.2-2017 auto 16.5.3.1
  extern virtual function void set_packed_ints (ref int unsigned stream[]);

  // @uvm-ieee 1800.2-2017 auto 16.5.3.1
  extern virtual function void set_packed_longints (ref longint unsigned stream[]);
   
  // Function: get_packed_*
  // Implementation of P1800.2 16.5.3.2
  //
  // The LRM specifies the get_packed_* methods as being
  // signed, whereas the <uvm_object::pack> methods are specified
  // as unsigned.  This is being tracked in Mantis 6423.
  //
  // The reference implementation has implemented these methods
  // as unsigned so as to remain consistent.
  //
  //| virtual function void get_packed_bits( ref bit unsigned stream[] );
  //| virtual function void get_packed_bytes( ref byte unsigned stream[] );
  //| virtual function void get_packed_ints( ref int unsigned stream[] );
  //| virtual function void get_packed_longints( ref longint unsigned stream[] );
  //
  // @uvm-contrib This API is being considered for potential contribution to 1800.2
   
  // @uvm-ieee 1800.2-2017 auto 16.5.3.2
  extern virtual function void get_packed_bits (ref bit unsigned stream[]);

  // @uvm-ieee 1800.2-2017 auto 16.5.3.2
  extern virtual function void get_packed_bytes (ref byte unsigned stream[]);

  // @uvm-ieee 1800.2-2017 auto 16.5.3.2
  extern virtual function void get_packed_ints (ref int unsigned stream[]);

  // @uvm-ieee 1800.2-2017 auto 16.5.3.2
  extern virtual function void get_packed_longints (ref longint unsigned stream[]);
   
  //----------------//
  // Group -- NODOCS -- Packing //
  //----------------//

  // @uvm-ieee 1800.2-2017 auto 16.5.2.4
  static function void set_default (uvm_packer packer) ;
     uvm_coreservice_t coreservice ;
     coreservice = uvm_coreservice_t::get() ;
     coreservice.set_default_packer(packer) ;
  endfunction

  // @uvm-ieee 1800.2-2017 auto 16.5.2.5
  static function uvm_packer get_default () ;
     uvm_coreservice_t coreservice ;
     coreservice = uvm_coreservice_t::get() ;
     return coreservice.get_default_packer() ;
  endfunction

  
  // @uvm-ieee 1800.2-2017 auto 16.5.2.2
  extern virtual function void flush ();
  // Function -- NODOCS -- pack_field
  //
  // Packs an integral value (less than or equal to 4096 bits) into the
  // packed array. ~size~ is the number of bits of ~value~ to pack.

  // @uvm-ieee 1800.2-2017 auto 16.5.4.8
  extern virtual function void pack_field (uvm_bitstream_t value, int size);


  // Function -- NODOCS -- pack_field_int
  //
  // Packs the integral value (less than or equal to 64 bits) into the
  // pack array.  The ~size~ is the number of bits to pack, usually obtained by
  // ~$bits~. This optimized version of <pack_field> is useful for sizes up
  // to 64 bits.

  // @uvm-ieee 1800.2-2017 auto 16.5.2.1
  extern function new(string name="");

  // @uvm-ieee 1800.2-2017 auto 16.5.4.9
  extern virtual function void pack_field_int (uvm_integral_t value, int size);


  // @uvm-ieee 1800.2-2017 auto 16.5.4.1
  extern virtual function void pack_bits(ref bit value[], input int size = -1);


  // @uvm-ieee 1800.2-2017 auto 16.5.4.10
  extern virtual function void pack_bytes(ref byte value[], input int size = -1);


  // @uvm-ieee 1800.2-2017 auto 16.5.4.11
  extern virtual function void pack_ints(ref int value[], input int size = -1);

  // recursion functions


   
  // Function -- NODOCS -- pack_string
  //
  // Packs a string value into the pack array. 
  //
  // When the metadata flag is set, the packed string is terminated by a ~null~
  // character to mark the end of the string.
  //
  // This is useful for mixed language communication where unpacking may occur
  // outside of SystemVerilog UVM.

  // @uvm-ieee 1800.2-2017 auto 16.5.4.5
  extern virtual function void pack_string (string value);


  // Function -- NODOCS -- pack_time
  //
  // Packs a time ~value~ as 64 bits into the pack array.

  // @uvm-ieee 1800.2-2017 auto 16.5.4.6
  extern virtual function void pack_time (time value); 


  // Function -- NODOCS -- pack_real
  //
  // Packs a real ~value~ as 64 bits into the pack array. 
  //
  // The real ~value~ is converted to a 6-bit scalar value using the function
  // $real2bits before it is packed into the array.

  // @uvm-ieee 1800.2-2017 auto 16.5.4.7
  extern virtual function void pack_real (real value);


  // Function -- NODOCS -- pack_object
  //
  // Packs an object value into the pack array. 
  //
  // A 4-bit header is inserted ahead of the string to indicate the number of
  // bits that was packed. If a ~null~ object was packed, then this header will
  // be 0. 
  //
  // This is useful for mixed-language communication where unpacking may occur
  // outside of SystemVerilog UVM.

  // @uvm-ieee 1800.2-2017 auto 16.5.4.2
  extern virtual function void pack_object (uvm_object value);
  
  extern virtual function void pack_object_with_meta (uvm_object value);
  
  extern virtual function void pack_object_wrapper (uvm_object_wrapper value);


  //------------------//
  // Group -- NODOCS -- Unpacking //
  //------------------//
  
  // Function -- NODOCS -- is_null
  //
  // This method is used during unpack operations to peek at the next 4-bit
  // chunk of the pack data and determine if it is 0.
  //
  // If the next four bits are all 0, then the return value is a 1; otherwise
  // it is 0. 
  //
  // This is useful when unpacking objects, to decide whether a new object
  // needs to be allocated or not.

  // @uvm-ieee 1800.2-2017 auto 16.5.4.3
  extern virtual function bit is_null ();


  extern virtual function bit is_object_wrapper();
  // Function -- NODOCS -- unpack_field
  //
  // Unpacks bits from the pack array and returns the bit-stream that was
  // unpacked. ~size~ is the number of bits to unpack; the maximum is 4096 bits.

  // @uvm-ieee 1800.2-2017 auto 16.5.4.16
  extern virtual function uvm_bitstream_t unpack_field (int size);

  // Function -- NODOCS -- unpack_field_int
  //
  // Unpacks bits from the pack array and returns the bit-stream that was
  // unpacked. 
  //
  // ~size~ is the number of bits to unpack; the maximum is 64 bits. 
  // This is a more efficient variant than unpack_field when unpacking into
  // smaller vectors.

  // @uvm-ieee 1800.2-2017 auto 16.5.4.17
  extern virtual function uvm_integral_t unpack_field_int (int size);


  // @uvm-ieee 1800.2-2017 auto 16.5.4.18
  extern virtual function void unpack_bits(ref bit value[], input int size = -1);


  // @uvm-ieee 1800.2-2017 auto 16.5.4.19
  extern virtual function void unpack_bytes(ref byte value[], input int size = -1);


  // @uvm-ieee 1800.2-2017 auto 16.5.4.12
  extern virtual function void unpack_ints(ref int value[], input int size = -1);
   

  // Function -- NODOCS -- unpack_string
  //
  // Unpacks a string. 
  //
  // num_chars bytes are unpacked into a string. If num_chars is -1 then
  // unpacking stops on at the first ~null~ character that is encountered.

  // @uvm-ieee 1800.2-2017 auto 16.5.4.13
  extern virtual function string unpack_string ();


  // Function -- NODOCS -- unpack_time
  //
  // Unpacks the next 64 bits of the pack array and places them into a
  // time variable.

  // @uvm-ieee 1800.2-2017 auto 16.5.4.14
  extern virtual function time unpack_time (); 


  // Function -- NODOCS -- unpack_real
  //
  // Unpacks the next 64 bits of the pack array and places them into a
  // real variable. 
  //
  // The 64 bits of packed data are converted to a real using the $bits2real
  // system function.

  // @uvm-ieee 1800.2-2017 auto 16.5.4.15
  extern virtual function real unpack_real ();


  // Function -- NODOCS -- unpack_object
  //
  // Unpacks an object and stores the result into ~value~. 
  //
  // ~value~ must be an allocated object that has enough space for the data
  // being unpacked. The first four bits of packed data are used to determine
  // if a ~null~ object was packed into the array.
  //
  // The <is_null> function can be used to peek at the next four bits in
  // the pack array before calling this method.

  // @uvm-ieee 1800.2-2017 auto 16.5.4.4
  extern virtual function void unpack_object (uvm_object value);
  
  extern virtual function void unpack_object_with_meta (inout uvm_object value);

  extern virtual function uvm_object_wrapper unpack_object_wrapper();


  // Function -- NODOCS -- get_packed_size
  //
  // Returns the number of bits that were packed.

  // @uvm-ieee 1800.2-2017 auto 16.5.3.3
  extern virtual function int get_packed_size(); 


  //------------------//
  // Group -- NODOCS -- Variables //
  //------------------//

`ifdef UVM_ENABLE_DEPRECATED_API
  // Variable -- NODOCS -- physical
  //
  // This bit provides a filtering mechanism for fields.
  //
  // The <abstract> and physical settings allow an object to distinguish between
  // two different classes of fields. It is up to you, in the
  // <uvm_object::do_pack> and <uvm_object::do_unpack> methods, to test the
  // setting of this field if you want to use it as a filter.

  bit physical = 1;


  // Variable -- NODOCS -- abstract
  //
  // This bit provides a filtering mechanism for fields. 
  //
  // The abstract and physical settings allow an object to distinguish between
  // two different classes of fields. It is up to you, in the
  // <uvm_object::do_pack> and <uvm_object::do_unpack> routines, to test the
  // setting of this field if you want to use it as a filter.

  bit abstract;


  // Variable -- NODOCS -- big_endian
  //
  // This bit determines the order that integral data is packed (using
  // <pack_field>, <pack_field_int>, <pack_time>, or <pack_real>) and how the
  // data is unpacked from the pack array (using <unpack_field>,
  // <unpack_field_int>, <unpack_time>, or <unpack_real>). When the bit is set,
  // data is associated msb to lsb; otherwise, it is associated lsb to msb. 
  //
  // The following code illustrates how data can be associated msb to lsb and
  // lsb to msb:
  //
  //|  class mydata extends uvm_object;
  //|
  //|    logic[15:0] value = 'h1234;
  //|
  //|    function void do_pack (uvm_packer packer);
  //|      packer.pack_field_int(value, 16);
  //|    endfunction
  //|
  //|    function void do_unpack (uvm_packer packer);
  //|      value = packer.unpack_field_int(16);
  //|    endfunction
  //|  endclass
  //|
  //|  mydata d = new;
  //|  bit bits[];
  //|
  //|  initial begin
  //|    d.pack(bits);  // 'b0001001000110100
  //|    uvm_default_packer.big_endian = 0;
  //|    d.pack(bits);  // 'b0010110001001000
  //|  end

  bit big_endian = 0;
`endif

  // variables and methods primarily for internal use

  static bit bitstream[];   // local bits for (un)pack_bytes
  static bit fabitstream[]; // field automation bits for (un)pack_bytes
  int        m_pack_iter; // Used to track the bit of the next pack
  int        m_unpack_iter; // Used to track the bit of the next unpack

  bit   reverse_order;      //flip the bit order around
  byte  byte_size     = 8;  //set up bytesize for endianess
  int   word_size     = 16; //set up worksize for endianess
  bit   nopack;             //only count packable bits

  uvm_pack_bitstream_t m_bits;


`ifdef UVM_ENABLE_DEPRECATED_API
  extern virtual function bit  unsigned get_bit  (int unsigned index);
  extern virtual function byte unsigned get_byte (int unsigned index);
  extern virtual function int  unsigned get_int  (int unsigned index);

  extern virtual function void get_bits (ref bit unsigned bits[]);
  extern virtual function void get_bytes(ref byte unsigned bytes[]);
  extern virtual function void get_ints (ref int unsigned ints[]);

  extern virtual function void put_bits (ref bit unsigned bitstream[]);
  extern virtual function void put_bytes(ref byte unsigned bytestream[]);
  extern virtual function void put_ints (ref int unsigned intstream[]);

  extern virtual function void set_packed_size(); 
`endif
   
  extern function void index_error(int index, string id, int sz);
  extern function bit enough_bits(int needed, string id);

`ifdef UVM_ENABLE_DEPRECATED_API
  extern function void reset();
`endif
  
endclass



//------------------------------------------------------------------------------
// IMPLEMENTATION
//------------------------------------------------------------------------------

// NOTE- max size limited to BITSTREAM bits parameter (default: 4096)


// index_ok
// --------

function void uvm_packer::index_error(int index, string id, int sz);
    uvm_report_error("PCKIDX", 
        $sformatf("index %0d for get_%0s too large; valid index range is 0-%0d.",
                  index,id,((m_pack_iter+sz-1)/sz)-1), UVM_NONE);
endfunction


// enough_bits
// -----------

function bit uvm_packer::enough_bits(int needed, string id);
  if ((m_pack_iter - m_unpack_iter) < needed) begin
    uvm_report_error("PCKSZ",
        $sformatf("%0d bits needed to unpack %0s, yet only %0d available.",
                  needed, id, (m_pack_iter - m_unpack_iter)), UVM_NONE);
    return 0;
  end
  return 1;
endfunction


// get_packed_size
// ---------------

function int uvm_packer::get_packed_size();
  return m_pack_iter - m_unpack_iter;
endfunction


`ifdef UVM_ENABLE_DEPRECATED_API
// set_packed_size
// ---------------

function void uvm_packer::set_packed_size();
  /* doesn't actually do anything now */
endfunction

// reset
// -----

function void uvm_packer::reset();
  flush();
endfunction

function void uvm_packer::get_bits( ref bit bits [] ); get_packed_bits(bits); endfunction
function void uvm_packer::get_bytes( ref byte unsigned bytes [] ); get_packed_bytes(bytes); endfunction
function void uvm_packer::get_ints( ref int unsigned ints [] ); get_packed_ints(ints); endfunction
`endif

function void uvm_packer::flush();
  // The iterators are spaced 64b from the beginning, enough to store
  // the iterators during get_packed_* and retrieve them during
  // set_packed_*.  Without this, set_packed_[byte|int|longint] will
  // move the iterators too far.
  m_pack_iter = 64;
  m_unpack_iter = 64;
  m_bits       = 0;
  m_object_references.delete();
  m_object_references[0] = null;
  m_factory = null;
  super.flush();
endfunction : flush

// get_packed_bits
// --------

function void uvm_packer::get_packed_bits(ref bit unsigned stream[]);
  stream        = new[m_pack_iter];
  m_bits[31:0]  = m_pack_iter; /* Reserved bits */
  m_bits[63:32] = m_unpack_iter; /* Reserved bits */
  for (int i=0;i<m_pack_iter;i++)
    stream[i] = m_bits[i];
endfunction

`ifdef UVM_ENABLE_DEPRECATED_API
// When deprecated, we support big_endian
`define M__UVM_GET_PACKED(T) \
function void uvm_packer::get_packed_``T``s (ref T unsigned stream[] ); \
   int sz;                                                              \
   T v;                                                                 \
   sz = (m_pack_iter + $high(v)) / $bits(T);                            \
   stream = new[sz];                                                    \
   m_bits[31:0] = m_pack_iter; /* Reserved Bits */                      \
   m_bits[63:32] = m_unpack_iter; /* Reserved Bits */                   \
   foreach (stream[i]) begin                                            \
      if (i != sz-1 || (m_pack_iter % $bits(T)) == 0)                   \
	v = m_bits[ i* $bits(T) +: $bits(T) ];                          \
      else                                                              \
	v = m_bits[ i* $bits(T) +: $bits(T) ] & ({$bits(T){1'b1}} >> ($bits(T)-(m_pack_iter%$bits(T)))); \
      if(big_endian)                                                    \
	v = {<<{v}};                                                    \
      stream[i] = v;                                                    \
   end                                                                  \
endfunction 
`else // !`ifdef UVM_ENABLE_DEPRECATED_API
`define M__UVM_GET_PACKED(T) \
function void uvm_packer::get_packed_``T``s (ref T unsigned stream[] ); \
   int sz;                                                              \
   T v;                                                                 \
   sz = (m_pack_iter + $high(v)) / $bits(T);                            \
   m_bits[31:0] = m_pack_iter; /* Reserved Bits */                      \
   m_bits[63:32] = m_unpack_iter; /* Reserved Bits */                   \
   stream = new[sz];                                                    \
   foreach (stream[i]) begin                                            \
      if (i != sz-1 || (m_pack_iter % $bits(T)) == 0)                   \
	v = m_bits[ i* $bits(T) +: $bits(T) ];                          \
      else                                                              \
	v = m_bits[ i* $bits(T) +: $bits(T) ] & ({$bits(T){1'b1}} >> ($bits(T)-(m_pack_iter%$bits(T)))); \
      stream[i] = v;                                                    \
   end                                                                  \
endfunction 
`endif // !`ifdef UVM_ENABLE_DEPRECATED_API

`M__UVM_GET_PACKED(byte)
`M__UVM_GET_PACKED(int)
`M__UVM_GET_PACKED(longint)

`undef M__UVM_GET_PACKED

`ifdef UVM_ENABLE_DEPRECATED_API
function void uvm_packer::put_bits( ref bit bitstream [] ); set_packed_bits(bitstream); endfunction
function void uvm_packer::put_bytes( ref byte unsigned bytestream [] ); set_packed_bytes(bytestream); endfunction
function void uvm_packer::put_ints( ref int unsigned intstream [] ); set_packed_ints(intstream); endfunction
`endif

// set_packed_bits
// --------

function void uvm_packer::set_packed_bits (ref bit stream []);

  int bit_size;

  bit_size = stream.size();

`ifdef UVM_ENABLE_DEPRECATED_API
  if(big_endian)
    for (int i=bit_size-1;i>=0;i--)
      m_bits[i] = stream[i];
  else
`endif
    for (int i=0;i<bit_size;i++)
      m_bits[i] = stream[i];

  m_pack_iter = m_bits[31:0]; /* Reserved Bits */
  m_unpack_iter = m_bits[63:32]; /* Reserved Bits */
 
endfunction

`ifdef UVM_ENABLE_DEPRECATED_API
// In deprecated mode we support big_endian
`define M__UVM_SET_PACKED(T) \
function void uvm_packer::set_packed_``T``s (ref T unsigned stream []); \
   int count;                                                           \
   foreach(stream[i]) begin                                             \
     if (big_endian)                                                    \
       m_bits[count +: $bits(T)] = {<<{stream[i]}};                     \
     else                                                               \
       m_bits[count +: $bits(T)] = stream[i];                           \
     count += $bits(T);                                                 \
   end                                                                  \
  m_pack_iter = m_bits[31:0]; /* Reserved Bits */                       \
  m_unpack_iter = m_bits[63:32]; /* Reserved Bits */                    \
endfunction 
`else // !`ifdef UVM_ENABLE_DEPRECATED_API
`define M__UVM_SET_PACKED(T) \
function void uvm_packer::set_packed_``T``s (ref T unsigned stream []); \
   int count;                                                           \
   foreach(stream[i]) begin                                             \
     m_bits[count +: $bits(T)] = stream[i];                             \
     count += $bits(T);                                                 \
   end                                                                  \
  m_pack_iter = m_bits[31:0]; /* Reserved Bits */                       \
  m_unpack_iter = m_bits[63:32]; /* Reserved Bits */                    \
endfunction 
`endif

`M__UVM_SET_PACKED(byte)
`M__UVM_SET_PACKED(int)
`M__UVM_SET_PACKED(longint)

`undef M__UVM_SET_PACKED

`ifdef UVM_ENABLE_DEPRECATED_API
// get_bit
// -------

function bit unsigned uvm_packer::get_bit(int unsigned index);
  if (index >= m_pack_iter)
    index_error(index, "bit",1);
  return m_bits[index];
endfunction


// get_byte
// --------

function byte unsigned uvm_packer::get_byte(int unsigned index);
  if (index >= (m_pack_iter+7)/8)
    index_error(index, "byte",8);
  return m_bits[index*8 +: 8];
endfunction


// get_int
// -------

function int unsigned uvm_packer::get_int(int unsigned index);
  if (index >= (m_pack_iter+31)/32)
    index_error(index, "int",32);
  return m_bits[(index*32) +: 32];
endfunction
`endif

// PACK


// pack_object
// ---------

function void uvm_packer::pack_object(uvm_object value);
  uvm_field_op field_op;
  if (value == null ) begin
    m_bits[m_pack_iter +: 4] = 0;
    m_pack_iter += 4;
    return ;
  end
  else begin
    m_bits[m_pack_iter +: 4]  = 4'hF;
    m_pack_iter += 4;
  end
  
  push_active_object(value);
  field_op = uvm_field_op::m_get_available_op() ;
  field_op.set(UVM_PACK,this,value);
  value.do_execute_op(field_op);
  if (field_op.user_hook_enabled()) begin
    value.do_pack(this);
  end
  field_op.m_recycle();
  void'(pop_active_object());
endfunction

// Function: pack_object_with_meta
// Packs ~obj~ into the packer data stream, such that
// it can be unpacked via an associated <unpack_object_with_meta>
// call.
//
// Unlike <pack_object>, the pack_object_with_meta method keeps track
// of what objects have already been packed in this call chain.  The
// first time an object is passed to pack_object_with_meta after a
// call to <flush>, the object is assigned a unique id.  Subsequent
// calls to pack_object_with_meta will only add the unique id to the
// stream.  This allows structural information to be maintained through
// pack/unpack operations.
//
// Note: pack_object_with_meta is not compatible with <unpack_object>
// and <is_null>.  The object can only be unpacked via 
// <unpack_object_with_meta>.
//
// @uvm-contrib This API is being considered for potential contribution to 1800.2
function void uvm_packer::pack_object_with_meta(uvm_object value);
  int reference_id;
  foreach(m_object_references[i]) begin
    if (m_object_references[i] == value) begin
      pack_field_int(i,32);
      return;
    end
  end
  
  // Size will always be >0 because 0 is the null
  reference_id = m_object_references.size();
  pack_field_int(reference_id,32);
  m_object_references[reference_id] = value;
  pack_object_wrapper(value.get_object_type());

  pack_object(value); 
endfunction


function void uvm_packer::pack_object_wrapper(uvm_object_wrapper value);
  string type_name;
  if (value != null) begin 
    pack_string(value.get_type_name());
  end
endfunction   
// pack_real
// ---------

function void uvm_packer::pack_real(real value);
  pack_field_int($realtobits(value), 64);
endfunction
  

// pack_time
// ---------

function void uvm_packer::pack_time(time value);
  pack_field_int(value, 64);
  //m_bits[m_pack_iter +: 64] = value; this overwrites endian adjustments
endfunction
  

// pack_field
// ----------

function void uvm_packer::pack_field(uvm_bitstream_t value, int size);
  for (int i=0; i<size; i++)
`ifdef UVM_ENABLE_DEPRECATED_API
    if(big_endian == 1)
      m_bits[m_pack_iter+i] = value[size-1-i];
    else
`endif
      m_bits[m_pack_iter+i] = value[i];
  m_pack_iter += size;
endfunction
  

// pack_field_int
// --------------

function void uvm_packer::pack_field_int(uvm_integral_t value, int size);
  for (int i=0; i<size; i++)
`ifdef UVM_ENABLE_DEPRECATED_API
    if(big_endian == 1)
      m_bits[m_pack_iter+i] = value[size-1-i];
    else
`endif
      m_bits[m_pack_iter+i] = value[i];
  m_pack_iter += size;
endfunction
  
// pack_bits
// -----------------

function void uvm_packer::pack_bits(ref bit value[], input int size = -1);
   if (size < 0)
     size = value.size();

   if (size > value.size()) begin
      `uvm_error("UVM/BASE/PACKER/BAD_SIZE",
                 $sformatf("pack_bits called with size '%0d', which exceeds value.size() of '%0d'",
                           size,
                           value.size()))
      return;
   end
   
   for (int i=0; i<size; i++)
`ifdef UVM_ENABLE_DEPRECATED_API
     if (big_endian == 1)
       m_bits[m_pack_iter+i] = value[size-1-i];
     else
`endif
       m_bits[m_pack_iter+i] = value[i];
   m_pack_iter += size;
endfunction 

// pack_bytes
// -----------------

function void uvm_packer::pack_bytes(ref byte value[], input int size = -1);
   int max_size = value.size() * $bits(byte);
   
   if (size < 0)
     size = max_size;

   if (size > max_size) begin
      `uvm_error("UVM/BASE/PACKER/BAD_SIZE",
                 $sformatf("pack_bytes called with size '%0d', which exceeds value size of '%0d'",
                           size,
                           max_size))
      return;
   end
   else begin
      int idx_select;

      for (int i=0; i<size; i++) begin
`ifdef UVM_ENABLE_DEPRECATED_API
         if (big_endian == 1)
           idx_select = size-1-i;
         else
`endif
           idx_select = i;
         
         m_bits[m_pack_iter+i] = value[idx_select / $bits(byte)][idx_select % $bits(byte)];
      end
   
      m_pack_iter += size;
   end
endfunction 

// pack_ints
// -----------------

function void uvm_packer::pack_ints(ref int value[], input int size = -1);
   int max_size = value.size() * $bits(int);
   
   if (size < 0)
     size = max_size;

   if (size > max_size) begin
      `uvm_error("UVM/BASE/PACKER/BAD_SIZE",
                 $sformatf("pack_ints called with size '%0d', which exceeds value size of '%0d'",
                           size,
                           max_size))
      return;
   end
   else begin
      int idx_select;

      for (int i=0; i<size; i++) begin
`ifdef UVM_ENABLE_DEPRECATED_API
         if (big_endian == 1)
           idx_select = size-1-i;
         else
`endif
           idx_select = i;
         
         m_bits[m_pack_iter+i] = value[idx_select / $bits(int)][idx_select % $bits(int)];
      end
   
      m_pack_iter += size;
   end
endfunction 


// pack_string
// -----------

function void uvm_packer::pack_string(string value);
  byte b;
`ifdef UVM_ENABLE_DEPRECATED_API
  foreach (value[index]) begin
    if(big_endian == 1) begin
      b = value[index];
      for(int i=0; i<8; ++i)
        m_bits[m_pack_iter+i] = b[7-i];
    end 
    else 
      m_bits[m_pack_iter +: 8] = value[index];
    m_pack_iter += 8;
  end
`else // !`ifdef UVM_ENABLE_DEPRECATED_API
  foreach (value[index]) begin
     m_bits[m_pack_iter +: 8] = value[index];
     m_pack_iter += 8;
  end
`endif // !`ifdef UVM_ENABLE_DEPRECATED_API
   m_bits[m_pack_iter +: 8] = 0;
   m_pack_iter += 8;
endfunction 


// UNPACK


// is_null
// -------

function bit uvm_packer::is_null();
    return (m_bits[m_unpack_iter+:4]==0);
endfunction


function bit uvm_packer::is_object_wrapper();
    return (m_bits[m_unpack_iter+:4]==1);
endfunction
// unpack_object
// -------------

function void uvm_packer::unpack_object(uvm_object value);
  uvm_field_op field_op;
  
  if (is_null()) begin
    if (value != null) begin
      `uvm_error("UVM/BASE/PACKER/UNPACK/N2NN", "attempt to unpack a null object into a not-null object!")
      return;
    end
    m_unpack_iter += 4; // advance past the null
    return;
  end
  else begin
    if (value == null) begin
      `uvm_error("UVM/BASE/PACKER/UNPACK/NN2N", "attempt to unpack a non-null object into a null object!")
      return;
    end
    m_unpack_iter += 4; // advance past the !null
    push_active_object(value);
    field_op = uvm_field_op::m_get_available_op() ;
    field_op.set(UVM_UNPACK,this,value);
    value.do_execute_op(field_op);
    if (field_op.user_hook_enabled()) begin
       value.do_unpack(this);
    end
    field_op.m_recycle();
    void'(pop_active_object());
  end

endfunction

// Function: unpack_object_with_meta
// Unpacks an object which was packed into the packer data
// stream using <pack_object_with_meta>.
//
// Unlike <unpack_object>, the unpack_object_with_meta method keeps track
// of what objects have already been unpacked in this call chain.  If the
// packed object was null, then ~value~ is set to null.  Otherwise, if this
// is the first time the object's unique id  
// has been encountered since a call to <flush>,
// then unpack_object_with_meta checks ~value~ to determine if it is the
// correct type.  If it is not the correct type, or if ~value~ is null, 
// then the packer shall create a new object instance for the unpack operation, 
// using the data provided by <pack_object_with_meta>. If ~value~ is of the 
// correct type, then it is used as the object instance for the unpack operation.
// Subsequent calls to unpack_object_with_meta for this unique id shall
// simply set ~value~ to this object instance.
//
// Note: unpack_object_with_meta is not compatible with <pack_object>
// or <is_null>.  The object must have been packed via
// <pack_object_with_meta>.
//
// @uvm-contrib This API is being considered for potential contribution to 1800.2
function void uvm_packer::unpack_object_with_meta(inout uvm_object value);
  int reference_id; 
  reference_id = unpack_field_int(32);
  if (m_object_references.exists(reference_id)) begin
    value = m_object_references[reference_id];
    return;
  end
  else begin
    uvm_object_wrapper __wrapper = unpack_object_wrapper();
    if ((__wrapper != null) && 
        ((value == null) || (value.get_object_type() != __wrapper))) begin 
      value = __wrapper.create_object("");
      if (value == null) begin
        value = __wrapper.create_component("",null);
      end
    end 
  end
  m_object_references[reference_id] = value;
  unpack_object(value);
endfunction


function uvm_object_wrapper uvm_packer::unpack_object_wrapper();
  string type_name;
  type_name = unpack_string();
  if (m_factory == null)
    m_factory = uvm_factory::get();
  if (m_factory.is_type_name_registered(type_name)) begin
    return m_factory.find_wrapper_by_name(type_name);
  end
  return null;

endfunction
  
// unpack_real
// -----------

function real uvm_packer::unpack_real();
  if (enough_bits(64,"real")) begin
    return $bitstoreal(unpack_field_int(64));
  end
endfunction
  

// unpack_time
// -----------

function time uvm_packer::unpack_time();
  if (enough_bits(64,"time")) begin
    return unpack_field_int(64);
  end
endfunction
  

// unpack_field
// ------------

function uvm_bitstream_t uvm_packer::unpack_field(int size);
  unpack_field = 'b0;
  if (enough_bits(size,"integral")) begin
    m_unpack_iter += size;
    for (int i=0; i<size; i++)
`ifdef UVM_ENABLE_DEPRECATED_API
      if(big_endian == 1)
        unpack_field[i] = m_bits[m_unpack_iter-i-1];
      else
`endif
        unpack_field[i] = m_bits[m_unpack_iter-size+i];
  end
endfunction
  

// unpack_field_int
// ----------------

function uvm_integral_t uvm_packer::unpack_field_int(int size);
  unpack_field_int = 'b0;
  if (enough_bits(size,"integral")) begin
    m_unpack_iter += size;
    for (int i=0; i<size; i++)
`ifdef UVM_ENABLE_DEPRECATED_API
      if(big_endian == 1)
        unpack_field_int[i] = m_bits[m_unpack_iter-i-1];
      else
`endif
        unpack_field_int[i] = m_bits[m_unpack_iter-size+i];
  end
endfunction
  
// unpack_bits
// -------------------

function void uvm_packer::unpack_bits(ref bit value[], input int size = -1);
   if (size < 0)
     size = value.size();

   if (size > value.size()) begin
      `uvm_error("UVM/BASE/PACKER/BAD_SIZE",
                 $sformatf("unpack_bits called with size '%0d', which exceeds value.size() of '%0d'",
                           size,
                           value.size()))
      return;
   end
   
   if (enough_bits(size, "integral")) begin
      m_unpack_iter += size;
      for (int i=0; i<size; i++)
`ifdef UVM_ENABLE_DEPRECATED_API
        if (big_endian == 1)
          value[i] = m_bits[m_unpack_iter-i-1];
        else
`endif
          value[i] = m_bits[m_unpack_iter-size+i];
   end
endfunction

// unpack_bytes
// -------------------

function void uvm_packer::unpack_bytes(ref byte value[], input int size = -1);
   int max_size = value.size() * $bits(byte);
   if (size < 0)
     size = max_size;

   if (size > max_size) begin
      `uvm_error("UVM/BASE/PACKER/BAD_SIZE",
                 $sformatf("unpack_bytes called with size '%0d', which exceeds value size of '%0d'",
                           size,
                           value.size()))
      return;
   end
   else begin
      if (enough_bits(size, "integral")) begin
         m_unpack_iter += size;

         for (int i=0; i<size; i++) begin
`ifdef UVM_ENABLE_DEPRECATED_API
            if (big_endian == 1)
              value[ i / $bits(byte) ][ i % $bits(byte) ] = m_bits[m_unpack_iter-i-1];
            else
`endif
              value[ i / $bits(byte) ][ i % $bits(byte) ] = m_bits[m_unpack_iter-size+i];
         
         end
      end // if (enough_bits(size, "integral"))
   end
endfunction

// unpack_ints
// -------------------

function void uvm_packer::unpack_ints(ref int value[], input int size = -1);
   int max_size = value.size() * $bits(int);
   if (size < 0)
     size = max_size;

   if (size > max_size) begin
      `uvm_error("UVM/BASE/PACKER/BAD_SIZE",
                 $sformatf("unpack_ints called with size '%0d', which exceeds value size of '%0d'",
                           size,
                           value.size()))
      return;
   end
   else begin
      if (enough_bits(size, "integral")) begin
         m_unpack_iter += size;

         for (int i=0; i<size; i++) begin
`ifdef UVM_ENABLE_DEPRECATED_API
            if (big_endian == 1)
              value[ i / $bits(int) ][ i % $bits(int) ] = m_bits[m_unpack_iter-i-1];
            else
`endif
              value[ i / $bits(int) ][ i % $bits(int) ] = m_bits[m_unpack_iter-size+i];
         end   
      end
   end
endfunction


// unpack_string
// -------------

// If num_chars is not -1, then the user only wants to unpack a
// specific number of bytes into the string.
function string uvm_packer::unpack_string();
  byte b;
  int i; i=0;

  while(enough_bits(8,"string") && 
        (m_bits[m_unpack_iter+:8] != 0) )
  begin
    // silly, because cannot append byte/char to string
    unpack_string = {unpack_string," "};
`ifdef UVM_ENABLE_DEPRECATED_API
    if(big_endian == 1) begin
      for(int j=0; j<8; ++j)
        b[7-j] = m_bits[m_unpack_iter+j];
      unpack_string[i] = b;
    end 
    else
`endif
      unpack_string[i] = m_bits[m_unpack_iter +: 8];
    m_unpack_iter += 8;
    ++i;
  end
  if(enough_bits(8,"string"))
    m_unpack_iter += 8;
endfunction 


// Constructor implementation
function uvm_packer::new(string name="");
  super.new(name);
  flush();
endfunction


