Virtual function in system verilog

Hello,

I would like to define a base coverage class that have implementation of monitoring registers accesses,
and each time a new register captured, the base class will call a virtual function “sample_register_coverage();”
this function is implemented in each child and collect relevant coverage.

base class code:


class base_coverage extends uvm_component;

    uvm_analysis_imp #(uvm_reg_item, base_coverage)   in_ap;

    `uvm_component_utils(base_coverage)

    function new(string name, uvm_component parent= null);
        super.new(name, parent);
        in_ap     = new("in_ap",  this);
    endfunction : new


    function void write(uvm_reg_item t);
        uvm_reg r;    
        $cast(r,t.element);       
        reg_addr = r.get_address();
        reg_data = r.get();    
        sample_register_coverage();       
    endfunction


    virtual function void sample_register_coverage();
        `uvm_info(get_type_name(),  "base_coverage::sample_register_coverage ", UVM_MEDIUM)        
    endfunction : sample_register_coverage

endclass


Child1 class code:



class coverage_child1 extends base_coverage;
    
    `uvm_component_utils(coverage_child1)
  
    function new(string name, uvm_component parent= null);
        super.new(name, parent);
    endfunction : new

    function void sample_register_coverage();
        `uvm_info(get_type_name(),  "coverage_child1::sample_register_coverage ", UVM_MEDIUM) 
    endfunction : sample_register_coverage

endclass


I have more then 1 child, but for now lets assume I have the same code within all children, the only different is the name.

instances of coverage classed in the environment:




class env extends uvm_component;

    base_coverage base_cov;
    coverage_child1 cov_child1;
    coverage_child2 cov_child2;
    reg_monitor reg_mon;
    
    `uvm_component_utils(env)


    function new(string name, uvm_component parent= null);
        super.new(name, parent);
    endfunction : new


    function void build_phase(uvm_phase phase);
        super.build_phase(phase);
        base_cov   = base_coverage::type_id::create("base_cov",this); 
        cov_child1 = coverage_child1::type_id::create("cov_child1 ",this);  
        cov_child2 = coverage_child2::type_id::create("cov_child2 ",this);
        reg_mon    = reg_monitor::type_id::create("reg_mon",this);
    endfunction : build_phase


    function void connect_phase(uvm_phase phase);
        reg_mon.reg_ap.connect(base_cov.in_ap);
    endfunction : connect_phase

endclass


My issue is that I see in my prints (and also when tried to collect coverage)
that the sample_register_coverage is done on the base only.
I’m not entering to the children’s sample_register_coverage at all.

As I checked in some exapmles I think that the “virtual” defined well.
maybe I have a misunderstood with the base and children connection / instances ?
I mean, Can I create a base and run on the base my “write” function and within the “write” call a virtual function? Is that supposed to be okay ?

Thanks in advance.

In reply to AL_verification:

You are confusing child-parent object relationships with inheritance. In the UVM, parent objects (env) create their children (base_cov, cove_child1, cov_child2, reg_mon). coverage_child1 is an extension of base_cov, not a child of it. Would be better to rename coverage_child1 to just coverage1. Then what you probably wanted to write is

class env extends uvm_component;
 
    base_coverage base_cov;
    reg_monitor reg_mon;
    `uvm_component_utils(env)
 
    function new(string name, uvm_component parent= null);
        super.new(name, parent);
    endfunction : new
 
    function void build_phase(uvm_phase phase);
        base_cov   = coverage1::type_id::create("cov_child1 ",this);  
        reg_mon    = reg_monitor::type_id::create("reg_mon",this);
    endfunction : build_phase
 
    function void connect_phase(uvm_phase phase);
        reg_mon.reg_ap.connect(base_cov.in_ap);
    endfunction : connect_phase
 
endclass

In reply to dave_59:

Thanks for the explanation.

But I still have a misunderstood.
what about coverage2 ?
should I define all coverage1…N with base_cov type?
and then should I connect each one of them ?

This way : ?


