Hi, today I am going to write about how to write different constraints in SystemVerilog to achieve the same goal, to randomize the number of ones in a variable.
- Using built-in $countones function
- Using a function call
- Adding every bit up manually
- Using a helper array
This is the most intuitive way for me. You can simply just call the built-in function, and SystemVerilog will take care of everything else for you.
The function $countbits counts the number of bits that have value 1 in a bit vector. A more detailed introduction to $countones is described in RFM 20.9.
class c1;
rand bit [7:0] w;
constraint c1 {
$countones(w) == 3; //call the built-in function
}
endclass
c1 c1_h;
initial begin
c1_h = new();
repeat(3) begin
if(c1_h.randomize())
$display("w is %b", c1_h.w);
end
end
endprogram
For constraints, we can always try to build a function to implement more complicated features. So, we are going to build a function outside the constraint, and call it from the constraint.
Since different vendors may interpret code differently. I tried this on Synopsys VCS, and it didn’t work. It worked seamlessly on Cadence Incisive.
class c1;
rand bit [7:0] w;
rand int length;
constraint c1 {
count_ones(w) == length;
}
constraint c2 {
length == 3;
}
//calculate the set bits in a vector
function int count_ones (bit [7:0] data_in);
//using for loop to do the bit masking
for (; data_in !=0; data_in = data_in>>1) begin
count_ones +=data_in&1'b1;
end
endfunction
endclass
c1 c1_h;
initial begin
c1_h = new();
repeat(3) begin
if(c1_h.randomize())
$display("w is %b", c1_h.w);
end
end
endprogram
This is the least efficient way, and it is going to be really hard for code reuse. I just write it here as a supplement method.
We need to unroll all bits manually using bitmasks. This requires us to know the width of the variable, also write the code for each bit.
class c1;
rand bit [7:0] w;
//constraint to add every bit up manually
constraint c1 {
(((w>>7)&1'b1)+((w>>6)&1'b1)+((w>>5)&1'b1)+((w>>4)&1'b1)+
((w>>3)&1'b1)+((w>>2)&1'b1)+((w>>1)&1'b1)+((w)&1'b1)) == 3;
}
endclass
c1 c1_h;
initial begin
c1_h = new();
repeat(3) begin
if(c1_h.randomize())
$display("w is %b", c1_h.w);
end
end
endprogram
We can also approach this problem by adding a helper array. We can create a dynamic array. The size of the array is equal to the number of ones we want to set. The values in the array should be chosen from the bits of the variable that we want to set to 1 and they should be unique. In the post_randomize function, we are going to map each integer in the dynamic array to the corresponding bit in the variable.
class c1;
bit [7:0] w; //not rand anymore
rand int unsigned bitset[];
//set the dynamic array size to the number of bits we want to set
constraint c1 {
bitset.size() == 3;
}
//The values should be chosen from the indexes
constraint c2 {
foreach(bitset[i])
bitset[i] inside {[0:$bits(w)-1]}; //This equals to bitset[i] inside {[0:7]};
}
//They are different from each other
constraint c3 {
unique {bitset};
}
//map the index to the variable
function void post_randomize();
w = 0;
foreach(bitset[i]) begin
w[bitset[i]] = 1'b1;
end
endfunction
endclass
c1 c1_h;
initial begin
c1_h = new();
repeat(3) begin
if(c1_h.randomize())
$display("w is %b", c1_h.w);
end
end
endprogram
Thanks for reading. Those are all the ways I can think of for now. Leave me a comment below if you can come up with another way to do this.