LoginSignup
8
3

More than 5 years have passed since last update.

fork joinとその仲間たち

Last updated at Posted at 2018-09-25

本記事は以下の3部構成の一部です。

  1. fork joinとその仲間たち
  2. Forkしたプロセスの制御 - disable fork
  3. Forkしたプロセスの制御 - processクラス

verilogには複数のプロセスを同時に実行する機能としてfork joinがありますが、System Verilogでは拡張され、fork join_anyとfork join_noneが仲間に加わりました。どちらも便利な機能で検証で頻繁に使用されますので紹介したいと思います。
全て複数のプロセスを同時に実行するというforkの部分は同じなのですが、どのタイミングで次のステートメントの実行に移るかのjoinの部分の振る舞いが異なります。

fork join

最初にverilog時代からあったfork joinの復習です。fork joinの中のプロセス(begin endで囲ったブロック)が並列に実行されます。全てのプロセスの実行が終了するとjoinの次のステートメントに実行が移ります。
以下がサンプルプログラムと実行結果です。forkしたプロセスが全て終了した20nsでjoinの次のステートメントが実行されています。

`timescale 1ns/1ns

program top();
  initial begin
    $timeformat(-9, 0, " ns", 6);
    fork
      begin
        #10ns $display("Process A Finished at %t", $realtime());
      end
      begin
        #20ns $display("Process B Finished at %t", $realtime());
      end
    join
    $display("Current time is %t", $realtime());
  end
endprogram
# KERNEL: Process A Finished at  10 ns
# KERNEL: Process B Finished at  20 ns
# KERNEL: Current time is  20 ns
# RUNTIME: Info: RUNTIME_0068 $finish called.

用途としてはLRMにもあるように、順番の不定な複数のイベントの終了を待つのに使用できます。以下の例ではevent_aとevent_b両方の発生後に次に進みます (本例のようにプロセスの単位が1ステートメントの場合はbegin endで囲う必要はありません)。


fork
  @event_a;
  @event_b;
join
$display("Both evant_a and event_b have occurred.");

fork join_any

join_anyでは同時実行されたプロセスのうちどれか一つでも終了したら次のステートメントの実行に移ります。終了していないプロセスはそのまま実行が継続されます。
以下がサンプルプログラムと実行結果です。Process Aの終了時間である10nsでjoin_anyの次のステートメントが実行されていることがわかります。

`timescale 1ns/1ns

program top();
  initial begin
    $timeformat(-9, 0, " ns", 6);
    fork
      begin
        #10ns $display("Process A Finished at %t", $realtime());
      end
      begin
        #20ns $display("Process B Finished at %t", $realtime());
      end
    join_any
    $display("Current time is %t", $realtime());
    #15ns;
    $display("Current time is %t", $realtime());
  end
endprogram
# KERNEL: Process A Finished at  10 ns
# KERNEL: Current time is  10 ns
# KERNEL: Process B Finished at  20 ns
# KERNEL: Current time is  25 ns
# RUNTIME: Info: RUNTIME_0068 $finish called.

用途としてはステータスポーリングのタイムアウトが挙げられます。下記例では1 usをタイムアウトとし、REGから非0の値が読めるまでデータを読み出し続けます。例えREGS.REG.read()がハングして値を返さなくても、確実にタイムアウトが可能です。

bit is_timeout = 1'b0;

fork
  begin
    int v;
    // Keep reading until non-zero is read from the register
    do (REGS.REG.read(v))
      ;
    while (!v);
  end
  #1us is_timeout = 1'b1;
join_any
disable fork;
if (is_timeout) begin
  `uvm_error("Timeout during reading REG");
end

fork join_none

join_noneではプロセスをforkしたら即座に次のステートメントの実行に移ります。
以下がサンプルプログラムと実行結果です。fork直後にjoin_noneの次のステートメントが実行されていることがわかります。

`timescale 1ns/1ns

program top();
  initial begin
    $timeformat(-9, 0, " ns", 6);
    fork
      begin
        #10ns $display("Process A Finished at %t", $realtime());
      end
      begin
        #20ns $display("Process B Finished at %t", $realtime());
      end
    join_none
    $display("Current time is %t", $realtime());
    #35ns;
    $display("Current time is %t", $realtime());
  end
endprogram
# KERNEL: Current time is   0 ns
# KERNEL: Process A Finished at  10 ns
# KERNEL: Process B Finished at  20 ns
# KERNEL: Current time is  35 ns
# RUNTIME: Info: RUNTIME_0068 $finish called.

本機能は検証環境内でDUTと信号レベルでやりとりをする部分に使用することが多いです。
例えばDUTの信号のモニタなどに使用できます。下記のようにfork join_noneで独立プロセスとしてモニタを実行し、後はDUTからのイベントドリブンでステートメントを実行します。

  fork
    forever begin
      @vif.signal_a;
      $display("signal_a value changed %x",@vif.signal_a);
    end
  join_none

参考文献

"9.3.2 Parallel blocks". 1800-2017 - IEEE Standard for SystemVerilog--Unified Hardware Design, Specification, and Verification Language. pp.210-212.

8
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
3