I know that similar questions have been asked and answered, but I think my situation might be different enough that it warrants discussion.
I have RTL with inline assertions (i.e. concurrent and immediate assertions in the RTL source code). When these assertions fail, I want to report them as UVM errors.
One of the complications here is that I don’t want the RTL to assume a UVM TB environment, thus the `uvm_error and related reporting macros are wrapped in generic macros that may be expanded differently depending on the verification environment. The argument format of my wrapper macros is identical to the $display and SystemVerilog built-in severity system tasks (e.g. $error, $warning, etc.). I discovered this nifty hack which allows defining variadic macros in order to help with this:
Here is an example of my `ERROR macro (max number of $sformatf() arguments is limited to 3 for readability):
`ifdef USE_UVM_REPORT_MACROS
`include “uvm_macros.svh”
import uvm_pkg::*;
define _DELIM define ERROR(p0, p1=ELIM, p2=ELIM)
ifdef _D``p1 \ do uvm_error(“DUT_ERROR”, $sformatf(“(%m) %s”, $sformatf(p0))) while(0)
elsif _D``p2 \ do uvm_error(“DUT_ERROR”, $sformatf(“(%m) %s”, $sformatf(p0, p1))) while(0)
else \ do uvm_error(“DUT_ERROR”, $sformatf(“(%m) %s”, $sformatf(p0, p1, p2))) while(0)
`endif
`else
`define ERROR $error
`endif
This all works fine when uvm_pkg is imported OUTSIDE of the top-level module instance because the declarations within that package will be global to the entire module hierarchy. I’ve seen UVM examples with the "import uvm_pkg::" statement both INSIDE and OUTSIDE the top-level module instance, and I’m not 100% sure which is more correct. It seems like it should be OUTSIDE, otherwise the global uvm_report_ functions defined in uvm_globals.svh wont’ be visible to code within the module-based hierarchy as intended.
Here is where things get really complicated. Our projects are complicated enough where the DUT consisting of an entire chip may be complied piecemeal (e.g. one IP at a time) AND into different libraries, requiring configuration blocks to elaborate everything properly. When integrating lots of IP together from different design groups (or from 3rd parties), sometimes with module namespace conflicts between separately developed IP, separate compilation libraries for different IP is one way to solve these collisions.
For example, lets say we have two different compilation units for parts of the DUT RTL, each compiled into its own library, AND both are compiled with +define+USE_UVM_REPORT_MACROS (see above). When everything is elaborated together into UVM top-level bench, the RTL from the different compiled libraries will EACH have their OWN instance of uvm_top (due to the import statement above) within its containing library, and the UVM error counts from the RTL won’t be tied into those of the top-level UVM bench.
The problem is that within the separate complication paradigm, I don’t think it is possible to refer to things which are declared in the $unit (i.e. outside the top level) scope of different compilation units. What I really want is for ALL RTL accross ALL libraries to resolve to the global uvm_report_* functions declared as globals in the top-level UVM TB.
The only workaround that I have found so far is to create a “dummy” uvm_component within the top-level module (i.e. tb_top), whose parent component will be the bench’s uvm_root, and then use $root.tb_top.dummy.uvm_report_* to get to the uvm reporting functions.
I have really twisted myself into a knot here, but I suspect there is a much more straight forward solution for what I want to achieve…