SystemVerilogのrandom constraint中のfunctionが思ってたのと違った

SystemVerilogではclassの変数にrandを指定すると、randomize()でランダムに値を設定する事ができる。その際にconstraintで生成する値に制約を与える事ができる。例えば
```verilog:ランダム制約の例
  rand int a,b,c;
  constraint exammple_const { c > a + b;}
```
みたいに。
そしてそのランダム制約内にfunctionを使う事ができる。
```verilog:ランダム制約内で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](https://ieeexplore.ieee.org/document/6469140), 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:
> ~~~verilog
> 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)。

```verilog:TB
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
```

```makefile:Makefile
VIVADO_DIR := /tools/Xilinx/Vivado/2022.2/bin

run :
	$(VIVADO_DIR)/xvlog -sv tb.sv
	$(VIVADO_DIR)/xelab tb -R
```

```text:log
> 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を使う意味がない。
いくつかの記事では
```verilog
  constraint assign_const {c == calc(a,b);}
```
の様にfunctionの戻り値を他の変数に代入する所で使っている例を紹介していた。しかしこれはあまり意味がない。だったらわざわざ制約で書かず代入で書けばよい。randomize()で値を確定させたければpost_randomize()の中で代入すればよい。
```verilog
  function post_randomize();
    super.post_randomize();
    c = calc(a,b);
  endfunction
```
やはり、引数も同時にその制約を受けてrandomizeもできないのに、わざわざconstraint中にfunctionを使用できる様にした意味が分からない。

***
参考記事

https://www.amiq.com/consulting/2015/03/12/gotcha-function-calls-in-systemverilog-constraints/

https://verificationguide.com/systemverilog/systemverilog-functions-in-constraints/

https://vlsiverify.com/system-verilog/function-in-constraint