Uvm report macros in DUT assertions (UVM version 1.1)

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…

In reply to thomase:

Importing uvm_pkg outside a module means it is available in the file scope. This is want you narmally don’t waant to have. Importing it in the module it is available in the corresponding module scope.

The recommendation is to hold concurrent assertions in a seperate unit which will be bound to the design. Your problem is a good example why you should follow this recommendation and avoid to implement concurrent assertions inside the RTL. If you might modify/extend your assertions you are running in even more trouble. Your problems might not be controllable in any way.
The question is, what the effort would be to cut out your aassertions from the RTL.

In reply to thomase:

Thomase,

I believe you may have made a few incorrect assumptions.

You should be able to use the UVM reporting macros regardless of whether the testbench is UVM or not. If fact I have many times recommended to people wary of adopting the UVM that if nothing else, they use the UVM reporting mechanism to gain control have how their reports get generated.

There should only be one uvm_top because there is only one uvm_pkg. It doesn’t matter how many times you import the uvm_pkg, there is only one instance of a package. That is one of the key points of using packages instead of compiling the UVM base class library into separate compilation units. If you are getting multiple report summaries, you have set up your compilation flow incorrectly.

It turns out that this was a compilation flow issue. If the uvm_pkg definition is read in ONLY once (the work library doesn’t matter), then further separate compilation steps will be able to resolve references to that package even though it may have been read into a different work library.

Regarding chr_sue’s comment about putting concurrent assertions in a separate unit, these assertions have been written by the RTL designer in order to check assumptions made in the process of writing the RTL code, not unlike how a software programmer uses assertions. Many are immediate assertions deep within nested control-flow statements, and can not easily be moved to a separate unit.

An even better catch-all for this would be to override the SystemVerilog severity tasks with VPI and tie this back in to the UVM reporting mechanism. I think this would also capture the case where the assertion has no explicit fail statement and $error() is called by default.