for文で並列forkプロセスを複数個実行する

SystemVerilogでは`for`文を使って`fork`の並列プロセスを複数実行する事ができる。その際、`for`のindex変数を`fork`内に直接渡すのではなく、`automatic`で定義した変数に一旦渡す。

```verilog:automatic変数を介する
module for_fork_test();
    initial begin
        int val[] = '{0,1,2,3,4,5};

        foreach(val[i])
            fork
                automatic int auto_i = i; // <====== This is necessary!!!!
                begin
                    #(10*auto_i);
                    $display("time %2t: process %0d finsihed, val = %0d", $time, auto_i, val[auto_i]);
                end
            join_none

        wait fork;

    end
endmodule
```

```:結果
 time  0: process 0 finished, val = 0
 time 10: process 1 finished, val = 1
 time 20: process 2 finished, val = 2
 time 30: process 3 finished, val = 3
 time 40: process 4 finished, val = 4
 time 50: process 5 finished, val = 5
```

`automatic`の変数を介さず直接渡すと。。。

```verilog:直接渡す
module for_fork_test();
    initial begin
        int val[] = '{0,1,2,3,4,5,6};

        for(int i=0; i<6; i++)
            fork
                begin
                    #(10*i);
                    $display("time %2t, process %0d : val = %0d", $time, i, val[i]);
                end
            join_none

        wait fork;

    end
endmodule
```

```:結果
 time 60: process 6 finished, val = 6
 time 60: process 6 finished, val = 6
 time 60: process 6 finished, val = 6
 time 60: process 6 finished, val = 6
 time 60: process 6 finished, val = 6
 time 60: process 6 finished, val = 6
```
と、この様に`fork`の各プロセスの実行が`for`文が回り終わって`i=6`となった後にスケジュールされる為、その時点での`i=6`が参照されて、みんな同じ実行をしてしまう。

`fork/join_none`でプロセス分岐した後、`wait fork`で全ての`fork`プロセスの終了を待つようにしているが、これは少し注意が必要。`wait fork`は同一のプロセス或いはそこから`fork`で分岐した子プロセス以下すべての`fork`プロセスの終了を待つ。したがって、以下の様に他に`fork`がある場合にはそのプロセスの終了も待ってしまう。

```verilog:他のforkプロセスも待ってしまう
module for_fork_test();
    initial begin
        int val[] = '{0,1,2,3,4,5};

        fork : first_fork
            forever #1000 $display("Hello!!");
        join_none

        //---------------------------------
        foreach(val[i])
            fork : second_fork
                automatic int auto_i = i;
                begin
                    #(10*auto_i);
                    $display("time %2t: process %0d finsihed, val = %0d", $time, auto_i, val[auto_i]);
                end
            join_none

        wait fork;  // <==== wait for both end of first_fork and second_fork!!!!
        //---------------------------------
    
    end
endmodule
```

この様に`foreach`文で分岐させた`second_fork`のラベルを付けた`fork/join_none`プロセスの終了を待ちたいのに、`first_fork`のラベルを付けた別の`fork/join_none`プロセスの終了も待ってしまう。この例の様にそのプロセスがずっと終わらないプロセスだと`wait_for`の先に進まない。
この様な場合どうすればよいかと言うと、`wait fork`とそれで待ちたい`fork`のみを別途ダミーの`fork/join`で囲って、ひとまとまりのプロセスとして区別し、`wait fork`が効く範囲を限定する。`wait fork`が効く範囲は前述のとおり、同一プロセスとその子プロセス以下なので。

```verilog:second_forkのみを待つ
module for_fork_test();
    initial begin
        int val[] = '{0,1,2,3,4,5};

        fork : first_fork
            forever #1000 $display("Hello!!");
        join_none

        fork : guard_fork
            begin //---------------------------------
            foreach(val[i])
                fork : second_fork
                    automatic int auto_i = i;
                    begin
                        #(10*auto_i);
                        $display("time %2t: process %0d finsihed, val = %0d", $time, auto_i, val[auto_i]);
                    end
                join_none

            wait fork;  // <==== wait for end of second_fork
            end   //---------------------------------
        join

    end
endmodule
```

以下参考

https://stackoverflow.com/questions/68911060/wait-for-only-some-threads-to-complete-after-fork-join-none-in-systemverilog?answertab=createdasc#tab-top