Self registering interface

I’m writing an interface to be bound to a module which instantiated multiple times in my dut. In order to control the assertions included in the interface I’ve found inspiration from a post from Tudor Timisescu (My Take on SVA Usage with UVM) which in turns build on the notion of a “phase aware interface” introduced by Litterick (SVA Encapsulation in UVM) and other papers.

Unfortunately is not that straightforward to register the interface in the config_db since we don’t have an handle on all the instantiated interfaces as a result of the bind directive.

So I thought about the idea of a self-published interface introduced by Vance and others (My Testbench Used to Break! Now it Bends: Adapting to Changing Design Configurations, where we can use the %m to have a unique id in the config_db:

initial begin
  string s;
  s = $sformatf("%m");
  uvm_config_db #(sva_checker_wrapper)::set(null, s, "checker_wrapper", checker_wrapper);
end

In this case though I don’t want to register the interface, but rather an object called sva_checker_wrapper that it is used by the agent in the build phase so that we can fake the checker_proxy to be under the agent hierarchy.

The agent is a very simple one and has the following code in the build_phase:

virtual function void build_phase(uvm_phase phase);
  sva_checker_wrapper checker_wrapper;
  super.build_phase(phase);
  if (!uvm_config_db #(sva_checker_wrapper)::get(null, $sformatf("*%0d*%s", id, target_scope), "checker_wrapper", checker_wrapper))
            `uvm_fatal("CFGERR", $sformatf("*%0d*%s", id, target_scope))

   sva_checker = checker_wrapper.get_proxy("sva_checker", this);
endfunction: build_phase

the id and the target_scope are set by the environment when the agents are created in order to facilitate the retrieval of the checker_wrapper object stored by the bound interface.

I’ve added the wildcards so that it would match the instance of the interface, but unfortunately we don’t seem to have a match and the get fails to find the object in the config db.

Any idea or suggestion? My assumption is that this two strings should match when I’m trying to get the object from the config_db:

tb.dut.module[0].another_module.bound_intf
*0*bound_intf

while apparently they don’t…

After some deep dive into the config_db infrastructure I realized that my actual code was missing several pieces that did not make it into the sanitized example posted here.

First of all the parametrization of the config_db set and get calls is critical. I happened to have missed to add the correct namespace in the parameter and therefore was setting and getting two different objects. While I was setting the concrete sva_checker_wrapper class, the agent was trying to get the abstract one. So the correct calls should be more like:

// code in the interface
// without the namespace in place the interface will implement 
// the abstract class and therefore have a different type
uvm_config_db #(sva_pkg::sva_checker_wrapper)::set(null, s, "checker_wrapper", checker_wrapper);

// code in the agent
uvm_config_db #(sva_pkg::sva_checker_wrapper)::get(null, s, "checker_wrapper", checker_wrapper);

Secondly, I discovered that regex matching doesn’t work when the pattern is in the get, since the implementation logic retrieves all the objects of the same type (hence the importance of using the right namespace in the parametrization), puts them into a queue and then goes through all of them performing a regex matching between the scope of the set value and the string passed in the get function, as a combination of “context”, “inst_name” and “field_name”. The regex pattern is calculated on the set value, not the get value, hence the impossibility to use the approach I was trying to leverage.

This is a bit of a bummer cause now my environment needs to know the full hierarchy of the bound interface, hampering vertical reusability:

// creating agents (i've dropped the need of the 'id' class member and 
// only rely on the 'target_scope')
foreach(sva_checker_agt[i])
  sva_checker_agt[i] = sva_checker_agent::type_id::create($sformatf("sva_checker_agt[%0d]", i), this);
  sva_checker_agt[i].target_scope = $sformatf("tb.dut.mod1.sub[%0d].target_mod", i);
end

In order to overcome this limitation I believe I could get the ‘target_scope’ from an environment configuration class that has been correctly created by the test prior to creating the environment, but it still means the environment can’t be reused as is vertically, cause likely the bound interface will end up in a different hierarchy when the dut is going to be included in a larger dut.

Since the regex matching is only working in one way, I guess the true way forward is to do some string manipulation gymnastic before the interface is set in the config_db. The instance can be extracted from the "%m" format specifier and then passed to the config_db, in a way that could facilitate the vertical reuse.

If any of this doesn’t make any sense, or hasn’t really added anything insightful I could still argue that it helped at least me understanding a bit more of the config_db and its intricacies.

For completeness, these are the articles I went through to tackle the problem, on top of the ones already included in the OP:

  1. New and Active Ways to Bind to Your Designs
  2. Verification prowess with the UVM harness
  3. Flexible UVM Components: Configuring Bus Functional Models
  4. The Missing Link: The Testbench to DUT Connection

They all provide very interesting techniques. If anyone has any other references please let me know.