SystemVerilog: fork-join and disable fork - IKSciting
1599
post-template-default,single,single-post,postid-1599,single-format-standard,bridge-core-2.8.7,qodef-qi--no-touch,qi-addons-for-elementor-1.7.1,qode-page-transition-enabled,ajax_fade,page_not_loaded,,qode-title-hidden,qode_grid_1300,footer_responsive_adv,qode-content-sidebar-responsive,qode-theme-ver-27.1,qode-theme-bridge,qode_header_in_grid,wpb-js-composer js-comp-ver-6.6.0,vc_responsive,elementor-default,elementor-kit-838

fork-join and disable fork

BFM(Bus Functional Model), scoreboard 등 testbench를 개발하면서 두 개 이상의 process를 동시에 실행하도록 구성해야 하는 경우가 있다. 간단한 예로, 특정 task를 수행하되 일정 시간이 지나면 timeout이 발생하도록 구현하는 경우 두 개의 process가 필요하다. 이 때 사용할 수 있는 것이 SystemVerilog의 forkjoin이다. 이와 더불어 join_anyjoin_none 그리고 disable fork에 대해서도 설명한다.

fork-join

forkjoin 내부에 기술된 각 구문은 별개의 process로 동시에 시작된다. 다음 예는 총 5개의 process가 동시에 시작된다. 이 때 ‘hello’와 ‘world’ 중 어떤 것이 먼저 출력되는지는 알 수 없다. 하지만 ‘foo’가 ‘bar’보다 먼저 출력되는 것은 확실하다.

fork
  // process 1
  $display("hello");
  // process 2
  $display("world");
  // process 3
  my_task();
  // process 4
  begin
    $display("foo"); 
    $display("bar");
  end
  // process 5
  repeat (5) begin
    #1 my_function();
  end
join

forkjoin 구문 내부에 기술된 모든 구문이 종료되어야 join 이후에 기술된 구문이 수행된다.

fork
  #5  $display("@%2t: A", $realtime);
  #10 $display("@%2t: B", $realtime);
join

$display("@%2t: C", $realtime);
@ 5: A
@10: B
@10: C
SystemVerilog fork join

fork-join_any

forkjoin_anyforkjoin과 다른 점은 내부에 기술된 구문 중 어느 하나라도 종료되면 join_any 이후에 기술된 구문이 수행된다는 점이다.

fork
  #5  $display("@%2t: A", $realtime);
  #10 $display("@%2t: B", $realtime);
join_any

$display("@%2t: C", $realtime);
@ 5: A
@ 5: C
@10: B
SystemVerilog fork join_any

fork-join_none

forkjoin_noneforkjoin과 다른 점은 내부에 기술된 구문의 종료 여부와 관련 없이 바로 join_none 이후에 기술된 구문이 수행된다는 점이다. fork-join_none Gotcha 페이지도 참고하자.

fork
  #5  $display("@%2t: A", $realtime);
  #10 $display("@%2t: B", $realtime);
join_none

$display("@%2t: C", $realtime);
@ 0: C
@ 5: A
@10: B
SystemVerilog fork join_none

disable fork

Process를 강제로 종료해야 할 때는 disable fork 구문을 사용한다. disable fork 사용 시에는 주의가 필요하므로 Safe Use of disable fork 페이지를 참고하자. 먼저 위의 forkjoin_any 예제에 disable fork를 추가하면 다음과 같은 결과를 얻을 수 있다. join_any를 사용하였기 때문에 ‘A’를 출력하는 process가 종료되는 시점에 disable fork가 실행되었고, ‘B’를 출력하는 process는 강제 종료되어 ‘B’가 출력되지 않는 것을 확인할 수 있다.

fork
  #5  $display("@%2t: A", $realtime);
  #10 $display("@%2t: B", $realtime);
join_any
disable fork;

$display("@%2t: C", $realtime);
@ 5: A
@ 5: C

Summary

joinjoin_anyjoin_none의 차이와 정확한 동작을 이해하고 사용하면 다양한 상황에서 매우 유용하게 활용할 수 있는 문법이지만, 그렇지 않을 경우 오히려 큰 혼란을 야기할 수 있으므로 개념을 확실히 잡고 있는 것이 좋다. 마지막으로 forkjoin과 disable fork를 활용하여 구현한 timeout 기능을 소개한다.

module top;
  
  timeunit 1ns;
  timeprecision 1ns;

  bit timeout = 0;

  initial begin
    fork
      my_task();
      #100 timeout = 1;
    join_any
    disable fork;

    if (timeout) begin
      $display("timeout");
      $finish;
    end
  end

  task my_task();
    #1000; // takes longer than #100
  endtask: my_task

endmodule: top

References

3 Comments
  • Unknown
    Posted at 23:37h, 13 September

    fork 를 통해 생성된 ‘그것’ 들을 process 로 봐야할까요 아니면 thread 로 봐야할까요..

    • IKS
      Posted at 23:38h, 13 September

      제가 위 포스트에서는 전반적으로 process라는 용어를 사용했지만 thread로 대체해도 무방합니다.
      일반적인 OS 개념에서는 process는 독립적인 메모리 영역을 할당 받고 thread는 메모리 영역을 공유하는 등의 차이가 있으며 하나의 process가 하나 이상의 thread를 가질 수 있습니다만, SystemVerilog LRM 상에서는 fork-join block을 기술할 때 process와 thread를 모두 혼용하여 사용하고 있으며 크게 차이를 두고 있지는 않습니다.
      간단히 말해서 fork 수행 시 여러 process가 생성되고 각 process 내에서 thread가 실행된다고 생각하시면 될 것 같네요.

  • IKS
    Posted at 07:38h, 23 June

    Comment disabled due to spam comments.