SystemVerilogではclassの変数にrandを指定すると、randomize()でランダムに値を設定する事ができる。その際にconstraintで生成する値に制約を与える事ができる。例えば
rand int a,b,c;
constraint exammple_const { c > a + b;}
みたいに。
そしてそのランダム制約内にfunctionを使う事ができる。
rand int a,b,c;
constraint exammple_const { c > add(a,b);}
function int add(a,b);
add = a + b;
endfunction
みたいに。
なのでこれらの記述は等価だと思ってた。
『constraint内にfunctionを使っていてその引数がrandだった場合、その引数もその制約をうけて同時にrandomizeされる。』
だと思っていた。けど違った。
正しくは、
『constraint内にfunctionを使っていてその引数がrandだった場合、その引数は先にrandomizeされて、functionの戻り値はその制約では定数として扱われる。』
だった。
IEEE 1800-2012 IEE Standard for SystemVerilog, 18.5.12 Functions in constraintsにも以下の記述があった。
— Functions shall be called before constraints are solved, and their return values shall be treated as state variables.
— Random variables used as function arguments shall establish an implicit variable ordering or priority. Constraints that include only variables with higher priority are solved before other, lower priority constraints. Random variables solved as part of a higher priority set of constraints become state variables to the remaining set of constraints. For example:class B; rand int x, y; constraint C { x <= F(y); } constraint D { y inside { 2, 4, 8 } ; } endclass
forces y to be solved before x. Thus, constraint D is solved separately before constraint C, which uses the values of y and F(y) as state variables.
以下のコードを実際に実行してみた。使用したシミュレータはVivado Simulator v2022.2 (xsim)。
module tb;
class c_oneline;
rand int a,b,c;
constraint base_const {
a > 0;
b > 0;
c > 0; c < 100;
}
constraint rel_const { c > a + b; }
int fail;
function void print();
$display("a = %2d, b = %2d, c = %2d", a, b, c);
endfunction
endclass
class c_function extends c_oneline;
constraint rel_const { c > add(a,b); }
function int add(a,b);
add = a + b;
endfunction
endclass
initial begin
int n = 10;
begin
c_oneline c = new();
repeat(n) begin
if(!c.randomize()) c.fail++;
c.print();
end
$display("##### One Line Randomization Failed Count : %0d", c.fail);
end
begin
c_function c = new();
repeat(n) begin
if(!c.randomize()) c.fail++;
c.print();
end
$display("##### Function Randomization Failed Count : %0d", c.fail);
end
end
endmodule
VIVADO_DIR := /tools/Xilinx/Vivado/2022.2/bin
run :
$(VIVADO_DIR)/xvlog -sv tb.sv
$(VIVADO_DIR)/xelab tb -R
> make run
.
.
.
a = 1, b = 96, c = 98
a = 1, b = 1, c = 5
a = 10, b = 37, c = 70
a = 23, b = 53, c = 83
a = 43, b = 18, c = 83
a = 90, b = 1, c = 99
a = 22, b = 24, c = 61
a = 50, b = 3, c = 97
a = 25, b = 55, c = 86
a = 46, b = 18, c = 80
##### One Line Randomization Failed Count : 0
a = 1060900488, b = 1686341031, c = 41
a = 1289983730, b = 1446889115, c = 49
a = 261038675, b = 1633129134, c = 86
a = 1298098478, b = 1812452994, c = 52
a = 343390342, b = 79075089, c = 66
a = 398859431, b = 1863439544, c = 7
a = 1778677638, b = 144273060, c = 86
a = 112762956, b = 359252750, c = 29
a = 37869716, b = 908894402, c = 40
a = 1453237526, b = 116372384, c = 77
##### Function Randomization Failed Count : 0
exit
と
constraint rel_const { c > a + b; }
の場合には変数a,b,cの全てのランダム生成に対してこの制約が有効になっている。
それに対して
constraint rel_const { c > add(a,b; }
では変数a,bが先にrandomizeされた後、そのa,bに対してのadd(a,b)の値が、cのみのランダム生成の制約として使われている。
因みにVivado Simulator v2022.2ではrandomize()の成否が戻り値としてきちんと戻されていない様で
if(!c.randomize()) c.fail++;
の部分でrandomize()が失敗した場合でもc.fail++が実行されていない。通常のちゃんとしたお高いシミュレータでは、randomize()が成功した場合には1を失敗した場合には0を戻り値として返す。
今回のこの例のadd()の様な簡単な式なら別にfunction使わずに直接制約に書けば問題ないのだが、もっと複雑な式だった場合には一体どうするんだろう。
そして一体何のためにconstraint中にfunctionを使える様にしたのだろうか。複雑な式の制約にfunctionが使えないのであればconstraint中にfunctionを使う意味がない。
いくつかの記事では
constraint assign_const {c == calc(a,b);}
の様にfunctionの戻り値を他の変数に代入する所で使っている例を紹介していた。しかしこれはあまり意味がない。だったらわざわざ制約で書かず代入で書けばよい。randomize()で値を確定させたければpost_randomize()の中で代入すればよい。
function post_randomize();
super.post_randomize();
c = calc(a,b);
endfunction
やはり、引数も同時にその制約を受けてrandomizeもできないのに、わざわざconstraint中にfunctionを使用できる様にした意味が分からない。
参考記事