SystemVerilog Constraint, Fixing value every nth iteration - system-verilog

class Base
rand bit b;
// constraint c1 { every 5th randomization should have b =0;}
endclass
I know I can make a static count variable and update that count variable and then, in constraint I can check if count%5 is zero, then make b=0, but is there a better way to do that? Thanks.

There's no need to make count static, just non-random.
class Base;
rand bit b;
int count;
constraint c1 { count%5 -> b==0;}
function post_randomize();
count++;
endfunction
endclass

If you know the upper limit of b, then you can write a constraint like follow.
constraint abc
{
b dist {0:=20, 1:=80}
}
This will make weight of 0 to 20 and, weight of 1 to 80. So in this way, 0 will occur once in every 5 randomization.

Related

System verilog constraint for uniform address pattern

I want to add a constraint on "rand bit [7:0] addr[10]" such that we have address generated in uniformly increasing order from 0th index to 5th and uniformly decreasing order from 6th to last index. I am new to SV constraints, can someone help with ways to do this.
class A;
rand bit [7:0] addr[10];
rand int step; // this needs to be an int to catch underflow/overflow
constraint uniform {
foreach (addr[index]) {
index inside {[0:4]} -> addr[index] + step == addr[index+1];
index inside {[6:8]} -> addr[index] - step == addr[index+1];
}
step inside {[1:25]};
}
endclass

Can someone explain this strange systemverilog constraint behavior?

I am having hard time understanding this behavior with randomization in vcs simulation. can someone please help me?
class c;
rand bit [31:0] base;
constraint base_c {
base +32'h40_0000 < 32'hFFFF_FFFF;
}
endclass
module m;
c c_obj = new();
initial begin
c_obj.randomize() with {
base == 'hffe6_f6e2;
};
end
endmodule
I am not getting constraint error. base is of type bit which is unsigned. If I change the constraint as follows
constraint base_c {
base < 32'hFFFF_FFFF - 32'h40_0000;
}
I get constraint error now. How does this work?
It's very simple to work out the math here. All you need to do is plug in the values.
In the first case, you have
32'hffe6_f6e2 + 32'h40_0000 < 32'hFFFF_FFFF
which reduces down to
32'h0026_f6e2 < 32'hFFFF_FFFF
1'b1 // True
But when you have
32'hffe6_f6e2 < 32'hFFFF_FFFF - 32'h40_0000
This reduces down to
32'hffe6_f6e2 < 32'hffbf_ffff
1'b0 // False
It's not clear from your question what you are expecting, but you need to take underflow and overflow into account when writing constraints.
I just wrote a related blog post on these kind of math issues here.

system verilog 2 dimensional dynamic array randomization

