Search code examples
system-verilogassertionsuvmsystem-verilog-assertions

Concurrent Assertion - UVM test dependency


I want to write concurrent assertion which starts after some register write is performed on the DUT from UVM testbench.

class test extends uvm_test;
bit flag;
task run_phase(uvm_phase phase);
//call register write task , data is chosen in a random fashion
write(addr,data);
flag = 1;    // flag gives the time when the register is written.
// after this task the assertions must start depending on data.
endtask
endclass

module top();
//instantiate dut, interface and other stuff

property p1(logic a,logic [4:0] b,logic flag,logic c);
disable iff(!reset || b[i] == 1 || !flag)
/* 
default register value is 0, enable assertion only if 0 is written into it. 
(0 is for enabling certain feature in DUT and by-default it is enabled. 
1 is for disabling that feature. Assertion must never be enabled if b[i] contains 1)
*/
@(posedge clk)
a == 0 |-> $stable(c);
endproperty

assert property p1(dut_top.path_to_dut_signal,uvm_test_top.data,uvm_test_top.flag,dut_top.path_to_dut_other_signal); // gives compile error
// basically I need here dut's signal and the data written to register

// call run_test and set config_db for interface here
initial
begin
// ... run_test 
end

This gives cross module reference compilation error since uvm_test_top is created run-time and I guess assertion inputs are set at compile time.

So, I found a few workarounds, for this.

Declaring some temp and flag variables in global space as follows:

bit flag;   // in global space
logic [4:0] temp;
class test extends uvm_test;
task run_phase(uvm_phase phase);
//call register write task 
write(addr,data);
temp=data, flag=1;
// after this task the assertions must start depending on temp.
endtask
endclass

/** In top, **/

property p1(logic a,logic [4:0] b,logic flag,logic c);
disable iff(!reset || b[i] == 0 || flag ==0)
@(posedge clk)
a == 0 |-> $stable(c);
endproperty

assert property p1(dut_top.path_to_dut_signal,temp,flag,dut_top.path_to_dut_other_signal); 
// solves the error

Here two global space variables are used; which, better be avoided.

Either way, I can take these variables in top and access them by $root in test. But, I'd not prefer using $root for reusability and packages issues (in case my top name changes, or seprate module for assertion is written, the test will get an error).

I am wondering is there any other finer way to accomplish this? Such that I can delay the execution of assertion. Even if I delay it by 1timestamp, I would get assess to uvm_test_top (sounds crazy..!!).

Also, I do not want to use the interface signals, since the DUT is protocol agnostic, it can support different interfaces based on `ifdefs. (Actually this is also another workaround!!)

Any help is greatly appreciated.


Solution

  • The most portable thing I can think of is declare an event inside your SVA interface/module/checker which signals when the interesting register write happens:

    interface sva_checker(...);
      event register_written;
    
      property my_prop;
        register_written |-> ...
      endproperty
    endinterface
    

    This way you only trigger your property after the register write happens. You also don't need to figure out when the register write happened based on the protocol signals, because you're going to rely on information from the testbench.

    Now the interesting part is triggering this event from your UVM testbench. First you need to pass this interface to your UVM testbench. If this is the only interface you have, then it's pretty easy. You just do a config DB set and a corresponding get:

    // in top or wherever you instantiate your SVA checker
    uvm_config_db #(virtual sva_checker)::set(...);
    
    // in your UVM TB
    uvm_config_db #(virtual sva_checker)::get(..., sva_checker);
    

    Now in your code you can just trigger the event after you know that you wrote to the register:

    task run_phase(uvm_phase phase);
      //call register write task , data is chosen in a random fashion
      write(addr,data);
      -> sva_checker.register_written;
      // after this task the assertions must start depending on data.
    endtask
    

    This way you don't use any hierarchical paths and you don't get problems with reusability.

    P.S. I'd consider using the UVM register package for registers. This way you could make it so this event gets triggered automatically after a register write, without having to call it manually.