Create a coverpoint with its bit width varies

I am collecting a coverage of value of register fields, which is defined as

uvm_reg_field reg_fields[string];

For some reason, the covergroup could only be defined in another class with respect to reg_fields.

The obstacle I met is that the bit width of each field is different. I tried like this

class field_coverage;

  covergroup field_cg (input cg_name) with function sample (int field_value);
    valid_value: coverpoint field_value;
    option.name = cg_name;
    option.per_instance = 1;
  endgroup

  function new(string name="");
    field_cg = new(name);
  endfunction

endclass

class A;
  uvm_reg_field reg_fields[string];
  field_coverage field_cg_inst[string]; 
  
  ....

  function init_cov (string name="");
     foreach (reg_fields[field_name]) begin
       field_cg_inst[field_name] = new(field_name);
     end
  endfunction

  task collect_cov ();
     foreach (reg_fields[field_name]) begin
       field_cg_inst[field_name].field_cg.sample(reg_fields[field_name].value());
     end
  endtask

endclass

It works, but by the auto_bin_max with input of sample function defined as int, there are always 64 bins with a large range, even though the field is only a 1 bit value.

Then I change the covergroup to this

  covergroup field_cg (input cg_name, int bit_width) with function sample (int field_value);
    valid_value: coverpoint field_value { bins fv[64] = {[0:2**bit_width-1]}; }
    option.name = cg_name;
    option.per_instance = 1;
  endgroup

It turns out that coverpoint only shows fv[0], fv[1]. When field width is larger than 6, it’s hard to review the coverage. For example, with width = 8, fv[8] is actually correspondent to value 32~35.

What I want to do is when the bit width is less than 6, it only shows its possible value to each bins. Like fv[0], fv[1], fv[2], ... fv[7] when bit width = 3. When the bit width is larger than 6, it shows 64 bins with auto_bins like auto[0:3], auto[4:7], ...

Is there any method to write the coverage to achieve this?

If I understood what you’re asking correctly, then I suggest using a with clause will help with that, the covergroup will be something like

covergroup field_cg (string name, int bit_width) with function sample(int field_value);
    option.name = name;
    option.per_instance = 1; 
    
    cp_field_values: coverpoint field_value { 
      bins fv_less_6[] = {[0:2**bit_width-1]} with (bit_width < 6);
      bins fv_g_or_eq_6[64] = {[0:2**bit_width-1]} with (bit_width >= 6);
    }
  endgroup

Thanks for your reply, but it doesn’t solve my problem.

bins fv_less_6[] = {[0:2**bit_width-1]};

it create 2**bit_width bins for each value, no matter how big the bit_width is. And it’s hard to review when bit_width is large, for there are too many bins.

bins fv_g_or_eq_6[64] = {[0:2**bit_width-1]}

create at most 64 bins.

When bit_width < 6, which means it couldn’t create 64 bins, it will create only 2**bit_width bins, the same as above.

When bit width >= 6, it create 64 bins only, fv_g_or_eq_6[0], fv_g_or_eq_6[1], …, fv_g_or_eq_6[63].

So, you don’t need with clause to create these two bins.
Just bins fv_g_or_eq_6[64] = {[0:2**bit_width-1]} would be the same.

What I want is that it could generate at most 64 bins with each bins show the range if bit width > 6, the same way auto bins do.

For example, let’s consider bit width = 7 and 8, which means size of each bins would be 2 and 4. When the cover report shows below for both

fv_g_or_eq_6[0] not covered,
fv_g_or_eq_6[1] covered,
...
fv_g_or_eq_6[63] not covered

It actually means [0~1], [126~127] is not covered for width = 7, while [0~3], [252~255] is not covered for width = 8.

The reviewer needs to know the bit width and needs to do the calculation. For my case, there are multiple different bit width when new the covergroup, but the width didn’t show on the report.

I hope it could generate like below

width = 7: fv_g_or_eq_6[0:1], fv_g_or_eq_6[2:3], ..., fv_g_or_eq_6[126:127]
width = 8: fv_g_or_eq_6[0:3], fv_g_or_eq_6[4:7], ..., fv_g_or_eq_6[252:255]

It’s easily for reviewer to know which range is not covered, the same way auto bin is generated.

===

I read the LRM, where it says

If a coverage point of an integral expression does not define any bins except ignored or illegal bins (see 19.5.5 and 19.5.6), SystemVerilog automatically creates state bins.

But it seems that it creates all the bins first then doing the exclude. So when I try

bins field_value = { ignore_bins invalid = {[2**bit_width:2**31-1]};
                     ignore_bins negative = {[-(2**31):-1]};
                   }

It create each bin with huge size = (2^32/2^6) = 2^26, and exclude the part of ignore_bins. Only 1 bin auto[0:2**bit_width-1] left.

I am now writing as below

valid_value: coverpoint field_value { bins fv[64] = {[0:2**bit_width-1]}; }

and passing the bit_width in the comment of coverpoint as a workaround, so the reviewer could do the calculation.

Still seek for better solution…