{"id":16755,"date":"2021-12-14T09:31:44","date_gmt":"2021-12-14T14:31:44","guid":{"rendered":"https:\/\/blogs.sw.siemens.com\/verificationhorizons\/?p=16755"},"modified":"2026-03-27T08:47:54","modified_gmt":"2026-03-27T12:47:54","slug":"logging-in-pyuvm","status":"publish","type":"post","link":"https:\/\/blogs.sw.siemens.com\/verificationhorizons\/2021\/12\/14\/logging-in-pyuvm\/","title":{"rendered":"Logging in pyuvm"},"content":{"rendered":"\n<h1>Logging in pyuvm<\/h1>\n\n<blockquote>\n<p>This is part of the <a href=\"https:\/\/verificationacademy.com\/news\/the-python-for-verification-series\" target=\"_blank\" rel=\"noopener\">Python for Verification series<\/a> of blog posts.<\/p>\n<\/blockquote>\n\n<p>The IEEE UVM specification (1800.2-2020) devotes Section 6 to \u201cReporting classes\u201d because SystemVerilog does not provide a robust logging system. Python does. The <code>logging<\/code> module provides standard logging functionality across the Python ecosystem, including <strong>cocotb<\/strong>. <\/p>\n\n<p>Therefore, <strong>pyuvm<\/strong> does not implement the UVM reporting classes. Instead, it incorporates the <code>logging<\/code> module into the UVM hierarchy, links <strong>pyuvm<\/strong> logging to <strong>cocotb<\/strong> logging, and hides elements of the <code>logging<\/code> module that frustrate new users.<\/p>\n\n<p>This blog post provides a brief overview of <code>logging<\/code> and compares it to UVM reporting. Then it discusses logging in <strong>pyuvm<\/strong>.<\/p>\n\n<h2>Logging levels<\/h2>\n\n<p>SystemVerilog UVM combines several concepts to control logging. <em>Severity<\/em> is the most basic, ranging from <code>UVM_INFO<\/code> to <code>UVM_FATAL<\/code> and supported by macro calls that apply the severity to a log message.<\/p>\n\n<p><em>Verbosity<\/em> applies only to <code>UVM_INFO<\/code> messages and controls whether a given info message gets logged. When users invoke the <code>uvm_info()<\/code> macro, they supply a verbosity that ranges from <code>UVM_NONE<\/code> through <code>UVM_LOW<\/code> up to <code>UVM_FULL<\/code>.<\/p>\n\n<p>Users can control information messages by setting a verbosity level in a component. The <code>set_verbosity_level()<\/code> method creates a verbosity ceiling. The UVM reporting system prints a component\u2018s <code>uvm_info()<\/code> messages only if their verbosity is below the ceiling.<\/p>\n\n<p>This ceiling system means information messages with verbosity <code>UVM_LOW<\/code> get printed before those with <code>UVM_FULL<\/code>. The <code>uvm_warning<\/code>, <code>uvm_error<\/code>, and <code>uvm_fatal<\/code> messages ignore verbosity.<\/p>\n\n<p>The Python <code>logging<\/code> module combines and simplifies these ideas into logging levels. There are five levels by default:<\/p>\n\n\n\n<figure class=\"wp-block-table aligncenter is-style-regular\"><table><tbody><tr><td class=\"has-text-align-center\" data-align=\"center\">Logging Level<\/td><td class=\"has-text-align-center\" data-align=\"center\">Numeric Value<\/td><\/tr><tr><td class=\"has-text-align-center\" data-align=\"center\">CRITICAL<\/td><td class=\"has-text-align-center\" data-align=\"center\">50<\/td><\/tr><tr><td class=\"has-text-align-center\" data-align=\"center\">ERROR<\/td><td class=\"has-text-align-center\" data-align=\"center\">40<\/td><\/tr><tr><td class=\"has-text-align-center\" data-align=\"center\">WARNING<\/td><td class=\"has-text-align-center\" data-align=\"center\">30<\/td><\/tr><tr><td class=\"has-text-align-center\" data-align=\"center\">INFO<\/td><td class=\"has-text-align-center\" data-align=\"center\">20<\/td><\/tr><tr><td class=\"has-text-align-center\" data-align=\"center\">DEBUG<\/td><td class=\"has-text-align-center\" data-align=\"center\">10<\/td><\/tr><tr><td class=\"has-text-align-center\" data-align=\"center\">NOTSET<\/td><td class=\"has-text-align-center\" data-align=\"center\">0<\/td><\/tr><\/tbody><\/table><figcaption>Logging levels<\/figcaption><\/figure>\n\n\n\n<p>You log messages using functions such as <code>self.logger.info()<\/code> or <code>self.logger.critical()<\/code>. Then you set the logging level on a component. The messages created in that component get printed if their level is greater than or equal to the component logging level.<\/p>\n\n<p><strong>pyuvm<\/strong> provides methods that set the logging level in components or component hierarchies.<\/p>\n\n<h2>Logging in <strong>pyuvm<\/strong><\/h2>\n\n<p><strong>pyuvm<\/strong> follows the IEEE 1800.2 specification in that it defines a class named <code>uvm_report_object<\/code> and extends that class to create <code>uvm_component<\/code>. This means that all <code>uvm_components<\/code> are report objects, which is why you <em>must <\/em> call <code>super().__init__()<\/code> if you override the <code>__init__()<\/code> method in a <code>uvm_component<\/code>.<\/p>\n\n<p>UVM components support logging with the following steps.<\/p>\n\n<ul>\n\t<li>Every component creates a logger stored in <code>self.logger<\/code>. These loggers are part of the <strong>cocotb<\/strong> logging system.<\/li>\n\t<li>The component sets the logger\u2019s level to the default logging level. By default the default is <code>INFO<\/code>. You can change the default by using the class method <code>uvm_report_object.set_default_logging_level()<\/code> before awaiting <code>uvm_root().run_test()<\/code>.<\/li>\n\t<li><strong>pyuvm<\/strong> defines <code>PyuvmFormatter()<\/code> which makes <strong>pyuvm<\/strong> log messages look like UVM log messages.<\/li>\n\t<li>The component creates a <code>StreamHandler<\/code> that prints messages to <code>sys.stdout<\/code> rather than <code>sys.stderr<\/code>.<\/li>\n<\/ul>\n\n<p>Now any component can log messages.<\/p>\n\n<h2>Logging messages in <strong>pyuvm<\/strong><\/h2>\n\n<p>We log messages using the <code>critical<\/code>, <code>warning<\/code>, <code>error<\/code>, <code>info<\/code>, and <code>debug<\/code> functions in <code>self.logger<\/code>. For example, these components print warning, info, and debug messages:<\/p>\n\n<pre><code>class WarningComp(uvm_component):\n    def start_of_simulation_phase(self):\n        self.logger.debug(\"This is WARNING\")\n\nclass InfoComp(uvm_component):\n    def start_of_simulation_phase(self):\n        self.logger.info(\"This is INFO\")\n\nclass DebugComp(uvm_component):\n    def start_of_simulation_phase(self):\n        self.logger.debug(\"This is DEBUG\")<\/code><\/pre>\n\n<p>We instantiate these components in a test and call the test from a <code>@cocotb.test()<\/code>:<\/p>\n\n<pre><code>class LogTest(uvm_test):\n    def build_phase(self):\n        self.info = InfoComp(\"info\", self)\n        self.warning = WarningComp(\"warning\", self)\n        self.debug = DebugComp(\"debug\", self)\n\n@cocotb.test()\nasync def default_logging_levels(_):\n    \"\"\"Demonstrate default logging levels\"\"\"\n    await uvm_root().run_test(\"LogTest\")<\/code><\/pre>\n\n<p>And <em>voila<\/em>!<\/p>\n\n<pre><code>#      0.00ns INFO     running default_logging_levels (1\/5)\n#      0.00ns INFO     testbench.py(14)[uvm_test_top.info]: This is INFO\n#      0.00ns WARNING  testbench.py(10)[uvm_test_top.warning]: This is WARNING\n#   1000.00ns INFO     default_logging_levels passed<\/code><\/pre>\n\n<h3>Examining the log message<\/h3>\n\n<p>The first question you might ask is, \u201cWhere is the <code>DEBUG<\/code> message?\u201d It is not there because the default log level is <code>INFO<\/code> (20), and <code>DEBUG<\/code> (10) is less than 20.<\/p>\n\n<p>On a terminal we\u2019d see that <strong>cocotb<\/strong> has colorized the <code>WARNING<\/code> message. It\u2019s also added the time stamp to the <code>INFO<\/code> and <code>WARNING<\/code> messages.<\/p>\n\n<p>The <strong>pyuvm<\/strong> log messages look much like the UVM log messages with the following fields:<\/p>\n\n<ul>\n\t<li>The timestamp comes from the simulator. <code>0.00ns<\/code> in this case because we haven\u2019t started the simulation.<\/li>\n\t<li>The level in all caps (<code>INFO<\/code>, <code>WARNING<\/code>) matches the function call.<\/li>\n\t<li>The file name and line number shows where the message was logged.<\/li>\n\t<li><strong>pyuvm<\/strong> logging does not have the message ID from SystemVerilog UVM reporting. Instead, we find the UVM hierarchy between square brackets (<code>[]<\/code>)<\/li>\n\t<li>Finally, we see the string passed to the logging function.<\/li>\n<\/ul>\n\n<h2>Changing the logging level<\/h2>\n\n<p>There are two ways to change the logging level. The first way (as of pyuvm 2.5) is to change the default logging level before awaiting the <code>run_test()<\/code> coroutine. This way, all components get their logging level set to the new default:<\/p>\n\n<pre><code>@cocotb.test()\nasync def debug_default_logging_level(_):\n    \"\"\"Demonstrate debug logging level\"\"\"\n    uvm_report_object.set_default_logging_level(DEBUG)\n    await uvm_root().run_test(\"LogTest\")<\/code><\/pre>\n\n<p>The second way to change the logging level is to use the <code>set_logging_level_hier()<\/code> method in the <code>end_of_elaboration_phase()<\/code>. This is analogous to the <code>set_verbosity_level_hier()<\/code> method in the SystemVerilog UVM. It changes the logging level from this point in the hierarchy down. There is also a <code>set_logging_level()<\/code> method that only changes the calling component&#8217;s logging level.<\/p>\n\n<p>We extend <code>LogTest<\/code> and change the logging level:<\/p>\n\n<pre><code>class DebugTest(LogTest):\n    def end_of_elaboration_phase(self):\n        self.set_logging_level_hier(DEBUG)<\/code><\/pre>\n\n<p>Both approaches deliver the same result:<\/p>\n\n<pre><code>1000.00ns INFO     running debug_default_logging_level (2\/5)\n#   1000.00ns INFO     testbench.py(14)[uvm_test_top.info]: This is INFO\n#   1000.00ns WARNING  testbench.py(10)[uvm_test_top.warning]: This is WARNING\n#   1000.00ns DEBUG    testbench.py(18)[uvm_test_top.debug]: This is DEBUG\n#   2000.00ns INFO     debug_default_logging_level passed\n#   2000.00ns INFO     running debug_logging_level (3\/5)\n#   2000.00ns INFO     testbench.py(14)[uvm_test_top.info]: This is INFO\n#   2000.00ns WARNING  testbench.py(10)[uvm_test_top.warning]: This is WARNING\n#   2000.00ns DEBUG    testbench.py(18)[uvm_test_top.debug]: This is DEBUG\n#   3000.00ns INFO     debug_logging_level passed<\/code><\/pre>\n\n<h2>Controlling output<\/h2>\n\n<p>The <code>logging<\/code> module controls output using handlers that extend <code>logging.Handler<\/code>. There are several predefined handlers in the <code>logging.handlers<\/code> module. We will discuss three of them.<\/p>\n\n<p>Every <code>uvm_components<\/code> has a data member named <code>self.logger<\/code>, which contains a <code>StreamingHandler<\/code> that writes to stdout. You can remove this default handler using <code>self.remove_streaming_handler()<\/code>, at which point your logger will have no handler, so you\u2019ll replace it with another handler.<\/p>\n\n<h3>Writing to a file<\/h3>\n\n<p>If you want to log your information to a file instead of writing to the screen, you add a <code>FileHandler<\/code> and remove the streaming handlers:<\/p>\n\n<pre><code>class FileTest(LogTest):\n    def end_of_elaboration_phase(self):\n        file_handler = logging.FileHandler(\"log.txt\", mode=\"w\")\n        self.add_logging_handler_hier(file_handler)\n        self.remove_streaming_handler_hier()<\/code><\/pre>\n\n<p>The result is that the <strong>pyuvm<\/strong> log messages go into <code>log.txt<\/code>:<\/p>\n\n<pre><code>#  cat log.txt\n  3000.00ns INFO     testbench.py(14)[uvm_test_top]: This is INFO\n  3000.00ns WARNING  testbench.py(10)[uvm_test_top]: This is WARNING\n  3000.00ns DEBUG    testbench.py(18)[uvm_test_top]: This is DEBUG<\/code><\/pre>\n\n<p>The <code>add_logging_handler()<\/code> and <code>add_logging_handler_hier()<\/code> methods work for any of the handlers that extend <code>logging.Handler<\/code>.<\/p>\n\n<h3>Disabling Logging<\/h3>\n\n<p>To completely disable logging use <code>self.disable_logging()<\/code> or <code>self.disable_logging_hier()<\/code>which removes the stream handler and replaces it with a <code>NullHandler<\/code>.<\/p>\n\n<pre><code>class NoLog(LogTest):\n    def end_of_elaboration_phase(self):\n        self.disable_logging_hier()<\/code><\/pre>\n\n<p>Which gives the result:<\/p>\n\n<pre><code>#   4000.00ns INFO     running disable_logging (5\/5)\n#   5000.00ns INFO     disable_logging passed<\/code><\/pre>\n\n<h2>Summary<\/h2>\n\n<p>This blog post showed how <strong>pyuvm<\/strong> incorporated the <code>logging<\/code> module into a UVM testbench. We learned the difference between SystemVerilog UVM\u2019s <em>severity<\/em> and <em>verbosity<\/em> levels and how they relate to the <em>levels<\/em> in the <code>logging<\/code> module.<\/p>\n\n<p>We saw how <strong>pyuvm<\/strong> mimics the message structure of SystemVerilog UVM and how it replaced the <em>message-id<\/em> with the UVM component path between the square brackets.<\/p>\n\n<p>Finally, we saw how to direct <strong>pyuvm<\/strong> logging output to different locations using <code>logging.Handler<\/code> objects and how to disable logging.<\/p>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Logging in pyuvm This is part of the Python for Verification series of blog posts. The IEEE UVM specification (1800.2-2020)&#8230;<\/p>\n","protected":false},"author":74314,"featured_media":16760,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"spanish_translation":"","french_translation":"","german_translation":"","italian_translation":"","polish_translation":"","japanese_translation":"","chinese_translation":"","footnotes":""},"categories":[984,1,981,10],"tags":[902,971,787,819],"industry":[],"product":[],"coauthors":[947],"class_list":["post-16755","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-cocotb","category-news","category-pyuvm","category-tips-tricks","tag-python","tag-pyuvm","tag-uvm","tag-verification"],"featured_image_url":"https:\/\/blogs.sw.siemens.com\/wp-content\/uploads\/sites\/54\/2021\/12\/cover.png","_links":{"self":[{"href":"https:\/\/blogs.sw.siemens.com\/verificationhorizons\/wp-json\/wp\/v2\/posts\/16755","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blogs.sw.siemens.com\/verificationhorizons\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blogs.sw.siemens.com\/verificationhorizons\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blogs.sw.siemens.com\/verificationhorizons\/wp-json\/wp\/v2\/users\/74314"}],"replies":[{"embeddable":true,"href":"https:\/\/blogs.sw.siemens.com\/verificationhorizons\/wp-json\/wp\/v2\/comments?post=16755"}],"version-history":[{"count":4,"href":"https:\/\/blogs.sw.siemens.com\/verificationhorizons\/wp-json\/wp\/v2\/posts\/16755\/revisions"}],"predecessor-version":[{"id":16767,"href":"https:\/\/blogs.sw.siemens.com\/verificationhorizons\/wp-json\/wp\/v2\/posts\/16755\/revisions\/16767"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blogs.sw.siemens.com\/verificationhorizons\/wp-json\/wp\/v2\/media\/16760"}],"wp:attachment":[{"href":"https:\/\/blogs.sw.siemens.com\/verificationhorizons\/wp-json\/wp\/v2\/media?parent=16755"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.sw.siemens.com\/verificationhorizons\/wp-json\/wp\/v2\/categories?post=16755"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.sw.siemens.com\/verificationhorizons\/wp-json\/wp\/v2\/tags?post=16755"},{"taxonomy":"industry","embeddable":true,"href":"https:\/\/blogs.sw.siemens.com\/verificationhorizons\/wp-json\/wp\/v2\/industry?post=16755"},{"taxonomy":"product","embeddable":true,"href":"https:\/\/blogs.sw.siemens.com\/verificationhorizons\/wp-json\/wp\/v2\/product?post=16755"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/blogs.sw.siemens.com\/verificationhorizons\/wp-json\/wp\/v2\/coauthors?post=16755"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}