Creating a custom array of cover bins

Hi,

Similar to auto binning (where you select the number of bins and the numerical range is split among them). I want to create a set/array of bins that is not bound to the bitwidth, but instead some arbitrarily set maximum. (e.g. localparam DATA_MAX = 0xFCE0;)

I’ve tried a few methods and I think the solution is converging to using a function. However, I cannot seem to get the datatypes to work nicely.

Does anyone have some example code that would work? My initial solution tried to replicate CrossValType/CrossQueueType and assign a set of bins that way.

Example code that does NOT work.

        
function bins [BINS_CNT-1:0] bin_sel (int minVal, int maxVal, int bin_cnt);
            int stepSize = (maxVal-minVal)/bin_cnt;
            for(int i = 0; i < bin_cnt; i++) begin
                if(i == bin_cnt-1) begin
                    bin_sel[i] = {[i*stepSize:maxVal]};
                    //bin_sel.push_back([i*stepSize:maxVal]);
                end
                else if(i == 0) begin
                    bin_sel[i] = {[minVal:stepSize]};
                    //bin_sel.push_back([minVal:stepSize]);
                end
                else begin
                    bin_sel[i] = {[i*stepSize:i*(stepSize+1)]};
                    //bin_sel.push_back([i*stepSize:i*(stepSize+1)]);
                end
            end
        endfunction

In reply to mhild7:

OK so the answer is to KISS:

bins range_bins [BINS_CNT] = {[MIN_VAL:MAX_VAL]};

However, this has one disadvantage over auto-binning as you lose the true numerical range of the bin in the bin description in the coverage tool I use.

Also i’d still like to know how it would be possible to create bins using a function. I feel like there are real use cases with that. the WITH keyword does not work for ultra-wide data buses as it becomes computationally intractable.

In reply to mhild7:

You can create queue of values that can be used as your bins if you use the right expression


module test();
  parameter WIDTH = 8;
  bit [WIDTH-1:0] sig;
  typedef bit [WIDTH-1:0] bins_queue_t[$];
  
  
  covergroup cg(ref bit [WIDTH-1:0] x, int maxVal, int minVal, int bin_cnt);
    cp: coverpoint x {
      bins custom_bins[] = bins_queue_compute(maxVal, minVal, bin_cnt);
    }
  endgroup
  
  function automatic bins_queue_t bins_queue_compute (int maxVal, int minVal, int bin_cnt);
    bins_queue_t bins_q;
    int stepping;
    stepping = (maxVal - minVal)/bin_cnt;
    for(int i = minVal; i < maxVal; i+=stepping) begin
      bins_q.push_back(i);
    end
    $display("bins_q = %p", bins_q);
    return bins_q;
  endfunction
  
  initial begin
    cg   m_cg;
    int upper;
    int lower;
    int num;
    upper  = 20;
    lower   = 5;
    num     = 5;
    m_cg = new(sig, upper, lower, num);
    repeat(10) begin
      if(!std::randomize(sig) with {sig inside {[lower:upper]};}) $fatal(1, "Randomize Failed");
      $display("sig = %p", sig);
      m_cg.sample();
      $display("m_cg coverage = %0g", m_cg.get_inst_coverage());
    end
    
  end
  
endmodule

HTH,
-R

In reply to rgarcia07:

Hi RGarcia07,

Thanks for the detailed reply! One question though, on this line:

bins_q.push_back(i);

This seems like a bin for a single value. I am looking for bins that contain ranges (e.g. [0:100]). Can you confirm/deny what this approach really creates?

In reply to mhild7:

As far as I understand ranges are not allowed, you cannot construct a queue of range expressions, only integral data types for bins in coverpoints, it would be a nice addition to find some way to provide a list ranges, as you tried already, or in the case of transition coverage a list of transitions, but I guess this is language limitation.

HTH,
-R

Hmm that is too bad. My simple solution replaces the informative bin name from describing the included numerical range per bin for auto binning to bin [0]->[BIN_CNT-1] name in the coverage tool I use.

I guess this could be avoided entirely by using a preprocessing language but that adds another layer to this whole schema.

In reply to mhild7:

I thought I understood your question and what you wanted but I think differently now, could you show some more details of how the bins and coverage would look like, probably enums and a function could help but I need a bit more details

In reply to rgarcia07:

Sure!

Lets say some DataBus:
localparam WIDTH = 32;
logic [WIDTH-1:0] dataBusTest;

where dataBusTest can be (0x100 → 0xFFFF_FC00). This is limited by some mathematical constraints on the upstream logic.

I want to have the following bins:

  1. min_value = { 0x100 }
  2. max_value = { 0xFFFF_FC00 }
  3. 64 bins equally spaced from 0x100 to 0xFFFF_FC00, where the bin names reflect the numerical range they contain.

#3 is the issue. I cant seem to create bins that contain a range of numerical values without getting syntax errors. My current workaround is this:

bins range_bins [BINS_CNT] = {[MIN_VAL:MAX_VAL]};

but the problem is, lets say for bin[0] i get the name bin[0], with auto binning, i would get the bin name bin[256:67109100].

Is this enough information for you?

In reply to mhild7:

You may be pushing to much into the bin construction and could be using a simple coverpoint expression instead with an array of covergroups. Here is a complete (untested) example of what I think you are trying to do:

class cg_object #(int BITWIDTH);
  typedef bit [BITWIDTH-1:0] uint;
  class cg_wrapper; // this is how we get around the fact that you can't have arrays of embedded covergroups in a class. 
    covergroup cg(uint min,max) with function sample(uint value);
      option.per_instance = 1;
      coverpoint value inside {[min:max]} {
        bins hit = {1};
      }
    endgroup
    function new(uint min,max);
      cg =new(min,max);
      cg.option.comment = $sformatf("range_%0d_%0d",min,max);
      $display(cg.option.comment);
    endfunction
  endclass
  
  cg_wrapper cgo[];

  function new(int minVal, int maxVal, int bin_cnt);
    int stepSize = (maxVal-minVal)/bin_cnt;
    cgo = new[bin_cnt];
    foreach(cgo[i]) begin
      uint start = minVal + i*stepSize;
      if (i == bin_cnt-1)
        cgo[i] = new(start,maxVal);
      else
        cgo[i] = new(start, start + stepSize-1);
    end
  endfunction
  function void sample(uint value);
    foreach(cgo[i]) cgo[i].cg.sample(value);
  endfunction
endclass

module top;
  cg_object#(8) cg = new(10, 100, 4);
  
  initial repeat (100) cg.sample($urandom_range(100));
                                 
endmodule

Hi Dave,

You do make a good point that I could complete this with increasing the number of covergroups instead of trying to fit it all in one. Thanks!

Currently playing around with your sample code. Haven’t quite got it to work yet (TB build works but I dont see any new covergroups generated yet).

I’ll post again once I have some more success.

After some more playing with it - got it to work!

However, it’s a bit clunky. I find in my coverage tool the bins are out of order by default and requires a lot of clicking through to collect all comments. I think I prefer the multi cover point and the simplicity of the other solution. The workaround to the lack of ranges displayed with my original solution is to just generate a longer comment describing all of the bins, so it kinda works.

Thank you both for your help.

In reply to dave_59:

Hi Dave,
when create cg, why no need to declare object name(just like cg cg_int)?
Thanks

In reply to peter:

See Section 19.4 Using covergroup in classes. in the IEEE 1800-2017 SystemVerilog LRM