Coverage for volatile registers in UVM RAL

I have defined a register and instantiated as below:

Highlights:
It is a volatile register
No reads/ writes (frontdoor or backdoor) is being done.
The field is updated by the DUT only

Query:
I have enabled the coverage at block level also.
But i am unable to get coverage when the field is modified by the DUT. Am i missing anything ?

 class reg_ABC extends uvm_reg;
 
    rand uvm_reg_field field_x;
    uvm_reg_data_t cov_actual; 
    uvm_cov_op_e access_op;
    uvm_cov_op_e ignore_op;
 
    covergroup cg_op_actual_num_bins ();
       cp_field_x: coverpoint cov_actual[0:0] {bins val_range[`SLA_RAL_FIELD_BINNED_CP_NBINS] = {[0:$]};}
       cp_op: coverpoint access_op {bins op[2] = {UVM_CVR_READ,UVM_CVR_WRITE}; ignore_bins ib_op = {ignore_op};}
       cr_field_x: cross cp_field_x, cp_op;
    endgroup
 
    covergroup cg_field_modified ();
       cp_field_x: coverpoint (field_x.get_reset() != cov_actual[0:0]) {bins was_modified = {1};}
    endgroup
 
    function new(string name = "");
 	   super.new(name, 64, build_coverage(UVM_CVR_REG_NUM_BINS | UVM_CVR_REG_MODIFIED));
 
 	   if (has_coverage(UVM_CVR_REG_NUM_BINS)) 
 		   cg_op_actual_num_bins = new();
 	   if (has_coverage(UVM_CVR_REG_MODIFIED)) 
 		   cg_field_modified = new();
    endfunction: new
 
    virtual function void build();
 
       field_x = new("field_x");
       field_x.configure(.parent(this), .size(1), .lsb_pos(0), .access("W1C"), .volatile(1), .reset(1'h0), .has_reset(1), .is_rand(0), .individually_accessible(0));
 
    endfunction: build
    
    virtual function void sample (uvm_reg_data_t data, uvm_reg_data_t byte_en,bit is_read,uvm_reg_map map);
 
       cov_actual = data;
       access_op = is_read ? UVM_CVR_READ : UVM_CVR_WRITE;
 
       if (get_coverage(UVM_CVR_REG_NUM_BINS)) 
          cg_op_actual_num_bins.sample();
       if (get_coverage(UVM_CVR_REG_MODIFIED)) 
          cg_field_modified.sample();
 		
 	endfunction
 
 endclass : reg_ABC
 
 reg_ABC = new("reg_ABC");
 reg_ABC.configure(this, null);
 reg_ABC.build();
 ip_reg_model.add_reg(reg_ABC, `UVM_REG_ADDR_WIDTH'h210, "RW");
 end
 if (reg_ABC != null) begin
   reg_ABC.add_hdl_path('{
             '{{IP_TOP_PATH, "reg_inst.registers.reg_ABC.field_x"}, 0, 1}
   });
 
 
 uvm_reg::include_coverage(` `"*"` `,UVM_CVR_REG_BITS | UVM_CVR_REG_NUM_BINS | UVM_CVR_REG_MODIFIED); 
 ip_regmodel = ip_ral_extended_top_block::type_id::create(` `"ip_regmodel"` `,this);   ` `// Instantiate the regmodel`
 
 ip_regmodel.build();                                                            ` `// Build the regmodel`
 
 ip_regmodel.set_coverage(UVM_CVR_ALL);  `

This won’t work without something to trigger the covergroup sampling. Either a read/write prediction or an explicit call to the sample method.

But I don’t see why you modeled this as a register at all.

Thanks for the clarification. This was just a sample register. We have many registers and fields like that.

What would be a better course of action ?

I would suggest using the bind construct to insert a coverage module into your dut that monitors the outputs of these registers.

If there is a significant number of these registers and you have an in-house registered model tool, you may want to have them automatically inserted by the same tool. Otherwise, you might have to build your own tool to do it.

This is the best free advice I can give you. :slight_smile: