#
#-----------------------------------------------------------------------------
# Copyright 2007-2011 Mentor Graphics Corporation
# Copyright 2007-2011 Cadence Design Systems, Inc.
# Copyright 2010 Synopsys, Inc.
# Copyright 2013 NVIDIA Corporation
# Copyright 2019-2020 Tuomas Poikela (tpoikela)
# 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.
#-----------------------------------------------------------------------------
from typing import Dict, Any, List
from .sv import sv, sv_obj
from .uvm_misc import UVMStatusContainer
from .uvm_object_globals import (UVM_PRINT, UVM_NONE, UVM_COPY, UVM_COMPARE,
UVM_RECORD, UVM_SETINT, UVM_SETOBJ, UVM_SETSTR, UVM_PACK, UVM_UNPACK)
from .uvm_globals import uvm_report_error, uvm_report_warning, uvm_report_info
from typing import Tuple
[docs]class UVMObject(sv_obj):
"""
The `UVMObject` class is the base class for all UVM data and hierarchical classes.
Its primary role is to define a set of methods for such common operations as `create`,
`copy`, `compare`, `print`, and `record`.
Classes deriving from `UVMObject` must implement methods such as
`create` and `get_type_name`.
:ivar str name: Name of the object
:ivar int inst_id: Unique instance ID for this object
Group: Seeding
:cvar bool use_uvm_seeding: This bit enables or disables the UVM seeding
mechanism. It globally affects the operation of the `reseed` method.
When enabled, UVM-based objects are seeded based on their type and full
hierarchical name rather than allocation order. This improves random
stability for objects whose instance names are unique across each type.
The `UVMComponent` class is an example of a type that has a unique
instance name.
"""
# Should be set by uvm_*_utils macro
type_id = None # type: Any
depth = 0
m_inst_count = 0
m_inst_count = 0
use_uvm_seeding = True
uvm_global_copy_map = {} # type: Dict['UVMObject', 'UVMObject']
_m_uvm_status_container = UVMStatusContainer()
def __init__(self, name: str):
""" Creates a new uvm_object with the given instance `name`. If `name` is not
supplied, the object is unnamed.
"""
sv_obj.__init__(self)
self.name = name
self.inst_id = UVMObject.m_inst_count
UVMObject.m_inst_count += 1
self.leaf_name = name
[docs] def reseed(self) -> None:
"""
Calls `srandom` on the object to reseed the object using the UVM seeding
mechanism, which sets the seed based on type name and instance name instead
of based on instance position in a thread.
If the `use_uvm_seeding` static variable is set to 0, then reseed() does
not perform any function.
"""
if (UVMObject.use_uvm_seeding):
pass
"""
Group: Identification
"""
[docs] def set_name(self, name: str):
"""
Sets the instance name of this object, overwriting any previously
given name.
Args:
name (str): Name for the object.
"""
self.leaf_name = name
[docs] def get_name(self) -> str:
"""
Returns the name of the object, as provided by the `name` argument in the
`new` constructor or `set_name` method.
Returns:
str: Name of the object.
"""
return self.leaf_name
[docs] def get_full_name(self) -> str:
"""
Objects possessing hierarchy, such as <uvm_components>, override the default
implementation. Other objects might be associated with component hierarchy
but are not themselves components. For example, <uvm_sequence #(REQ,RSP)>
classes are typically associated with a <uvm_sequencer #(REQ,RSP)>. In this
case, it is useful to override get_full_name to return the sequencer's
full name concatenated with the sequence's name. This provides the sequence
a full context, which is useful when debugging.
Returns:
str: The full hierarchical name of this object. The default
implementation is the same as <get_name>, as uvm_objects do not inherently
possess hierarchy.
"""
return self.get_name()
[docs] def get_inst_id(self) -> int:
"""
Returns:
int: The object's unique, numeric instance identifier.
"""
return self.inst_id
[docs] @classmethod
def get_inst_count(self) -> int:
"""
Returns:
int: The current value of the instance counter, which represents the
total number of uvm_object-based objects that have been allocated in
simulation. The instance counter is used to form a unique numeric instance
identifier.
"""
return UVMObject.m_inst_count
[docs] def get_type(self) -> None:
"""
Returns the type-proxy (wrapper) for this object. The `UVMFactory`'s
type-based override and creation methods take arguments of
`uvm_object_wrapper`. This method, if implemented, can be used as convenient
means of supplying those arguments.
The default implementation of this method produces an error and returns
`None`. To enable use of this method, a user's subtype must implement a
version that returns the subtype's wrapper.
For example:
.. code-block:: python
class cmd(UVMObject):
type_id = None
@classmethod
def get_type(cls):
return cls.type_id.get()
Then, to use:
.. code-block:: python
factory.set_type_override(cmd.get_type(), subcmd.get_type())
This function is implemented by the uvm_*_utils functions, if employed.
Returns:
"""
uvm_report_error("NOTYPID", "get_type not implemented in derived class: "
+ str(self), UVM_NONE)
return None
[docs] def get_object_type(self) -> Any:
"""
Function: get_object_type
Returns the type-proxy (wrapper) for this object. The `uvm_factory`'s
type-based override and creation methods take arguments of
`uvm_object_wrapper`. This method, if implemented, can be used as convenient
means of supplying those arguments. This method is the same as the static
`get_type` method, but uses an already allocated object to determine
the type-proxy to access (instead of using the static object).
The default implementation of this method does a factory lookup of the
proxy using the return value from `get_type_name`. If the type returned
by `get_type_name` is not registered with the factory, then a `None`
handle is returned.
For example:
.. code-block:: python
class cmd (UVMObject):
type_id = UVMObjectRegistry()
@classmethod
def type_id get_type(cls):
return type_id.get()
def get_object_type(self):
return cmd.type_id.get()
This function is implemented by the `uvm_*_utils macros, if employed.
Returns:
"""
from .uvm_coreservice import UVMCoreService
cs = UVMCoreService.get()
factory = cs.get_factory()
if self.get_type_name() == "<unknown>":
return None
return factory.find_wrapper_by_name(self.get_type_name())
[docs] def get_type_name(self) -> str:
"""
This function returns the type name of the object, which is typically the
type identifier enclosed in quotes. It is used for various debugging
functions in the library, and it is used by the factory for creating
objects.
This function must be defined in every derived class.
A typical implementation is as follows:
.. code-block:: python
class mytype (UVMObject):
...
type_name = "mytype"
def get_type_name(self):
return my_type.type_name
We define the `type_name` static variable to enable access to the type name
without need of an object of the class, i.e., to enable access via the
scope operator, ~mytype::type_name~.
Returns:
str: Type name of the object.
"""
return "<unknown>"
[docs] def create(self, name="") -> 'UVMObject':
"""
Group: Creation
The `create` method allocates a new object of the same type as this object
and returns it via a base uvm_object handle. Every class deriving from
uvm_object, directly or indirectly, must implement the create method.
A typical implementation is as follows:
.. code-block:: python
class mytype (UVMObject):
...
def create(self, name=""):
mytype t = mytype(name)
return t
Args:
name (str): Name of the created object.
Returns:
obj: New object.
"""
return UVMObject(name)
[docs] def clone(self) -> 'UVMObject':
"""
The `clone` method creates and returns an exact copy of this object.
The default implementation calls `create` followed by `copy`. As clone is
virtual, derived classes may override this implementation if desired.
Returns:
UVMObject: Clone of the object.
"""
tmp = self.create(self.get_name())
if tmp is None:
uvm_report_warning("CRFLD", sv.sformatf(
"The create method failed for %s, object cannot be cloned",
self.get_name()), UVM_NONE)
else:
tmp.copy(self)
return tmp
[docs] def print_obj(self, printer=None) -> None:
"""
Group: Printing
Function: print
The `print` method deep-prints this object's properties in a format and
manner governed by the given `printer` argument; if the `printer` argument
is not provided, the global `uvm_default_printer` is used. See
`uvm_printer` for more information on printer output formatting. See also
`uvm_line_printer`, `uvm_tree_printer`, and `uvm_table_printer` for details
on the pre-defined printer "policies," or formatters, provided by the UVM.
The `print` method is not virtual and must not be overloaded. To include
custom information in the `print` and `sprint` operations, derived classes
must override the `do_print` method and use the provided printer policy
class to format the output.
Args:
printer (UVMPrinter): Printer that is used in printing.
"""
if printer is None:
from .uvm_global_vars import uvm_default_printer
printer = uvm_default_printer
if printer is None:
uvm_report_error("NonePRINTER", "uvm_default_printer is None")
sv.fwrite(printer.knobs.mcd, self.sprint(printer))
[docs] def sprint(self, printer=None) -> str:
"""
The `sprint` method works just like the `print` method, except the output
is returned in a string rather than displayed.
The `sprint` method is not virtual and must not be overloaded. To include
additional fields in the `print` and `sprint` operation, derived classes
must override the `do_print` method and use the provided printer policy
class to format the output. The printer policy will manage all string
concatenations and provide the string to `sprint` to return to the caller.
Args:
printer (UVMPrinter): Printer that is used in printing.
Returns:
str: String representation of the object.
"""
if printer is None:
from .uvm_global_vars import uvm_default_printer
printer = uvm_default_printer
if not printer.istop():
UVMObject._m_uvm_status_container.printer = printer
self._m_uvm_field_automation(None, UVM_PRINT, "")
self.do_print(printer)
return ""
self._m_uvm_status_container = UVMObject._m_uvm_status_container
printer.print_object(self.get_name(), self)
if printer.m_string != "":
return printer.m_string
return printer.emit()
[docs] def do_print(self, printer) -> None:
"""
The `do_print` method is the user-definable hook called by `print` and
`sprint` that allows users to customize what gets printed or sprinted
beyond the field information provided by the `uvm_field_* macros,
<Utility and Field Macros for Components and Objects>.
The `printer` argument is the policy object that governs the format and
content of the output. To ensure correct `print` and `sprint` operation,
and to ensure a consistent output format, the `printer` must be used
by all `do_print` implementations. That is, instead of using ~$display~ or
string concatenations directly, a `do_print` implementation must call
through the ~printer's~ API to add information to be printed or sprinted.
An example implementation of `do_print` is as follows::
class mytype (UVMObject):
data_obj data
int f1
virtual function void do_print (uvm_printer printer)
super.do_print(printer)
printer.print_field_int("f1", f1, $bits(f1), UVM_DEC)
printer.print_object("data", data)
endfunction
Then, to print and sprint the object, you could write::
t = mytype()
t.print()
uvm_report_info("Received",t.sprint())
See `UVMPrinter` for information about the printer API.
Args:
printer (UVMPrinter): Printer that is used in printing.
"""
return
[docs] def convert2string(self) -> str:
"""
This virtual function is a user-definable hook, called directly by the
user, that allows users to provide object information in the form of
a string. Unlike `sprint`, there is no requirement to use a `uvm_printer`
policy object. As such, the format and content of the output is fully
customizable, which may be suitable for applications not requiring the
consistent formatting offered by the `print`/`sprint`/`do_print`
API.
Fields declared in <Utility Macros> macros (`uvm_field_*), if used, will
not automatically appear in calls to convert2string.
An example implementation of convert2string follows.
.. code-block:: python
class Base(UVMObject):
field = "foo"
def convert2string(self):
return "base_field=" + self.field
class Obj2(UVMObject):
field = "bar"
def convert2string()
convert2string = "child_field=" + self.field
class Obj(Base):
addr = 0x123
data = 0x456
write = 1
child = Obj2()
def convert2string(self):
convert2string = super().convert2string() +
sv.sformatf(" write=%0d addr=%8h data=%8h ",write,addr,data) +
child.convert2string()
Then, to display an object, you could write:
.. code-block:: python
o = Obj()
uvm_report_info("BusMaster", "Sending:\n " + o.convert2string())
The output will look similar to::
UVM_INFO @ 0: reporter [BusMaster] Sending:
base_field=foo write=1 addr=00000123 data=00000456 child_field=bar
Returns:
str: Object converted into string.
"""
return ""
def _m_uvm_field_automation(self, tmp_data__, what__, str__) -> None:
pass
[docs] def record(self, recorder=None) -> None:
"""
Group: Recording
The `record` method deep-records this object's properties according to an
optional `recorder` policy. The method is not virtual and must not be
overloaded. To include additional fields in the record operation, derived
classes should override the `do_record` method.
The optional `recorder` argument specifies the recording policy, which
governs how recording takes place. See
`uvm_recorder` for information.
A simulator's recording mechanism is vendor-specific. By providing access
via a common interface, the uvm_recorder policy provides vendor-independent
access to a simulator's recording capabilities.
Args:
recorder (UVMRecorder):
"""
if recorder is None:
return
UVMObject._m_uvm_status_container.recorder = recorder
recorder.recording_depth += 1
self._m_uvm_field_automation(None, UVM_RECORD, "")
self.do_record(recorder)
recorder.recording_depth -= 1
[docs] def do_record(self, recorder) -> None:
"""
The `do_record` method is the user-definable hook called by the `record`
method. A derived class should override this method to include its fields
in a record operation.
The `recorder` argument is policy object for recording this object. A
do_record implementation should call the appropriate recorder methods for
each of its fields. Vendor-specific recording implementations are
encapsulated in the `recorder` policy, thereby insulating user-code from
vendor-specific behavior. See `uvm_recorder` for more information.
A typical implementation is as follows:
.. code-block:: python
class mytype (UVMObject):
data_obj data
int f1
def do_record (self, recorder):
recorder.record_field("f1", f1, sv.bits(f1), UVM_DEC)
recorder.record_object("data", data)
Args:
recorder (UVMRecorder): Recorder policy object.
"""
return
[docs] def copy(self, rhs: 'UVMObject'):
"""
The copy makes this object a copy of the specified object.
The `copy` method is not virtual and should not be overloaded in derived
classes. To copy the fields of a derived class, that class should override
the `do_copy` method.
Args:
rhs (UVMObject): An object to be copied.
"""
# For cycle checking
UVMObject.depth = 0
if (rhs is not None) and rhs in UVMObject.uvm_global_copy_map:
return
if rhs is None:
uvm_report_warning("NoneCP",
"A None object was supplied to copy; copy is ignored", UVM_NONE)
return
UVMObject.uvm_global_copy_map[rhs] = self
UVMObject.depth += 1
self._m_uvm_field_automation(rhs, UVM_COPY, "")
self.do_copy(rhs)
UVMObject.depth -= 1
if UVMObject.depth == 0:
UVMObject.uvm_global_copy_map = {}
[docs] def do_copy(self, rhs) -> None:
"""
The `do_copy` method is the user-definable hook called by the `copy` method.
A derived class should override this method to include its fields in a `copy`
operation.
A typical implementation is as follows:
.. code-block:: python
class mytype (UVMObject):
...
field_1 = 0
def do_copy(self, rhs):
super.do_copy(rhs)
# Optionanl type checking
field_1 = rhs.field_1
The implementation must call `super().do_copy`, and can optionally do
type checking before copying.
Args:
rhs (UVMObject): Object to be copied.
"""
return
[docs] def compare(self, rhs, comparer=None) -> bool:
"""
Deep compares members of this data object with those of the object provided
in the `rhs` (right-hand side) argument, returning 1 on a match, 0 otherwise.
The `compare` method is not virtual and should not be overloaded in derived
classes. To compare the fields of a derived class, that class should
override the `do_compare` method.
The optional `comparer` argument specifies the comparison policy. It allows
you to control some aspects of the comparison operation. It also stores the
results of the comparison, such as field-by-field miscompare information
and the total number of miscompares. If a compare policy is not provided,
then the global `uvm_default_comparer` policy is used. See `uvm_comparer`
for more information.
Args:
rhs (UVMObject): Object to be compared against.
comparer (UVMComparer): Comparer policy object.
Returns:
bool: True if objects match, False otherwise.
"""
# t = 0
dc = 0
#static int style
# style = 0
done = 0
cls = UVMObject
if comparer is not None:
cls._m_uvm_status_container.comparer = comparer
else:
from .uvm_global_vars import uvm_default_comparer
cls._m_uvm_status_container.comparer = uvm_default_comparer
comparer = cls._m_uvm_status_container.comparer
if(not cls._m_uvm_status_container.scope.depth()):
comparer.compare_map.delete()
comparer.result = 0
comparer.miscompares = ""
comparer.scope = cls._m_uvm_status_container.scope
if self.get_name() == "":
cls._m_uvm_status_container.scope.down("<object>")
else:
cls._m_uvm_status_container.scope.down(self.get_name())
if(not done and (rhs is None)):
if(cls._m_uvm_status_container.scope.depth()):
comparer.print_msg_object(self, rhs)
else:
comparer.print_msg_object(self, rhs)
uvm_report_info("MISCMP",
sv.sformatf("%0d Miscompare(s) for object %s@%0d vs. None",
comparer.result,
cls._m_uvm_status_container.scope.get(),
self.get_inst_id()),
cls._m_uvm_status_container.comparer.verbosity)
done = 1
if(not done and comparer.compare_map.exists(rhs)):
if(comparer.compare_map[rhs] != self):
comparer.print_msg_object(self, comparer.compare_map[rhs])
done = 1 # don't do any more work after this case, but do cleanup
if(not done and comparer.check_type and (rhs is not None) and
(self.get_type_name() != rhs.get_type_name())):
cls._m_uvm_status_container.stringv = ("lhs type = \"" + self.get_type_name()
+ "' : rhs type = '" + rhs.get_type_name() + "'")
comparer.print_msg(cls._m_uvm_status_container.stringv)
if not done:
comparer.compare_map[rhs] = self
self._m_uvm_field_automation(rhs, UVM_COMPARE, "")
dc = self.do_compare(rhs, comparer)
if cls._m_uvm_status_container.scope.depth() == 1:
cls._m_uvm_status_container.scope.up()
if rhs is not None:
comparer.print_rollup(self, rhs)
return (comparer.result == 0 and dc == 1)
[docs] def do_compare(self, rhs, comparer) -> bool:
"""
The `do_compare` method is the user-definable hook called by the `compare`
method. A derived class should override this method to include its fields
in a compare operation. It should return 1 if the comparison succeeds, 0
otherwise.
A typical implementation is as follows:
.. code-block:: python
class mytype (UVMObject):
...
f1 = 0
def do_compare(self, rhs, comparer):
do_compare = super.do_compare(rhs,comparer)
# Optional type checking
do_compare &= comparer.compare_field_int("f1", f1, rhs.f1)
return do_compare
A derived class implementation must call `super().do_compare` to ensure its
base class' properties, if any, are included in the comparison. If type
matching is required instead of duck-typing, the user can also
implemented this checking.
The actual comparison should be implemented using the `UVMComparer` object
rather than direct field-by-field comparison. This enables users of your
class to customize how comparisons are performed and how much miscompare
information is collected. See `UVMComparer` for more details.
Args:
rhs (UVMObject):
comparer (UVMComparer):
Returns:
bool: True if objects match, False otherwise.
"""
return True
# // Group: Packing
# // Function: pack
#
# extern function int pack (ref bit bitstream[],
# input uvm_packer packer=None)
[docs] def pack(self, packer=None) -> Tuple[Any, Any]:
packer = self.m_pack(packer)
return packer.get_packed_size(), packer.get_bits()
# // Function: pack_bytes
#
# extern function int pack_bytes (ref byte unsigned bytestream[],
# input uvm_packer packer=None)
[docs] def pack_bytes(self, bytestream, packer=None) -> Any:
packer = self.m_pack(packer)
packed_bytes = packer.get_bytes()
for b in packed_bytes:
bytestream.append(b)
return packer.get_packed_size()
[docs] def pack_ints(self, intstream: List, packer=None) -> Any:
"""
Function: pack_ints
The pack methods bitwise-concatenate this object's properties into an array
of bits, bytes, or ints. The methods are not virtual and must not be
overloaded. To include additional fields in the pack operation, derived
classes should override the <do_pack> method.
The optional `packer` argument specifies the packing policy, which governs
the packing operation. If a packer policy is not provided, the global
<uvm_default_packer> policy is used. See <uvm_packer> for more information.
The return value is the total number of bits packed into the given array.
Use the array's built-in `size` method to get the number of bytes or ints
consumed during the packing process.
"""
#
# extern function int pack_ints (ref int unsigned intstream[],
# input uvm_packer packer=None)
packer = self.m_pack(packer)
ints = packer.get_ints()
for i in ints:
intstream.append(i)
return packer.get_packed_size()
[docs] def do_pack(self, packer) -> None:
"""
Function: do_pack
The `do_pack` method is the user-definable hook called by the <pack> methods.
A derived class should override this method to include its fields in a pack
operation.
The `packer` argument is the policy object for packing. The policy object
should be used to pack objects.
A typical example of an object packing itself is as follows
.. code-block:: python
class mysubtype(mysupertype):
...
# shortint myshort
# obj_type myobj
# byte myarray[]
...
function void do_pack (uvm_packer packer)
super.do_pack(packer); // pack mysupertype properties
packer.pack_field_int(len(myarray), 32)
for index in range(len(myarray):
packer.pack_field_int(myarray[index], 8)
packer.pack_field_int(myshort, sv.bits(myshort))
packer.pack_object(myobj)
endfunction
The implementation must call ~super.do_pack~ so that base class properties
are packed as well.
If your object contains dynamic data (object, string, queue, dynamic array,
or associative array), and you intend to unpack into an equivalent data
structure when unpacking, you must include meta-information about the
dynamic data when packing as follows.
- For queues, dynamic arrays, or associative arrays, pack the number of
elements in the array in the 32 bits immediately before packing
individual elements, as shown above.
- For string data types, append a zero byte after packing the string
contents.
- For objects, pack 4 bits immediately before packing the object. For `None`
objects, pack 4'b0000. For non-`None` objects, pack 4'b0001.
When the uvm_field_* macros are used,
<Utility and Field Macros for Components and Objects>,
the above meta information is included provided the <uvm_packer::use_metadata>
variable is set for the packer.
Packing order does not need to match declaration order. However, unpacking
order must match packing order.
"""
return
# // Group: Unpacking
#
# // Function: unpack
#
# extern function int unpack (ref bit bitstream[],
# input uvm_packer packer=None)
[docs] def unpack(self, bitstream, packer=None) -> Any:
packer = self.m_unpack_pre(packer)
packer.put_bits(bitstream)
self.m_unpack_post(packer)
packer.set_packed_size()
return packer.get_packed_size()
# // Function: unpack_bytes
#
# extern function int unpack_bytes (ref byte unsigned bytestream[],
# input uvm_packer packer=None)
[docs] def unpack_bytes(self, bytestream, packer=None) -> Any:
packer = self.m_unpack_pre(packer)
packer.put_bytes(bytestream)
self.m_unpack_post(packer)
packer.set_packed_size()
return packer.get_packed_size()
[docs] def unpack_ints(self, intstream, packer=None) -> Any:
"""
The unpack methods extract property values from an array of bits, bytes, or
ints. The method of unpacking `must` exactly correspond to the method of
packing. This is assured if (a) the same `packer` policy is used to pack
and unpack, and (b) the order of unpacking is the same as the order of
packing used to create the input array.
The unpack methods are fixed (non-virtual) entry points that are directly
callable by the user. To include additional fields in the <unpack>
operation, derived classes should override the <do_unpack> method.
The optional `packer` argument specifies the packing policy, which governs
both the pack and unpack operation. If a packer policy is not provided,
then the global `uvm_default_packer` policy is used. See uvm_packer for
more information.
The return value is the actual number of bits unpacked from the given array.
Args:
instream (List):
packer (UVMPacker):
Returns:
int: Packed size
"""
packer = self.m_unpack_pre(packer)
packer.put_ints(intstream)
self.m_unpack_post(packer)
packer.set_packed_size()
return packer.get_packed_size()
# // Function: do_unpack
# //
# // The `do_unpack` method is the user-definable hook called by the <unpack>
# // method. A derived class should override this method to include its fields
# // in an unpack operation.
# //
# // The `packer` argument is the policy object for both packing and unpacking.
# // It must be the same packer used to pack the object into bits. Also,
# // do_unpack must unpack fields in the same order in which they were packed.
# // See <uvm_packer> for more information.
# //
# // The following implementation corresponds to the example given in do_pack.
# //
# //| function void do_unpack (uvm_packer packer)
# //| int sz
# //| super.do_unpack(packer); // unpack super's properties
# //| sz = packer.unpack_field_int(myarray.size(), 32)
# //| myarray.delete()
# //| for(int index=0; index<sz; index++)
# //| myarray[index] = packer.unpack_field_int(8)
# //| myshort = packer.unpack_field_int($bits(myshort))
# //| packer.unpack_object(myobj)
# //| endfunction
# //
# // If your object contains dynamic data (object, string, queue, dynamic array,
# // or associative array), and you intend to <unpack> into an equivalent data
# // structure, you must have included meta-information about the dynamic data
# // when it was packed.
# //
# // - For queues, dynamic arrays, or associative arrays, unpack the number of
# // elements in the array from the 32 bits immediately before unpacking
# // individual elements, as shown above.
# //
# // - For string data types, unpack into the new string until a `None` byte is
# // encountered.
# //
# // - For objects, unpack 4 bits into a byte or int variable. If the value
# // is 0, the target object should be set to `None` and unpacking continues to
# // the next property, if any. If the least significant bit is 1, then the
# // target object should be allocated and its properties unpacked.
[docs] def do_unpack(self, packer) -> None:
return
[docs] def set_int_local(self, field_name: str, value: int, recurse=True):
"""
Group: Configuration
Args:
field_name (str): Variable to set
value: Value for the variable
recurse (bool):
"""
UVMObject._m_uvm_status_container.cycle_check.clear()
UVMObject._m_uvm_status_container.m_uvm_cycle_scopes.clear()
UVMObject._m_uvm_status_container.status = False
UVMObject._m_uvm_status_container.bitstream = value
self._m_uvm_field_automation(None, UVM_SETINT, field_name)
if UVMObject._m_uvm_status_container.warning and not self._m_uvm_status_container.status:
uvm_report_error("NOMTC", sv.sformatf("did not find a match for field %s", field_name),UVM_NONE)
UVMObject._m_uvm_status_container.cycle_check.clear()
[docs] def set_string_local(self, field_name: str, value: str, recurse=True):
"""
Function: set_string_local
Args:
field_name (str): Variable to set
value: Value for the variable
recurse (bool): If True, recurse into sub-objects.
"""
UVMObject._m_uvm_status_container.cycle_check.clear()
UVMObject._m_uvm_status_container.m_uvm_cycle_scopes.clear()
UVMObject._m_uvm_status_container.status = False
UVMObject._m_uvm_status_container.stringv = value
self._m_uvm_field_automation(None, UVM_SETSTR, field_name)
if UVMObject._m_uvm_status_container.warning and not UVMObject._m_uvm_status_container.status:
uvm_report_error("NOMTC", sv.sformatf("did not find a match for field %s (@%0d)",
field_name, self.get_inst_id()), UVM_NONE)
UVMObject._m_uvm_status_container.cycle_check.clear()
[docs] def set_object_local(self, field_name: str, value: 'UVMObject', clone=1, recurse=1):
"""
These methods provide write access to integral, string, and
uvm_object-based properties indexed by a `field_name` string. The object
designer choose which, if any, properties will be accessible, and overrides
the appropriate methods depending on the properties' types. For objects,
the optional `clone` argument specifies whether to clone the `value`
argument before assignment.
The global `uvm_is_match` function is used to match the field names, so
`field_name` may contain wildcards.
An example implementation of all three methods is as follows.
.. code-block:: python
class mytype(UVMObject):
def __init__(self, name):
super().__init__(name)
self.myint = 0
self.mybyte = 0
self.myshort = 0
self.mystring = ""
self.myobj = None
# provide access to integral properties
def set_int_local(self, field_name, value):
if (uvm_is_match (field_name, "myint")):
self.myint = value
elif (uvm_is_match (field_name, "mybyte")):
selef.mybyte = value
# provide access to string properties
def set_string_local(self, field_name, value):
if (uvm_is_match (field_name, "mystring")):
self.mystring = value
# provide access to sub-objects
def set_object_local(self, field_name, value,clone=1):
if (uvm_is_match (field_name, "myobj")):
if (value is not None):
tmp = None
# if provided value is not correct type, produce error
if (!$cast(tmp, value)):
# error
else:
if(clone)
self.myobj = tmp.clone()
else
self.myobj = tmp
else:
myobj = None # value is None, so simply assign None to myobj
end
...
Although the object designer implements these methods to provide outside
access to one or more properties, they are intended for internal use (e.g.,
for command-line debugging and auto-configuration) and should not be called
directly by the user.
Args:
field_name (str): Variable to set
value: Value for the variable
clone (bool):
recurse (bool):
"""
# cc = None # uvm_object cc
UVMObject._m_uvm_status_container.cycle_check.clear()
UVMObject._m_uvm_status_container.m_uvm_cycle_scopes.clear()
if clone and (value is not None):
cc = value.clone()
if cc is not None:
cc.set_name(field_name)
value = cc
UVMObject._m_uvm_status_container.status = False
UVMObject._m_uvm_status_container.object = value
UVMObject._m_uvm_status_container.clone = clone
self._m_uvm_field_automation(None, UVM_SETOBJ, field_name)
if UVMObject._m_uvm_status_container.warning and not UVMObject._m_uvm_status_container.status:
uvm_report_error("NOMTC", sv.sformatf("did not find a match for field %s", field_name), UVM_NONE)
UVMObject._m_uvm_status_container.cycle_check.clear()
# //---------------------------------------------------------------------------
# // **** Internal Methods and Properties ***
# // Do not use directly
# //---------------------------------------------------------------------------
#
# extern local function void m_pack (inout uvm_packer packer)
[docs] def m_pack(self, packer) -> Any:
if packer is not None:
UVMObject._m_uvm_status_container.packer = packer
else:
from .uvm_global_vars import uvm_default_packer
UVMObject._m_uvm_status_container.packer = uvm_default_packer
packer = UVMObject._m_uvm_status_container.packer
packer.reset()
packer.scope.down(self.get_name())
self._m_uvm_field_automation(None, UVM_PACK, "")
self.do_pack(packer)
packer.set_packed_size()
packer.scope.up()
return packer
# extern local function void m_unpack_pre (inout uvm_packer packer)
[docs] def m_unpack_pre(self, packer) -> Any:
if packer is not None:
UVMObject._m_uvm_status_container.packer = packer
else:
from .uvm_global_vars import uvm_default_packer
UVMObject._m_uvm_status_container.packer = uvm_default_packer
packer = UVMObject._m_uvm_status_container.packer
packer.reset()
return packer
# extern local function void m_unpack_post (uvm_packer packer)
[docs] def m_unpack_post(self, packer) -> None:
provided_size = packer.get_packed_size()
# Put this object into the hierarchy
packer.scope.down(self.get_name())
self._m_uvm_field_automation(None, UVM_UNPACK, "")
self.do_unpack(packer)
# Scope back up before leaving
packer.scope.up()
if packer.get_packed_size() != provided_size:
uvm_report_warning("BDUNPK", sv.sformatf(
"Unpack operation unsuccessful: unpacked %0d bits from a total of %0d bits",
packer.get_packed_size(), provided_size), UVM_NONE)