🤔

Verilog/SystemVerilogでのbegin-end,fork-joinの違い

に公開

VerilogとSystemVerilogの間にはbegin-end (sequential block) と fork-join (parallel block) に文法上の違いがあります。SystemVerilog では追加仕様があり、より柔軟な記述が可能です。

無名ブロックでのスコープ生成

Verilog では 名前付き の begin-end / fork-join ブロックのみがスコープを生成します。
そのため、無名ブロックに変数を宣言するとコンパイルエラーになります。

// 名前付きのbegin-end,fork-joinブロックのみ、
// モジュールやタスク内部の階層的なスコープとして機能します。
// これにより、ブロック内で宣言されたローカル変数を一意に識別できます。

module verilog_example;
  initial begin
    integer i; // OK: 宣言可能
    begin : blk1
      integer j; // OK: 名前付きブロックは新しいスコープを持つ
    end

    begin
      integer k; // ERROR: 無名ブロックでは新しいスコープを作らない
    end
  end
endmodule

SystemVerilog では 無名ブロックでも変数宣言を含む場合、新しいスコープが作られます。

// 名前の付けられていないbegin-end/fork-jonブロックに対しても、
// そのブロックが直接、変数宣言や型宣言などのブロックアイテム宣言を含んでいる場合にのみ、
// 新しい階層スコープを作成します。
// このスコープ内で宣言された要素は外部から参照できる階層名をもたないため、
// ブロック外部から参照できません。

module sv_example;
  initial begin
    begin
      int a = 10; // OK: 無名ブロック内でもスコープが生成される
      $display("a = %0d", a);
    end

    // 外部から a にアクセスできない (スコープが切れる)
    // $display("a = %0d", a); // コンパイルエラー
  end
endmodule

名前つきブロックのブロック後名前記述

SystemVerilog では、名前付きブロックにおいて ブロックの終了部分にも名前を記述できます。
これにより begin-end, fork-join の対応を明示でき、ミスを防ぐことが可能です。

module block_name_check;
  initial begin : my_block
    int x = 42;
    $display("x = %0d", x);
  end : my_block // ← ブロック名を再度書ける
endmodule

join_any, join_none の追加

Verilog では fork-joinブロックは常にjoinキーワードで終了し、親プロセスはブロック内のすべての並列プロセスが完了するまで実行をブロックしました。

module verilog_fork;
  initial begin
    fork
      #10 $display("Task A done");
      #20 $display("Task B done");
    join
    $display("All tasks finished");
  end
endmodule
出力例:
#10: Task A done
#20: Task B done
#20: All tasks finished

SystemVerilogでfork-joinブロックに類する新しいブロック終了キーワードのjoin_anyとjoin_noneが追加されました。

fork-join_any

親プロセスは、fork-join_anyブロック内で生成された並列プロセスのいずれか1つが完了した時点で、自身の実行を再開します。

module sv_fork_any;
  initial begin
    fork
      #10 $display("Task A done");
      #20 $display("Task B done");
    join_any
    $display("At least one task finished");
  end
endmodule
出力例:

#10: Task A done
#10: At least one task finished
#20: Task B done

fork-join_none

親プロセスは、fork-join_noneブロック内で生成されたすべての並列プロセスと並行して実行を続行します。

module sv_fork_none;
  initial begin
    fork
      #10 $display("Task A done");
      #20 $display("Task B done");
    join_none
    $display("Parent continues immediately");
    #30 $display("Simulation end");
  end
endmodule
出力例:

#0 : Parent continues immediately
#10: Task A done
#20: Task B done
#30: Simulation end

Discussion