Reputation: 7573
I've got a protocol that supports bursts, where each transaction is composed of N
individual transfers. Each transfer has a unique index from 0
to N-1
. I would like to cover that all transfer orders have been seen (i.e. 0 1 2 3
, 1 2 3 0
, 0 2 3 1
, etc.). The value of N
is variable (though in my case I only care about 4 and 8 for now).
The naive approach would be to cover each index individually and cross them, but this would mean I would need multiple covergroups
, one per each value of N
.
For N = 4
:
covergroup some_cg;
coverpoint idxs[0];
coverpoint idxs[1];
coverpoint idxs[2];
coverpoint idxs[3];
cross idxs[0], idxs[1], idxs[2], idxs[3] {
ignore_bins non_unique_idxs[] = some_func_that_computes_bins_for_4_idxs();
}
endgroup
For N = 8
:
covergroup some_cg;
coverpoint idx[0];
coverpoint idx[1];
coverpoint idx[2];
coverpoint idx[3];
coverpoint idx[4];
coverpoint idx[5];
coverpoint idx[6];
coverpoint idx[7];
cross idx[0], idx[1], idx[2], idx[3], idx[4], idx[5], idx[6], idx[7] {
ignore_bins non_unique_idxs[] = some_func_that_computes_bins_for_8_idxs();
}
endgroup
The two functions that generate the ignore bins each have to return different types (queue of struct
with 4/8 fields), even though conceptually the operation for computing all illegal combinations is similar, regardless of the value of N
. This could probably be solved with some clever use of the streaming operator, to stream the contents of the structs
into arrays of N
elements. The issue of redundancy in the covergroup
definitions remains though. The only way I can think of solving this is by generating the code.
Another idea would be to pack all indexes into a packed array of the appropriate size and cover that:
covergroup some_other_cg;
coverpoint packed_idxs {
ignore_bins non_unique_idxs[] = some_func_that_computes_bins_for_N_idxs();
}
endgroup
// Probably won't compile, but it would also be possible to use the streaming op
// Idea is to pack into integral type
foreach (idxs[i])
packed_idxs[i * idx_len +: len] = idxs[i];
It's a pain to debug coverage holes, as it's difficult to figure what transfer order a certain packed value belongs to, especially if the values are shown in decimal radix. I believe the way values are displayed varies from tool to tool and it's not possible to control this. I also don't see any possibility to give names to individual bins using strings.
I would welcome any input that would improve upon any of the two suggestions. The goal is to have one coverage wrapper class with a parameter for the number of transfers and to be able to instantiate that and get the coverage:
class transfer_orders_coverage #(int unsigned NUM_TRANSFERS);
// ...
endclass
Upvotes: 2
Views: 205
Reputation: 1992
I guess, the following solution may work in this case.
covergroup some_cg (int num);
4n_cp : coverpoint ({idxs[0], idxs[1], idxs[2], idxs[3]}) iff (num == 4)
{
option.weight = (num == 4) ? 1 : 0; // Weight might change depending on other coverpoints
ignore_bins non_unique_index[] = <Your Function>;
}
8n_cp : coverpoint ({idxs[0], idxs[1], idxs[2], idxs[3], idxs[4], idxs[5], idxs[6], idxs[7]}) iff (num == 8)
{
option.weight = (num == 8) ? 1 : 0; // Weight might change depending on other coverpoints
ignore_bins non_unique_index[] = <Your Function>;
}
endgroup
// Where you instantiate covergroups
some_cg c1 = new(4);
Let me know, how the above idea works.
Upvotes: 0
Reputation: 4381
Adding as and 'Answer' since it was too long for comment. Pardon me if I am not clear.
Can you add some logic before sampling CG and use some input argument that denotes array position of idxs
? For the crosses, you can maintain a packed array of size N and make individual bits 1
when a particular pattern is detected. At the end of sim, you can sample the coverage for that pattern in some different CG.
Basically the idea is to offload logic inside the covergroups and add logic surrounding sample
function. Here is a rough idea about what I was thinking.
class transfer_orders_coverage #(int unsigned N = 4);
int idxs[N];
bit [(N -1) : 0] pattern; // Make indexes HIGH according to sampled pattern
// ...
covergroup cross_cg;
mycp_cross: coverpoint pattern{
ignore_bins myignbn = {some_generic_function_for_N(pattern)};
}
endgroup
covergroup indiv_cg with function sample (int index);
mycp_indiv: coverpoint idxs[index]{
// some bins to be covered in ith position of idxs
}
endgroup
function new();
cross_cg = new;
indiv_cg = new;
endfunction
function bit [(N -1) : 0] some_generic_function_for_N(bit [(N -1) : 0] pattern);
// check which bits in "pattern" are to be covered and which are to be ignored
//return some_modified_pattern;
endfunction
function void start();
// Any logic for sampling ith position
foreach(idxs[i]) begin
indiv_cg.sample(i);
pattern[i] = 1'b1;
end
cross_cg.sample();
endfunction
endclass
module top();
transfer_orders_coverage #(4) tr;
initial begin
tr = new;
tr.start();
end
endmodule
Let me know if it seems feasible or not.
Upvotes: 1