class env extends uvm_component;
 
    base_coverage cov1;
    base_coverage cov2;
    base_coverage cov3;
    reg_monitor reg_mon;
    `uvm_component_utils(env)
 
    function new(string name, uvm_component parent= null);
        super.new(name, parent);
    endfunction : new
 
    function void build_phase(uvm_phase phase);
        supeer.build_pahse(phase);
        cov1       = coverage1::type_id::create("cov1",this);  
        cov2       = coverage2::type_id::create("cov2",this);  
        cov3       = coverage3::type_id::create("cov3",this);  
        reg_mon    = reg_monitor::type_id::create("reg_mon",this);
    endfunction : build_phase
 
    function void connect_phase(uvm_phase phase);
        reg_mon.reg_ap.connect(cov1.in_ap);
        reg_mon.reg_ap.connect(cov2.in_ap);
        reg_mon.reg_ap.connect(cov3.in_ap);
    endfunction : connect_phase
 
endclass



why is not working when I define only the base and connect him.
all extended classes have already the base tasks and implementation.
where am I wrong ?

In reply to AL_verification:

Yes, if you create three different coverage objects, you need to connect three objects to the monitor. Again you sre confusing class type inheritance with class objects. If you create three children, You have three mouths to feed.

In reply to dave_59:

Understand, Thanks.

Can I add a coverage_top that will be like a top of all coverage classes, and in the env I will instance the coverage top ?
Is it ok that this coverage_top will be also the top of coverage classes, I mean will instance them, and also all coverage classes will extends him. is it makes sense ?

I will how what I’m meaning:

coverage_top:


class coverage_top extends uvm_component;
 
    int reg_addr;
    int reg_data;
    uvm_analysis_imp #(uvm_reg_item, coverage_top )   in_ap;
 
    base_coverage cov1;
    base_coverage cov2;

    `uvm_component_utils(coverage_top )
 
    function new(string name, uvm_component parent= null);
        super.new(name, parent);
        in_ap     = new("in_ap",  this);
    endfunction : new
 

    function void build_phase(uvm_phase phase);
        supeer.build_pahse(phase);
        cov1       = coverage1::type_id::create("cov1",this);  
        cov2       = coverage2::type_id::create("cov2",this);  
    endfunction : build_phase

 
    function void write(uvm_reg_item t);
        uvm_reg r;    
        $cast(r,t.element);       
        reg_addr = r.get_address();
        reg_data = r.get();    
        sample_register_coverage();       
    endfunction
 
 
    virtual function void sample_register_coverage();
        `uvm_info(get_type_name(),  "coverage_top ::sample_register_coverage ", UVM_MEDIUM)        
    endfunction : sample_register_coverage
 
endclass

coverage1:


class coverage1 extends coverage_top;
 
    `uvm_component_utils(coverage1 )
 
    function new(string name, uvm_component parent= null);
        super.new(name, parent);
    endfunction : new
 
    function void sample_register_coverage();
        `uvm_info(get_type_name(),  "coverage1 ::sample_register_coverage ", UVM_MEDIUM) 
    endfunction : sample_register_coverage
 
endclass

env:



class env extends uvm_component;
 
    coverage_top cov_top;
    reg_monitor reg_mon;

    `uvm_component_utils(env )
 
    function new(string name, uvm_component parent= null);
        super.new(name, parent);
    endfunction : new
 
    function void build_phase(uvm_phase phase);
        super.build_pahse(phase);
        cov_top= coverage_top::type_id::create("cov_top",this); 
        reg_mon    = reg_monitor::type_id::create("reg_mon",this);
    endfunction : build_phase
 
    function void connect_phase(uvm_phase phase);
        reg_mon.reg_ap.connect(cov_top.in_ap);
    endfunction : connect_phase
 
endclass


Is this the correct way to do it?
If no, what will be the correct one ?
I want to have 1 connection of the analysis port and 1 implementation task of the related “write” , and few sample_register_coverage implementations - different for each coverage class 1…N

I believe there should be a way.

In reply to AL_verification:

I think we are getting into an XY problem where we are trying to solve your attempt at solving a problem, and not helping with your underlying problem. Show us the coverage model (covergreoups) and how they need to get sampled without getting into virtual functions.

In reply to dave_59:

So I’ll explain my question.

