Part of the  

Chip Design Magazine

  Network

About  |  Contact

Part II: The Ecstasy and the Agony of UVM Abstraction and Encapsulation Featuring the AMIQ APB VIP

Part II of our tour through UVM reusability through TLM ports and the factory in the AMIQ APB VIP.

by Hamilton Carter – Senior Editor

Tuning the Receiver
Part I didn’t answer how, (or indeed if), the monitor’s messages make their way over to the write_item_from_mon method in the coverage collector.   Remember, the method is defined in the coverage collector, but apparently called from nowhere.  It gets worse.  Not only is the method not called from within the AMBA/APB package, but apparently it’s not called from the cagt package either!  Go ahead and look.  Take your time, grep through the files… I’ll be here when you get back.

You probably found the definition of the method up in the cagt package, but not a call to the method.  There’s a bit of UVM macro chicanery going on here.  You might have noticed a macro call at the top of the cagt version of the coverage file:

       `uvm_analysis_imp_decl(_item_from_mon)
       //coverage class
       class cagt_coverage#(type VIRTUAL_INTF_TYPE=int, type MONITOR_ITEM=uvm_sequence_item) extends 
             uvm_component;
              //pointer to the agent configuration class
              cagt_agent_config #(VIRTUAL_INTF_TYPE) agent_config;
               //port for receiving items collected by the monitor
               uvm_analysis_imp_item_from_mon#(MONITOR_ITEM,
                cagt_coverage#(VIRTUAL_INTF_TYPE, MONITOR_ITEM)) item_from_mon_port;

 

Interestingly, the argument to the `uvm_analysis_imp_decl macro contains the last few underscore terms of the method we’re looking for, ‘_item_from_mon’.  A little further down, notice that the partial phrase turns up again where the ‘port for receiving items…’ is declared.  The macro sets up a subclass of a TLM analysis port that’s specifically named uvm_analysis_imp* where the * is replaced by the macro’s argument.  Within that sub-class, unseen by mortal code-browsing eyes, the macro also sets up the call to the coverage collectors input method ‘write_item_from_mon’ which is defined as write* where the *, you guessed it, is once again replaced by the argument to the macro: ’_item_from_mon’.

Connecting the Components
OK, now we’ve located the pertinent communications methods, but where are the two ports attached to each other?  Is magical code automatically created that attaches the ports under the sheets?  Nope!  Look in the agent file within the cagt package.  There, you’ll find both the instantiation code for the monitor and the coverage collector, as well as the code that connects the two:

       if(coverage != null) begin
               coverage.agent_config = agent_config;
               monitor.output_port.connect(coverage.item_from_mon_port);

There, everything is well-explained… except for one last little detail.  Remember the cagt package doesn’t know a thing about the APB monitor or the APB coverage collector, nor should it.  It just sits back and happily and dumbly connects an abstracted monitor, (that doesn’t do anything), to an abstracted coverage collector, (that also doesn’t do anything).  Getting the environment to actually work requires the UVM factory to pull a switcheroo of object types using set_inst_override at the last minute in the APB agent file:

function new(string name = "amiq_apb_agent", uvm_component parent);
        super.new(name, parent);

        cagt_monitor#(.VIRTUAL_INTF_TYPE(amiq_apb_vif_t),
          .MONITOR_ITEM(amiq_apb_mon_item))::type_id::set_inst_override(amiq_apb_monitor::get_type(),
          "monitor", this);

        cagt_coverage#(.VIRTUAL_INTF_TYPE(amiq_apb_vif_t), 
          .MONITOR_ITEM(amiq_apb_mon_item))::type_id::set_inst_override(amiq_apb_coverage::get_type(),
          "coverage", this);

 endfunction

 

So, there you have it.  It took a bit of up-front planning—mostly defined by the UVM architecture—and a bit more work to dig through the code, (the first time anyway). Here’s what we got in return:

  1. Using the cagt package as a base, we never have to wire monitors to coverage collectors again.  We can build subclasses of cagt_monitor and cagt_coverage that add our specific code.  The banal connection code is executed automatically in the cagt_agent.
  2. Again with cagt: we never have to instantiate TLM analysis ports in either our monitors or coverage collectors ever again.
  3. Our monitor has no ties to the rest of the environment.  It needs its cagt base class, but it knows nothing of the specific objects it’s attached to, or for that matter, who’s doing the attaching.
  4. Ditto for the coverage collector.
  5. The base class can be used as the basic structure because it has no knowledge of what it’s specifically putting together.  Specifics are all handled by the factory’s switch at the last possible moment.  Here are more details about that little trick.

Consequently, we get a much better chance of write-once-and-walk-away-code—WOAWAC, as the natives say.   Oh!  And we get to look super-smart in the process!

 

Leave a Reply