I am trying to use system verilog constraint solver to solve the following problem statement :
We have N balls each with unique weight and these balls need to be distributed into groups , such that weight of each group does not exceed a threshold ( MAX_WEIGHT) .
Now i want to find all such possible solutions . The code I wrote in SV is as follows :
`define NUM_BALLS 5
`define MAX_WEIGHT_BUCKET 100
class weight_distributor;
int ball_weight [`NUM_BALLS];
rand int unsigned solution_array[][];
constraint c_solve_bucket_problem
{
foreach(solution_array[i,j]) {
solution_array[i][j] inside {ball_weight};
//unique{solution_array[i][j]};
foreach(solution_array[ii,jj])
if(!((ii == i) & (j == jj))) solution_array[ii][jj] != solution_array[i][j];
}
foreach(solution_array[i,])
solution_array[i].sum() < `MAX_WEIGHT_BUCKET;
}
function new();
ball_weight = {10,20,30,40,50};
endfunction
function void post_randomize();
foreach(solution_array[i,j])
$display("solution_array[%0d][%0d] = %0d", i,j,solution_array[i][j]);
$display("solution_array size = %0d",solution_array.size);
endfunction
endclass
module top;
weight_distributor weight_distributor_o;
initial begin
weight_distributor_o = new();
void'(weight_distributor_o.randomize());
end
endmodule
The issue i am facing here is that i want the size of both the dimentions of the array to be randomly decided based on the constraint solution_array[i].sum() < `MAX_WEIGHT_BUCKET; . From my understanding of SV constraints i believe that the size of the array will be solved before value assignment to the array .
Moreover i also wanted to know if unique could be used for 2 dimentional dynamic array .
You can't use the random number generator (RNG) to enumerate all possible solutions of your problem. It's not built for this. An RNG can give you one of these solutions with each call to randomize(). It's not guaranteed, though, that it gives you a different solution with each call. Say you have 3 solutions, S0, S1, S2. The solver could give you S1, then S2, then S1 again, then S1, then S0, etc. If you know how many solutions there are, you can stop once you've seen them all. Generally, though, you don't know this beforehand.
What an RNG can do, however, is check whether a solution you provide is correct. If you loop over all possible solutions, you can filter out only the ones that are correct. In your case, you have N balls and up to N groups. You can start out by putting each ball into one group and trying if this is a correct solution. You can then put 2 balls into one group and all the other N - 2 into a groups of one. You can put two other balls into one group and all the others into groups of one. You can start putting 2 balls into one group, 2 other balls into one group and all the other N - 4 into groups of one. You can continue this until you put all N balls into the same group. I'm not really sure how you can easily enumerate all solutions. Combinatorics can help you here. At each step of the way you can check whether a certain ball arrangement satisfies the constraints:
// Array describing an arrangement of balls
// - the first dimension is the group
// - the second dimension is the index within the group
typedef unsigned int unsigned arrangement_t[][];
// Function that gives you the next arrangement to try out
function arrangement_t get_next_arrangement();
// ...
endfunction
arrangement = get_next_arrangement();
if (weight_distributor_o.randomize() with {
solution.size() == arrangement.size();
foreach (solution[i]) {
solution[i].size() == arrangement[i].size();
foreach (solution[i][j])
solution[i][j] == arrangement[i][j];
}
})
all_solutions.push_back(arrangement);
Now, let's look at weight_distributor. I'd recommend you write each requirement in an own constraint as this makes the code much more readable.
You can shorten you uniqueness constraint that you wrote as a double loop to using the unique operator:
class weight_distributor;
// ...
constraint unique_balls {
unique { solution_array };
}
endclass
You already had a constraint that each group can have at most MAX_WEIGHT in it:
class weight_distributor;
// ...
constraint max_weight_per_group {
foreach (solution_array[i])
solution_array[i].sum() < `MAX_WEIGHT_BUCKET;
}
endclass
Because of the way array sizes are solved, it's not possible to write constraints that will ensure that you can compute a valid solution using simple calls randomize(). You don't need this, though, if you want to check whether a solution is valid. This is due to the constraints on array sizes in the between arrangement and solution_array.
Try this.!
class ABC;
rand bit[3:0] md_array [][]; // Multidimansional Arrays with unknown size
constraint c_md_array {
// First assign the size of the first dimension of md_array
md_array.size() == 2;
// Then for each sub-array in the first dimension do the following:
foreach (md_array[i]) {
// Randomize size of the sub-array to a value within the range
md_array[i].size() inside {[1:5]};
// Iterate over the second dimension
foreach (md_array[i][j]) {
// Assign constraints for values to the second dimension
md_array[i][j] inside {[1:10]};
}
}
}
endclass
module tb;
initial begin
ABC abc = new;
abc.randomize();
$display ("md_array = %p", abc.md_array);
end
endmodule
https://www.chipverify.com/systemverilog/systemverilog-foreach-constraint

Parameterizable cross length

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
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.
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.

Overflow in SystemVerilog constraints

Integer types in SystemVerilog, as in most languages, wrap around on overflow. I was wondering if this is also true in constraints. For example:
class Test;
rand bit [3:0] a;
rand bit [3:0] b;
constraint c { a + b <= 4'h6; }
endclass;
When randomizing an object of this class, is it possible to get a solution where a == 7 and b == 12, which would satisfy the constraint since 7 + 12 = 19 which wraps around to 3, and 3 is less than 6?
If so, would it help to formulate the constraint as
constraint c { a + b <= 6; }
where 6 is a 32-bit signed int and the sum is forced to be calculated with 32-bit precision? (This of course is not a solution if the random variables are of type int)
You are correct. Expression evaluation is identical whether you are inside a constraint or not. In addition to overflow, you also need to be concerned about truncation and sign conversion. Integral expressions are weakly typed in SystemVerilog.