From f248b2f4f76fb2dd40566e541dfc83da46105a60 Mon Sep 17 00:00:00 2001 From: Ray Salemi Date: Thu, 27 Jun 2024 12:07:00 -0400 Subject: [PATCH 1/7] Added rft information to docstrings in base classes --- pyuvm/s05_base_classes.py | 109 ++++++++++++++++++++++++-------------- 1 file changed, 70 insertions(+), 39 deletions(-) diff --git a/pyuvm/s05_base_classes.py b/pyuvm/s05_base_classes.py index 14c277a..a89eaa7 100644 --- a/pyuvm/s05_base_classes.py +++ b/pyuvm/s05_base_classes.py @@ -19,6 +19,9 @@ class uvm_object(utility_classes.uvm_void): # 5.3.2 def __init__(self, name=''): + """ + :param name: Name of the object. Default is empty string. + """ assert (isinstance(name, str)), \ f"{name} is not a string it is a {type(name)}" self.set_name(name) @@ -31,7 +34,9 @@ def get_uvm_seeding(self): # 5.3.3.2 def set_uvm_seeding(self, enable): - """ Not implemented """ + """ + Not implemented + """ raise error_classes.UVMNotImplemented( 'set_uvm_seeding not implemented') @@ -43,6 +48,8 @@ def reseed(self): # 5.3.3.4 def get_name(self): """ + :return: String with name of uvm_object. + Return the name of this object as passed by the constructor """ assert (self._obj_name is not None), \ @@ -52,6 +59,8 @@ def get_name(self): # 5.3.4.1 def set_name(self, name): """ + :param name: Name of the object + Set the name """ assert (isinstance(name, str)), "Must set the name to a string" @@ -60,6 +69,8 @@ def set_name(self, name): # 5.3.4.3 def get_full_name(self): """ + :return: The full path and name of the object + The full name for a uvm_object is simply the name """ return self.get_name() @@ -67,8 +78,9 @@ def get_full_name(self): # 5.3.4.4 def get_inst_id(self): """ - Returns the python ID which fits the bill - for what the ID is supposed to be. + :return: The python ID which fits the bill for what the ID + is supposed to be. + """ return id(self) @@ -95,7 +107,8 @@ def get_object_type(self): # 5.3.4.7 def get_type_name(self): """ - Returns types __name__ magic variable + :return: Returns the type's ``__name__`` magic variable + """ return type(self).__name__ @@ -111,9 +124,8 @@ def create(cls, name): # 5.3.5.2 def clone(self): """ - Unlike the clone in the SystemVerilog UVM, this clone() - uses copy.deepcopy so the user does not need to override - do_copy() + :return: A new object with the same name and data as this object. + """ new = self.__class__(self.get_name()) new.copy(self) @@ -143,10 +155,9 @@ def do_print(self): # 5.3.6.4 def convert2string(self): """ - :return: __str__() + :return: The result of ``__str__()`` - Returns the result of self.__str__(). - Perhaps better to just use __str__() directly? + Override if you want something different than ``__str__()`` """ return self.__str__() @@ -154,10 +165,10 @@ def convert2string(self): # 5.3.7 def record(self): """ - Not implemented as we are not in a simulator. + Not implemented. """ raise error_classes.UVMNotImplemented( - 'Python does not run in the simulator, so no recording') + 'Perhaps a future project?') # 5.3.7.2 def do_record(self): @@ -165,19 +176,27 @@ def do_record(self): Not implemented as we are not in a simulator """ raise error_classes.UVMNotImplemented( - 'Python does not run in the simulator, so no recording') + 'No recording') # 5.3.8.1 def copy(self, rhs): """ - Copy fields from rhs to this object + :param rhs: The object to copy from + :return: None + + Copy fields from rhs to this object using ``self.do_copy()`` + """ self.do_copy(rhs) # 5.3.8.2 def do_copy(self, rhs): """ - Copies name. Override to copy additional data members + :param rhs: The object to copy from + :return: None + + By default we copy the name. Override this function + to copy the rest of the object members. """ self.set_name(rhs.get_name()) @@ -196,14 +215,18 @@ def compare(self, rhs): # 5.3.9.2 def do_compare(self, rhs): """ - Recommend overriding __eq__() rather than this method. + :param rhs: The object being compared. + :returns: True if the objects are the same. + + Uses ``__eq__()`` to compare the objects. Override this + to change the compare behavior. """ return self.__eq__(rhs) # 5.3.10.1 def pack(self): """ - Not implemented yet. There are Pythonic solutions to this. + Not implemented. There are Pythonic solutions to this. """ raise error_classes.UsePythonMethod( "use struct, pickle, json, or yaml.") @@ -211,7 +234,7 @@ def pack(self): # 5.3.10.1 def pack_bytes(self): """ - Not implemented yet. There are Pythonic solutions to this. + Not implemented. There are Pythonic solutions to this. """ raise error_classes.UsePythonMethod( "use struct, pickle, json, or yaml.") @@ -219,7 +242,7 @@ def pack_bytes(self): # 5.3.10.1 def pack_ints(self): """ - Not implemented yet. There are Pythonic solutions to this. + Not implemented. There are Pythonic solutions to this. """ raise error_classes.UsePythonMethod( "use struct, pickle, json, or yaml.") @@ -227,7 +250,7 @@ def pack_ints(self): # 5.3.10.1 def pack_longints(self): """ - Not implemented yet. There are Pythonic solutions to this. + Not implemented. There are Pythonic solutions to this. """ raise error_classes.UsePythonMethod( "use struct, pickle, json, or yaml.") @@ -235,7 +258,7 @@ def pack_longints(self): # 5.3.10.2 def do_pack(self): """ - Not implemented yet. There are Pythonic solutions to this. + Not implemented. There are Pythonic solutions to this. """ raise error_classes.UsePythonMethod( "use struct, pickle, json, or yaml.") @@ -243,7 +266,7 @@ def do_pack(self): # 5.3.11.1 def unpack(self): """ - Not implemented yet. There are Pythonic solutions to this. + Not implemented. There are Pythonic solutions to this. """ raise error_classes.UsePythonMethod( "use pickle, json, or yaml.") @@ -251,7 +274,7 @@ def unpack(self): # 5.3.14.1 def push_active_policy(self): """ - Not implemented yet. + Not implemented. """ raise error_classes.UVMNotImplemented( "policies not implemented yet") @@ -259,21 +282,21 @@ def push_active_policy(self): # 5.3.14.2 def pop_active_policy(self): """ - Not implemented yet. + Not implemented. """ raise error_classes.UVMNotImplemented("policies not implemented yet") # 5.3.14.3 def get_active_policy(self): """ - Not implemented yet. + Not implemented. """ raise error_classes.UVMNotImplemented("policies not implemented yet") # 5.3.11.1 def unpack_bytes(self): """ - Not implemented yet. There are Pythonic solutions to this. + Not implemented. There are Pythonic solutions to this. """ raise error_classes.UsePythonMethod( "use struct, pickle, json, or yaml.") @@ -281,7 +304,7 @@ def unpack_bytes(self): # 5.3.11.1 def unpack_ints(self): """ - Not implemented yet. There are Pythonic solutions to this. + Not implemented. There are Pythonic solutions to this. """ raise error_classes.UsePythonMethod( "use struct, pickle, json, or yaml.") @@ -289,7 +312,7 @@ def unpack_ints(self): # 5.3.11.1 def unpack_longints(self): """ - Not implemented yet. There are Pythonic solutions to this. + Not implemented. There are Pythonic solutions to this. """ raise error_classes.UsePythonMethod( "use struct, pickle, json, or yaml.") @@ -297,7 +320,7 @@ def unpack_longints(self): # 5.3.11.2 def do_unpack(self): """ - Not implemented yet. There are Pythonic solutions to this. + Not implemented. There are Pythonic solutions to this. """ raise error_classes.UsePythonMethod( "use struct, pickle, json, or yaml.") @@ -356,7 +379,7 @@ class uvm_transaction(uvm_object): def __init__(self, name="", initiator=None): """ :param name: Object name - :param initiator: component that is initiator + :param initiator: component that is the initiator """ super().__init__(name) self.set_initiator(initiator) @@ -367,7 +390,7 @@ def __init__(self, name="", initiator=None): def set_id_info(self, other): """ - :param other: uvm_transaction + :param other: uvm_transaction with transaction_id :return: None Set transaction_id from other @@ -399,8 +422,9 @@ def __not_implemented(self): # 5.4.2.2 def accept_tr(self, accept_time=0): """ - 5.4.2.2 - :param time: simulation time + :param accept_time: Simulation time when the transaction is accepted + + IEEE 1800.2 5.4.2.2 """ if (accept_time is not None) and (accept_time != 0): self._accept_time = accept_time @@ -412,7 +436,7 @@ def accept_tr(self, accept_time=0): # 5.4.2.3 def do_accept_tr(self): """ - User definable method + User definable method to add to ``accept_tr()`` """ pass @@ -532,26 +556,31 @@ def get_event_pool(self): # 5.4.2.16 def get_accept_time(self) -> int: """ - Returns the Accept time of transaction + :return: Accept time of transaction + """ return self._accept_time def get_begin_time(self) -> int: """ - Returns the Begin time of transaction + :return: Begin time of transaction + """ return self._begin_time def get_end_time(self) -> int: """ - Returns the End time of transaction + :return: End time of transaction + """ return self._end_time # 5.4.2.17 def set_transaction_id(self, txn_id): """ - Sets variable transaction_id + :param txn_id: Transaction ID + + Sets transaction's transaction_id """ assert (isinstance(txn_id, int)), "Transaction ID must be an integer." self.transaction_id = txn_id @@ -559,7 +588,9 @@ def set_transaction_id(self, txn_id): # 5.4.2.18 def get_transaction_id(self): """ - Returns variable transaction_id + :return: Transaction ID + + Returns transaction_id """ if self.transaction_id is None: return id(self) From 0c7b092675931938795c468269ddb8a2ac53df4d Mon Sep 17 00:00:00 2001 From: Ray Salemi Date: Thu, 27 Jun 2024 12:18:20 -0400 Subject: [PATCH 2/7] commented reporting classes --- docs/conf.py | 3 -- pyuvm/s06_reporting_classes.py | 50 ++++++++++++++++++++++++++++++++-- 2 files changed, 47 insertions(+), 6 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 9f90279..c3b386b 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -15,12 +15,9 @@ # documentation root, use os.path.abspath to make it absolute, like shown here. #sys.path.insert(0, os.path.abspath('.')) src_path = pathlib.Path(__file__).parents[1] # /"pyuvm" -print("src_path is: ",src_path) # src_path = src_path/"pyuvm" src_path = src_path.resolve().as_posix() -print("Inserted src_path is: ",src_path) sys.path.insert(0, src_path) -print("THIS IS THE SYS PATH: ",sys.path) # -- General configuration ----------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. diff --git a/pyuvm/s06_reporting_classes.py b/pyuvm/s06_reporting_classes.py index 0a986e1..66c7227 100644 --- a/pyuvm/s06_reporting_classes.py +++ b/pyuvm/s06_reporting_classes.py @@ -17,10 +17,18 @@ class PyuvmFormatter(SimColourLogFormatter): def __init__(self, full_name): + """ + :param full_name: The full name of the object + + """ self.full_name = full_name super().__init__() def format(self, record): + """ + :param record: The log record + + """ new_msg = f"[{self.full_name}]: {record.msg}" record.msg = new_msg name_temp = record.name @@ -38,6 +46,11 @@ class uvm_report_object(uvm_object): __default_logging_level = logging.INFO """ The basis of all classes that can report """ def __init__(self, name): + """ + :param name: The name of the object + :returns: None + + """ super().__init__(name) uvm_root_logger = logging.getLogger('uvm') # Every object gets its own logger @@ -57,18 +70,35 @@ def __init__(self, name): @staticmethod def set_default_logging_level(default_logging_level): + """ + :param default_logging_level: The default logging level + :returns: None + + """ uvm_report_object.__default_logging_level = default_logging_level @staticmethod def get_default_logging_level(): + """ + :returns: The default logging level + + """ return uvm_report_object.__default_logging_level def set_logging_level(self, logging_level): - """ Sets the logger level """ + """ + :param logging_level: The logging level + :returns: None + + """ self.logger.setLevel(logging_level) def add_logging_handler(self, handler): - """ Adds a handler """ + """ + :param handler: The logging handler + :returns: None + + """ assert isinstance(handler, logging.Handler), \ f"You must pass a logging.Handler not {type(handler)}" if handler.formatter is None: @@ -77,14 +107,28 @@ def add_logging_handler(self, handler): self.logger.addHandler(handler) def remove_logging_handler(self, handler): - """ Removes a specific handler """ + """ + :param handler: The logging handler to remove + :returns: None + + """ assert isinstance(handler, logging.Handler), \ f"You must pass a logging.Handler not {type(handler)}" self.logger.removeHandler(handler) def remove_streaming_handler(self): + """ + :returns: None + + Removes the streaming handler + """ self.logger.removeHandler(self._streaming_handler) def disable_logging(self): + """ + :returns: None + + Disables logging + """ self.remove_streaming_handler() self.add_logging_handler(NullHandler()) From 2cd6a8b64d0ccb5bd769f91b7090f89868b96108 Mon Sep 17 00:00:00 2001 From: Ray Salemi Date: Thu, 27 Jun 2024 16:21:15 -0400 Subject: [PATCH 3/7] Doc for s08_factory_classes --- README.md | 2 +- pyuvm/s06_reporting_classes.py | 2 +- pyuvm/s08_factory_classes.py | 107 ++++++++++++++++++--------------- 3 files changed, 62 insertions(+), 49 deletions(-) diff --git a/README.md b/README.md index a1d8a2e..487da6f 100644 --- a/README.md +++ b/README.md @@ -471,7 +471,7 @@ Credits: # License -Copyright 2020 Siemens EDA +Copyright 2020 Siemens EDA and Ray Salemi Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pyuvm/s06_reporting_classes.py b/pyuvm/s06_reporting_classes.py index 66c7227..c7777e6 100644 --- a/pyuvm/s06_reporting_classes.py +++ b/pyuvm/s06_reporting_classes.py @@ -49,7 +49,7 @@ def __init__(self, name): """ :param name: The name of the object :returns: None - + """ super().__init__(name) uvm_root_logger = logging.getLogger('uvm') diff --git a/pyuvm/s08_factory_classes.py b/pyuvm/s08_factory_classes.py index 3ccd1a5..3ccf91e 100644 --- a/pyuvm/s08_factory_classes.py +++ b/pyuvm/s08_factory_classes.py @@ -28,7 +28,7 @@ # 8.3.1.1 class uvm_factory(metaclass=utility_classes.Singleton): """ - The uvm_factory is a singleton that delivers all factory functions. + The uvm_factory is a singleton that delivers all UVM factory functions. """ # 8.3.1.2.1 get @@ -46,10 +46,16 @@ def __init__(self): self.debug_level = 1 def clear_all(self): + """ + Clear all the classes and overrides from the factory + """ self.fd.clear_classes() self.clear_overrides() def clear_overrides(self): + """ + Clear all the overrides from the factory + """ self.fd.clear_overrides() def __set_override(self, original, override, path=None): @@ -61,10 +67,12 @@ def __set_override(self, original, override, path=None): def set_inst_override_by_type(self, original_type, override_type, full_inst_path): """ - Override an instance with a new type if original type is at that path :param original_type: The original type being overridden :param override_type: The overriding type - :param full_inst_path: The inst where this happens. + :param full_inst_path: The inst where this happens + :return: None + + Override an instance with a new type if original type is at that path """ # The intention here is to only override when a type of original_type @@ -90,12 +98,13 @@ def set_inst_override_by_type(self, original_type, override_type, def set_inst_override_by_name(self, original_type_name, override_type_name, full_inst_path): """ - Override a specific instance using strings that contain - the names of the types. :param original_type_name: the name of type being replaced :param override_type_name: the name of the substitute type :param full_inst_path: The path to the instance - :return: + :return: None + + Override a specific instance using strings that contain + the names of the types. """ # Here we use the names of classes instead of the classes. @@ -136,8 +145,9 @@ def set_type_override_by_type(self, original_type, override_type, :param original_type: The original type to be overridden :param override_type: The new type that will override it :param replace: If the override exists, only replace it if this is True + :return: None - Override one type with another type globally + Override one type with another type globally """ assert issubclass(original_type, utility_classes.uvm_void), \ "You tried to override a non-uvm_void class" @@ -184,15 +194,6 @@ def set_type_override_by_name(self, original_type_name, self.__set_override(original_type, override_type) def __find_override(self, requested_type, parent_inst_path="", name=""): - """ - An internal function that finds overrides - - :param requested_type: The type that could be overridden - :param parent_inst_path: The parent inst path for an override - :param name: The name of the object, concatenated with parent - inst path for override - :return: either the requested_type or its override - """ if not isinstance(requested_type, str): assert (issubclass(requested_type, utility_classes.uvm_void)), \ f"You must create uvm_void descendants not {requested_type}" @@ -215,12 +216,14 @@ def __find_override(self, requested_type, parent_inst_path="", name=""): def create_object_by_type(self, requested_type, parent_inst_path="", name=""): """ - 8.3.1.5 Creation :param requested_type: The type that we request but that can be - overridden + overridden :param parent_inst_path: The get_full_name path of the parent :param name: The name of the instance requested_type("name") + :raises: UVMFactoryError if the type is not in the factory :return: Type that is child of uvm_object. + + 8.3.1.5 Creation If the type is is not in the factory we raise UVMFactoryError """ new_type = self.__find_override(requested_type, parent_inst_path, name) @@ -233,12 +236,13 @@ def create_object_by_type(self, requested_type, parent_inst_path="", def create_object_by_name(self, requested_type_name, parent_inst_path="", name=""): """ - Create an object using a string to define its uvm_object type. - :param requested_type_name: the type that could be overridden :param parent_inst_path: A path if we are checking for inst overrides :param name: The name of the new object. + :raises: UVMFactoryError if the type is not in the factory :return: A uvm_object with the name given + + Create an object using a string to define its uvm_object type. """ try: requested_type = utility_classes.FactoryData().classes[requested_type_name] # noqa @@ -259,7 +263,7 @@ def create_component_by_type(self, requested_type, parent_inst_path="", :param name: Concatenated with parent_inst_path if it exists for inst overrides :param parent: The parent component - + :raises: UVMFactoryError if the type is not in the factory :return: a uvm_component with the name an parent given. Create a component of the requested uvm_component type. @@ -289,6 +293,7 @@ def create_component_by_name(self, requested_type_name, :param parent_inst_path: A path if we are checking for inst overrides :param name: The name of the new object. :param parent: The component's parent component + :raises: UVMFactoryError if the type is not in the factory :return: A uvm_object with the name given """ if name is None: @@ -308,11 +313,12 @@ def create_component_by_name(self, requested_type_name, # 8.3.1.6.1 def set_type_alias(self, alias_type_name, original_type): """ - Not implemented as it does not seem to exist in SystemVerilog UVM - :param alias_type_name:A string that will reference the original type :param original_type:The original type toe be referenced + :raises: UVMNotImplemented Not implemented as it does not seem + to exist in the SystemVerilog UVM :return:None + """ # This method does not seem to be implemented in SystemVerilog # so I'm skipping it now. @@ -322,12 +328,13 @@ def set_type_alias(self, alias_type_name, original_type): # 8.3.1.6.2 def set_inst_alias(self, alias_type_name, original_type, full_inst_path): """ - Not implemented as it does not seem to exist in SystemVerilog UVM - :param alias_type_name:A string that will reference the original type :param original_type:The original type toe be referenced :param full_inst_path: The instance path where this alias is active + :raises: UVMNotImplemented Not implemented as it does + not seem to exist in SystemVerilog UVM :return:None + """ # This method does not seem to be implemented in SystemVerilog # so I'm skipping it now. @@ -339,11 +346,12 @@ def set_inst_alias(self, alias_type_name, original_type, full_inst_path): # 8.3.1.7.1 def find_override_by_type(self, requested_type, full_inst_path): """ - Given a type and instance path, return the override class object. - :param requested_type: The type whose override you want :param full_inst_path: The inst path where one looks + :raises: UVMFactoryError if the type is not in the factory :return: class object + + Given a type and instance path, return the override class object. """ override = self.__find_override(requested_type, full_inst_path) return override @@ -351,11 +359,12 @@ def find_override_by_type(self, requested_type, full_inst_path): # 8.3.1.7.1 def find_override_by_name(self, requested_type_name, full_inst_path): """ - Given a path and the name of a class return its overriding class object - :param requested_type_name: :param full_inst_path: + :raises: UVMFactoryError if the type is not in the factory :return: class object + + Given a path and the name of a class return its overriding class object """ assert (isinstance(requested_type_name, str)), \ f"requested_type_name must be a string not a {type(requested_type_name)}" # noqa @@ -370,7 +379,9 @@ def find_override_by_name(self, requested_type_name, full_inst_path): # 8.3.1.7.2 def find_wrapper_by_name(self): """ - There are no wrappers in pyuvm so this is not implemented. + :raises: UVMNotImplemented There are no wrappers in + **pyuvm** so this is not implemented. + """ raise error_classes.UVMNotImplemented( "There are no wrappers in pyuvm. " @@ -379,10 +390,10 @@ def find_wrapper_by_name(self): # 8.3.1.7.3 def is_type_name_registered(self, type_name): """ - Checks that a type of this name is registered with the factory. - :param type_name: string that is name of a type - :return: boolean + :return: boolean True if type is registered + + Checks that a type of this name is registered with the factory. """ assert (isinstance(type_name, str)), \ ("is_type_name_registered() takes a" @@ -392,11 +403,13 @@ def is_type_name_registered(self, type_name): # 8.3.1.7.4 def is_type_registered(self, uvm_type): """ + :param uvm_type: The type to be checked + :return: boolean True if type is registered + Checks that a type is registered. The argument is named "obj" in the spec, but that name is ridiculous and confusing. - :param uvm_type: The type to be checked - :return: boolean + """ assert (issubclass(uvm_type, utility_classes.uvm_void)), \ ("is_type_registered() takes a subclass of uvm_void " @@ -406,18 +419,18 @@ def is_type_registered(self, uvm_type): @property def debug_level(self): """ - uvm_factory().debug_level = 0 : overrides - uvm_factory().debug_level = 1 : user defined types + above - uvm_factory().debug_level = 2 : uvm_* types + above + * uvm_factory().debug_level = 0 : overrides + * uvm_factory().debug_level = 1 : user defined types + above + * uvm_factory().debug_level = 2 : uvm_* types + above """ return self.__debug_level @debug_level.setter def debug_level(self, debug_level): """ - uvm_factory().debug_level = 0 : overrides - uvm_factory().debug_level = 1 : user defined types + above - uvm_factory().debug_level = 2 : uvm_* types + above + * uvm_factory().debug_level = 0 : overrides + * uvm_factory().debug_level = 1 : user defined types + above + * uvm_factory().debug_level = 2 : uvm_* types + above """ assert (0 <= debug_level <= 2), \ "uvm_factory().all_type must be 0, 1, or 2" @@ -467,15 +480,15 @@ def __str__(self): # 8.3.1.7.5 def print(self, debug_level=1): """ + :param debug_level: + * ``debug_level`` = 0 : overrides + * ``debug_level`` = 1 : user defined types + above ( default) + * ``debug_level`` = 2 : uvm_* types + above + :return: None + Prints the factory data using debug_level to control the amount of output. The uvm_factory().debug_level variable can control this for __str__() - - debug_level = 0 : overrides - debug_level = 1 : user defined types + above - debug_level = 2 : uvm_* types + above - - :return: None """ saved_debug_level = self.debug_level # Avoiding a side effect self.debug_level = debug_level From 0b24373502572a2f474cb35b8ed142f8485d9037 Mon Sep 17 00:00:00 2001 From: Ray Salemi Date: Thu, 27 Jun 2024 16:26:57 -0400 Subject: [PATCH 4/7] docstrings for phasing --- pyuvm/s09_phasing.py | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/pyuvm/s09_phasing.py b/pyuvm/s09_phasing.py index f71b47b..d557caf 100644 --- a/pyuvm/s09_phasing.py +++ b/pyuvm/s09_phasing.py @@ -4,16 +4,14 @@ # 9.1 # -# This is a dramatically simplified version of UVM phasing. We don't have -# to deal with simulation time and we are not going to deal with a generalized -# phasing system. -# +# This is a dramatically simplified version of UVM phasing. We are not going +# to deal with the complexities of custom phasing as described in IEEE 1800.2 +# # # So this system simply traverses the common phases, calling the appropriate # method in each component. # # Much of the work in the SV phasing code has to do with handling the passage -# of time. There is no timewheel in Python, so all of that code can go -# away. +# of time. We use cocotb for our simulation interaction, so that all goes away. # # Also, the generalized phasing system is rarely used and so that # is left as an exercise for future developers. Instead we have a simple @@ -55,10 +53,10 @@ class uvm_topdown_phase(uvm_phase): @classmethod def traverse(cls, comp): """ + :param comp: The component whose hierarchy will be traversed + Given a component, we traverse the component tree top to bottom calling the phase functions as we go - - :param comp: The component whose hierarchy will be traversed """ cls.execute(comp) # first we execute this node then its children for child in comp.get_children(): @@ -71,6 +69,12 @@ class uvm_bottomup_phase(uvm_phase): """ @classmethod def traverse(cls, comp): + """ + :param comp: The component whose hierarchy will be traversed + + Given a component, we traverse the component tree + bottom to top calling the phase functions as we go + """ for child in comp.get_children(): cls.traverse(child) cls.execute(comp) @@ -150,7 +154,8 @@ class uvm_final_phase(uvm_topdown_phase): # I cannot imagine why anyone would implement this. # One could add phases by simply extending uvm_topdown_phase # or uvm_bottom_up phase with a new phase named 'uvm_my_phase' and adding -# the my_phase() method to a uvm component with setattr. +# the my_phase() method to a uvm component with setattr. Then insert the new +# phase into the list of phases to be executed below: uvm_common_phases = [uvm_build_phase, uvm_connect_phase, From 9692df5ee08e77955b136af79d5dd00a1e5e1644 Mon Sep 17 00:00:00 2001 From: Ray Salemi Date: Thu, 27 Jun 2024 17:24:05 -0400 Subject: [PATCH 5/7] commented uvm_tlm --- pyuvm/s08_factory_classes.py | 1 + pyuvm/s12_uvm_tlm_interfaces.py | 196 ++++++++++++++++++++++++++----- pyuvm/s14_15_python_sequences.py | 2 +- 3 files changed, 167 insertions(+), 32 deletions(-) diff --git a/pyuvm/s08_factory_classes.py b/pyuvm/s08_factory_classes.py index 3ccf91e..aff6e50 100644 --- a/pyuvm/s08_factory_classes.py +++ b/pyuvm/s08_factory_classes.py @@ -317,6 +317,7 @@ def set_type_alias(self, alias_type_name, original_type): :param original_type:The original type toe be referenced :raises: UVMNotImplemented Not implemented as it does not seem to exist in the SystemVerilog UVM + :return:None """ diff --git a/pyuvm/s12_uvm_tlm_interfaces.py b/pyuvm/s12_uvm_tlm_interfaces.py index 7c3926a..8f06d48 100644 --- a/pyuvm/s12_uvm_tlm_interfaces.py +++ b/pyuvm/s12_uvm_tlm_interfaces.py @@ -1,5 +1,5 @@ -# The UVM TLM class hierarchy wraps the behavior of objects (usually Queues) in -# a set of classes and objects. +# The UVM TLM class hierarchy wraps the behavior of objects +# (usually cocotb.Queues) in a set of classes and objects. # # The UVM LRM calls for three levels of classes to # implement TLM: ports, exports, and imp: @@ -55,6 +55,7 @@ # uvm_export_base provides the provided_to # associative array. + class uvm_export_base(uvm_component): def __init__(self, name, parent): super().__init__(name, parent) @@ -63,16 +64,16 @@ def __init__(self, name, parent): class uvm_port_base(uvm_export_base): """ - A uvm_port_base is a uvm_component with a connect() function. - The connect function creates an __export data member that + A ``uvm_port_base`` is a uvm_component with a ``connect()`` function. + The ``connect`` function creates an ``__export`` data member that implements the put/get,etc methods. - We'll build functionality from uvm_port_base to create the + We'll build functionality from ``uvm_port_base`` to create the other combinations of ports through multiple inheritance. - pyuvm will make extensive use of Pythons "ask forgiveness" policy + **pyuvm** will make extensive use of Pythons "ask forgiveness" policy If you try to use the wrong method for the port you created - then you'll get a exception, maybe a missing attribute one, though + then you'll get a exception, maybe a missing attribute exception, though we could catch that one and deliver a more useful message. Unlike the SV implementation of UVM we return results from get and peek @@ -94,7 +95,6 @@ def __init__(self, name, parent): self.connected_to = {} self.export = None self.needed_methods = [] - # Compare the list of all tlm methods to the # methods in this class to create a list of # needed @@ -102,7 +102,7 @@ def __init__(self, name, parent): if hasattr(self, method): self.needed_methods.append(method) - def check_export(self, export): + def _check_export(self, export): """Check that the export implements needed methods""" if not isinstance(export, uvm_export_base): raise UVMTLMConnectionError( @@ -115,12 +115,13 @@ def check_export(self, export): def connect(self, export): """ - Attach this port to the associated export. + :param export: The export that has the functions + :return: None + :raises: UVMTLMConnectionError if there is a connect error + Attach this port to the associated export. - :param export: - :return: """ - self.check_export(export) + self._check_export(export) try: self.export = export self.connected_to[export.get_full_name()] = export @@ -143,8 +144,10 @@ class uvm_blocking_put_port(uvm_port_base): async def put(self, datum): """ :param datum: Datum to put + :raises: UVMTLMConnectionError if export is missing :return: None + put the datum """ try: await self.export.put(datum) @@ -163,11 +166,12 @@ class uvm_nonblocking_put_port(uvm_port_base): # 12.2.4.2.4 def try_put(self, data): """ + :param data: data to deliver + :raises: UVMTLMConnectionError if export is missing + :return: boolean True = success + Tries to put data on a port, but if the port is full it returns False - - :param data: data to deliver - :return: True = success """ try: success = self.export.try_put(data) @@ -183,7 +187,7 @@ def can_put(self): Returns true if there is room for data to be put on the port - :return: bool + :return: True if there is room to put """ try: can_do_it = self.export.can_put() @@ -210,9 +214,10 @@ class uvm_blocking_get_port(uvm_port_base): # 12.2.4.2.2 async def get(self): """ + :raises: UVMTLMConnectionError if export is missing + :return: data A blocking get that returns the data got - :return: data """ try: data = await self.export.get() @@ -231,6 +236,9 @@ class uvm_nonblocking_get_port(uvm_port_base): def try_get(self): """ + :raises: UVMTLMConnectionError if export is missing + :return: (success, data) + 12.2.4.2.6 Returns a tuple containing success and the data This is different than SV UVM that returns the @@ -250,9 +258,9 @@ def try_get(self): # 12.2.4.2.7 def can_get(self): """ - Returns true if there is data to get + :raises: UVMTLMConnectionError if export is missing + :return: bool Returns true if there is data to get - :return: bool """ try: can = self.export.can_get() @@ -278,10 +286,10 @@ class uvm_blocking_peek_port(uvm_port_base): # 12.2.4.2.3 async def peek(self): """ - A blocking peek that returns data without - consuming it. - + :raises: UVMTLMConnectionError if export is missing :return: datum + + A blocking peek that returns data without consuming it. """ try: datum = await self.export.peek() @@ -301,10 +309,11 @@ class uvm_nonblocking_peek_port(uvm_port_base): # 12.2.4.2.8 def try_peek(self): """ + :raises: UVMTLMConnectionError if export is missing + :return: (success, data) Tries to peek for data and returns a tuple with success and the data - :return: (success, data) """ try: success, data = self.export.try_peek() @@ -317,9 +326,10 @@ def try_peek(self): # 12.2.4.2.9 def can_peek(self): """ - Checks if peeking will be successful - + :raises: UVMTLMConnectionError if export is missing :return: True if can peek + + Checks if peeking will be successful """ can = self.export.can_peek() return can @@ -351,6 +361,14 @@ def __init__(self, name, parent): super().__init__(name, parent) async def transport(self, put_data): + """ + :param put_data: data to send + :raises: UVMTLMConnectionError if export is missing + :return: data received + + Puts data and blocks if there is no room, then blocks + if there is no data to get and gets data. + """ try: get_data = await self.export.transport(put_data) except AttributeError: @@ -366,6 +384,14 @@ def __init__(self, name, parent): super().__init__(name, parent) def nb_transport(self, put_data): + """ + :param put_data: data to send + :raises: UVMTLMConnectionError if export is missing + :return: (success, data) + + Non-blocking transport. Returns a tuple with success + if the transport was successful and the data could be returned + """ try: success, get_data = self.export.nb_transport(put_data) except AttributeError: @@ -420,7 +446,11 @@ def __init__(self, name, parent): def write(self, datum): """ :param datum: data to send + :raises: UVMTLMConnectionError if export is missing :return: None + + Write to all connected analysis ports. This is a broadcast. + Returns regardless of whether there are any subscribers. """ for export in self.subscribers: if not hasattr(export, "write"): @@ -429,7 +459,7 @@ def write(self, datum): export.write(datum) def connect(self, export): - self.check_export(export) + self._check_export(export) self.subscribers.append(export) @@ -538,7 +568,7 @@ class uvm_analysis_export(uvm_export_base): # 12.2.8 FIFO Classes # # These classes provide synchronization control between -# threads using the Queue class. +# threads using the ``cocotb.Queue`` class. # # One note. The RLM has only 12.2.8.1.3 and 12.2.8.1.4, put_export # and get_peek_export, but the UVM code has all the variants @@ -569,6 +599,13 @@ class uvm_tlm_fifo_base(uvm_component): class uvm_BlockingPutExport(uvm_QueueAccessor, uvm_blocking_put_export): async def put(self, item): + """ + :param item: item to put + :return: None + + A coroutine that blocks if the FIFO is full. + + """ self.logger.log(FIFO_DEBUG, f"blocking put: {item}") await self.queue.put(item) self.logger.log(FIFO_DEBUG, f"success put {item}") @@ -579,9 +616,20 @@ class uvm_NonBlockingPutExport(uvm_QueueAccessor, uvm_nonblocking_put_export): def can_put(self): + """ + :return: True if can put + """ return not self.queue.full() def try_put(self, item): + """ + :param item: item to put + :raises: QueueFull if the queue is full + :return: True if successful + + The ``try_put`` is implemented with an exception + rather than returning a boolean. + """ try: self.queue.put_nowait(item) self.ap.write(item) @@ -594,6 +642,11 @@ class uvm_PutExport(uvm_BlockingPutExport, uvm_NonBlockingPutExport): class uvm_BlockingGetExport(uvm_QueueAccessor, uvm_blocking_get_export): async def get(self): + """ + :return: item + + A coroutine that blocks if the FIFO is empty + """ self.logger.log(FIFO_DEBUG, "Attempting blocking get") item = await self.queue.get() self.logger.log(FIFO_DEBUG, f"got {item}") @@ -603,10 +656,16 @@ async def get(self): class uvm_NonBlockingGetExport(uvm_QueueAccessor, uvm_nonblocking_get_export): def can_get(self): + """ + :return: True if can get + """ get_ok = not self.queue.empty() return get_ok def try_get(self): + """ + :return: (success, item) + """ try: item = self.queue.get_nowait() self.ap.write(item) @@ -619,6 +678,11 @@ class uvm_GetExport(uvm_BlockingGetExport, uvm_NonBlockingGetExport): class uvm_BlockingPeekExport(uvm_QueueAccessor, uvm_blocking_peek_export): async def peek(self): + """ + :return: item + + A coroutine that blocks if the FIFO is empty + """ self.logger.log(FIFO_DEBUG, "Attempting blocking peek") peek_data = await self.queue.peek() self.logger.log(FIFO_DEBUG, f"peeked at {peek_data}") @@ -627,9 +691,15 @@ async def peek(self): class uvm_NonBlockingPeekExport(uvm_QueueAccessor, uvm_nonblocking_peek_export): def can_peek(self): + """ + :return: True if can peek + """ return not self.queue.empty() def try_peek(self): + """ + :return: (success, item) + """ try: datum = self.queue.peek_nowait() return True, datum @@ -686,30 +756,64 @@ def __init__(self, name, parent, maxsize=1): self.get_peek_export = self.uvm_GetPeekExport("get_peek_export", self, self.queue, self.get_ap) # noqa: E501 async def put(self, item): + """ + :param item: item to put + + Blocking put coroutine + """ await self.put_export.put(item) def can_put(self): + """ + :return: True if can put + """ return self.put_export.can_put() def try_put(self, item): + """ + :param item: item to put + :return: True if successful + """ return self.put_export.try_put(item) async def get(self): + """ + :return: item + + coroutine that blocks if FIFO is empty + """ return await self.get_export.get() def can_get(self): + """ + :return: True if can get + """ return self.get_export.can_get() def try_get(self): + """ + :return: (success, item) + """ return self.get_export.try_get() async def peek(self): + """ + :return: item + + A coroutine that blocks if FIFO is empty + """ return await self.peek_export.peek() def can_peek(self): + """ + :return: True if can peek + """ return self.peek_export.can_peek() def try_peek(self): + """ + :return: (success, item) + """ return self.peek_export.try_peek() @@ -723,18 +827,19 @@ def __init__(self, name=None, parent=None, size=1): # 12.2.8.2.2 def size(self): """ - Return the size of the fifo - :return: size of FIFO + + Return the size of the fifo """ return self.queue.maxsize # 12.2.8.2.3 def used(self): """ + :return: Number of items in the FIFO + How much of the FIFO is being used? - :return: Size of the FIFO """ return self.queue.qsize() @@ -776,6 +881,12 @@ def flush(self): class uvm_tlm_analysis_fifo(uvm_tlm_fifo): class uvm_AnalysisExport(uvm_QueueAccessor, uvm_analysis_port): def write(self, item): + """ + :param item: item to write + + Writes the item to all the subscribers, or no one if + there are no subscribers. + """ try: self.queue.put_nowait(item) except QueueFull: @@ -799,21 +910,44 @@ def __init__(self, name, parent, put_export, get_peek_export): self.get_peek_export = get_peek_export async def put(self, item): + """ + :param item: item to put + + A coroutine that blocks if the FIFO is full + """ await self.put_export.put(item) def can_put(self): + """ + :return: True if can put + """ return self.put_export.can_put() def try_put(self, item): + """ + :param item: item to put + :return: True if successful + """ return self.put_export.try_put(item) async def get(self): + """ + :return: item + + A coroutine that blocks if the FIFO is empty + """ return await self.get_peek_export.get() def can_get(self): + """ + :return: True if can get + """ return self.get_peek_export.can_get() def try_get(self): + """ + :return: (success, item) + """ return self.get_peek_export.try_get def __init__(self, name, parent=None, request_fifo_size=1, diff --git a/pyuvm/s14_15_python_sequences.py b/pyuvm/s14_15_python_sequences.py index 9cac59d..5200afc 100644 --- a/pyuvm/s14_15_python_sequences.py +++ b/pyuvm/s14_15_python_sequences.py @@ -227,7 +227,7 @@ async def get_response(self, transaction_id=None): class uvm_seq_item_port(uvm_port_base): def connect(self, export): - self.check_export(export) + self._check_export(export) super().connect(export) async def put_req(self, item): From decea2822bced1a2d7ef9272c772ac9bc0f1e9a1 Mon Sep 17 00:00:00 2001 From: Ray Salemi Date: Thu, 27 Jun 2024 17:45:53 -0400 Subject: [PATCH 6/7] more docstring work --- docs/conf.py | 4 ++-- pyuvm/s08_factory_classes.py | 7 +++---- pyuvm/s12_uvm_tlm_interfaces.py | 25 ++++++++++++++----------- pyuvm/s14_15_python_sequences.py | 7 ++++++- 4 files changed, 25 insertions(+), 18 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index c3b386b..1017252 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,5 +1,5 @@ -# -# This file is execfile()d with the current directory set to its containing dir. +# This file is execfile()d with the current directory set +# to its containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. diff --git a/pyuvm/s08_factory_classes.py b/pyuvm/s08_factory_classes.py index aff6e50..743d02f 100644 --- a/pyuvm/s08_factory_classes.py +++ b/pyuvm/s08_factory_classes.py @@ -315,9 +315,8 @@ def set_type_alias(self, alias_type_name, original_type): """ :param alias_type_name:A string that will reference the original type :param original_type:The original type toe be referenced - :raises: UVMNotImplemented Not implemented as it does not seem - to exist in the SystemVerilog UVM - + :raises: UVMNotImplemented Not implemented as it does not seem to + exist in the SystemVerilog UVM :return:None """ @@ -333,7 +332,7 @@ def set_inst_alias(self, alias_type_name, original_type, full_inst_path): :param original_type:The original type toe be referenced :param full_inst_path: The instance path where this alias is active :raises: UVMNotImplemented Not implemented as it does - not seem to exist in SystemVerilog UVM + not seem to exist in SystemVerilog UVM :return:None """ diff --git a/pyuvm/s12_uvm_tlm_interfaces.py b/pyuvm/s12_uvm_tlm_interfaces.py index 8f06d48..260718d 100644 --- a/pyuvm/s12_uvm_tlm_interfaces.py +++ b/pyuvm/s12_uvm_tlm_interfaces.py @@ -115,12 +115,14 @@ def _check_export(self, export): def connect(self, export): """ - :param export: The export that has the functions - :return: None - :raises: UVMTLMConnectionError if there is a connect error + :param export: The export that has the functions + :raises: UVMTLMConnectionError if there is a connect error + :return: None + Attach this port to the associated export. """ + self._check_export(export) try: self.export = export @@ -362,12 +364,12 @@ def __init__(self, name, parent): async def transport(self, put_data): """ + Puts data and blocks if there is no room, then blocks + if there is no data to get and gets data. + :param put_data: data to send :raises: UVMTLMConnectionError if export is missing :return: data received - - Puts data and blocks if there is no room, then blocks - if there is no data to get and gets data. """ try: get_data = await self.export.transport(put_data) @@ -385,12 +387,12 @@ def __init__(self, name, parent): def nb_transport(self, put_data): """ + Non-blocking transport. Returns a tuple with success + if the transport was successful and the data could be returned + :param put_data: data to send :raises: UVMTLMConnectionError if export is missing :return: (success, data) - - Non-blocking transport. Returns a tuple with success - if the transport was successful and the data could be returned """ try: success, get_data = self.export.nb_transport(put_data) @@ -445,12 +447,13 @@ def __init__(self, name, parent): # 12.2.8.1 def write(self, datum): """ + Write to all connected analysis ports. This is a broadcast. + Returns regardless of whether there are any subscribers. + :param datum: data to send :raises: UVMTLMConnectionError if export is missing :return: None - Write to all connected analysis ports. This is a broadcast. - Returns regardless of whether there are any subscribers. """ for export in self.subscribers: if not hasattr(export, "write"): diff --git a/pyuvm/s14_15_python_sequences.py b/pyuvm/s14_15_python_sequences.py index 5200afc..823f6e1 100644 --- a/pyuvm/s14_15_python_sequences.py +++ b/pyuvm/s14_15_python_sequences.py @@ -231,7 +231,12 @@ def connect(self, export): super().connect(export) async def put_req(self, item): - """Put a request item in the request queue""" + """ + Put a request item in the request queue + :param item: The request item + + A coroutine that blocks until the request is put in the queue + """ await self.export.put_req(item) def put_response(self, item): From 910bb85988d687764193c6637d687a2be0de69c7 Mon Sep 17 00:00:00 2001 From: Ray Salemi Date: Thu, 27 Jun 2024 18:15:41 -0400 Subject: [PATCH 7/7] docstrings in place. --- pyuvm/s08_factory_classes.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pyuvm/s08_factory_classes.py b/pyuvm/s08_factory_classes.py index 743d02f..3560093 100644 --- a/pyuvm/s08_factory_classes.py +++ b/pyuvm/s08_factory_classes.py @@ -332,10 +332,11 @@ def set_inst_alias(self, alias_type_name, original_type, full_inst_path): :param original_type:The original type toe be referenced :param full_inst_path: The instance path where this alias is active :raises: UVMNotImplemented Not implemented as it does - not seem to exist in SystemVerilog UVM + not seem to exist in SystemVerilog UVM :return:None """ + # This method does not seem to be implemented in SystemVerilog # so I'm skipping it now. raise error_classes.UVMNotImplemented(