Skip to content

Commit 5ba68ef

Browse files
authored
[uvm_report, logging] Add opt-in SV-UVM-style reporting over Python logging (pyuvm#396)
1 parent b67441c commit 5ba68ef

15 files changed

Lines changed: 1816 additions & 42 deletions

README.md

Lines changed: 67 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ You can read the API documentation for **pyuvm** on [ReadTheDocs](https://pyuvm.
2424
|Section|Name|Description|
2525
|-------|----|-----------|
2626
|5|Base Classes|Basic classes such as `uvm_void` and `uvm_object`|
27-
|6|Reporting Classes|**pyuvm** uses the **logging** package to implement reporting, but integrates it within some of the UVM reporting functionality.|
27+
|6|Reporting Classes|**pyuvm** uses the **logging** package as the reporting backend and provides optional SV-UVM-style reporting APIs, verbosity filtering, report IDs, severity counts, and summaries.|
2828
|8|Factory Classes|**pyuvm** implements all the UVM factory functionality without using the macros needed in SystemVerilog. The factory supports any class extended from `uvm_void`.|
2929
|9|Phasing|IEEE 1800.2 describes basic phasing that everyone uses and a complicated custom phasing system that almost nobody uses. **pyuvm** only implmenents the phasing that everyone uses, but you can extend phasing using Python OOP techniques.|
3030
|12|UVM TLM Interfaces|**pyuvm** fully implements the UVM *Transaction Level Modeling* (TLM) system. |
@@ -173,7 +173,7 @@ You'll see the following in the test:
173173
174174
* The `ConfigDB()` singleton acts the same way as the `uvm_config_db` interface in the SystemVerilog UVM. **pyuvm** refactored away the `uvm_resource_db` as there are no issues with classes to manage.
175175
176-
* **pyuvm** leverages the Python logging system and does not implement the UVM reporting system. Every descendent of `uvm_report_object` has a `logger` data member.
176+
* **pyuvm**'s default reporting use model leverages the Python logging system. Every `uvm_object` instance, including descendant instances, has a logger available through `self.logger`. The optional SV-UVM-style reporting path is described below.
177177
178178
* Sequences work as they do in the SystemVerilog UVM.
179179
@@ -278,7 +278,7 @@ The scoreboard receives commands from the command monitor and results from the r
278278
* The scoreboard exposes the FIFO exports by copying them into class data members. As we see in the environment above, this allows us to connect the exports without reaching into the `Scoreboard's` inner workings.
279279
* We connect the exports in the `connect_phase()`
280280
* The `check_phase()` runs after the `run_phase()`. At this point the scoreboard has all operations and results. It loops through the operations and predicts the result, then it compares the predicted and actual result.
281-
* Notice that we do not use UVM reporting. Instead we us the Python `logging` module. Every `uvm_report_object` and its children has its own logger stored in `self.logger.`
281+
* This example uses the Python `logging` module directly. Every `uvm_object` instance, including descendant instances, has a logger available through `self.logger`. Tests that need UVM-style report IDs and verbosity filtering can use `self.uvm_report` instead.
282282
283283
```python
284284
class Scoreboard(uvm_component):
@@ -457,6 +457,70 @@ class AluSeqItem(uvm_sequence_item):
457457
458458
```
459459
460+
## SV-UVM-style reporting
461+
462+
Existing pyuvm testbenches can continue to use Python logging directly:
463+
464+
```python
465+
self.logger.info("Covered all operations")
466+
self.logger.error("Functional coverage error")
467+
```
468+
469+
For testbenches that need UVM-style report IDs, UVM verbosity filtering, severity counts, report summaries, or report catching, pyuvm also provides an opt-in SV-UVM-style reporting path. Python logging remains the backend; the UVM-style layer decides whether a report is enabled, assigns UVM severity and report ID metadata, and then emits through logging.
470+
471+
Enable the shared SV-UVM-style reporting mode before creating UVM objects or components:
472+
473+
```bash
474+
PYUVM_ENABLE_SV_UVM_STYLE_REPORTING=1
475+
```
476+
477+
You can also enable it from Python before constructing the testbench:
478+
479+
```python
480+
from pyuvm import set_sv_uvm_style_reporting_enabled
481+
482+
set_sv_uvm_style_reporting_enabled(True)
483+
```
484+
485+
The enable flag changes the logging topology so `uvm_object` loggers propagate to the shared `uvm` logger. In this mode, `uvm_report_object` does not install a default stream handler on every report object. If you also want centralized counts, summaries, and report catching, create the report server early in the test:
486+
487+
```python
488+
from pyuvm import UVM_LOW, uvm_report_server
489+
490+
report_server = uvm_report_server.create(verbosity=UVM_LOW)
491+
```
492+
493+
Then write reports through `self.uvm_report`:
494+
495+
```python
496+
from pyuvm import UVM_HIGH, UVM_LOW, uvm_component
497+
498+
499+
class Scoreboard(uvm_component):
500+
def check_phase(self):
501+
self.uvm_report.info("SCOREBOARD", "checking final results", UVM_LOW)
502+
503+
if self.mismatch_seen:
504+
self.uvm_report.error("SCOREBOARD", "result mismatch detected")
505+
else:
506+
self.uvm_report.info("SCOREBOARD", "all results matched", UVM_HIGH)
507+
```
508+
509+
UVM verbosity is handled separately from Python logging levels. A UVM info report passes when its message verbosity is less than or equal to the configured verbosity. Warning, error, and fatal reports are severity reports and are not suppressed by info verbosity. Python logging levels still carry severity to the backend formatter and handlers.
510+
511+
At the end of a test, the report server can emit a summary and check failure policy:
512+
513+
```python
514+
import logging
515+
from pyuvm import uvm_report_server
516+
517+
report_server = uvm_report_server.get()
518+
report_server.log_summary(logging.getLogger("uvm"))
519+
report_server.assert_no_failures("end of test")
520+
```
521+
522+
Output formatting still uses Python logging formatters. pyuvm's `PyuvmFormatter` remains the customization point for teams that want a different log layout while keeping the UVM-style report metadata and filtering behavior.
523+
460524
461525
# Contributing
462526
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
SV-UVM-Style Reporting
2+
======================
3+
4+
pyuvm uses Python logging as its reporting backend. Existing testbenches can
5+
continue to use ``self.logger.info()``, ``self.logger.warning()``, and
6+
``self.logger.error()`` directly.
7+
8+
For testbenches that need UVM-style report IDs, UVM verbosity filtering,
9+
severity counts, report summaries, or report catching, pyuvm also provides an
10+
opt-in SV-UVM-style reporting path. The UVM-style layer performs the UVM
11+
reporting decisions and then emits through Python logging.
12+
13+
Enabling SV-UVM-Style Reporting
14+
-------------------------------
15+
16+
Enable the shared reporting mode before creating UVM objects or components:
17+
18+
.. code-block:: bash
19+
20+
PYUVM_ENABLE_SV_UVM_STYLE_REPORTING=1 make sim
21+
22+
You can also enable it from Python before constructing the testbench:
23+
24+
.. code-block:: python
25+
26+
from pyuvm import set_sv_uvm_style_reporting_enabled
27+
28+
set_sv_uvm_style_reporting_enabled(True)
29+
30+
When this mode is enabled, ``uvm_object`` loggers propagate to the shared
31+
``uvm`` logger. In this mode, ``uvm_report_object`` does not install a default
32+
stream handler on every report object. This preserves Python logging as the
33+
transport while giving the testbench a shared path for report rendering.
34+
35+
To collect severity counts, use report summaries, or install report catcher
36+
rules, create the report server early in the test:
37+
38+
.. code-block:: python
39+
40+
from pyuvm import UVM_LOW, uvm_report_server
41+
42+
report_server = uvm_report_server.create(verbosity=UVM_LOW)
43+
44+
Writing Reports
45+
---------------
46+
47+
Every ``uvm_object`` has a ``uvm_report`` property. Use it when you want
48+
UVM-style report IDs and UVM verbosity semantics:
49+
50+
.. code-block:: python
51+
52+
from pyuvm import UVM_HIGH, UVM_LOW, uvm_component
53+
54+
55+
class Scoreboard(uvm_component):
56+
def check_phase(self):
57+
self.uvm_report.info("SCOREBOARD", "checking final results", UVM_LOW)
58+
59+
if self.mismatch_seen:
60+
self.uvm_report.error("SCOREBOARD", "result mismatch detected")
61+
else:
62+
self.uvm_report.info("SCOREBOARD", "all results matched", UVM_HIGH)
63+
64+
The available report methods are:
65+
66+
* ``self.uvm_report.info(report_id, message, verbosity)``
67+
* ``self.uvm_report.warning(report_id, message)``
68+
* ``self.uvm_report.error(report_id, message)``
69+
* ``self.uvm_report.fatal(report_id, message)``
70+
71+
UVM Verbosity
72+
-------------
73+
74+
UVM verbosity is not mapped onto Python logging levels. It is evaluated before
75+
the logging call using UVM semantics: an info report passes when its message
76+
verbosity is less than or equal to the configured verbosity.
77+
78+
.. code-block:: python
79+
80+
from pyuvm import UVM_HIGH, UVM_LOW, uvm_report_server
81+
82+
uvm_report_server.create(verbosity=UVM_LOW)
83+
84+
self.uvm_report.info("LOW_ID", "visible", UVM_LOW)
85+
self.uvm_report.info("HIGH_ID", "suppressed", UVM_HIGH)
86+
87+
Warning, error, and fatal reports are severity reports. They are not suppressed
88+
by info verbosity. Python logging levels still carry severity to the backend
89+
formatter and handlers.
90+
91+
Report Summary and Failure Policy
92+
---------------------------------
93+
94+
The report server tracks UVM severity counts. At the end of a test, emit a
95+
summary and assert the failure policy:
96+
97+
.. code-block:: python
98+
99+
import logging
100+
from pyuvm import uvm_report_server
101+
102+
report_server = uvm_report_server.get()
103+
report_server.log_summary(logging.getLogger("uvm"))
104+
report_server.assert_no_failures("end of test")
105+
106+
By default, warnings do not fail the test, while errors and fatals do. The
107+
policy can be changed when the report server is created:
108+
109+
.. code-block:: python
110+
111+
from pyuvm import uvm_report_policy, uvm_report_server
112+
113+
policy = uvm_report_policy(fail_on_warning=True, max_quit_count=5)
114+
uvm_report_server.create(policy=policy)
115+
116+
Report Catching
117+
---------------
118+
119+
The report server supports simple severity rewrites by report ID and message
120+
regular expression:
121+
122+
.. code-block:: python
123+
124+
from pyuvm import UVM_WARNING, uvm_report_server
125+
126+
report_server = uvm_report_server.get()
127+
report_server.add_change_sev("KNOWN_ISSUE", "temporary", UVM_WARNING)
128+
129+
self.uvm_report.error("KNOWN_ISSUE", "temporary mismatch")
130+
131+
Formatter Relationship
132+
----------------------
133+
134+
SV-UVM-style reporting does not replace Python logging. It provides UVM-shaped
135+
report metadata and filtering before the message reaches logging. The rendered
136+
timestamp, hierarchy, filename, line number, and final text layout still come
137+
from the active Python logging formatter.
138+
139+
pyuvm's ``PyuvmFormatter`` remains the customization point for teams that want
140+
a different log layout while keeping the UVM-style report metadata, verbosity
141+
filtering, counts, summaries, and catcher behavior.

docs/docsources/UVM_and_Python.rst

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,4 +141,29 @@ There are no field macros and thus no need to implement this class.
141141
Reporting Classes
142142
^^^^^^^^^^^^^^^^^
143143

144-
We use Python logging instead of the UVM reporting system.
144+
pyuvm uses Python logging as the reporting backend. Existing tests can keep
145+
using ``self.logger`` directly, and every ``uvm_object`` instance, including
146+
descendant instances, has a logger available through ``self.logger``.
147+
148+
pyuvm also provides an opt-in SV-UVM-style reporting path for tests that need
149+
report IDs, UVM verbosity filtering, severity counts, summaries, and report
150+
catching. Enable it before constructing the testbench:
151+
152+
.. code-block:: python
153+
154+
from pyuvm import set_sv_uvm_style_reporting_enabled
155+
156+
set_sv_uvm_style_reporting_enabled(True)
157+
158+
or from the shell:
159+
160+
.. code-block:: bash
161+
162+
PYUVM_ENABLE_SV_UVM_STYLE_REPORTING=1 make sim
163+
164+
UVM verbosity is handled separately from Python logging levels. Info reports
165+
pass when the report verbosity is less than or equal to the configured UVM
166+
verbosity. Python logging levels continue to represent severity for the backend
167+
formatter and handlers.
168+
169+
See :doc:`SV_UVM_Style_Reporting` for examples.

docs/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ pyuvm
66

77
docsources/README.md
88
docsources/UVM_and_Python
9+
docsources/SV_UVM_Style_Reporting
910
apidocs/index

pyproject.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,11 @@ known-third-party = [
9797
[tool.ruff.lint.per-file-ignores]
9898
"pyuvm/__init__.py" = [
9999
"F401",
100+
"I001",
101+
]
102+
"pyuvm/uvm_reporting/__init__.py" = [
103+
"E402",
104+
"I001",
100105
]
101106

102107
[tool.codespell]

pyuvm/__init__.py

Lines changed: 55 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,35 @@
109109

110110
# Section 6
111111
from pyuvm._s06_reporting_classes import uvm_report_object
112+
from pyuvm.uvm_reporting.uvm_report_catcher import (
113+
uvm_report_action_e,
114+
uvm_report_catcher,
115+
uvm_report_message,
116+
)
117+
from pyuvm.uvm_reporting.uvm_report_server import (
118+
uvm_report_policy,
119+
uvm_report_server,
120+
uvm_report_stats,
121+
)
122+
from pyuvm.uvm_reporting import (
123+
get_sv_uvm_style_reporting_enabled,
124+
set_sv_uvm_style_reporting_enabled,
125+
)
126+
from pyuvm.uvm_reporting.uvm_verbosity import (
127+
UVM_DEBUG,
128+
UVM_ERROR,
129+
UVM_FATAL,
130+
UVM_FULL,
131+
UVM_HIGH,
132+
UVM_INFO,
133+
UVM_LOW,
134+
UVM_MEDIUM,
135+
UVM_NONE,
136+
UVM_WARNING,
137+
parse_uvm_verbosity,
138+
resolve_uvm_verbosity,
139+
uvm_reporter,
140+
)
112141

113142
# Section 8
114143
from pyuvm._s08_factory_classes import uvm_factory
@@ -342,7 +371,28 @@
342371
"uvm_policy",
343372
"uvm_transaction",
344373
# Section 6 - Reporting classes
374+
"UVM_DEBUG",
375+
"UVM_ERROR",
376+
"UVM_FATAL",
377+
"UVM_FULL",
378+
"UVM_HIGH",
379+
"UVM_INFO",
380+
"UVM_LOW",
381+
"UVM_MEDIUM",
382+
"UVM_NONE",
383+
"UVM_WARNING",
384+
"parse_uvm_verbosity",
385+
"resolve_uvm_verbosity",
386+
"get_sv_uvm_style_reporting_enabled",
387+
"set_sv_uvm_style_reporting_enabled",
388+
"uvm_report_action_e",
389+
"uvm_report_catcher",
390+
"uvm_report_message",
345391
"uvm_report_object",
392+
"uvm_report_policy",
393+
"uvm_report_server",
394+
"uvm_report_stats",
395+
"uvm_reporter",
346396
# Section 8 - Factory classes
347397
"uvm_factory",
348398
# Section 9 - Phasing classes
@@ -458,7 +508,8 @@
458508
# Set the __module__ attribute for all re-exported names.
459509
# This is necessary for the documentation generation tools to link objects correctly.
460510
for _name in __all__:
461-
# Skip non-classes and non-functions.
462-
if _name in {"__version__", "FIFO_DEBUG", "PYUVM_DEBUG", "uvm_common_phases"}:
463-
continue
464-
globals()[_name].__module__ = __name__
511+
_obj = globals()[_name]
512+
# Constants and collection exports do not have __module__; re-home only
513+
# exported objects that expose it for autodoc links.
514+
if hasattr(_obj, "__module__"):
515+
_obj.__module__ = __name__

0 commit comments

Comments
 (0)