I’m working on a top verification environment.
for now what I have is:

  1. coverage class for each block
  2. base coverage class, each ‘block coverage class’ extends the ‘base coverage claass’
  3. coverage top - which creates an instance for each ‘block coverage class’,
    this ‘coverage top’ is created and instanced within the env.

now, each ‘block coverage class’ will implement different cover-groups and cover-points on registers accesses, each is related for this specific block.

for now the “write” function of the register analysis port is implemented in 1 ‘block coverage class’
and now I need to added registers coverage each are related to another ‘block coverage class’
I don’t like to copy the “write” function again, and connect the analysis port again.
I expect it to be in more then 2 ‘block coverage class’
so I thought maybe I can implement the ‘write’ function in the base, and in each ‘block coverage class’ implement just the registers cover-group and sample.

I will show in the example the way I can do it if I’m not using the base and virtual idea.

Block 1 coverage class:



class coverage1 extends base_coverage;
   
    uvm_analysis_imp #(uvm_reg_item, base_coverage)   in_ap;
    covergroup coverage1_register_addresses();
        reg_addr_block_A_cp :coverpoint reg_addr{
            bins bin_num1 = {32'h8000_0000};
            bins bin_num2 = {32'h9000_0000};
        }
    endgroup   

    `uvm_component_utils(coverage1)
 
    function new(string name, uvm_component parent= null);
        super.new(name, parent);
        in_ap = new("in_ap",  this);
        coverage1_register_addresses = new();
    endfunction : new
 
   
    function void write(uvm_reg_item t);
        uvm_reg r;
    
        $cast(r,t.element);
       
        reg_addr = r.get_address();
        reg_data = r.get();
         
        coverage1_register_addresses.sample();
     
    endfunction 

endclass



Block 2 coverage class:



class coverage2 extends base_coverage;
   
    uvm_analysis_imp #(uvm_reg_item, base_coverage)   in_ap;

    covergroup coverage2_register_addresses();
        reg_addr_block_B_cp :coverpoint reg_addr{
            bins bin_num1 = {32'hAAAA_0000};
            bins bin_num2 = {32'hBBBB_0000};
        }
    endgroup   

    `uvm_component_utils(coverage2)
 
    function new(string name, uvm_component parent= null);
        super.new(name, parent);
        in_ap = new("in_ap",  this);
        coverage2_register_addresses= new();
    endfunction : new
 
   
    function void write(uvm_reg_item t);
        uvm_reg r;
    
        $cast(r,t.element);
       
        reg_addr = r.get_address();
        reg_data = r.get();
         
        coverage2_register_addresses.sample();
     
    endfunction 

endclass



Base coverage class:



class base_coverage extends uvm_component;
    
    int                         coverage_enable=1; 
    int unsigned                reg_addr;
    int unsigned                reg_data;

    `uvm_component_utils(base_coverage)
 
    function new(string name, uvm_component parent= null);
        super.new(name, parent);
    endfunction : new
 
endclass



coverage top class:



class coverage_top extends uvm_component;
 
    coverage1 cov1;
    coverage2 cov2;

    `uvm_component_utils(coverage_top)
 
    function new(string name, uvm_component parent= null);
        super.new(name, parent);
    endfunction : new
 
    function void build_phase(uvm_phase phase);
        supeer.build_pahse(phase);
        cov1       = coverage1::type_id::create("cov1",this);  
        cov2       = coverage2::type_id::create("cov2",this); 
    endfunction : build_phase
  
endclass


env:



class env extends uvm_component;
 
    coverage_top cov_top;
    reg_monitor reg_mon;

    `uvm_component_utils(env)
 
    function new(string name, uvm_component parent= null);
        super.new(name, parent);
    endfunction : new
 
    function void build_phase(uvm_phase phase);
        supeer.build_pahse(phase);
        cov_top    = coverage_top::type_id::create("cov_top",this);  
        reg_mon    = reg_monitor::type_id::create("reg_mon",this);
    endfunction : build_phase
 
    function void connect_phase(uvm_phase phase);
        reg_mon.reg_ap.connect(cov_top.cov1.in_ap);
        reg_mon.reg_ap.connect(cov_top.cov2.in_ap);
    endfunction : connect_phase
 
endclass

In reply to AL_verification:

Any Idea ?